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}
+ {#if isCloud}
+
+ {/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 @@
-
-
-
-
+
-
-
- {#if $app.themeInUse === 'dark'}
-
- {:else}
-
- {/if}
-
+ Now in public beta
+
+
+
+
+
Integrate with your favourite technologies
+
+ {#each technologies as tech}
+
+
+
+ {/each}
+
+
+
+
+ {:else}
+
+
+
+
+
+
+
+ {#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}
+
+ {/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 @@
+
+
+
+
+
+
+
+
+
{
+ /* noop */
+ }}>
+
+
+
+
+
+ {#each new Array(THICKNESS) as _, i (i)}
+
+ {/each}
+
+
+
+
+
+
+
+
+
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}
+ triggerConfettiKey++}>
+ 🎉
+ {#each Array(triggerConfettiKey) as _, i (i)}
+
+ {/each}
+
+
+
+
+ {#if variant === 'owner'}
+
+ {/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}
+
+
+
+
+
+
+
+
+ {#if !cardActive}
+
+
+
+ {/if}
+ {#if browser}
+
+ {/if}
+
+ {#if cardActive}
+
(cardActive = false)}
+ on:keydown={() => {
+ /* no-op */
+ }}
+ transition:fade />
+ {/if}
+
+
(cardIsFlipped = !cardIsFlipped)}
+ aria-label="Rotate card">
+
+
+
+ Spin
+
+
+
+
+
+ Get Embed Code
+
+ HTML Code
+
+
+
+ Copy
+
+
+
+
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 @@
Sign in
+ or
+
+
+
+ Sign in with GitHub
+
+
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
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