diff --git a/app/Filament/Server/Widgets/ServerConsole.php b/app/Filament/Server/Widgets/ServerConsole.php index f1ca9f370..347ed6be4 100644 --- a/app/Filament/Server/Widgets/ServerConsole.php +++ b/app/Filament/Server/Widgets/ServerConsole.php @@ -3,6 +3,7 @@ namespace App\Filament\Server\Widgets; use App\Exceptions\Http\HttpForbiddenException; +use App\Livewire\AlertBanner; use App\Models\Permission; use App\Models\Server; use App\Models\User; @@ -113,4 +114,14 @@ class ServerConsole extends Widget cache()->put($cacheKey, $data, now()->addMinute()); } } + + #[On('websocket-error')] + public function websocketError(): void + { + AlertBanner::make() + ->title('Could not connect to websocket!') + ->body('Check your browser console for more details.') + ->danger() + ->send(); + } } diff --git a/app/Livewire/AlertBanner.php b/app/Livewire/AlertBanner.php index 068593a0b..5c99d4ee6 100644 --- a/app/Livewire/AlertBanner.php +++ b/app/Livewire/AlertBanner.php @@ -2,40 +2,83 @@ namespace App\Livewire; +use Closure; use Filament\Notifications\Concerns; use Filament\Support\Concerns\EvaluatesClosures; -use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Support\Str; +use Livewire\Wireable; -final class AlertBanner implements Arrayable +final class AlertBanner implements Wireable { use Concerns\HasBody; use Concerns\HasIcon; + use Concerns\HasId; use Concerns\HasStatus; use Concerns\HasTitle; use EvaluatesClosures; - public static function make(): static + protected bool|Closure $closable = false; + + public static function make(?string $id = null): AlertBanner { - return new self(); + $static = new self(); + $static->id($id ?? Str::orderedUuid()); + + return $static; } - public function toArray(): array + public function toLivewire(): array { return [ + 'id' => $this->getId(), 'title' => $this->getTitle(), 'body' => $this->getBody(), 'status' => $this->getStatus(), 'icon' => $this->getIcon(), + 'closeable' => $this->isCloseable(), ]; } - public function send(): static + public static function fromLivewire(mixed $value): AlertBanner { - $alerts = session()->get('alert-banners', []); - $alerts[] = $this->toArray(); + $static = AlertBanner::make(); - session()->flash('alert-banners', $alerts); + $static->id($value['id']); + $static->title($value['title']); + $static->body($value['body']); + $static->status($value['status']); + $static->icon($value['icon']); + $static->closable($value['closeable']); + + return $static; + } + + public function closable(bool|Closure $closable = true): AlertBanner + { + $this->closable = $closable; return $this; } + + public function isCloseable(): bool + { + return $this->evaluate($this->closable); + } + + public function send(): AlertBanner + { + session()->push('alert-banners', $this->toLivewire()); + + return $this; + } + + public function getColorClasses(): string + { + return match ($this->getStatus()) { + 'success' => 'text-success-600 dark:text-success-500', + 'warning' => 'text-warning-600 dark:text-warning-500', + 'danger' => 'text-danger-600 dark:text-danger-500', + default => 'text-info-600 dark:text-info-500', + }; + } } diff --git a/app/Livewire/AlertBannerContainer.php b/app/Livewire/AlertBannerContainer.php new file mode 100644 index 000000000..5b1901c04 --- /dev/null +++ b/app/Livewire/AlertBannerContainer.php @@ -0,0 +1,35 @@ +alertBanners = []; + $this->pullFromSession(); + } + + public function pullFromSession(): void + { + foreach (session()->pull('alert-banners', []) as $alertBanner) { + $alertBanner = AlertBanner::fromLivewire($alertBanner); + $this->alertBanners[$alertBanner->getId()] = $alertBanner; + } + } + + public function remove(string $id): void + { + unset($this->alertBanners[$id]); + } + + public function render(): View + { + return view('livewire.alerts.alert-banner-container'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ea05a4380..d2cdf1b47 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -118,8 +118,8 @@ class AppServiceProvider extends ServiceProvider ); FilamentView::registerRenderHook( - PanelsRenderHook::CONTENT_START, - fn () => view('filament.alerts.alert-banner-container'), + PanelsRenderHook::PAGE_START, + fn () => Blade::render('@livewire(\App\Livewire\AlertBannerContainer::class)'), ); FilamentView::registerRenderHook( diff --git a/resources/views/filament/alerts/alert-banner-container.blade.php b/resources/views/filament/alerts/alert-banner-container.blade.php deleted file mode 100644 index e224a6836..000000000 --- a/resources/views/filament/alerts/alert-banner-container.blade.php +++ /dev/null @@ -1,11 +0,0 @@ -@php - $alertBanners = session()->get('alert-banners', []); -@endphp - -@if (isset($alertBanners)) -
- @foreach ($alertBanners as $alertBanner) - @include('filament.alerts.alert-banner', ['alertBanner' => $alertBanner]) - @endforeach -
-@endif diff --git a/resources/views/filament/alerts/alert-banner.blade.php b/resources/views/filament/alerts/alert-banner.blade.php deleted file mode 100644 index efae7e663..000000000 --- a/resources/views/filament/alerts/alert-banner.blade.php +++ /dev/null @@ -1,40 +0,0 @@ -@props(['alertBanner']) - -@isset ($alertBanner) - @php - $status = $alertBanner['status']; - - $title = $alertBanner['title']; - $body = $alertBanner['body']; - - $icon = $alertBanner['icon'] ?? match ($status) { - "success" => "tabler-circle-check", - "warning" => "tabler-exclamation-circle", - "danger" => "tabler-circle-x", - default => "tabler-info-circle", - }; - - $colorClasses = match ($status) { - "success" => "text-success-600 dark:text-success-500", - "warning" => "text-warning-600 dark:text-warning-500", - "danger" => "text-danger-600 dark:text-danger-500", - default => "text-info-600 dark:text-info-500", - }; - @endphp - -
- @if (filled($icon)) - - @endif - -
- @if (filled($title)) -

