diff --git a/package-lock.json b/package-lock.json index 61530ef93d..1201290435 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@analytics/google-analytics": "^1.0.5", + "@analytics/google-tag-manager": "^0.5.3", "@appwrite.io/pink": "^0.0.4", "@aw-labs/appwrite-console": "^13.1.0", "@popperjs/core": "^2.11.6", @@ -19,6 +20,7 @@ "logrocket": "^3.0.1", "pretty-bytes": "^6.1.0", "prismjs": "^1.29.0", + "svelte-confetti": "^1.2.2", "tippy.js": "^6.3.7", "web-vitals": "^3.1.1" }, @@ -113,6 +115,11 @@ "resolved": "https://registry.npmjs.org/@analytics/google-analytics/-/google-analytics-1.0.5.tgz", "integrity": "sha512-I/yfiCVQo8AeT72KPa6z571LJKDbLdm4ntDqOHiE2Xehw9UjKcXQWiKTHL1/nfOOiekfpLf/6rFS5P6dQ0vImw==" }, + "node_modules/@analytics/google-tag-manager": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@analytics/google-tag-manager/-/google-tag-manager-0.5.3.tgz", + "integrity": "sha512-hk9cQtccAA48y3yP7wZ7k0ugi7qqi4dGB/w8d+hXwnPbo52MNu07ZtclL94S4GCb2vfVPfUgGz8vpuYBUO1xZw==" + }, "node_modules/@analytics/localstorage-utils": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/@analytics/localstorage-utils/-/localstorage-utils-0.1.8.tgz", @@ -7973,6 +7980,24 @@ "svelte": "^3.55.0" } }, + "node_modules/svelte-check/node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/svelte-confetti": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/svelte-confetti/-/svelte-confetti-1.2.2.tgz", + "integrity": "sha512-LkvWO732jRNmYykWi0IOK7xoBX241p+p+tC7Ef1EcO3TK9b9lpB/vYqKkcwVya+onG2SgQsX2g+JVbVKxq5ixQ==" + }, "node_modules/svelte-hmr": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz", diff --git a/package.json b/package.json index 0d0865856e..f10776a936 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@analytics/google-analytics": "^1.0.5", + "@analytics/google-tag-manager": "^0.5.3", "@appwrite.io/pink": "^0.0.4", "@aw-labs/appwrite-console": "^13.1.0", "@popperjs/core": "^2.11.6", @@ -30,6 +31,7 @@ "logrocket": "^3.0.1", "pretty-bytes": "^6.1.0", "prismjs": "^1.29.0", + "svelte-confetti": "^1.2.2", "tippy.js": "^6.3.7", "web-vitals": "^3.1.1" }, diff --git a/src/app.html b/src/app.html index 994a0b038d..3a25d1151b 100644 --- a/src/app.html +++ b/src/app.html @@ -43,6 +43,8 @@ + + %sveltekit.head% diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index c4324b9704..66a0181404 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -1,7 +1,8 @@ import { page } from '$app/stores'; import { user } from '$lib/stores/user'; -import { ENV, MODE, VARS } from '$lib/system'; +import { ENV, MODE, VARS, isCloud } from '$lib/system'; import googleAnalytics from '@analytics/google-analytics'; +import googleTagManager from '@analytics/google-tag-manager'; import { AppwriteException } from '@aw-labs/appwrite-console'; import Analytics from 'analytics'; import { get } from 'svelte/store'; @@ -11,7 +12,14 @@ const analytics = Analytics({ plugins: [ googleAnalytics({ measurementIds: [VARS.GOOGLE_ANALYTICS || 'G-R4YJ9JN8L4'] - }) + }), + ...(isCloud + ? [ + googleTagManager({ + containerId: [VARS.GOOGLE_TAG || 'GTM-P3T9TBV'] + }) + ] + : []) ] }); diff --git a/src/lib/elements/forms/button.svelte b/src/lib/elements/forms/button.svelte index 27764b2a3e..7895f7e4a6 100644 --- a/src/lib/elements/forms/button.svelte +++ b/src/lib/elements/forms/button.svelte @@ -3,6 +3,7 @@ export let submit = false; export let secondary = false; + export let github = false; export let text = false; export let danger = false; export let disabled = false; @@ -38,6 +39,7 @@ class="button" class:is-only-icon={round} class:is-secondary={secondary} + class:is-github={github} class:is-text={text} class:is-danger={danger} class:is-full-width={fullWidth} @@ -53,6 +55,7 @@ class="button" class:is-only-icon={round} class:is-secondary={secondary} + class:is-github={github} class:is-danger={danger} class:is-text={text} class:is-full-width={fullWidth} @@ -62,3 +65,17 @@ {/if} + + diff --git a/src/lib/images/appwrite-cloud.svg b/src/lib/images/appwrite-cloud.svg new file mode 100644 index 0000000000..c7b7a06942 --- /dev/null +++ b/src/lib/images/appwrite-cloud.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/images/login/cloud-1.svg b/src/lib/images/login/cloud-1.svg new file mode 100644 index 0000000000..a56f11b0ab --- /dev/null +++ b/src/lib/images/login/cloud-1.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/lib/images/login/cloud-2.svg b/src/lib/images/login/cloud-2.svg new file mode 100644 index 0000000000..f11896e563 --- /dev/null +++ b/src/lib/images/login/cloud-2.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/lib/images/login/cloud-bg-mobile.svg b/src/lib/images/login/cloud-bg-mobile.svg new file mode 100644 index 0000000000..063ca22016 --- /dev/null +++ b/src/lib/images/login/cloud-bg-mobile.svg @@ -0,0 +1,626 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/images/login/cloud-bg.svg b/src/lib/images/login/cloud-bg.svg new file mode 100644 index 0000000000..f4b555694f --- /dev/null +++ b/src/lib/images/login/cloud-bg.svg @@ -0,0 +1,632 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lib/layout/header.svelte b/src/lib/layout/header.svelte index 88a9a0b4b6..1b8e7863ed 100644 --- a/src/lib/layout/header.svelte +++ b/src/lib/layout/header.svelte @@ -5,24 +5,25 @@ DropList, DropListItem, DropListLink, - FeedbackGeneral + FeedbackGeneral, + FeedbackNPS } from '$lib/components'; - import { app, feedback } from '$lib/stores/app'; - import { user } from '$lib/stores/user'; - import { organizationList, organization, newOrgModal } from '$lib/stores/organization'; import AppwriteLogo from '$lib/images/appwrite-gray-light.svg'; - import LightMode from '$lib/images/mode/light-mode.svg'; import DarkMode from '$lib/images/mode/dark-mode.svg'; + import LightMode from '$lib/images/mode/light-mode.svg'; import SystemMode from '$lib/images/mode/system-mode.svg'; - import { FeedbackNPS } from '$lib/components'; + import { app, feedback } from '$lib/stores/app'; + import { newOrgModal, organization, organizationList } from '$lib/stores/organization'; + import { user } from '$lib/stores/user'; - let showFeedback = false; - import { slide } from 'svelte/transition'; + import { goto } from '$app/navigation'; import { page } from '$app/stores'; import { Submit, trackEvent } from '$lib/actions/analytics'; import { sdkForConsole } from '$lib/stores/sdk'; - import { goto } from '$app/navigation'; + import { slide } from 'svelte/transition'; + import { isCloud } from '$lib/system'; + let showFeedback = false; let showDropdown = false; let droplistElement: HTMLDivElement; @@ -52,6 +53,17 @@ $: if (showDropdown) { trackEvent('click_menu_dropdown'); } + + const slideFade: typeof slide = (node, options) => { + const slideTrans = slide(node, options); + return { + ...slideTrans, + css: (t, u) => ` + ${slideTrans.css(t, u)}; + opacity: ${t}; + ` + }; + }; @@ -113,7 +125,7 @@ {#if showDropdown}
+ transition:slideFade={{ duration: 150 }}> {#if $organizationList?.total}
    @@ -201,9 +213,81 @@
+ {#if isCloud} +
+ + Claim your Cloud card + +
+ {/if}
{/if} {/if} + + diff --git a/src/lib/layout/notifications.svelte b/src/lib/layout/notifications.svelte index 2027027997..b5ea496b59 100644 --- a/src/lib/layout/notifications.svelte +++ b/src/lib/layout/notifications.svelte @@ -26,4 +26,12 @@ right: 24px; z-index: 1000; } + + @media (max-width: 512px) { + section { + top: calc(var(--main-header-height) + 16px); + right: 16px; + left: 16px; + } + } diff --git a/src/lib/layout/unauthenticated.svelte b/src/lib/layout/unauthenticated.svelte index e2470d309a..3f4ebff5f0 100644 --- a/src/lib/layout/unauthenticated.svelte +++ b/src/lib/layout/unauthenticated.svelte @@ -1,10 +1,17 @@ -
-
- -
+
+

Cloud is Live

+ + +
-
-
- {#if $app.themeInUse === 'dark'} - - {:else} - - {/if} -
+

Now in public beta

+ +
+ +
+

Integrate with your favourite technologies

+
    + {#each technologies as tech} +
  • + +
  • + {/each} +
+
+
+
+ {:else} +
+
+ + Appwrite + +
+ +
+ +
+
+ {#if $app.themeInUse === 'dark'} + + {:else} + + {/if} +
-
- -
-
-

Integrate with your favourite technologies

-
    - {#each technologies as tech} -
  • - -
  • - {/each} -
-
-
-
-
-
+
+
+

Integrate with your favourite technologies

+
    + {#each technologies as tech} +
  • + +
  • + {/each} +
+
+
+
+ {/if} +
+
+ {#if isCloud} + + +
+
Public beta
+
+ {/if} +
-

-
+

+ {#if isCloud} + + + {/if} + +

+
@@ -83,12 +178,181 @@
- -
-

- -

-
+ + diff --git a/src/lib/stores/windowFocus.ts b/src/lib/stores/windowFocus.ts new file mode 100644 index 0000000000..b0c28abca4 --- /dev/null +++ b/src/lib/stores/windowFocus.ts @@ -0,0 +1,29 @@ +import { readable, type Readable } from 'svelte/store'; + +function isCurrentWindowFocused(): boolean { + if (!window) return false; + if (!window.document) return false; + if (typeof window.document.hasFocus !== 'function') return false; + + return window.document.hasFocus(); +} + +export function windowFocusStore(): Readable { + const visibility = readable(isCurrentWindowFocused(), (set) => { + function handler() { + set(isCurrentWindowFocused()); + } + + if (window) { + window.addEventListener('focus', handler); + window.addEventListener('blur', handler); + + return () => { + window.removeEventListener('focus', handler); + window.removeEventListener('blur', handler); + }; + } + }); + + return visibility; +} diff --git a/src/lib/system.ts b/src/lib/system.ts index 596e4faca8..9847882851 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -10,7 +10,8 @@ export const VARS = { | undefined, CONSOLE_MODE: import.meta.env?.VITE_CONSOLE_MODE?.toString() as string | undefined, VERCEL_ENV: import.meta.env?.VITE_VERCEL_ENV?.toString() as string | undefined, - GOOGLE_ANALYTICS: import.meta.env?.VITE_GA_PROJECT?.toString() as string | undefined + GOOGLE_ANALYTICS: import.meta.env?.VITE_GA_PROJECT?.toString() as string | undefined, + GOOGLE_TAG: import.meta.env?.VITE_GTM_PROJECT?.toString() as string | undefined }; export const ENV = { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index fcc41255ea..37c4267e67 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -60,11 +60,20 @@ * Handle initial load. */ if (!$page.url.pathname.startsWith('/auth')) { - const acceptedRoutes = ['/login', '/register', '/recover', '/invite']; + const acceptedRoutes = [ + '/login', + '/register', + '/recover', + '/invite', + '/card', + '/hackathon' + ]; if ($user) { if ( !$page.url.pathname.startsWith('/console') && - !$page.url.pathname.startsWith('/invite') + !$page.url.pathname.startsWith('/invite') && + !$page.url.pathname.startsWith('/card') && + !$page.url.pathname.startsWith('/hackathon') ) { await goto(`${base}/console`, { replaceState: true @@ -142,4 +151,37 @@ border-right-color: hsl(var(--p-tooltip--bg-color)); } } + + .theme-dark .with-separators { + --separator-color: hsl(var(--color-neutral-200)); + --separator-text: hsl(var(--color-neutral-100)); + } + + .with-separators { + --separator-color: hsl(var(--color-neutral-5)); + --separator-text: hsl(var(--color-neutral-50)); + } + + .with-separators { + display: flex; + align-items: center; + gap: 1rem; + + text-transform: uppercase; + width: 100%; + + color: var(--separator-text); + + &::before, + &::after { + content: ''; + flex: 1; + height: 1px; + background: var(--separator-color); + } + } + + [type='checkbox']:where(:checked)::before { + content: '\ea38'; + } diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 9d628c7ead..72d08d69e3 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -31,7 +31,9 @@ export const load: LayoutLoad = async ({ depends, url }) => { '/invite', '/auth/magic-url', '/auth/oauth2/success', - '/auth/oauth2/failure' + '/auth/oauth2/failure', + '/card', + '/hackathon' ]; if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) { diff --git a/src/routes/card/+layout.svelte b/src/routes/card/+layout.svelte new file mode 100644 index 0000000000..9a0263a423 --- /dev/null +++ b/src/routes/card/+layout.svelte @@ -0,0 +1,13 @@ + + + + + +