Skip to content

Commit cfc7880

Browse files
authored
Merge pull request #2237 from appwrite/feat-SER-165-Show-Banner-if-Unverified
feat: added dismissible prop to header alert and email verification b…
2 parents 7b9eaa4 + 3024ae7 commit cfc7880

4 files changed

Lines changed: 85 additions & 3 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<script lang="ts">
2+
import { base } from '$app/paths';
3+
import { goto } from '$app/navigation';
4+
import { Button } from '$lib/elements/forms';
5+
import { HeaderAlert } from '$lib/layout';
6+
import { Typography } from '@appwrite.io/pink-svelte';
7+
import { hideNotification, shouldShowNotification } from '$lib/helpers/notifications';
8+
import { user } from '$lib/stores/user';
9+
import { wizard } from '$lib/stores/wizard';
10+
import { page } from '$app/state';
11+
12+
const { emailBannerClosed, onEmailBannerClose } = $props<{
13+
emailBannerClosed: boolean;
14+
onEmailBannerClose: (closed: boolean) => void;
15+
}>();
16+
17+
const isOnOnboarding = $derived(() => page.route?.id?.includes('/(console)/onboarding'));
18+
19+
const hasUser = $derived(!!$user);
20+
const needsEmailVerification = $derived(hasUser && !$user.emailVerification);
21+
const shouldShowNotificationBanner = $derived.by(() =>
22+
shouldShowNotification('email-verification-banner')
23+
);
24+
const wizardNotActive = $derived(!$wizard.show && !$wizard.cover);
25+
const bannerNotClosed = $derived(!emailBannerClosed);
26+
const notOnOnboarding = $derived(!isOnOnboarding);
27+
28+
const shouldShowEmailBanner = $derived(
29+
hasUser &&
30+
needsEmailVerification &&
31+
shouldShowNotificationBanner &&
32+
wizardNotActive &&
33+
bannerNotClosed &&
34+
notOnOnboarding
35+
);
36+
37+
function navigateToAccount() {
38+
goto(`${base}/account`);
39+
}
40+
41+
function handleDismiss() {
42+
onEmailBannerClose(true);
43+
hideNotification('email-verification-banner', { coolOffPeriod: 24 * 365 * 100 });
44+
}
45+
</script>
46+
47+
{#if shouldShowEmailBanner}
48+
<HeaderAlert
49+
type="warning"
50+
title="Your email address needs to be verified"
51+
dismissible
52+
on:dismiss={handleDismiss}>
53+
<svelte:fragment>
54+
To avoid losing access to your projects, make sure <Typography.Text
55+
variant="m-500"
56+
style="display:inline">{$user.email}</Typography.Text> is valid and up to date. Email
57+
verification will be required soon.
58+
</svelte:fragment>
59+
<svelte:fragment slot="buttons">
60+
<Button secondary size="s" on:click={navigateToAccount}>Update email address</Button>
61+
</svelte:fragment>
62+
</HeaderAlert>
63+
{/if}

src/lib/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,5 @@ export { default as UsageCard } from './usageCard.svelte';
8484
export { default as ViewToggle } from './viewToggle.svelte';
8585
export { default as RegionEndpoint } from './regionEndpoint.svelte';
8686
export { default as ExpirationInput } from './expirationInput.svelte';
87+
export { default as EmailVerificationBanner } from './alerts/emailVerificationBanner.svelte';
8788
export { default as SortButton, type SortDirection } from './sortButton.svelte';

src/lib/layout/headerAlert.svelte

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@
77
<script lang="ts">
88
import { onDestroy, onMount } from 'svelte';
99
import { isTabletViewport } from '$lib/stores/viewport';
10+
import { createEventDispatcher } from 'svelte';
11+
import { Button } from '$lib/elements/forms';
12+
import { Icon } from '@appwrite.io/pink-svelte';
13+
import { IconX } from '@appwrite.io/pink-icons-svelte';
1014
1115
export let title: string;
1216
export let type: 'info' | 'success' | 'warning' | 'error' | 'default' = 'info';
17+
export let dismissible = false;
1318
1419
let container: HTMLElement | null = null;
20+
const dispatch = createEventDispatcher();
1521
1622
function setNavigationHeight() {
1723
const alertHeight = container ? container.getBoundingClientRect().height : 0;
@@ -75,9 +81,14 @@
7581
<slot />
7682
</p>
7783
</div>
78-
{#if $$slots.buttons}
84+
{#if $$slots.buttons || dismissible}
7985
<div class="alert-buttons u-flex u-gap-16 u-cross-child-center">
8086
<slot name="buttons" />
87+
{#if dismissible}
88+
<Button text icon size="s" on:click={() => dispatch('dismiss')}>
89+
<Icon icon={IconX} slot="start" size="s" />
90+
</Button>
91+
{/if}
8192
</div>
8293
{/if}
8394
</div>

src/routes/(console)/+layout.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { BillingPlan, INTERVAL } from '$lib/constants';
44
import Footer from '$lib/layout/footer.svelte';
55
import Shell from '$lib/layout/shell.svelte';
6+
67
import { app } from '$lib/stores/app';
78
import { database, checkForDatabaseBackupPolicies } from '$lib/stores/database';
89
import { newOrgModal, organization, type Organization } from '$lib/stores/organization';
@@ -39,11 +40,12 @@
3940
import MobileSupportModal from './wizard/support/mobileSupportModal.svelte';
4041
import { showSupportModal } from './wizard/support/store';
4142
import { activeHeaderAlert, consoleVariables } from './store';
43+
44+
import { base } from '$app/paths';
4245
import { headerAlert } from '$lib/stores/headerAlert';
4346
import { UsageRates } from '$lib/components/billing';
44-
import { base } from '$app/paths';
4547
import { canSeeProjects } from '$lib/stores/roles';
46-
import { BottomModalAlert } from '$lib/components';
48+
import { BottomModalAlert, EmailVerificationBanner } from '$lib/components';
4749
import {
4850
IconAnnotation,
4951
IconBookOpen,
@@ -57,6 +59,7 @@
5759
import type { LayoutData } from './$types';
5860
5961
export let data: LayoutData;
62+
let emailBannerClosed = false;
6063
6164
function kebabToSentenceCase(str: string) {
6265
return str
@@ -344,6 +347,10 @@
344347
<Footer slot="footer" />
345348
</Shell>
346349

350+
<EmailVerificationBanner
351+
{emailBannerClosed}
352+
onEmailBannerClose={(closed) => (emailBannerClosed = closed)} />
353+
347354
{#if $wizard.show && $wizard.component}
348355
<svelte:component this={$wizard.component} {...$wizard.props} />
349356
{:else if $wizard.cover}

0 commit comments

Comments
 (0)