integrate deletion to collab sessions

This commit is contained in:
Ryan Di 2025-06-02 14:50:39 +10:00
parent f71c200106
commit 08a39e2034
8 changed files with 90 additions and 8 deletions

View File

@ -23,6 +23,7 @@ export enum WS_SUBTYPES {
INVALID_RESPONSE = "INVALID_RESPONSE",
INIT = "SCENE_INIT",
UPDATE = "SCENE_UPDATE",
DELETE = "SCENE_DELETE",
MOUSE_LOCATION = "MOUSE_LOCATION",
IDLE_STATUS = "IDLE_STATUS",
USER_VISIBLE_SCENE_BOUNDS = "USER_VISIBLE_SCENE_BOUNDS",

View File

@ -72,6 +72,7 @@ import {
} from "../data/FileManager";
import { LocalData } from "../data/LocalData";
import {
deleteRoomFromFirebase,
isSavedToFirebase,
loadFilesFromFirebase,
loadFromFirebase,
@ -115,6 +116,7 @@ export interface CollabAPI {
onPointerUpdate: CollabInstance["onPointerUpdate"];
startCollaboration: CollabInstance["startCollaboration"];
stopCollaboration: CollabInstance["stopCollaboration"];
deleteRoom: CollabInstance["deleteRoom"];
syncElements: CollabInstance["syncElements"];
fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
setUsername: CollabInstance["setUsername"];
@ -228,6 +230,7 @@ class Collab extends PureComponent<CollabProps, CollabState> {
isCollaborating: this.isCollaborating,
onPointerUpdate: this.onPointerUpdate,
startCollaboration: this.startCollaboration,
deleteRoom: this.deleteRoom,
syncElements: this.syncElements,
fetchImageFilesFromFirebase: this.fetchImageFilesFromFirebase,
stopCollaboration: this.stopCollaboration,
@ -675,6 +678,18 @@ class Collab extends PureComponent<CollabProps, CollabState> {
break;
}
case WS_SUBTYPES.DELETE: {
const { roomId } = decryptedData.payload;
if (this.portal.roomId === roomId) {
this.destroySocketClient({ isUnload: true });
this.setIsCollaborating(false);
this.setActiveRoomLink(null);
this.setErrorDialog(t("alerts.collabRoomDeleted"));
window.history.pushState({}, APP_NAME, window.location.origin);
}
break;
}
default: {
assertNever(decryptedData, null);
}
@ -894,6 +909,42 @@ class Collab extends PureComponent<CollabProps, CollabState> {
});
};
deleteRoom = async (): Promise<void> => {
if (!this.portal.socket || !this.portal.roomId) {
return;
}
const { roomId, roomKey } = this.portal;
if (!roomId || !roomKey) {
return;
}
const link = this.getActiveRoomLink();
if (!link) {
return;
}
// check if the room belongs to the current user
const isOwner = await roomManager.isRoomOwnedByUser(link);
if (!isOwner) {
return;
}
try {
this.portal.broadcastRoomDeletion();
await deleteRoomFromFirebase(roomId, roomKey);
await roomManager.deleteRoom(roomId);
this.stopCollaboration(false);
this.setActiveRoomLink(null);
window.history.pushState({}, APP_NAME, window.location.origin);
} catch (error) {
console.error("Failed to delete room:", error);
this.setErrorDialog(t("errors.roomDeletionFailed"));
throw error;
}
};
public setLastBroadcastedOrReceivedSceneVersion = (version: number) => {
this.lastBroadcastedOrReceivedSceneVersion = version;
};

View File

@ -252,6 +252,20 @@ class Portal {
this.socket.emit(WS_EVENTS.USER_FOLLOW_CHANGE, payload);
}
};
broadcastRoomDeletion = async () => {
if (this.socket?.id) {
const data: SocketUpdateDataSource["ROOM_DELETED"] = {
type: WS_SUBTYPES.DELETE,
payload: {
socketId: this.socket.id as SocketId,
roomId: this.roomId!,
},
};
this._broadcastSocketData(data as SocketUpdateData);
}
};
}
export default Portal;

View File

@ -315,3 +315,10 @@ export const loadFilesFromFirebase = async (
return { loadedFiles, erroredFiles };
};
export const deleteRoomFromFirebase = async (
roomId: string,
roomKey: string,
): Promise<void> => {
// TODO: delete the room...
};

View File

@ -119,6 +119,13 @@ export type SocketUpdateDataSource = {
username: string;
};
};
ROOM_DELETED: {
type: WS_SUBTYPES.DELETE;
payload: {
socketId: SocketId;
roomId: string;
};
};
};
export type SocketUpdateDataIncoming =
@ -310,7 +317,7 @@ export const exportToBackend = async (
const response = await fetch(BACKEND_V2_POST, {
method: "POST",
body: payload.buffer,
body: new Uint8Array(payload.buffer),
});
const json = await response.json();
if (json.id) {

View File

@ -186,7 +186,7 @@ class RoomManager {
return false;
}
const [, roomId] = match;
const roomId = match[1];
return rooms.some((room) => room.roomId === roomId);
} catch (error) {
console.warn("Failed to check room ownership:", error);

View File

@ -198,10 +198,10 @@ const ActiveRoomDialog = ({
color="danger"
onClick={() => {
trackEvent("share", "room deleted");
// TODO: handle deletion
// 1. stop collaboration (for all users?)
// 2. delete room from backend (firebase)
// 3. close
collabAPI.deleteRoom();
if (!collabAPI.isCollaborating()) {
handleClose();
}
}}
/>
)}

View File

@ -253,7 +253,8 @@
"resetLibrary": "This will clear your library. Are you sure?",
"removeItemsFromsLibrary": "Delete {{count}} item(s) from library?",
"invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled.",
"collabOfflineWarning": "No internet connection available.\nYour changes will not be saved!"
"collabOfflineWarning": "No internet connection available.\nYour changes will not be saved!",
"collabRoomDeleted": "This collab room has been deleted by its owner."
},
"errors": {
"unsupportedFileType": "Unsupported file type.",
@ -280,7 +281,8 @@
},
"asyncPasteFailedOnRead": "Couldn't paste (couldn't read from system clipboard).",
"asyncPasteFailedOnParse": "Couldn't paste.",
"copyToSystemClipboardFailed": "Couldn't copy to clipboard."
"copyToSystemClipboardFailed": "Couldn't copy to clipboard.",
"roomDeletionFailed": "Couldn't delete the collaboration room."
},
"toolBar": {
"selection": "Selection",