From 95b3430240bfdd97e5c545ef821f20a8a554b96e Mon Sep 17 00:00:00 2001 From: DavePk04 <Dave.Pikop.Pokam@ulb.be> Date: Thu, 30 Jan 2025 09:16:38 +0100 Subject: [PATCH 1/6] ui: add the user status logic --- .../src/routes/sessions/[id]/Message.svelte | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/frontend/src/routes/sessions/[id]/Message.svelte b/frontend/src/routes/sessions/[id]/Message.svelte index 943515d2..9908f0b2 100644 --- a/frontend/src/routes/sessions/[id]/Message.svelte +++ b/frontend/src/routes/sessions/[id]/Message.svelte @@ -185,6 +185,32 @@ await message.deleteFeedback(feedback); } + + function getUserStatus(user: User, isInConversation: boolean): string { + console.log("status", user.is_active); + if (!user.is_active) { + return 'red'; + } + if (isInConversation) { + return 'green'; + } + return 'orange'; +} + +function getUserStatusTooltip(status: string): string { + switch (status) { + case 'green': + return 'User is in this conversation'; + case 'orange': + return 'User is connected but not in this conversation'; + case 'red': + return 'User is not connected to LL'; + default: + return ''; + } +} + + </script> <div @@ -193,13 +219,22 @@ class:chat-start={!isSender} class:chat-end={isSender} > - <div class="rounded-full mx-2 chat-image size-12" title={message.user.nickname}> - <img - src={`https://gravatar.com/avatar/${message.user.emailHash}?d=identicon`} - alt={user.nickname} - class="rounded-full border border-neutral-400 text-sm" - /> - </div> +<div class="relative rounded-full mx-2 chat-image size-12" title={message.user.nickname}> + <img + src={`https://gravatar.com/avatar/${message.user.emailHash}?d=identicon`} + alt={user.nickname} + class="rounded-full border border-neutral-400 text-sm" + /> + <div + class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ + getUserStatus(message.user, true) === 'green' ? 'bg-green-500' : + getUserStatus(message.user, false) === 'orange' ? 'bg-orange-500' : + getUserStatus(message.user, false) === 'red' ? 'bg-red-500' : '' + }`} + ></div> +</div> + + <div class="chat-bubble text-black" class:bg-blue-50={isSender} class:bg-gray-300={!isSender}> {#if replyToMessage} -- GitLab From c6153a350c2b692406cfac30b7795c0a284e951f Mon Sep 17 00:00:00 2001 From: DavePk04 <Dave.Pikop.Pokam@ulb.be> Date: Thu, 30 Jan 2025 09:19:46 +0100 Subject: [PATCH 2/6] fix linting --- .../src/routes/sessions/[id]/Message.svelte | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/frontend/src/routes/sessions/[id]/Message.svelte b/frontend/src/routes/sessions/[id]/Message.svelte index 9908f0b2..aa19c714 100644 --- a/frontend/src/routes/sessions/[id]/Message.svelte +++ b/frontend/src/routes/sessions/[id]/Message.svelte @@ -187,30 +187,28 @@ } function getUserStatus(user: User, isInConversation: boolean): string { - console.log("status", user.is_active); - if (!user.is_active) { - return 'red'; - } - if (isInConversation) { - return 'green'; - } - return 'orange'; -} - -function getUserStatusTooltip(status: string): string { - switch (status) { - case 'green': - return 'User is in this conversation'; - case 'orange': - return 'User is connected but not in this conversation'; - case 'red': - return 'User is not connected to LL'; - default: - return ''; - } -} - + console.log('status', user.is_active); + if (!user.is_active) { + return 'red'; + } + if (isInConversation) { + return 'green'; + } + return 'orange'; + } + function getUserStatusTooltip(status: string): string { + switch (status) { + case 'green': + return 'User is in this conversation'; + case 'orange': + return 'User is connected but not in this conversation'; + case 'red': + return 'User is not connected to LL'; + default: + return ''; + } + } </script> <div @@ -219,22 +217,24 @@ function getUserStatusTooltip(status: string): string { class:chat-start={!isSender} class:chat-end={isSender} > -<div class="relative rounded-full mx-2 chat-image size-12" title={message.user.nickname}> - <img - src={`https://gravatar.com/avatar/${message.user.emailHash}?d=identicon`} - alt={user.nickname} - class="rounded-full border border-neutral-400 text-sm" - /> - <div - class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ - getUserStatus(message.user, true) === 'green' ? 'bg-green-500' : - getUserStatus(message.user, false) === 'orange' ? 'bg-orange-500' : - getUserStatus(message.user, false) === 'red' ? 'bg-red-500' : '' - }`} - ></div> -</div> - - + <div class="relative rounded-full mx-2 chat-image size-12" title={message.user.nickname}> + <img + src={`https://gravatar.com/avatar/${message.user.emailHash}?d=identicon`} + alt={user.nickname} + class="rounded-full border border-neutral-400 text-sm" + /> + <div + class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ + getUserStatus(message.user, true) === 'green' + ? 'bg-green-500' + : getUserStatus(message.user, false) === 'orange' + ? 'bg-orange-500' + : getUserStatus(message.user, false) === 'red' + ? 'bg-red-500' + : '' + }`} + ></div> + </div> <div class="chat-bubble text-black" class:bg-blue-50={isSender} class:bg-gray-300={!isSender}> {#if replyToMessage} -- GitLab From ea5bc2c2524366c7920a3d921a60f21c175ee80f Mon Sep 17 00:00:00 2001 From: DavePk04 <Dave.Pikop.Pokam@ulb.be> Date: Thu, 30 Jan 2025 10:29:28 +0100 Subject: [PATCH 3/6] feat: Update user active status on login/logout and improve chat UI --- frontend/src/routes/+layout.server.ts | 12 +++++++-- frontend/src/routes/logout/+page.server.ts | 18 +++++++++++-- .../src/routes/sessions/[id]/Message.svelte | 26 ++++++------------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/frontend/src/routes/+layout.server.ts b/frontend/src/routes/+layout.server.ts index b3aecd87..c403b2a7 100644 --- a/frontend/src/routes/+layout.server.ts +++ b/frontend/src/routes/+layout.server.ts @@ -1,4 +1,5 @@ import { type ServerLoad, redirect } from '@sveltejs/kit'; +import { patchUserAPI } from '$lib/api/users'; const publicly_allowed = ['/login', '/register', '/tests', '/surveys', '/tutor/register']; @@ -11,10 +12,17 @@ const isPublic = (path: string) => { return false; }; -export const load: ServerLoad = async ({ locals, url }) => { +export const load: ServerLoad = async ({ locals, url, fetch }) => { + if (locals.user == null || locals.user == undefined) { if (!isPublic(url.pathname)) { - redirect(303, `/login`); + throw redirect(303, `/login`); + } + } else { + try { + await patchUserAPI(fetch, locals.user.id, { is_active: true }); + } catch (error) { + console.error('Failed to update user status:', error); } } diff --git a/frontend/src/routes/logout/+page.server.ts b/frontend/src/routes/logout/+page.server.ts index 7790fb9c..7cd9bda3 100644 --- a/frontend/src/routes/logout/+page.server.ts +++ b/frontend/src/routes/logout/+page.server.ts @@ -1,8 +1,22 @@ import { type ServerLoad, redirect } from '@sveltejs/kit'; +import { patchUserAPI } from '$lib/api/users'; + +export const load: ServerLoad = async ({ cookies, locals, fetch }) => { + if (locals.user) { + try { + const success = await patchUserAPI(fetch, locals.user.id, { is_active: false }); + if (!success) { + console.error('Failed to update user status.'); + } + } catch (error) { + console.error('Error updating user status:', error); + } + } -export const load: ServerLoad = async ({ cookies }) => { cookies.set('access_token_cookie', '', { maxAge: -1, path: '/' }); cookies.set('refresh_token_cookie', '', { maxAge: -1, path: '/' }); - redirect(303, '/login'); + locals.user = null; + + throw redirect(303, '/login'); }; diff --git a/frontend/src/routes/sessions/[id]/Message.svelte b/frontend/src/routes/sessions/[id]/Message.svelte index aa19c714..796beded 100644 --- a/frontend/src/routes/sessions/[id]/Message.svelte +++ b/frontend/src/routes/sessions/[id]/Message.svelte @@ -186,28 +186,20 @@ await message.deleteFeedback(feedback); } - function getUserStatus(user: User, isInConversation: boolean): string { - console.log('status', user.is_active); + function getUserStatus(user: User | null, isInConversation: boolean): string { + if (!user) { + return 'red'; + } + if (!user.is_active) { return 'red'; } + if (isInConversation) { return 'green'; } - return 'orange'; - } - function getUserStatusTooltip(status: string): string { - switch (status) { - case 'green': - return 'User is in this conversation'; - case 'orange': - return 'User is connected but not in this conversation'; - case 'red': - return 'User is not connected to LL'; - default: - return ''; - } + return 'orange'; } </script> @@ -229,9 +221,7 @@ ? 'bg-green-500' : getUserStatus(message.user, false) === 'orange' ? 'bg-orange-500' - : getUserStatus(message.user, false) === 'red' - ? 'bg-red-500' - : '' + : 'bg-red-500' }`} ></div> </div> -- GitLab From ebcec9ac040de36ff347a2874eede43e963799c6 Mon Sep 17 00:00:00 2001 From: DavePk04 <Dave.Pikop.Pokam@ulb.be> Date: Thu, 30 Jan 2025 10:30:14 +0100 Subject: [PATCH 4/6] fix linting --- frontend/src/routes/+layout.server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/routes/+layout.server.ts b/frontend/src/routes/+layout.server.ts index c403b2a7..52b690c6 100644 --- a/frontend/src/routes/+layout.server.ts +++ b/frontend/src/routes/+layout.server.ts @@ -13,7 +13,6 @@ const isPublic = (path: string) => { }; export const load: ServerLoad = async ({ locals, url, fetch }) => { - if (locals.user == null || locals.user == undefined) { if (!isPublic(url.pathname)) { throw redirect(303, `/login`); -- GitLab From 1b6e1e3f29850a02259223000423e0dbb8761416 Mon Sep 17 00:00:00 2001 From: DavePk04 <Dave.Pikop.Pokam@ulb.be> Date: Thu, 30 Jan 2025 19:28:45 +0100 Subject: [PATCH 5/6] --wip-- --- frontend/src/routes/Header.svelte | 42 +++++++++++++++---- .../src/routes/sessions/[id]/+page.svelte | 38 +++++++++++++---- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/frontend/src/routes/Header.svelte b/frontend/src/routes/Header.svelte index 876faf2f..99cf2da8 100644 --- a/frontend/src/routes/Header.svelte +++ b/frontend/src/routes/Header.svelte @@ -14,6 +14,22 @@ displayMetadataWarning = true; } } + + function getUserStatus(user: User | null, isIn: boolean): string { + if (!user) { + return 'red'; + } + + if (!user.is_active) { + return 'red'; + } + + if (isIn) { + return 'green'; + } + + return 'orange'; + } </script> <header class="navbar shadow bg-gray-200"> @@ -54,14 +70,26 @@ {#if user} <li> <details> - <summary class="px-3"> - <img - src={`https://gravatar.com/avatar/${user.emailHash}?d=identicon`} - alt={''} - class="rounded-full border text-sm size-8 border-neutral-400" - /> - {user.nickname} + <summary class="px-3 flex items-center space-x-2"> + <div class="relative"> + <img + src={`https://gravatar.com/avatar/${user.emailHash}?d=identicon`} + alt={''} + class="rounded-full border text-sm size-8 border-neutral-400" + /> + <div + class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ + getUserStatus(user, true) === 'green' + ? 'bg-green-500' + : getUserStatus(user, false) === 'orange' + ? 'bg-orange-500' + : 'bg-red-500' + }`} + ></div> + </div> + <span>{user.nickname}</span> </summary> + <ul class="menu menu-sm dropdown-content absolute right-0"> <li> <a data-sveltekit-reload href="/logout" class="whitespace-nowrap"> diff --git a/frontend/src/routes/sessions/[id]/+page.svelte b/frontend/src/routes/sessions/[id]/+page.svelte index 6f53193a..a217ed93 100644 --- a/frontend/src/routes/sessions/[id]/+page.svelte +++ b/frontend/src/routes/sessions/[id]/+page.svelte @@ -3,12 +3,28 @@ import type { PageData } from './$types.js'; import WeeklySurvey from './WeeklySurvey.svelte'; import Chatbox from './Chatbox.svelte'; + import type User from '$lib/types/user'; let { data }: { data: PageData } = $props(); let user = data.user!; let { session, jwt } = data; - let { onlineUsers } = session; + + function getUserStatus(user: User | null, isIn: boolean): string { + if (!user) { + return 'red'; + } + + if (!user.is_active) { + return 'red'; + } + + if (isIn) { + return 'green'; + } + + return 'orange'; + } </script> <div class="h-full flex lg:flex-row flex-col pt-2 lg:pt-0"> @@ -18,22 +34,28 @@ <h2 class="text-center font-bold">{$t('utils.words.participants')}</h2> <div class="pb-2 space-y-2"> {#each session.users as sessionUser (sessionUser.id)} - <div class="flex space-x-2"> - <div class="rounded-full mx-2 chat-image size-6" title={sessionUser.nickname}> + <div class="flex space-x-2 items-center"> + <div class="relative mx-2 chat-image size-6" title={sessionUser.nickname}> <img src={`https://gravatar.com/avatar/${sessionUser.emailHash}?d=identicon`} alt={sessionUser.nickname} - class="rounded-full border-2 text-sm {sessionUser.id == user?.id || - $onlineUsers.has(sessionUser.id) - ? 'border-green-500' - : 'border-red-500'}" + class="rounded-full border-2 text-sm size-6" /> + <div + class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ + getUserStatus(sessionUser, true) === 'green' + ? 'bg-green-500' + : getUserStatus(sessionUser, false) === 'orange' + ? 'bg-orange-500' + : 'bg-red-500' + }`} + ></div> </div> - <span class:font-bold={sessionUser === user}>{sessionUser.nickname}</span> </div> {/each} </div> + <h2 class="text-center font-bold border-t pt-2">{$t('utils.words.topics')}</h2> <p class="text-center text-sm text-neutral-500 italic">{$t('session.noTopic')}</p> </div> -- GitLab From 517f4172ce62f5f09d67ed3f11b865493b92ce77 Mon Sep 17 00:00:00 2001 From: DavePk04 <Dave.Pikop.Pokam@ulb.be> Date: Tue, 4 Feb 2025 13:01:40 +0100 Subject: [PATCH 6/6] revert to previous version --- .../src/routes/sessions/[id]/+page.svelte | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/frontend/src/routes/sessions/[id]/+page.svelte b/frontend/src/routes/sessions/[id]/+page.svelte index a217ed93..6f53193a 100644 --- a/frontend/src/routes/sessions/[id]/+page.svelte +++ b/frontend/src/routes/sessions/[id]/+page.svelte @@ -3,28 +3,12 @@ import type { PageData } from './$types.js'; import WeeklySurvey from './WeeklySurvey.svelte'; import Chatbox from './Chatbox.svelte'; - import type User from '$lib/types/user'; let { data }: { data: PageData } = $props(); let user = data.user!; let { session, jwt } = data; - - function getUserStatus(user: User | null, isIn: boolean): string { - if (!user) { - return 'red'; - } - - if (!user.is_active) { - return 'red'; - } - - if (isIn) { - return 'green'; - } - - return 'orange'; - } + let { onlineUsers } = session; </script> <div class="h-full flex lg:flex-row flex-col pt-2 lg:pt-0"> @@ -34,28 +18,22 @@ <h2 class="text-center font-bold">{$t('utils.words.participants')}</h2> <div class="pb-2 space-y-2"> {#each session.users as sessionUser (sessionUser.id)} - <div class="flex space-x-2 items-center"> - <div class="relative mx-2 chat-image size-6" title={sessionUser.nickname}> + <div class="flex space-x-2"> + <div class="rounded-full mx-2 chat-image size-6" title={sessionUser.nickname}> <img src={`https://gravatar.com/avatar/${sessionUser.emailHash}?d=identicon`} alt={sessionUser.nickname} - class="rounded-full border-2 text-sm size-6" + class="rounded-full border-2 text-sm {sessionUser.id == user?.id || + $onlineUsers.has(sessionUser.id) + ? 'border-green-500' + : 'border-red-500'}" /> - <div - class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ - getUserStatus(sessionUser, true) === 'green' - ? 'bg-green-500' - : getUserStatus(sessionUser, false) === 'orange' - ? 'bg-orange-500' - : 'bg-red-500' - }`} - ></div> </div> + <span class:font-bold={sessionUser === user}>{sessionUser.nickname}</span> </div> {/each} </div> - <h2 class="text-center font-bold border-t pt-2">{$t('utils.words.topics')}</h2> <p class="text-center text-sm text-neutral-500 italic">{$t('session.noTopic')}</p> </div> -- GitLab