From 62dd3f5cd796aeec73cd33f13fe98897de7c0b8b Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 17 Dec 2025 20:05:34 -0800 Subject: [PATCH] move notification indicator to menu button shows mail icon with pulse animation when notifications exist --- src/assets/icons/mail.svg | 3 + src/lib/components/Navigation.svelte | 132 +++++++++++++++++++-------- 2 files changed, 99 insertions(+), 36 deletions(-) create mode 100644 src/assets/icons/mail.svg diff --git a/src/assets/icons/mail.svg b/src/assets/icons/mail.svg new file mode 100644 index 00000000..ca34cb55 --- /dev/null +++ b/src/assets/icons/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/lib/components/Navigation.svelte b/src/lib/components/Navigation.svelte index 62191fb0..e2042c51 100644 --- a/src/lib/components/Navigation.svelte +++ b/src/lib/components/Navigation.svelte @@ -17,7 +17,6 @@ import UserSettingsModal from './UserSettingsModal.svelte' import InvitationsModal from './crew/InvitationsModal.svelte' import { authStore } from '$lib/stores/auth.store' - import { crewStore } from '$lib/stores/crew.store.svelte' // Props from layout data const { @@ -145,6 +144,15 @@ // Database back button hover state let databaseBackHovered = $state(false) + // Query for the user's crew (to determine if phantom claims should be fetched) + const myCrewQuery = createQuery(() => ({ + ...crewQueries.myCrew(), + enabled: isAuth + })) + + // Derived: whether the user is in a crew (from query, not store) + const isInCrew = $derived(myCrewQuery.data != null) + // Query for pending invitations (only when authenticated) const pendingInvitationsQuery = createQuery(() => ({ ...crewQueries.pendingInvitations(), @@ -154,7 +162,7 @@ // Query for pending phantom claims (only when authenticated and in a crew) const pendingPhantomClaimsQuery = createQuery(() => ({ ...crewQueries.pendingPhantomClaims(), - enabled: isAuth && crewStore.isInCrew + enabled: isAuth && isInCrew })) // Derived counts @@ -264,23 +272,16 @@ aria-label="Your account" class="profile-link" > - - {#if avatarSrc} - {username} - {/if} - {#if totalNotificationCount > 0} - - - - {/if} - + {#if avatarSrc} + {username} + {/if} {username} @@ -298,8 +299,14 @@
  • - - + + {#if totalNotificationCount > 0} + + {:else} + + {/if} @@ -317,11 +324,13 @@ {/if} - {#if isAuth && totalNotificationCount > 0} + {#if isAuth} {/if} @@ -556,25 +565,12 @@ align-items: center; gap: spacing.$unit-half; - .avatar-container { - position: relative; - display: flex; - align-items: center; - justify-content: center; - } - .user-avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; } - - .avatar-badge { - position: absolute; - top: -2px; - right: -4px; - } } // Dropdown button with badge (for Invitations) @@ -630,6 +626,70 @@ } } + // Notification pulse animation for the more trigger + @keyframes notification-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.6; + } + } + + :global(.nav-more-trigger.has-notification) { + animation: notification-pulse 2s ease-in-out infinite; + // Default pulse color (no element selected) + background-color: var(--button-primary-bg); + color: white; + // Compensate for larger mail icon (18px vs 14px ellipsis) + padding: spacing.$unit calc(spacing.$unit + 3px); + + &:hover { + animation: none; + background-color: var(--button-primary-bg-hover); + } + } + + // Element-specific notification colors + :global(.nav-more-trigger.has-notification.wind) { + background-color: var(--wind-button-bg); + &:hover { + background-color: var(--wind-button-bg-hover); + } + } + :global(.nav-more-trigger.has-notification.fire) { + background-color: var(--fire-button-bg); + &:hover { + background-color: var(--fire-button-bg-hover); + } + } + :global(.nav-more-trigger.has-notification.water) { + background-color: var(--water-button-bg); + &:hover { + background-color: var(--water-button-bg-hover); + } + } + :global(.nav-more-trigger.has-notification.earth) { + background-color: var(--earth-button-bg); + &:hover { + background-color: var(--earth-button-bg-hover); + } + } + :global(.nav-more-trigger.has-notification.light) { + background-color: var(--light-button-bg); + color: black; + &:hover { + background-color: var(--light-button-bg-hover); + } + } + :global(.nav-more-trigger.has-notification.dark) { + background-color: var(--dark-button-bg); + &:hover { + background-color: var(--dark-button-bg-hover); + } + } + // Style the new team button as a prominent circular button // Remove redundant styles that the Button component already handles :global(.new-team-button) {