256 lines
9.4 KiB
TypeScript
256 lines
9.4 KiB
TypeScript
import { Dispatch } from '@reduxjs/toolkit';
|
|
import { setBannerUrl, setLogoUrl, setPrimaryColor, setUserProfilePictureUrl } from 'core/reducers/appSlice';
|
|
import { store } from 'core/store/store';
|
|
import { BrowserTabSession, Constants } from 'core/utils/utils';
|
|
import { WebSocketReceivedMessagesCmds } from 'core/utils/webSocket';
|
|
import { addLessonPageContent, deleteLessonPageContent, updateLessonPageContent, updateLessonPageContentPosition } from 'features/Lessons/LessonPage/lessonPageSlice';
|
|
import {
|
|
addLessonContent,
|
|
deleteLessonContent,
|
|
setLessonThumbnailTitle,
|
|
setLessonThumbnailUrl,
|
|
setPageEditorLessonState,
|
|
updateLessonContent,
|
|
updateLessonContentPosition,
|
|
} from 'features/Lessons/LessonPageEditor/lessonPageEditorSlice';
|
|
import { addLesson, updateLessonPreviewThumbnail, updateLessonPreviewTitle, updateLessonState } from 'features/Lessons/lessonsSlice';
|
|
import { addTeamMember, deleteTeamMember, updateTeamMemberRole } from 'features/Team/teamSlice';
|
|
import { setProfilePictureUrl } from 'features/UserProfile/userProfileSlice';
|
|
|
|
interface WebSocketMessage {
|
|
Cmd: number;
|
|
Body: any;
|
|
}
|
|
|
|
class WebSocketService {
|
|
private url: string;
|
|
private socket: WebSocket | null = null;
|
|
private reconnectInterval: number = 2000; // in ms
|
|
private offlineQueue: WebSocketMessage[] = [];
|
|
private firstConnect: boolean = true;
|
|
|
|
private messageHandler: ((message: WebSocketMessage, dispatch: Dispatch) => void) | null = null;
|
|
|
|
constructor(url: string) {
|
|
this.url = url;
|
|
}
|
|
|
|
private dispatch: Dispatch | null = null;
|
|
|
|
public connect(): void {
|
|
this.socket = new WebSocket(`${this.url}?auth=${localStorage.getItem('session')}&bts=${BrowserTabSession}`);
|
|
|
|
this.socket.onopen = () => {
|
|
console.log('WebSocket connected', this.firstConnect);
|
|
|
|
// Send all messages from the offline queue
|
|
|
|
this.offlineQueue.forEach((message) => this.send(message));
|
|
this.offlineQueue = [];
|
|
|
|
// Dispatch event to notify that the WebSocket connection is established
|
|
|
|
if (!this.firstConnect) {
|
|
document.dispatchEvent(webSocketConnectionEvent);
|
|
} else {
|
|
this.firstConnect = false;
|
|
}
|
|
};
|
|
|
|
this.socket.onmessage = (event: MessageEvent) => {
|
|
const data: WebSocketMessage = JSON.parse(event.data);
|
|
|
|
if (this.messageHandler) {
|
|
this.messageHandler(data, this.dispatch!);
|
|
} else {
|
|
console.error('No handler defined for WebSocket messages');
|
|
}
|
|
};
|
|
|
|
this.socket.onclose = () => {
|
|
console.log('WebSocket disconnected. Reconnecting...');
|
|
setTimeout(() => this.connect(), this.reconnectInterval);
|
|
};
|
|
|
|
this.socket.onerror = (error: Event) => {
|
|
console.error('WebSocket error:', error);
|
|
};
|
|
}
|
|
|
|
public setHandler(handler: (message: WebSocketMessage, dispatch: Dispatch) => void, dispatch: Dispatch): void {
|
|
this.messageHandler = handler;
|
|
this.dispatch = dispatch;
|
|
}
|
|
|
|
public send(message: WebSocketMessage): void {
|
|
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
this.socket.send(
|
|
JSON.stringify({
|
|
Cmd: message.Cmd,
|
|
Body: message.Body,
|
|
})
|
|
);
|
|
} else {
|
|
this.offlineQueue.push(message);
|
|
}
|
|
}
|
|
|
|
public disconnect(): void {
|
|
if (this.socket) {
|
|
this.socket.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
const webSocketConnectionEventName = 'WebSocketConnectionEvent';
|
|
|
|
const webSocketConnectionEvent = new CustomEvent(webSocketConnectionEventName, {
|
|
detail: 'wsReconnect',
|
|
});
|
|
|
|
export function addWebSocketReconnectListener(callback: () => void): void {
|
|
document.addEventListener(webSocketConnectionEventName, callback);
|
|
}
|
|
|
|
export function removeWebSocketReconnectListener(callback: () => void): void {
|
|
document.removeEventListener(webSocketConnectionEventName, callback);
|
|
}
|
|
|
|
const webSocketService = new WebSocketService(Constants.WS_ADDRESS);
|
|
export default webSocketService;
|
|
|
|
export function WebSocketMessageHandler(message: WebSocketMessage, dispatch: Dispatch) {
|
|
const { Cmd, Body } = message;
|
|
|
|
console.log('WebSocketMessageHandler', Cmd, Body);
|
|
|
|
switch (Cmd) {
|
|
case WebSocketReceivedMessagesCmds.SettingsUpdated:
|
|
dispatch(setPrimaryColor(Body.PrimaryColor));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.SettingsUpdatedLogo:
|
|
dispatch(setLogoUrl(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.SettingsUpdatedBanner:
|
|
dispatch(setBannerUrl(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.SettingsUpdatedSubdomain:
|
|
localStorage.removeItem('session');
|
|
|
|
window.location.href = `${window.location.protocol}//${Body}.${window.location.hostname.split('.').slice(1).join('.')}`;
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.TeamAddedMember:
|
|
dispatch(addTeamMember(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.TeamUpdatedMemberRole:
|
|
dispatch(updateTeamMemberRole(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.TeamDeletedMember:
|
|
dispatch(deleteTeamMember(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonCreated:
|
|
dispatch(addLesson(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonPreviewTitleUpdated:
|
|
dispatch(updateLessonPreviewTitle(Body));
|
|
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(setLessonThumbnailTitle(Body.Title));
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonPreviewThumbnailUpdated:
|
|
dispatch(updateLessonPreviewThumbnail(Body));
|
|
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(setLessonThumbnailUrl(Body.ThumbnailUrl));
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonStateUpdated:
|
|
dispatch(updateLessonState(Body));
|
|
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(setPageEditorLessonState(Body.State));
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonAddedContent:
|
|
if (Body.LessonId === store.getState().lessonPage.currentLessonId) {
|
|
dispatch(addLessonPageContent(Body));
|
|
}
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(addLessonContent(Body));
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonDeletedContent:
|
|
if (Body.LessonId === store.getState().lessonPage.currentLessonId) {
|
|
dispatch(deleteLessonPageContent(Body.ContentId));
|
|
}
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(deleteLessonContent(Body.ContentId));
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonContentUpdated:
|
|
if (Body.LessonId === store.getState().lessonPage.currentLessonId) {
|
|
dispatch(
|
|
updateLessonPageContent({
|
|
id: Body.ContentId,
|
|
data: Body.Data,
|
|
})
|
|
);
|
|
}
|
|
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(
|
|
updateLessonContent({
|
|
id: Body.ContentId,
|
|
data: Body.Data,
|
|
})
|
|
);
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonContentUpdatedPosition:
|
|
if (Body.LessonId === store.getState().lessonPage.currentLessonId) {
|
|
dispatch(
|
|
updateLessonPageContentPosition({
|
|
contentId: Body.ContentId,
|
|
position: Body.Position,
|
|
})
|
|
);
|
|
}
|
|
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(
|
|
updateLessonContentPosition({
|
|
contentId: Body.ContentId,
|
|
position: Body.Position,
|
|
})
|
|
);
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonContentFileUpdated:
|
|
if (Body.LessonId === store.getState().lessonPage.currentLessonId) {
|
|
dispatch(
|
|
updateLessonPageContent({
|
|
id: Body.ContentId,
|
|
data: Body.Data,
|
|
})
|
|
);
|
|
}
|
|
|
|
if (Body.LessonId === store.getState().lessonPageEditor.currentLessonId) {
|
|
dispatch(
|
|
updateLessonContent({
|
|
id: Body.ContentId,
|
|
data: Body.Data,
|
|
})
|
|
);
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.UserProfilePictureUpdated:
|
|
dispatch(setProfilePictureUrl(Body));
|
|
dispatch(setUserProfilePictureUrl(Body));
|
|
break;
|
|
default:
|
|
console.error('Unknown message type:', Cmd);
|
|
}
|
|
}
|