313 lines
9.2 KiB
TypeScript
313 lines
9.2 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 {
|
|
addLikedQuestion,
|
|
addQuestion,
|
|
countDownLikedQuestion,
|
|
countUpLikedQuestion,
|
|
deleteLikedQuestion,
|
|
} from "features/Lessons/Questions/lessonQuestionSlice";
|
|
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;
|
|
case WebSocketReceivedMessagesCmds.LessonQuestionCreated:
|
|
if (Body.LessonId === store.getState().lessonPage.currentLessonId) {
|
|
dispatch(addQuestion(Body));
|
|
}
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonQuestionLiked:
|
|
dispatch(addLikedQuestion(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonQuestionCountUpLikes:
|
|
dispatch(countUpLikedQuestion(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonQuestionDisliked:
|
|
dispatch(deleteLikedQuestion(Body));
|
|
break;
|
|
case WebSocketReceivedMessagesCmds.LessonQuestionCountDownLikes:
|
|
dispatch(countDownLikedQuestion(Body));
|
|
break;
|
|
default:
|
|
console.error("Unknown message type:", Cmd);
|
|
}
|
|
}
|