diff --git a/app-modules/identity/src/ExternalIdentity/Enums/IdentityProvider.php b/app-modules/identity/src/ExternalIdentity/Enums/IdentityProvider.php index 47303a08..3f8e16d0 100644 --- a/app-modules/identity/src/ExternalIdentity/Enums/IdentityProvider.php +++ b/app-modules/identity/src/ExternalIdentity/Enums/IdentityProvider.php @@ -7,6 +7,7 @@ use App\Contracts\ApiKeyClientContract; use App\Contracts\OAuthClientContract; use App\Enums\Concerns\StringifyEnum; +use App\Support\ProfileHandle; use Filament\Support\Colors\Color; use Filament\Support\Contracts\HasColor; use Filament\Support\Contracts\HasDescription; @@ -106,6 +107,20 @@ public function getApiKeyClient(): ?ApiKeyClientContract }; } + public function profileUrl(?string $handle): ?string + { + if (blank($handle)) { + return null; + } + + return match ($this) { + self::GitHub => ProfileHandle::url('https://github.com/', $handle), + self::Twitch => ProfileHandle::url('https://twitch.tv/', $handle), + self::DevTo => ProfileHandle::url('https://dev.to/', $handle), + default => null, + }; + } + public function getClient(): ?OAuthClientContract { return match ($this) { diff --git a/app-modules/identity/tests/Unit/ExternalIdentity/IdentityProviderProfileUrlTest.php b/app-modules/identity/tests/Unit/ExternalIdentity/IdentityProviderProfileUrlTest.php new file mode 100644 index 00000000..f8154349 --- /dev/null +++ b/app-modules/identity/tests/Unit/ExternalIdentity/IdentityProviderProfileUrlTest.php @@ -0,0 +1,57 @@ +profileUrl('danielhe4rt'))->toBe($expected); +})->with([ + 'github' => [IdentityProvider::GitHub, 'https://github.com/danielhe4rt'], + 'twitch' => [IdentityProvider::Twitch, 'https://twitch.tv/danielhe4rt'], +]); + +it('returns null for discord, which has no public profile page', function (): void { + expect(IdentityProvider::Discord->profileUrl('danielhe4rt'))->toBeNull(); +}); + +it('returns null for providers outside the supported set', function (): void { + expect(IdentityProvider::Steam->profileUrl('danielhe4rt'))->toBeNull() + ->and(IdentityProvider::Spotify->profileUrl('danielhe4rt'))->toBeNull(); +}); + +it('strips a leading @ and surrounding spaces from the handle', function (string $handle): void { + expect(IdentityProvider::GitHub->profileUrl($handle))->toBe('https://github.com/danielhe4rt'); +})->with([ + 'plain' => ['danielhe4rt'], + 'at prefixed' => ['@danielhe4rt'], + 'padded' => [' danielhe4rt '], + 'at prefixed and padded' => [' @danielhe4rt '], +]); + +it('passes an already absolute url through untouched', function (): void { + expect(IdentityProvider::GitHub->profileUrl('https://github.com/danielhe4rt')) + ->toBe('https://github.com/danielhe4rt'); +}); + +it('returns null for an empty handle', function (?string $handle): void { + expect(IdentityProvider::GitHub->profileUrl($handle))->toBeNull(); +})->with([ + 'null' => [null], + 'empty' => [''], + 'only spaces' => [' '], +]); + +it('gives every supported provider a decided answer', function (): void { + $decided = [ + IdentityProvider::GitHub->value => 'https://github.com/handle', + IdentityProvider::Twitch->value => 'https://twitch.tv/handle', + IdentityProvider::DevTo->value => 'https://dev.to/handle', + IdentityProvider::Discord->value => null, + ]; + + foreach (IdentityProvider::supportedProviders() as $provider) { + expect($decided)->toHaveKey($provider->value) + ->and($provider->profileUrl('handle'))->toBe($decided[$provider->value]); + } +}); diff --git a/app-modules/panel-app/resources/views/components/profile-link.blade.php b/app-modules/panel-app/resources/views/components/profile-link.blade.php new file mode 100644 index 00000000..1d6646b6 --- /dev/null +++ b/app-modules/panel-app/resources/views/components/profile-link.blade.php @@ -0,0 +1,139 @@ +@props (['user' => null]) + +@php + $isLinkable = $user !== null && $user->banned_at === null && filled($user->username); +@endphp + +@if ($isLinkable) + + + {{ $slot }} + + +
+ + + +
+
+@else + {{ $slot }} +@endif diff --git a/app-modules/panel-app/resources/views/components/timeline/header.blade.php b/app-modules/panel-app/resources/views/components/timeline/header.blade.php index 2f9409ef..aafb348c 100644 --- a/app-modules/panel-app/resources/views/components/timeline/header.blade.php +++ b/app-modules/panel-app/resources/views/components/timeline/header.blade.php @@ -2,32 +2,30 @@ @php $displayName = $user?->name ?? 'Usuário removido'; - $avatarUrl = $user?->getFirstMediaUrl('avatar') ?: null; @endphp
- @if ($avatarUrl) - {{ $displayName }} - @else -
- {{ str($displayName)->substr(0, 2)->upper() }} -
- @endif + + + +
- {{ $displayName }} - @if ($user?->username) + {{ $displayName }} - @endif + @if ($user?->username) + + @endif + {{ $createdAt->diffForHumans(short: true) }}
diff --git a/app-modules/panel-app/resources/views/components/user-avatar.blade.php b/app-modules/panel-app/resources/views/components/user-avatar.blade.php new file mode 100644 index 00000000..8bd00191 --- /dev/null +++ b/app-modules/panel-app/resources/views/components/user-avatar.blade.php @@ -0,0 +1,41 @@ +@props (['user' => null, 'size' => 'md']) + +@use('He4rt\Profile\Support\ProfileInitials') + +@php + $displayName = $user?->name ?? 'Usuário removido'; + $avatarUrl = $user?->getFirstMediaUrl('avatar') ?: null; + $initials = ProfileInitials::for($displayName, $user?->username); + + $sizeClasses = match ($size) { + 'lg' => 'h-9 w-9 sm:h-10 sm:w-10', + 'sm' => 'h-7 w-7 sm:h-8 sm:w-8', + default => 'h-8 w-8', + }; + + $initialsClasses = match ($size) { + 'lg' => 'text-xs sm:text-sm', + 'sm' => 'text-[10px] sm:text-xs', + default => 'text-xs', + }; +@endphp + +@if ($avatarUrl) + {{ $displayName }}class(['shrink-0 rounded-full object-cover', $sizeClasses]) }} + /> +@else +
class([ + 'flex shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-purple-500 to-amber-500 font-semibold text-white', + $sizeClasses, + $initialsClasses, + ]) + }} + > + {{ $initials }} +
+@endif diff --git a/app-modules/panel-app/resources/views/livewire/timeline/composer.blade.php b/app-modules/panel-app/resources/views/livewire/timeline/composer.blade.php index d770e08c..44a26a56 100644 --- a/app-modules/panel-app/resources/views/livewire/timeline/composer.blade.php +++ b/app-modules/panel-app/resources/views/livewire/timeline/composer.blade.php @@ -4,23 +4,7 @@ class="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-wh >
- @if ($this->avatarUrl) - - @else - - @endif +