Merge pull request #2237 from appwrite/feat-SER-165-Show-Banner-if-Unverified

feat: added dismissible prop to header alert and email verification b…
This commit is contained in:
Matej Bačo
2025-08-27 10:00:12 +02:00
committed by GitHub
4 changed files with 85 additions and 3 deletions
@@ -0,0 +1,63 @@
<script lang="ts">
import { base } from '$app/paths';
import { goto } from '$app/navigation';
import { Button } from '$lib/elements/forms';
import { HeaderAlert } from '$lib/layout';
import { Typography } from '@appwrite.io/pink-svelte';
import { hideNotification, shouldShowNotification } from '$lib/helpers/notifications';
import { user } from '$lib/stores/user';
import { wizard } from '$lib/stores/wizard';
import { page } from '$app/state';
const { emailBannerClosed, onEmailBannerClose } = $props<{
emailBannerClosed: boolean;
onEmailBannerClose: (closed: boolean) => void;
}>();
const isOnOnboarding = $derived(() => page.route?.id?.includes('/(console)/onboarding'));
const hasUser = $derived(!!$user);
const needsEmailVerification = $derived(hasUser && !$user.emailVerification);
const shouldShowNotificationBanner = $derived.by(() =>
shouldShowNotification('email-verification-banner')
);
const wizardNotActive = $derived(!$wizard.show && !$wizard.cover);
const bannerNotClosed = $derived(!emailBannerClosed);
const notOnOnboarding = $derived(!isOnOnboarding);
const shouldShowEmailBanner = $derived(
hasUser &&
needsEmailVerification &&
shouldShowNotificationBanner &&
wizardNotActive &&
bannerNotClosed &&
notOnOnboarding
);
function navigateToAccount() {
goto(`${base}/account`);
}
function handleDismiss() {
onEmailBannerClose(true);
hideNotification('email-verification-banner', { coolOffPeriod: 24 * 365 * 100 });
}
</script>
{#if shouldShowEmailBanner}
<HeaderAlert
type="warning"
title="Your email address needs to be verified"
dismissible
on:dismiss={handleDismiss}>
<svelte:fragment>
To avoid losing access to your projects, make sure <Typography.Text
variant="m-500"
style="display:inline">{$user.email}</Typography.Text> is valid and up to date. Email
verification will be required soon.
</svelte:fragment>
<svelte:fragment slot="buttons">
<Button secondary size="s" on:click={navigateToAccount}>Update email address</Button>
</svelte:fragment>
</HeaderAlert>
{/if}
+1
View File
@@ -84,4 +84,5 @@ export { default as UsageCard } from './usageCard.svelte';
export { default as ViewToggle } from './viewToggle.svelte';
export { default as RegionEndpoint } from './regionEndpoint.svelte';
export { default as ExpirationInput } from './expirationInput.svelte';
export { default as EmailVerificationBanner } from './alerts/emailVerificationBanner.svelte';
export { default as SortButton, type SortDirection } from './sortButton.svelte';
+12 -1
View File
@@ -7,11 +7,17 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import { isTabletViewport } from '$lib/stores/viewport';
import { createEventDispatcher } from 'svelte';
import { Button } from '$lib/elements/forms';
import { Icon } from '@appwrite.io/pink-svelte';
import { IconX } from '@appwrite.io/pink-icons-svelte';
export let title: string;
export let type: 'info' | 'success' | 'warning' | 'error' | 'default' = 'info';
export let dismissible = false;
let container: HTMLElement | null = null;
const dispatch = createEventDispatcher();
function setNavigationHeight() {
const alertHeight = container ? container.getBoundingClientRect().height : 0;
@@ -75,9 +81,14 @@
<slot />
</p>
</div>
{#if $$slots.buttons}
{#if $$slots.buttons || dismissible}
<div class="alert-buttons u-flex u-gap-16 u-cross-child-center">
<slot name="buttons" />
{#if dismissible}
<Button text icon size="s" on:click={() => dispatch('dismiss')}>
<Icon icon={IconX} slot="start" size="s" />
</Button>
{/if}
</div>
{/if}
</div>
+9 -2
View File
@@ -3,6 +3,7 @@
import { BillingPlan, INTERVAL } from '$lib/constants';
import Footer from '$lib/layout/footer.svelte';
import Shell from '$lib/layout/shell.svelte';
import { app } from '$lib/stores/app';
import { database, checkForDatabaseBackupPolicies } from '$lib/stores/database';
import { newOrgModal, organization, type Organization } from '$lib/stores/organization';
@@ -39,11 +40,12 @@
import MobileSupportModal from './wizard/support/mobileSupportModal.svelte';
import { showSupportModal } from './wizard/support/store';
import { activeHeaderAlert, consoleVariables } from './store';
import { base } from '$app/paths';
import { headerAlert } from '$lib/stores/headerAlert';
import { UsageRates } from '$lib/components/billing';
import { base } from '$app/paths';
import { canSeeProjects } from '$lib/stores/roles';
import { BottomModalAlert } from '$lib/components';
import { BottomModalAlert, EmailVerificationBanner } from '$lib/components';
import {
IconAnnotation,
IconBookOpen,
@@ -57,6 +59,7 @@
import type { LayoutData } from './$types';
export let data: LayoutData;
let emailBannerClosed = false;
function kebabToSentenceCase(str: string) {
return str
@@ -344,6 +347,10 @@
<Footer slot="footer" />
</Shell>
<EmailVerificationBanner
{emailBannerClosed}
onEmailBannerClose={(closed) => (emailBannerClosed = closed)} />
{#if $wizard.show && $wizard.component}
<svelte:component this={$wizard.component} {...$wizard.props} />
{:else if $wizard.cover}