diff --git a/package-lock.json b/package-lock.json index f2185aa40e..a05ed67dac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,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" }, @@ -8011,6 +8012,11 @@ "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 6735fb03af..3bccef3cae 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,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/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.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/unauthenticated.svelte b/src/lib/layout/unauthenticated.svelte index 8672be7bdf..a4c75fc060 100644 --- a/src/lib/layout/unauthenticated.svelte +++ b/src/lib/layout/unauthenticated.svelte @@ -1,10 +1,16 @@ -
-
- -
+
+

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} + + {/if} +

@@ -84,11 +167,141 @@
-
-

- -

-
+ {#if isCloud} +
+
Public beta
+
+ {/if}
+ + 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/routes/+layout.svelte b/src/routes/+layout.svelte index fcc41255ea..78564d2c41 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -60,11 +60,12 @@ * Handle initial load. */ if (!$page.url.pathname.startsWith('/auth')) { - const acceptedRoutes = ['/login', '/register', '/recover', '/invite']; + const acceptedRoutes = ['/login', '/register', '/recover', '/invite', '/card']; if ($user) { if ( !$page.url.pathname.startsWith('/console') && - !$page.url.pathname.startsWith('/invite') + !$page.url.pathname.startsWith('/invite') && + !$page.url.pathname.startsWith('/card') ) { await goto(`${base}/console`, { replaceState: true @@ -142,4 +143,33 @@ 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); + } + } diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 9d628c7ead..d16bd6216d 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -31,7 +31,8 @@ export const load: LayoutLoad = async ({ depends, url }) => { '/invite', '/auth/magic-url', '/auth/oauth2/success', - '/auth/oauth2/failure' + '/auth/oauth2/failure', + '/card' ]; if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) { diff --git a/src/routes/card/+page.svelte b/src/routes/card/+page.svelte new file mode 100644 index 0000000000..ee82e07004 --- /dev/null +++ b/src/routes/card/+page.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/routes/card/+page.ts b/src/routes/card/+page.ts new file mode 100644 index 0000000000..bbeeb2f696 --- /dev/null +++ b/src/routes/card/+page.ts @@ -0,0 +1,8 @@ +import { isCloud } from '$lib/system'; +import { redirect } from '@sveltejs/kit'; + +export async function load({ parent }) { + if (!isCloud) throw redirect(303, '/'); + const { account } = await parent(); + if (!account) throw redirect(303, '/login'); +} diff --git a/src/routes/card/Card.svelte b/src/routes/card/Card.svelte new file mode 100644 index 0000000000..4f290de854 --- /dev/null +++ b/src/routes/card/Card.svelte @@ -0,0 +1,434 @@ + + + + + + +
+
+ +
+
+ + diff --git a/src/routes/card/[uid]/+page.svelte b/src/routes/card/[uid]/+page.svelte new file mode 100644 index 0000000000..e3f9086200 --- /dev/null +++ b/src/routes/card/[uid]/+page.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/routes/card/[uid]/+page.ts b/src/routes/card/[uid]/+page.ts new file mode 100644 index 0000000000..c54acf617a --- /dev/null +++ b/src/routes/card/[uid]/+page.ts @@ -0,0 +1,19 @@ +import { redirect } from '@sveltejs/kit'; +import { getCardImgUrls } from '../helpers.js'; + +export const prerender = true; +export const ssr = true; + +export async function load({ params }) { + const userId = params.uid; + const { frontImg } = getCardImgUrls(userId); + + const res = await fetch(frontImg); + if (!res.ok) { + throw redirect(303, '/'); + } + + return { + userId: params.uid + }; +} diff --git a/src/routes/card/display.svelte b/src/routes/card/display.svelte new file mode 100644 index 0000000000..6250cef63f --- /dev/null +++ b/src/routes/card/display.svelte @@ -0,0 +1,512 @@ + + + + + {seo.title} + + + + + + + + + + + + + + +
+
+

+ {title} + +

+ +
+ {#if variant === 'owner'} +
+ + Cloud Beta hoodies + Cloud Beta hoodies +
+ {/if} +
+
+

Cloud is live in public

+

Beta

+
+

+ {#if variant === 'owner'} + Share your Cloud card and you may win an exclusive Cloud hoodie! + {:else} + Create your Cloud account and unlock your exclusive card! + {/if} +

+
+
+ The front of the card +
    +
  • + + +
  • + {#if variant === 'owner'} +
  • + +
  • + {/if} +
  • + +
  • +
+ +
+
+ {#if !cardActive} +
+ +
+ {/if} + {#if browser} + + {/if} +
+ {#if cardActive} +
(cardActive = false)} + on:keydown={() => { + /* no-op */ + }} + transition:fade /> + {/if} +
+ +
+
+ + + Get Embed Code +
+ HTML Code + +
+ + + +
+ + diff --git a/src/routes/card/helpers.ts b/src/routes/card/helpers.ts new file mode 100644 index 0000000000..9de9a13a4d --- /dev/null +++ b/src/routes/card/helpers.ts @@ -0,0 +1,9 @@ +import { VARS } from '$lib/system'; + +export function getCardImgUrls(userId: string) { + const frontImg = `${VARS.APPWRITE_ENDPOINT}/cards/cloud?userId=${userId}`; + const backImg = `${VARS.APPWRITE_ENDPOINT}/cards/cloud-back?userId=${userId}`; + const ogImg = `${VARS.APPWRITE_ENDPOINT}/cards/cloud-og?userId=${userId}`; + + return { frontImg, backImg, ogImg }; +} diff --git a/src/routes/console/+layout.svelte b/src/routes/console/+layout.svelte index 870794b406..eb65d276e8 100644 --- a/src/routes/console/+layout.svelte +++ b/src/routes/console/+layout.svelte @@ -50,6 +50,7 @@ showSideNavigation={$page.url.pathname !== '/console' && !$page?.params.organization && !$page.url.pathname.includes('/console/account') && + !$page.url.pathname.includes('/console/card') && !$page.url.pathname.includes('/console/onboarding')}>
diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index b2f488a901..0b563cdfd4 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -37,6 +37,15 @@ trackError(error, Submit.AccountCreate); } } + + function onGithubLogin() { + sdkForConsole.account.createOAuth2Session( + 'github', + window.location.origin, + window.location.origin, + ['read:user', 'user:email'] + ); + } @@ -66,6 +75,13 @@ + or + + + diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte index cc0b939c02..bbdc10089a 100644 --- a/src/routes/register/+page.svelte +++ b/src/routes/register/+page.svelte @@ -16,11 +16,6 @@ import FormList from '$lib/elements/forms/formList.svelte'; import { Dependencies } from '$lib/constants'; import { trackEvent } from '$lib/actions/analytics'; - import LoginLight from '$lib/images/login/login-light-mode.svg'; - import LoginDark from '$lib/images/login/login-dark-mode.svg'; - - let imgLight = LoginLight; - let imgDark = LoginDark; let name: string, mail: string, pass: string, disabled: boolean; let terms = false; @@ -41,13 +36,22 @@ }); } } + + function onGithubLogin() { + sdkForConsole.account.createOAuth2Session( + 'github', + window.location.origin, + window.location.origin, + ['read:user', 'user:email'] + ); + } Sign up - Appwrite - + Sign up
@@ -56,20 +60,21 @@ id="name" label="Name" placeholder="Your name" - autofocus={true} + autofocus + required bind:value={name} /> By registering, you agree that you have read, understand, and acknowledge our + or + + +
diff --git a/static/images/cloud-beta-hoodies.png b/static/images/cloud-beta-hoodies.png new file mode 100644 index 0000000000..a076484074 Binary files /dev/null and b/static/images/cloud-beta-hoodies.png differ diff --git a/static/images/hoodie-1.png b/static/images/hoodie-1.png new file mode 100644 index 0000000000..6d2547e37e Binary files /dev/null and b/static/images/hoodie-1.png differ diff --git a/static/images/hoodie-2.png b/static/images/hoodie-2.png new file mode 100644 index 0000000000..5735f48bc9 Binary files /dev/null and b/static/images/hoodie-2.png differ diff --git a/static/images/hoodies-bg.png b/static/images/hoodies-bg.png new file mode 100644 index 0000000000..37a8803534 Binary files /dev/null and b/static/images/hoodies-bg.png differ