diff --git a/src/lib/components/billing/gradientBanner.svelte b/src/lib/components/billing/gradientBanner.svelte index 7f9b62a644..228e1f6542 100644 --- a/src/lib/components/billing/gradientBanner.svelte +++ b/src/lib/components/billing/gradientBanner.svelte @@ -6,10 +6,18 @@ import { isTabletViewport } from '$lib/stores/viewport'; import PinkBackground from '$lib/images/pink-background.svg'; import { bannerSpacing } from '$lib/layout/headerAlert.svelte'; - import { createEventDispatcher, onMount, onDestroy } from 'svelte'; + import { createEventDispatcher, getContext, onMount, onDestroy } from 'svelte'; export let variant: 'gradient' | 'image' = 'gradient'; + /** + * An AlertStack parent is itself fixed and measures its children to offset the shell. This + * banner is position:fixed, so inside a stack it measures as 0 and the stack resets the + * header offset we just set, leaving the banner covering the navigation until the next + * resize. Mirror headerAlert.svelte and stand down when the stack is in charge. + */ + const isInStack: boolean = getContext('isInAlertStack') ?? false; + let container: HTMLElement; const dispatch = createEventDispatcher(); @@ -20,6 +28,8 @@ }); const setNavigationHeight = () => { + if (isInStack) return; + const alertHeight = container?.getBoundingClientRect()?.height || 0; const { header, sidebar, content } = queryLayoutElements(); const headerHeight = header?.getBoundingClientRect().height || 0; @@ -51,6 +61,7 @@
{#if variant === 'gradient'}
@@ -92,6 +103,11 @@ position: fixed; padding: 0.8rem; + &.in-stack { + position: relative; + z-index: unset; + } + &.darker { background: var(--bgcolor-neutral-default); } diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 09b4d248c3..84a530f097 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -76,6 +76,7 @@ export { default as EmptyCardImageCloud } from './emptyCardImageCloud.svelte'; export { default as ImagePreview } from './imagePreview.svelte'; export { default as MfaChallengeFormList } from './mfaChallengeFormList.svelte'; export { default as BottomModalAlert } from './bottomModalAlert.svelte'; +export { default as NewConsoleBanner } from './newConsoleBanner.svelte'; export { default as Navbar } from './navbar.svelte'; export { default as Breadcrumbs } from './breadcrumbs.svelte'; export { default as Sidebar } from './sidebar.svelte'; diff --git a/src/lib/components/newConsoleBanner.svelte b/src/lib/components/newConsoleBanner.svelte new file mode 100644 index 0000000000..82857f99c3 --- /dev/null +++ b/src/lib/components/newConsoleBanner.svelte @@ -0,0 +1,46 @@ + + + + + + Introducing the new Appwrite Console, rebuilt from the ground up. + + + + + diff --git a/src/routes/(console)/+layout.svelte b/src/routes/(console)/+layout.svelte index b0f4267981..2cd9e62be6 100644 --- a/src/routes/(console)/+layout.svelte +++ b/src/routes/(console)/+layout.svelte @@ -44,7 +44,7 @@ import { headerAlert } from '$lib/stores/headerAlert'; import { UsageRates } from '$lib/components/billing'; import { canSeeProjects } from '$lib/stores/roles'; - import { BottomModalAlert } from '$lib/components'; + import { BottomModalAlert, NewConsoleBanner } from '$lib/components'; import { isSmallViewport } from '$lib/stores/viewport'; import { IconAnnotation, @@ -325,6 +325,19 @@ $registerSearchers(orgSearcher, projectsSearcher); + onMount(() => { + // Same shape as newDevUpgradePro: promo tier (importance 1) so it can never outrank a + // payment or usage warning, dismissal keyed on the alert id in localStorage. + if (isCloud && !localStorage.getItem('newConsoleBanner')) { + headerAlert.add({ + id: 'newConsoleBanner', + component: NewConsoleBanner, + show: true, + importance: 1 + }); + } + }); + $: (void $headerAlert, ($activeHeaderAlert = headerAlert.getExcluding('impersonation')));