Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/api/src/models/broadcast/broadcast-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {MessageType} from './message-type';

/**
* BroadcastMessage represents a message that can be broadcasted to all browser contexts.
* No methods should be added here, since the broadcast API uses structured cloning to transfer messages,
* which removes any methods from the message.
*/
export class BroadcastMessage {
readonly type: MessageType;
readonly params?: unknown;

constructor(type: MessageType, params?: unknown) {
this.type = type;
this.params = params;
}
}
2 changes: 2 additions & 0 deletions packages/api/src/models/broadcast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export {MessageType} from './message-type';
export {BroadcastMessage} from './broadcast-message';
3 changes: 3 additions & 0 deletions packages/api/src/models/broadcast/message-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum MessageType {
REPOSITORIES_UPDATED = 'repositoriesUpdated',
}
2 changes: 2 additions & 0 deletions packages/api/src/ontotext-workbench-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './models/plugins';
export * from './models/configuration';
export * from './models/notification';
export * from './models/translation';
export * from './models/broadcast';

// Export providers for external usages.
export * from './providers';
Expand Down Expand Up @@ -48,6 +49,7 @@ export * from './services/plugins';
export * from './services/configuration';
export * from './services/logging';
export * from './services/notification';
export * from './services/broadcast';

// Export utils for external usages.
export * from './services/utils';
Expand Down
43 changes: 43 additions & 0 deletions packages/api/src/services/broadcast/broadcast.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {BroadcastMessage} from '../../models/broadcast';

type MessageHandler = (message: BroadcastMessage) => void;

