loadUnreadStatus(); // Dispatch browser event to notify JavaScript $this->dispatch('switch-profile-unread-updated', hasUnread: $this->hasAnyUnread); } private function loadUnreadStatus() { $webUser = Auth::guard('web')->user(); if (!$webUser) { return; } // Get user's profiles using same logic as SwitchProfile $userWithRelations = \App\Models\User::with([ 'organizations', 'banksManaged', 'admins' ])->find($webUser->id); if (!$userWithRelations) { return; } // Filter out removed (soft-deleted) items $organizations = $userWithRelations->organizations->filter( fn ($organizations) => $organizations->deleted_at === null || $organizations->deleted_at > now() ); $banks = $userWithRelations->banksManaged->filter( fn ($bank) => $bank->deleted_at === null || $bank->deleted_at > now() ); $admins = $userWithRelations->admins->filter( fn ($admin) => $admin->deleted_at === null || $admin->deleted_at > now() ); // Merge profiles collections $profiles = $organizations ->concat($banks) ->concat($admins); // Collect all profile IDs $profileIds = [ 'user' => [$webUser->id], 'admin' => [], 'bank' => [], 'organization' => [], ]; foreach ($profiles as $profile) { if ($profile instanceof \App\Models\Organization) { $profileIds['organization'][] = $profile->id; } elseif ($profile instanceof \App\Models\Bank) { $profileIds['bank'][] = $profile->id; } elseif ($profile instanceof \App\Models\Admin) { $profileIds['admin'][] = $profile->id; } } // Get active profile info to exclude it from unread check $activeProfileType = session('activeProfileType'); $activeProfileId = session('activeProfileId'); // Check for unread messages $profilesWithUnread = $this->checkUnreadMessages($profileIds); // Check if ANY profile has unread (EXCLUDING the currently active profile) // Get the active profile type key $activeTypeKey = null; if ($activeProfileType) { $activeTypeKey = match($activeProfileType) { 'App\\Models\\User', 'App\Models\User' => 'user', 'App\\Models\\Admin', 'App\Models\Admin' => 'admin', 'App\\Models\\Bank', 'App\Models\Bank' => 'bank', 'App\\Models\\Organization', 'App\Models\Organization' => 'organization', default => null }; } // Check user profile (only if not active) if ($activeTypeKey !== 'user') { $this->hasAnyUnread = $profilesWithUnread['user'] ?? false; } // Check other profile types (excluding active) if (!$this->hasAnyUnread) { foreach (['admin', 'bank', 'organization'] as $type) { if (!empty($profilesWithUnread[$type])) { foreach ($profilesWithUnread[$type] as $profileId => $hasUnread) { // Skip if this is the active profile if ($activeTypeKey === $type && $activeProfileId == $profileId) { continue; } if ($hasUnread) { $this->hasAnyUnread = true; break 2; } } } } } } private function checkUnreadMessages(array $profileIds): array { $result = [ 'user' => false, 'admin' => [], 'bank' => [], 'organization' => [], ]; // Collect all profile type/id pairs $profilePairs = []; foreach ($profileIds as $type => $ids) { foreach ($ids as $id) { $modelClass = $this->getModelClass($type); $profilePairs[] = ['type' => $modelClass, 'id' => $id]; } } if (empty($profilePairs)) { return $result; } // Use parameter binding with IN clause for better escaping $typeIds = collect($profilePairs)->groupBy('type')->map(function ($items) { return $items->pluck('id')->toArray(); })->toArray(); // Check each type separately and combine results foreach ($typeIds as $modelClass => $ids) { $unreadForType = DB::table('wirechat_participants as p') ->select('p.participantable_type', 'p.participantable_id') ->join('wirechat_conversations as c', 'p.conversation_id', '=', 'c.id') ->where('p.participantable_type', '=', $modelClass) ->whereIn('p.participantable_id', $ids) ->whereNull('p.deleted_at') ->whereExists(function ($query) { $query->select(DB::raw(1)) ->from('wirechat_messages as m') ->whereColumn('m.conversation_id', 'p.conversation_id') ->whereNull('m.deleted_at') ->where(function ($q) { $q->whereNull('p.conversation_read_at') ->orWhereColumn('m.created_at', '>', 'p.conversation_read_at'); }) ->where(function ($q) { $q->where('m.sendable_id', '!=', DB::raw('p.participantable_id')) ->orWhere('m.sendable_type', '!=', DB::raw('p.participantable_type')); }); }) ->get(); foreach ($unreadForType as $row) { $type = $this->getTypeFromModelClass($row->participantable_type); $id = $row->participantable_id; if ($type === 'user') { $result['user'] = true; } else { $result[$type][$id] = true; } } } return $result; } private function getModelClass(string $type): string { return match($type) { 'user' => 'App\Models\User', 'admin' => 'App\Models\Admin', 'bank' => 'App\Models\Bank', 'organization' => 'App\Models\Organization', }; } private function getTypeFromModelClass(string $class): string { return match($class) { 'App\\Models\\User', 'App\Models\User' => 'user', 'App\\Models\\Admin', 'App\Models\Admin' => 'admin', 'App\\Models\\Bank', 'App\Models\Bank' => 'bank', 'App\\Models\\Organization', 'App\Models\Organization' => 'organization', default => 'unknown' }; } public function render() { return view('livewire.switch-profile-unread-indicator'); } }