From 9bbcac71f0ef8d2e7082c9a6ad91e02d919d6dcc Mon Sep 17 00:00:00 2001 From: Tajim Date: Thu, 24 Sep 2026 21:50:52 +0600 Subject: [PATCH 1/6] feat(nav): update mobile bottom nav with dynamic profile or login icon --- .../js/components/navigation/Navigation.tsx | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/resources/js/components/navigation/Navigation.tsx b/resources/js/components/navigation/Navigation.tsx index 60ac61e6..6fa5cd0b 100644 --- a/resources/js/components/navigation/Navigation.tsx +++ b/resources/js/components/navigation/Navigation.tsx @@ -841,6 +841,9 @@ export const SiteBottomNav = defineComponent({ name: 'SiteBottomNav', setup() { const page = usePage(); + const user = computed( + () => page.props.auth?.user as AuthedUser | undefined, + ); const currentUrl = computed(() => String(page.url)); const bottomNavItems = computed(() => primaryNavItems.filter((i) => i.showInBottom !== false), @@ -848,6 +851,30 @@ export const SiteBottomNav = defineComponent({ const homeHref = computed(() => preferredHomeHref(currentUrl.value)); + const authItemHref = computed(() => { + if (!user.value) { + return '/login'; + } + + return user.value.username + ? `/u/${user.value.username}` + : '/profile'; + }); + + const isAuthItemActive = computed(() => { + if (user.value) { + return ( + currentUrl.value.startsWith('/profile') || + currentUrl.value.startsWith('/u/') + ); + } + + return ( + currentUrl.value.startsWith('/login') || + currentUrl.value.startsWith('/register') + ); + }); + const isActive = (href: string, match?: (url: string) => boolean) => { if (match) { return match(currentUrl.value); @@ -899,6 +926,38 @@ export const SiteBottomNav = defineComponent({ ))} + + + + + {user.value ? 'Profile' : 'Login'} + + ); From 9b8c80e749b0af90727b254818faeb59973b8e79 Mon Sep 17 00:00:00 2001 From: Tajim Date: Thu, 24 Sep 2026 21:50:54 +0600 Subject: [PATCH 2/6] fix(chat): make user_id nullable on chat messages and handle deleted users gracefully --- app/Events/ChatMessageSent.php | 12 +++++-- app/Http/Controllers/ChatController.php | 10 +++++- ...ser_id_nullable_on_chat_messages_table.php | 32 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 database/migrations/2026_09_24_214500_make_user_id_nullable_on_chat_messages_table.php diff --git a/app/Events/ChatMessageSent.php b/app/Events/ChatMessageSent.php index debc56a9..f163b26d 100644 --- a/app/Events/ChatMessageSent.php +++ b/app/Events/ChatMessageSent.php @@ -31,14 +31,22 @@ public function __construct(ChatMessage $chatMessage) 'reply_to_content' => $chatMessage->reply_to_content, 'reactions' => $chatMessage->getFormattedReactions(null), 'created_at' => $chatMessage->created_at->toIso8601String(), - 'user' => [ + 'user' => $chatMessage->user ? [ 'id' => $chatMessage->user->id, 'name' => $chatMessage->user->name, 'username' => $chatMessage->user->username, 'image_url' => $chatMessage->user->image_url, 'institution' => $chatMessage->user->institution, 'is_verified' => $chatMessage->user->is_verified, - 'roles' => $chatMessage->user->roles->pluck('name')->toArray(), + 'roles' => $chatMessage->user->roles ? $chatMessage->user->roles->pluck('name')->toArray() : [], + ] : [ + 'id' => null, + 'name' => 'Deleted User', + 'username' => null, + 'image_url' => null, + 'institution' => null, + 'is_verified' => false, + 'roles' => [], ], ]; } diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index 5be50636..09cf4d41 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -74,7 +74,7 @@ public function index(Request $request) 'reply_to_content' => $msg->deleted_at ? null : $msg->reply_to_content, 'reactions' => $msg->getFormattedReactions($user?->id), 'created_at' => $msg->created_at->toIso8601String(), - 'user' => [ + 'user' => $msg->user ? [ 'id' => $msg->user->id, 'name' => $msg->user->name, 'username' => $msg->user->username, @@ -82,6 +82,14 @@ public function index(Request $request) 'institution' => $msg->user->institution, 'is_verified' => $msg->user->is_verified, 'roles' => $msg->user->roles->pluck('name')->toArray(), + ] : [ + 'id' => null, + 'name' => 'Deleted User', + 'username' => null, + 'image_url' => null, + 'institution' => null, + 'is_verified' => false, + 'roles' => [], ], ]); diff --git a/database/migrations/2026_09_24_214500_make_user_id_nullable_on_chat_messages_table.php b/database/migrations/2026_09_24_214500_make_user_id_nullable_on_chat_messages_table.php new file mode 100644 index 00000000..cac9916d --- /dev/null +++ b/database/migrations/2026_09_24_214500_make_user_id_nullable_on_chat_messages_table.php @@ -0,0 +1,32 @@ +dropForeign(['user_id']); + $table->foreignId('user_id')->nullable()->change(); + $table->foreign('user_id')->references('id')->on('users')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('chat_messages', function (Blueprint $table) { + $table->dropForeign(['user_id']); + $table->foreignId('user_id')->nullable(false)->change(); + $table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete(); + }); + } +}; From 9e6ca3f3b4ccf9ffb540079aad4ce3052a12c3bd Mon Sep 17 00:00:00 2001 From: Tajim Date: Thu, 24 Sep 2026 21:50:57 +0600 Subject: [PATCH 3/6] fix(chat): align verified badge with user name and handle deleted user state in ui --- resources/js/pages/Chat/Index.vue | 112 ++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/resources/js/pages/Chat/Index.vue b/resources/js/pages/Chat/Index.vue index bde6cb12..b3c2bd18 100644 --- a/resources/js/pages/Chat/Index.vue +++ b/resources/js/pages/Chat/Index.vue @@ -20,6 +20,7 @@ import { Radio, AtSign, Users, + UserX, } from 'lucide-vue-next'; import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'; import AuthModal from '@/components/AuthModal.vue'; @@ -34,9 +35,9 @@ import { getCsrfToken } from '@/lib/useCsrf'; import { formatDateDivider, formatTime } from '@/lib/useDate'; interface ChatUser { - id: number; + id: number | null; name: string; - username: string; + username: string | null; image_url: string | null; institution: string | null; is_verified: boolean; @@ -1340,6 +1341,10 @@ const isGroupedWithPrevious = (idx: number) => { return false; } + if (!prev.user?.id || !curr.user?.id) { + return false; + } + if (Number(prev.user.id) !== Number(curr.user.id)) { return false; } @@ -1358,6 +1363,10 @@ const isBanModalOpen = ref(false); const selectedUserToBan = ref(null); const openBanModal = (user: ChatUser) => { + if (!user.id) { + return; + } + closeMobileActions(); selectedUserToBan.value = { id: user.id, @@ -1598,6 +1607,7 @@ onUnmounted(() => { class="group relative flex items-start gap-2.5 rounded-xl px-2.5 py-1.5 transition-colors duration-150 sm:gap-3 sm:px-3 sm:py-2" :class="[ currentUser && + msg.user?.id && Number(currentUser.id) === Number(msg.user.id) ? 'bg-indigo-50/30 dark:bg-indigo-950/15' : 'hover:bg-slate-50/80 dark:hover:bg-zinc-800/40', @@ -1612,28 +1622,37 @@ onUnmounted(() => { >
- - - - {{ msg.user.name?.charAt(0) || 'U' }} - - + {
- {{ msg.user.name }} + {{ + msg.user.name + }} + + + {{ msg.user?.name || 'Deleted User' }} + You - - @{{ msg.user.username }} @@ -1874,13 +1905,14 @@ onUnmounted(() => { @@ -1891,6 +1923,7 @@ onUnmounted(() => { !msg.is_deleted && !msg.deleted_at && currentUser && + msg.user?.id && Number(currentUser.id) !== Number(msg.user.id) " @@ -1923,6 +1956,7 @@ onUnmounted(() => { v-if=" (!msg.is_deleted && !msg.deleted_at) || ((can('manage chat') || canDelete) && + msg.user?.id && Number(currentUser?.id) !== Number(msg.user.id)) " @@ -2060,7 +2094,7 @@ onUnmounted(() => { Replying to {{ - activeReplyTo.user.name + activeReplyTo.user?.name || 'Deleted User' }}: {
-
+
@@ -694,40 +762,28 @@ const getContributorAvatar = (contributor: Contributor) => { {{ form.errors.appreciations }}

- -
-
+ Terms & Conditions + + এবং + + Privacy Policy + + -তে সম্মতি দিচ্ছেন। +

@@ -744,7 +800,7 @@ const getContributorAvatar = (contributor: Contributor) => {