From f24b8784e900baacd54f845facfa8367e1a95fcd Mon Sep 17 00:00:00 2001 From: yyassin Date: Wed, 13 Mar 2024 07:54:03 -0400 Subject: [PATCH] Minwidths, fix electron closing, comment notifs, and fix cursor pos. --- client/electron/ipc/ipcHandlers.ts | 7 +++ client/electron/main.ts | 2 + client/electron/window.ts | 4 +- .../components/lib/CommentsSheetContent.tsx | 32 +---------- client/src/components/lib/CursorPresence.tsx | 31 ++++++----- .../lib/Titlebar/TitlebarWindows.tsx | 8 +-- client/src/views/Viewport.tsx | 53 ++++++++++++++++++- 7 files changed, 88 insertions(+), 49 deletions(-) diff --git a/client/electron/ipc/ipcHandlers.ts b/client/electron/ipc/ipcHandlers.ts index ce477ec..2f2dd25 100644 --- a/client/electron/ipc/ipcHandlers.ts +++ b/client/electron/ipc/ipcHandlers.ts @@ -59,9 +59,16 @@ const handleNotification = ( _event: IpcMainEvent, { title, body }: { title: string; body: string }, ) => { + const window = getWindow(_event); + if (!window?.isMinimized()) { + return; + } notification.title = title; notification.body = body; notification.show(); + notification.on('click', () => { + window?.show(); + }); }; /** diff --git a/client/electron/main.ts b/client/electron/main.ts index e53e55d..6a1ac7c 100644 --- a/client/electron/main.ts +++ b/client/electron/main.ts @@ -56,6 +56,8 @@ const createWindow = () => { transparent: true, frame: false, title: APP_NAME, + minWidth: 600, + minHeight: 400, }); setupWindow(win, VITE_DEV_SERVER_URL ?? ''); win.on('closed', () => (win = null)); diff --git a/client/electron/window.ts b/client/electron/window.ts index 147c312..e2e126b 100644 --- a/client/electron/window.ts +++ b/client/electron/window.ts @@ -84,13 +84,13 @@ export const setupTray = (win: BrowserWindow, tray: Tray, appName: string) => { { label: 'Show App', click: () => { - win && win.show(); + win.show(); }, }, { label: 'Exit', click: () => { - win && win.close(); + win.close(); }, }, ]); diff --git a/client/src/components/lib/CommentsSheetContent.tsx b/client/src/components/lib/CommentsSheetContent.tsx index 687c420..f38964f 100644 --- a/client/src/components/lib/CommentsSheetContent.tsx +++ b/client/src/components/lib/CommentsSheetContent.tsx @@ -82,7 +82,7 @@ const CommentsSheetContent = () => { 'userPicture', 'userID', ]); - const { socket, setWebsocketAction } = useWebSocketStore([ + const { setWebsocketAction } = useWebSocketStore([ 'socket', 'setWebsocketAction', ]); @@ -148,35 +148,6 @@ const CommentsSheetContent = () => { const { isViewingComments } = useAppStore(['isViewingComments']); - useEffect(() => { - socket?.on('addComment', (msg) => { - const { elemID, comment } = ( - msg as { payload: { elemID: string; comment: Comment } } - ).payload; - selectedElementIds[0] === elemID && - isViewingComments && - addComment(comment); - }); - - socket?.on('removeComment', (msg) => { - const { elemID, comment } = ( - msg as { payload: { elemID: string; comment: Comment } } - ).payload; - selectedElementIds[0] === elemID && - isViewingComments && - removeComment(comment.uid); - }); - - socket?.on('updateComment', (msg) => { - const { elemID, comment } = ( - msg as { payload: { elemID: string; comment: Comment } } - ).payload; - selectedElementIds[0] === elemID && - isViewingComments && - updateComment(comment); - }); - }, [socket, isViewingComments, selectedElementIds]); - useEffect(() => { const getComments = async () => { const comments = await axios.get(REST.comment.getComments, { @@ -368,6 +339,7 @@ const CommentsSheetContent = () => { addComment(newComment); setTextInput(''); + console.log('comment'); setWebsocketAction( { comment: newComment, elemID: selectedElementIds[0] }, 'addComment', diff --git a/client/src/components/lib/CursorPresence.tsx b/client/src/components/lib/CursorPresence.tsx index 7f9bc87..04d2b4a 100644 --- a/client/src/components/lib/CursorPresence.tsx +++ b/client/src/components/lib/CursorPresence.tsx @@ -1,5 +1,7 @@ +import { getScaleOffset } from '@/lib/canvasElements/render'; import { extractCollabID, extractUsername } from '@/lib/misc'; import { idToColour } from '@/lib/userColours'; +import { useAppStore } from '@/stores/AppStore'; import { useWebSocketStore } from '@/stores/WebSocketStore'; import { Vector2 } from '@/types'; import { MousePointer2 } from 'lucide-react'; @@ -42,6 +44,12 @@ Cursor.displayName = 'CursorPresence'; const CursorPresence = memo(() => { const { cursorPositions } = useWebSocketStore(['cursorPositions']); + const { panOffset, zoom, appHeight, appWidth } = useAppStore([ + 'panOffset', + 'zoom', + 'appHeight', + 'appWidth', + ]); return ( { }} > - {Object.entries(cursorPositions).map( - ([userId, position]) => - position.x !== null && - position.y !== null && ( - - ), - )} + {Object.entries(cursorPositions).map(([userId, position]) => { + if (position.x === null || position.y === null) return; + // Cursor Positions are in the absolute frame, but we send everything + // in the canvas relative frame (since the other clients may have + // a different zoom, and offset. So inverse the transformation + // according to the current zoom and panOffset for *our* client) + const scaleOffset = getScaleOffset(appHeight, appWidth, zoom); + const posX = position.x * zoom - scaleOffset.x + panOffset.x * zoom; + const posY = position.y * zoom - scaleOffset.y + panOffset.y * zoom; + return ; + })} ); diff --git a/client/src/components/lib/Titlebar/TitlebarWindows.tsx b/client/src/components/lib/Titlebar/TitlebarWindows.tsx index e401ca3..292a388 100644 --- a/client/src/components/lib/Titlebar/TitlebarWindows.tsx +++ b/client/src/components/lib/Titlebar/TitlebarWindows.tsx @@ -34,9 +34,11 @@ const TitlebarWin = ({ title, fg }: { title: string; fg: string }) => { const closeHandler = useCallback(() => { // Need to wait for the message to send. - socket?.leaveRoom().then(() => { - ipcAPI.close('close'); - }); + socket + ? socket?.leaveRoom().finally(() => { + ipcAPI.close('close'); + }) + : ipcAPI.close('close'); // HACK: We need to listen to these to get the updated socket since // the socket is mutable, it won't trigger rerenders on change. }, [socket, userId, roomID]); diff --git a/client/src/views/Viewport.tsx b/client/src/views/Viewport.tsx index b63ea6c..274bfc7 100644 --- a/client/src/views/Viewport.tsx +++ b/client/src/views/Viewport.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import Canvas from '@/components/lib/Canvas'; import DropDownMenu from '@/components/lib/DropDownMenu'; import ToolBar from '@/components/lib/ToolBar'; @@ -22,6 +22,9 @@ import ShareBoardDialog from '@/components/lib/ShareBoardDialog'; import { useCanvasBoardStore } from '@/stores/CanavasBoardStore'; import EditBoardDataDialog from '@/components/lib/EditBoardDataDialog'; import PublishTemplateDialog from '@/components/lib/PublishTemplateDialog'; +import { useWebSocketStore } from '@/stores/WebSocketStore'; +import { Comment, useCommentsStore } from '@/stores/CommentsStore'; +import { ipcAPI } from '@/data/ipc/ipcMessages'; /** * Primary viewport that houses the canvas @@ -30,7 +33,18 @@ import PublishTemplateDialog from '@/components/lib/PublishTemplateDialog'; * @authors Yousef Yassin, Abdalla Abdelhadi, Zakariyya Almalki */ const Viewport = () => { - const { tool, isTransparent } = useAppStore(['tool', 'isTransparent']); + const { tool, isTransparent, isViewingComments } = useAppStore([ + 'tool', + 'isTransparent', + 'isViewingComments', + ]); + const { updateComment, addComment, removeComment } = useCommentsStore([ + 'updateComment', + 'addComment', + 'removeComment', + ]); + const { socket } = useWebSocketStore(['socket']); + const viewportRef = useRef(null); const { selectedElementIds } = useCanvasElementStore(['selectedElementIds']); const isDrawingSelected = isDrawingTool(tool); @@ -39,6 +53,41 @@ const Viewport = () => { const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isPubDialogOpen, setIsPubDialogOpen] = useState(false); + useEffect(() => { + socket?.on('addComment', (msg) => { + const { elemID, comment } = ( + msg as { payload: { elemID: string; comment: Comment } } + ).payload; + selectedElementIds[0] === elemID && + isViewingComments && + addComment(comment); + ipcAPI && + ipcAPI.notification({ + title: comment.username, + body: comment.comment, + avatar: comment.avatar, + }); + }); + + socket?.on('removeComment', (msg) => { + const { elemID, comment } = ( + msg as { payload: { elemID: string; comment: Comment } } + ).payload; + selectedElementIds[0] === elemID && + isViewingComments && + removeComment(comment.uid); + }); + + socket?.on('updateComment', (msg) => { + const { elemID, comment } = ( + msg as { payload: { elemID: string; comment: Comment } } + ).payload; + selectedElementIds[0] === elemID && + isViewingComments && + updateComment(comment); + }); + }, [socket, isViewingComments, selectedElementIds]); + return (