From 409f44476f76b9111f0cea2037334ce3ada9d523 Mon Sep 17 00:00:00 2001 From: Veljko Tosic Date: Wed, 4 Mar 2026 23:09:02 +0100 Subject: [PATCH 01/11] front --- front/src/enums/index.ts | 11 ++ front/src/services/whiteboardHubService.ts | 37 ++++++ front/src/services/whiteboardService.ts | 10 +- front/src/stores/whiteboard.ts | 133 ++++++++++++++++----- front/src/stores/whiteboards.ts | 28 ++++- front/src/types/index.ts | 7 +- front/src/views/HomeView.vue | 32 ++++- front/src/views/WhiteboardView.vue | 40 ++++++- front/vite.config.ts | 3 + 9 files changed, 259 insertions(+), 42 deletions(-) diff --git a/front/src/enums/index.ts b/front/src/enums/index.ts index 21b7edd..170f8ef 100644 --- a/front/src/enums/index.ts +++ b/front/src/enums/index.ts @@ -9,3 +9,14 @@ export enum WhiteboardState { Inactive, Deleted } + +export enum MembershipStatus { + Pending, + Accepted, + Rejected, + Active, + Inactive, + Cancelled, + Kicked, + Banned +} diff --git a/front/src/services/whiteboardHubService.ts b/front/src/services/whiteboardHubService.ts index 0ef37f8..e031c7e 100644 --- a/front/src/services/whiteboardHubService.ts +++ b/front/src/services/whiteboardHubService.ts @@ -76,6 +76,38 @@ export const whiteboardHubService = { client.on('Leaved', callback) }, + onWaitingForApproval(callback: (userId: string) => void) { + client.on('WaitingForApproval', callback) + }, + + onUserWaitingForApproval(callback: (userId: string) => void) { + client.on('UserWaitingForApproval', callback) + }, + + onAccepted(callback: () => void) { + client.on('Accepted', callback) + }, + + onRejected(callback: () => void) { + client.on('Rejected', callback) + }, + + onUserCanceledJoinRequest(callback: (userId: string) => void) { + client.on('UserCanceledJoinRequest', callback) + }, + + async acceptUser(userId: string) { + await client.invoke('AcceptUser', userId) + }, + + async rejectUser(userId: string) { + await client.invoke('RejectUser', userId) + }, + + async cancelJoinRequest() { + await client.invoke('CancelJoinRequest') + }, + offAll() { client.off('InitWhiteboard') client.off('AddedRectangle') @@ -85,5 +117,10 @@ export const whiteboardHubService = { client.off('MovedShape') client.off('Joined') client.off('Leaved') + client.off('WaitingForApproval') + client.off('UserWaitingForApproval') + client.off('Accepted') + client.off('Rejected') + client.off('UserCanceledJoinRequest') }, } diff --git a/front/src/services/whiteboardService.ts b/front/src/services/whiteboardService.ts index db0711e..0757a88 100644 --- a/front/src/services/whiteboardService.ts +++ b/front/src/services/whiteboardService.ts @@ -1,4 +1,4 @@ -import type {Whiteboard} from "@/types"; +import type {JoinResult, Whiteboard} from "@/types"; import {api} from './api' export const whiteboardService = { @@ -22,6 +22,14 @@ export const whiteboardService = { async deleteWhiteboard(id: string): Promise { await api.delete(`/api/Whiteboard/${id}`) + }, + + async joinWhiteboard(code: string): Promise { + const raw = await api.post(`/api/Whiteboard/join`, {code: code}) + return { + whiteboardId: raw.whiteboardId, + status: raw.status + } } } diff --git a/front/src/stores/whiteboard.ts b/front/src/stores/whiteboard.ts index 758f350..01a0c7d 100644 --- a/front/src/stores/whiteboard.ts +++ b/front/src/stores/whiteboard.ts @@ -2,9 +2,13 @@ import { ref, computed } from 'vue' import { defineStore } from 'pinia' import type { Arrow, Line, Rectangle, Shape, ShapeTool, ShapeType, TextShape, Whiteboard } from '@/types/whiteboard.ts' import { whiteboardHubService } from '@/services/whiteboardHubService.ts' +import {useWhiteboardsStore} from "@/stores/whiteboards.ts"; +import router from "@/router"; export const useWhiteboardStore = defineStore('whiteboard', () => { const whiteboard = ref(null) + const pendingUsers = ref([]) + const selectedTool = ref('hand') const isConnected = ref(false) const isLoading = ref(false) @@ -27,6 +31,98 @@ export const useWhiteboardStore = defineStore('whiteboard', () => { } }) + async function initializeSession(id: string) { + isLoading.value = true; + error.value = null; + + try{ + await whiteboardHubService.connect() + isConnected.value = true; + + registerHubEvents() + + await whiteboardHubService.joinWhiteboard(id) + } catch (e: any) { + error.value = e?.message ?? 'Failed to join whiteboard' + isLoading.value = false + } + } + + function registerHubEvents() { + whiteboardHubService.onInitWhiteboard((wb) => { + whiteboard.value = wb + isLoading.value = false + }) + + whiteboardHubService.onAddedRectangle((rectangle) => { + whiteboard.value?.rectangles.push(rectangle) + }) + + whiteboardHubService.onAddedArrow((arrow) => { + whiteboard.value?.arrows.push(arrow) + }) + + whiteboardHubService.onAddedLine((line) => { + whiteboard.value?.lines.push(line) + }) + + whiteboardHubService.onAddedTextShape((textShape) => { + whiteboard.value?.textShapes.push(textShape) + }) + + whiteboardHubService.onMovedShape((command) => { + applyMoveShape(command.shapeId, command.newPositionX, command.newPositionY) + }) + + whiteboardHubService.onJoined((userId) => { + console.log('User joined:', userId) + }) + + whiteboardHubService.onLeaved((userId) => { + console.log('User left:', userId) + }) + + whiteboardHubService.onWaitingForApproval(() => { + const infoStore = useWhiteboardsStore() + infoStore.startWaitingToJoin() + }) + + whiteboardHubService.onUserWaitingForApproval((userId) => { + if (!pendingUsers.value.includes(userId)) { + pendingUsers.value.push(userId) + } + }) + + whiteboardHubService.onAccepted(() => { + const infoStore = useWhiteboardsStore() + infoStore.stopWaitingToJoin() + }) + + whiteboardHubService.onRejected(() => { + router.push('/') + alert('Your request to join was rejected.') + }) + + whiteboardHubService.onUserCanceledJoinRequest((userId) => { + pendingUsers.value = pendingUsers.value.filter(id => id !== userId) + }) + } + + async function approveUser(userId: string) { + await whiteboardHubService.acceptUser(userId) + pendingUsers.value = pendingUsers.value.filter(id => id !== userId) + } + + async function rejectUser(userId: string) { + await whiteboardHubService.rejectUser(userId) + pendingUsers.value = pendingUsers.value.filter(id => id !== userId) + } + + async function cancelJoinRequest() { + await whiteboardHubService.cancelJoinRequest() + whiteboard.value = null + } + async function joinWhiteboard(id: string) { isLoading.value = true error.value = null @@ -35,38 +131,7 @@ export const useWhiteboardStore = defineStore('whiteboard', () => { await whiteboardHubService.connect() isConnected.value = true - whiteboardHubService.onInitWhiteboard((wb) => { - whiteboard.value = wb - isLoading.value = false - }) - - whiteboardHubService.onAddedRectangle((rectangle) => { - whiteboard.value?.rectangles.push(rectangle) - }) - - whiteboardHubService.onAddedArrow((arrow) => { - whiteboard.value?.arrows.push(arrow) - }) - - whiteboardHubService.onAddedLine((line) => { - whiteboard.value?.lines.push(line) - }) - - whiteboardHubService.onAddedTextShape((textShape) => { - whiteboard.value?.textShapes.push(textShape) - }) - - whiteboardHubService.onMovedShape((command) => { - applyMoveShape(command.shapeId, command.newPositionX, command.newPositionY) - }) - - whiteboardHubService.onJoined((userId) => { - console.log('User joined:', userId) - }) - - whiteboardHubService.onLeaved((userId) => { - console.log('User left:', userId) - }) + registerHubEvents() await whiteboardHubService.joinWhiteboard(id) } catch (e: any) { @@ -183,6 +248,10 @@ export const useWhiteboardStore = defineStore('whiteboard', () => { toolColor, toolThickness, toolTextSize, + pendingUsers, + approveUser, + rejectUser, + cancelJoinRequest, joinWhiteboard, leaveWhiteboard, addRectangle, diff --git a/front/src/stores/whiteboards.ts b/front/src/stores/whiteboards.ts index a67650a..b544299 100644 --- a/front/src/stores/whiteboards.ts +++ b/front/src/stores/whiteboards.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { ref } from 'vue' -import type { Whiteboard } from '@/types' +import type {JoinResult, Whiteboard} from '@/types' import { whiteboardService } from '@/services/whiteboardService' export const useWhiteboardsStore = defineStore('whiteboards', () => { @@ -8,6 +8,7 @@ export const useWhiteboardsStore = defineStore('whiteboards', () => { const recentWhiteboards = ref([]) const currentWhiteboard = ref(null) const isLoading = ref(false) + const isWaitingToJoin = ref(false) const error = ref(null) async function getWhiteboardHistory() { @@ -53,6 +54,27 @@ export const useWhiteboardsStore = defineStore('whiteboards', () => { return newWhiteboard.id; } + async function joinWhiteboardWithCode(code: string): Promise { + isLoading.value = true; + + try { + return await whiteboardService.joinWhiteboard(code); + } catch (err: any) { + error.value = err.message ?? 'Failed to join whiteboard'; + throw err; + } finally { + isLoading.value = false; + } + } + + function startWaitingToJoin() { + isWaitingToJoin.value = true; + } + + function stopWaitingToJoin() { + isWaitingToJoin.value = false; + } + async function deleteWhiteboard(id: string): Promise { isLoading.value = true error.value = null @@ -92,10 +114,14 @@ export const useWhiteboardsStore = defineStore('whiteboards', () => { ownedWhiteboards: ownedWhiteboards, recentWhiteboards: recentWhiteboards, isLoading, + isWaitingToJoin, error, getWhiteboardHistory: getWhiteboardHistory, getRecentWhiteboards: getRecentWhiteboards, createNewWhiteboard: createNewWhiteboard, + joinWhiteboardWithCode: joinWhiteboardWithCode, + startWaitingToJoin: startWaitingToJoin, + stopWaitingToJoin: stopWaitingToJoin, deleteWhiteboard: deleteWhiteboard, getCurrentWhiteboard: getCurrentWhiteboard, selectWhiteboard: selectWhiteboard, diff --git a/front/src/types/index.ts b/front/src/types/index.ts index f26dc5e..d58463f 100644 --- a/front/src/types/index.ts +++ b/front/src/types/index.ts @@ -1,4 +1,4 @@ -import {type WhiteboardJoinPolicy, WhiteboardState} from "@/enums"; +import {MembershipStatus, type WhiteboardJoinPolicy, WhiteboardState} from "@/enums"; export interface User { userId: string @@ -22,6 +22,11 @@ export interface AuthResponse { refreshToken: string } +export interface JoinResult { + whiteboardId: string + status: MembershipStatus +} + export interface Whiteboard { id: string ownerId: string diff --git a/front/src/views/HomeView.vue b/front/src/views/HomeView.vue index 1d4d6a1..5a57e2a 100644 --- a/front/src/views/HomeView.vue +++ b/front/src/views/HomeView.vue @@ -1,11 +1,12 @@