{{str($title)->sanitizeHtml()->toHtmlString()}}

- @endif - - @if (filled($body)) -

{{str($body)->sanitizeHtml()->toHtmlString()}}

- @endif -
-
-@endisset diff --git a/resources/views/filament/components/server-console.blade.php b/resources/views/filament/components/server-console.blade.php index a27d63885..63ca38092 100644 --- a/resources/views/filament/components/server-console.blade.php +++ b/resources/views/filament/components/server-console.blade.php @@ -113,17 +113,8 @@ const socket = new WebSocket("{{ $this->getSocket() }}"); let token = '{{ $this->getToken() }}'; - socket.onerror = function(websocketErrorEvent) { - @php - $alerts = session()->get('alert-banners', []); - $alerts[] = [ - 'title' => 'Could not connect to websocket!', - 'body' => 'Check your browser console for more details.', - 'status' => 'danger', - ]; - - session()->flash('alert-banners', $alerts); - @endphp + socket.onerror = (event) => { + $wire.dispatchSelf('websocket-error'); }; socket.onmessage = function(websocketMessageEvent) { diff --git a/resources/views/livewire/alerts/alert-banner-container.blade.php b/resources/views/livewire/alerts/alert-banner-container.blade.php new file mode 100644 index 000000000..b8a8402d5 --- /dev/null +++ b/resources/views/livewire/alerts/alert-banner-container.blade.php @@ -0,0 +1,5 @@ +
+ @foreach (array_values($alertBanners) as $alertBanner) + @include('livewire.alerts.alert-banner', ['alertBanner' => $alertBanner]) + @endforeach +
diff --git a/resources/views/livewire/alerts/alert-banner.blade.php b/resources/views/livewire/alerts/alert-banner.blade.php new file mode 100644 index 000000000..68bae2706 --- /dev/null +++ b/resources/views/livewire/alerts/alert-banner.blade.php @@ -0,0 +1,27 @@ +@props(['alertBanner']) + +@php + $icon = $alertBanner->getIcon(); + $title = $alertBanner->getTitle(); + $body = $alertBanner->getBody(); +@endphp + +
+ @if (filled($icon)) + + @endif + +
+ @if (filled($title)) +

{{str($title)->sanitizeHtml()->toHtmlString()}}

+ @endif + + @if (filled($body)) +

{{str($body)->sanitizeHtml()->toHtmlString()}}

+ @endif +
+ + @if ($alertBanner->isCloseable()) + + @endif +