Initial commit

This commit is contained in:
Ronald Huynen
2026-03-23 21:37:59 +01:00
commit 2547717edb
2193 changed files with 972171 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
<?php
namespace App\Http\Livewire;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class SwitchProfileUnreadIndicator extends Component
{
public $hasAnyUnread = false;
public function mount()
{
$this->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');
}
}