/**
* A service that facilitates communication between different browser contexts
* (such as tabs, windows, or iframes) of the same origin.
* It uses the `BroadcastChannel` API to send and receive messages.
*/
export class BroadcastService {
private readonly CHANNEL_NAME = 'ontotext-workbench-broadcast';
private readonly MESSAGE_EVENT = 'message';

private channel?: BroadcastChannel;

/**
* Sends a message to all listening browser contexts on the same origin.
* @param {BroadcastMessage} message The message to be sent.
*/
sendMessage(message: BroadcastMessage): void {
this.getChannel().postMessage(message);
}

/**
* Subscribes to incoming messages.
* @param {MessageHandler} callback The function to be executed when a message is received.
* @returns {() => void} An unsubscribe function.
*/
onMessageReceived(callback: MessageHandler): () => void {
const channel = this.getChannel();
const listener = (event: MessageEvent) => callback(event.data);

channel.addEventListener(this.MESSAGE_EVENT, listener);
return () => channel.removeEventListener(this.MESSAGE_EVENT, listener);
}

private getChannel(): BroadcastChannel {
if (!this.channel) {
this.channel = new BroadcastChannel(this.CHANNEL_NAME);
}

Check warning on line 40 in packages/api/src/services/broadcast/broadcast.service.ts

View check run for this annotation

SonarQubeCloud / [Shared-components] SonarCloud Code Analysis

Prefer using nullish coalescing operator (`??=`) instead of an assignment expression, as it is simpler to read.

See more on https://sonarcloud.io/project/issues?id=Ontotext-AD_WB_migration_workbench&issues=AZrF55m-KQ2WkMd_qyFL&open=AZrF55m-KQ2WkMd_qyFL&pullRequest=2522
return this.channel;
}
}
1 change: 1 addition & 0 deletions packages/api/src/services/broadcast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {BroadcastService} from './broadcast.service';
31 changes: 20 additions & 11 deletions packages/legacy-workbench/src/js/angular/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ import {
EventService,
RepositoryContextService,
RepositoryService,
ServiceProvider,
service,
SecurityContextService,
BroadcastService,
MessageType,
} from "@ontotext/workbench-api";
import {EventConstants} from "./utils/event-constants";
import {CookieConsent} from "./models/cookie-policy/cookie-consent";
Expand Down Expand Up @@ -110,14 +112,14 @@ function homeCtrl($scope,
// Public functions
// =========================
$scope.getActiveRepositorySize = () => {
const repo = ServiceProvider.get(RepositoryContextService).getSelectedRepository();
const repo = service(RepositoryContextService).getSelectedRepository();

if (!repo) {
return;
}
$scope.activeRepositorySizeError = undefined;

ServiceProvider.get(RepositoryService).getRepositorySizeInfo(repo)
service(RepositoryService).getRepositorySizeInfo(repo)
.then(function(repositorySizeInfo) {
$scope.activeRepositorySize = repositorySizeInfo;
})
Expand All @@ -138,7 +140,7 @@ function homeCtrl($scope,
const subscriptions = [];

const onSelectedRepositoryUpdated = (repository) => {
const authService = ServiceProvider.get(AuthenticationService);
const authService = service(AuthenticationService);
const hasGqlRights = authService.hasGqlRights(repository);

// Don't call API, if no repo ID, or with GQL read-only or write rights
Expand All @@ -161,7 +163,7 @@ function homeCtrl($scope,
$scope.isAutocompleteEnabled = autocompleteEnabled;
};

subscriptions.push(ServiceProvider.get(RepositoryContextService).onSelectedRepositoryChanged(onSelectedRepositoryUpdated));
subscriptions.push(service(RepositoryContextService).onSelectedRepositoryChanged(onSelectedRepositoryUpdated));
subscriptions.push(WorkbenchContextService.onAutocompleteEnabledUpdated(onAutocompleteEnabledUpdated));

$scope.$on('$destroy', () => subscriptions.forEach((subscription) => subscription()));
Expand Down Expand Up @@ -209,7 +211,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos

// Update page permissions based on menu item role
const updatePermissions = () => {
const securityContextService = ServiceProvider.get(SecurityContextService);
const securityContextService = service(SecurityContextService);
const restrictedPages = securityContextService.getRestrictedPages();

const isAdministratorUser = $jwtAuth.isAdmin();
Expand Down Expand Up @@ -616,9 +618,9 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos
});
}

ServiceProvider.get(EventService).subscribe(EventName.LOGOUT, () => logout());
service(EventService).subscribe(EventName.LOGOUT, () => logout());

ServiceProvider.get(EventService).subscribe(EventConstants.RDF_SEARCH_ICON_CLICKED, () => {
service(EventService).subscribe(EventConstants.RDF_SEARCH_ICON_CLICKED, () => {
$rootScope.$broadcast('rdfResourceSearchExpanded');
});

Expand Down Expand Up @@ -658,7 +660,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos

const localStoreChangeHandler = (localStoreEvent) => {
if (authEvents.includes(localStoreEvent.key)) {
const newAuthenticationState = ServiceProvider.get(AuthenticationStorageService).isAuthenticated();
const newAuthenticationState = service(AuthenticationStorageService).isAuthenticated();
$jwtAuth.updateReturnUrl();
if (isAuthenticated !== newAuthenticationState) {
isAuthenticated = newAuthenticationState;
Expand Down Expand Up @@ -1150,11 +1152,11 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos
// properties.
let onSelectedRepositoryChangedSubscription;
// subscribe to repository changes, once we are certain they are loaded
const onAppDataLoaded = ServiceProvider.get(ApplicationLifecycleContextService).onApplicationDataStateChanged((dataLoaded) => {
const onAppDataLoaded = service(ApplicationLifecycleContextService).onApplicationDataStateChanged((dataLoaded) => {
if (dataLoaded) {
// unsubscribe of any previous subscription
onSelectedRepositoryChangedSubscription?.();
onSelectedRepositoryChangedSubscription = ServiceProvider.get(RepositoryContextService)
onSelectedRepositoryChangedSubscription = service(RepositoryContextService)
.onSelectedRepositoryChanged((repository) => {
$repositories.onRepositorySet(repository);
});
Expand All @@ -1168,9 +1170,16 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos
});
};

const broadcastUnsubscribe = service(BroadcastService).onMessageReceived((message) => {
if (message.type === MessageType.REPOSITORIES_UPDATED) {
$repositories.init();
}
});

$scope.$on('$destroy', () => {
onSelectedRepositoryChangedSubscription?.();
cookieConsentChangedSubscription?.();
broadcastUnsubscribe?.();
onAppDataLoaded();
document.removeEventListener('click', closeActiveRepoPopoverEventHandler);
window.removeEventListener('storage', localStoreChangeHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
RepositoryLocationContextService,
MapperProvider,
RepositoryListMapper,
BroadcastService,
BroadcastMessage,
MessageType,
} from "@ontotext/workbench-api";

const modules = [
Expand Down Expand Up @@ -429,6 +432,7 @@ repositories.service('$repositories', ['toastr', '$rootScope', '$timeout', '$loc
return RepositoriesRestService.deleteRepository(repo)
.success(function() {
that.init();
service(BroadcastService).sendMessage(new BroadcastMessage(MessageType.REPOSITORIES_UPDATED));
}).error(function(data) {
const msg = getError(data);
toastr.error(msg, $translate.instant('common.error'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import {decodeHTML} from "../../../app";
import './controllers/manage-remote-location-dialog.controller';
import {RemoteLocationType} from "../models/repository/remote-location.model";
import {service, BroadcastService, MessageType, BroadcastMessage} from '@ontotext/workbench-api';

const ENTITY_INDEX_SIZE_MIN = 10000;

Expand Down Expand Up @@ -667,6 +668,7 @@ function AddRepositoryCtrl($rootScope, $scope, toastr, $repositories, $location,
RepositoriesRestService.createRepository($scope.repositoryInfo)
.then(function() {
toastr.success($translate.instant('created.repo.success.msg', {repoId: $scope.repositoryInfo.id}));
service(BroadcastService).sendMessage(new BroadcastMessage(MessageType.REPOSITORIES_UPDATED));
return $repositories.init();
})
.then(() => $scope.goBackToPreviousLocation())
Expand Down