diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 71bb39ae7..9fe42390d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,6 +41,7 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" + "PUBLIC_CONSOLE_EMAIL_VERIFICATION=true" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}" @@ -82,6 +83,7 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" + "PUBLIC_CONSOLE_EMAIL_VERIFICATION=false" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" @@ -120,7 +122,8 @@ jobs: build-args: | "PUBLIC_CONSOLE_MODE=self-hosted" "PUBLIC_APPWRITE_MULTI_REGION=false" - "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" + "PUBLIC_CONSOLE_EMAIL_VERIFICATION=false" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" @@ -159,6 +162,7 @@ jobs: build-args: | "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_APPWRITE_MULTI_REGION=false" + "PUBLIC_CONSOLE_EMAIL_VERIFICATION=true" "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" diff --git a/Dockerfile b/Dockerfile index 879a4bc65..6bdc2d78a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ ADD ./static /app/static ARG PUBLIC_CONSOLE_MODE ARG PUBLIC_CONSOLE_FEATURE_FLAGS ARG PUBLIC_APPWRITE_MULTI_REGION +ARG PUBLIC_CONSOLE_EMAIL_VERIFICATION ARG PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ARG PUBLIC_APPWRITE_ENDPOINT ARG PUBLIC_GROWTH_ENDPOINT @@ -35,6 +36,7 @@ ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE ENV PUBLIC_CONSOLE_FEATURE_FLAGS=$PUBLIC_CONSOLE_FEATURE_FLAGS ENV PUBLIC_APPWRITE_MULTI_REGION=$PUBLIC_APPWRITE_MULTI_REGION +ENV PUBLIC_CONSOLE_EMAIL_VERIFICATION=$PUBLIC_CONSOLE_EMAIL_VERIFICATION ENV PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=$PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN diff --git a/build.js b/build.js index affdf3ddd..df5933adc 100644 --- a/build.js +++ b/build.js @@ -28,6 +28,8 @@ async function main() { logEnv('MULTI REGION', env?.PUBLIC_APPWRITE_MULTI_REGION); logEnv('APPWRITE ENDPOINT', env?.PUBLIC_APPWRITE_ENDPOINT, 'relative'); logEnv('GROWTH ENDPOINT', env?.PUBLIC_GROWTH_ENDPOINT); + logEnv('CONSOLE EMAIL VERIFICATION', env?.PUBLIC_CONSOLE_EMAIL_VERIFICATION); + logEnv('CONSOLE MOCK AI SUGGESTIONS', env?.PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS); log(); logDelimiter(); await build(); diff --git a/package.json b/package.json index eebc54597..496818696 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,11 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74", + "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39", "@appwrite.io/pink-icons": "0.25.0", - "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c", + "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@8f82877", "@appwrite.io/pink-legacy": "^1.0.3", - "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c", + "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8f82877", "@faker-js/faker": "^9.9.0", "@popperjs/core": "^2.11.8", "@sentry/sveltekit": "^8.38.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a4161ed8..827befa27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,20 +12,20 @@ importers: specifier: ^1.1.24 version: 1.1.24(svelte@5.25.3)(zod@3.24.3) '@appwrite.io/console': - specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74 - version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74 + specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39 + version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39 '@appwrite.io/pink-icons': specifier: 0.25.0 version: 0.25.0 '@appwrite.io/pink-icons-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c(svelte@5.25.3) + specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@8f82877 + version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@8f82877(svelte@5.25.3) '@appwrite.io/pink-legacy': specifier: ^1.0.3 version: 1.0.3 '@appwrite.io/pink-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c(svelte@5.25.3) + specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8f82877 + version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8f82877(svelte@5.25.3) '@faker-js/faker': specifier: ^9.9.0 version: 9.9.0 @@ -260,8 +260,8 @@ packages: '@analytics/type-utils@0.6.2': resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==} - '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74': - resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74} + '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39': + resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39} version: 1.10.0 '@appwrite.io/pink-icons-svelte@2.0.0-RC.1': @@ -269,8 +269,8 @@ packages: peerDependencies: svelte: ^4.0.0 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c} + '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@8f82877': + resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@8f82877} version: 2.0.0-RC.1 peerDependencies: svelte: ^4.0.0 @@ -284,8 +284,8 @@ packages: '@appwrite.io/pink-legacy@1.0.3': resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==} - '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c} + '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8f82877': + resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8f82877} version: 2.0.0-RC.2 peerDependencies: svelte: ^4.0.0 @@ -3703,13 +3703,13 @@ snapshots: '@analytics/type-utils@0.6.2': {} - '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74': {} + '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39': {} '@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)': dependencies: svelte: 5.25.3 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c(svelte@5.25.3)': + '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@8f82877(svelte@5.25.3)': dependencies: svelte: 5.25.3 @@ -3722,7 +3722,7 @@ snapshots: '@appwrite.io/pink-icons': 1.0.0 the-new-css-reset: 1.11.3 - '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c(svelte@5.25.3)': + '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@8f82877(svelte@5.25.3)': dependencies: '@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3) '@floating-ui/dom': 1.6.13 diff --git a/src/app.html b/src/app.html index 7ca4eefe8..1ce18283b 100644 --- a/src/app.html +++ b/src/app.html @@ -7,6 +7,30 @@ content="Appwrite is an open-source platform for building applications at any scale, using your preferred programming languages and tools." /> + + + + + + + + + + + + %sveltekit.head% diff --git a/src/lib/components/account/sendVerificationEmailModal.svelte b/src/lib/components/account/sendVerificationEmailModal.svelte index bbe676142..1ad53ed1b 100644 --- a/src/lib/components/account/sendVerificationEmailModal.svelte +++ b/src/lib/components/account/sendVerificationEmailModal.svelte @@ -1,79 +1,194 @@ - - - - - To continue using Appwrite Cloud, please verify your email address. An email will be - sent to {get(user)?.email} - - - +
+ + + + + To continue using Appwrite Cloud, please verify your email address. An email + will be sent to {email || get(user)?.email} + - - - - + logout()}>Switch account + + {#if emailSent && resendTimer > 0} +
+ + Didn't get the email? Try again in {resendTimer}s + +
+ {/if} + + + + + + + +
+ + diff --git a/src/lib/components/alerts/emailVerificationBanner.svelte b/src/lib/components/alerts/emailVerificationBanner.svelte deleted file mode 100644 index 96574d801..000000000 --- a/src/lib/components/alerts/emailVerificationBanner.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - -{#if shouldShowEmailBanner} - - - To avoid losing access to your projects, make sure {$user.email} is valid and up to date. Email - verification will be required soon. - - - - - - -{/if} diff --git a/src/lib/components/customId.svelte b/src/lib/components/customId.svelte index a46307c81..66a56c2d8 100644 --- a/src/lib/components/customId.svelte +++ b/src/lib/components/customId.svelte @@ -27,10 +27,12 @@ id = null; } - if (!id?.length) { + if (id !== null && !id.length) { id = null; } + }); + $effect(() => { if (show) { trackEvent(Click.ShowCustomIdClick); } diff --git a/src/lib/components/git/connectRepoModal.svelte b/src/lib/components/git/connectRepoModal.svelte index 68acdab02..0f5eb76d4 100644 --- a/src/lib/components/git/connectRepoModal.svelte +++ b/src/lib/components/git/connectRepoModal.svelte @@ -13,6 +13,7 @@ import { Click, trackEvent } from '$lib/actions/analytics'; import RepositoryBehaviour from '$lib/components/git/repositoryBehaviour.svelte'; import { page } from '$app/state'; + import { connectGitHub } from '$lib/stores/git'; let { show = $bindable(false), @@ -121,7 +122,7 @@ {#if repositoryBehaviour === 'existing'} - + Missing a repository? check your permissions diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 47afdf41a..b9d6e57ed 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -85,5 +85,5 @@ export { default as ViewToggle } from './viewToggle.svelte'; export { default as RegionEndpoint } from './regionEndpoint.svelte'; export { default as ExpirationInput } from './expirationInput.svelte'; export { default as EstimatedCard } from './estimatedCard.svelte'; -export { default as EmailVerificationBanner } from './alerts/emailVerificationBanner.svelte'; export { default as SortButton, type SortDirection } from './sortButton.svelte'; +export { default as SendVerificationEmailModal } from './account/sendVerificationEmailModal.svelte'; diff --git a/src/lib/elements/forms/inputDateTime.svelte b/src/lib/elements/forms/inputDateTime.svelte index 11bd78fd8..6a904cd53 100644 --- a/src/lib/elements/forms/inputDateTime.svelte +++ b/src/lib/elements/forms/inputDateTime.svelte @@ -4,7 +4,7 @@ export let id: string; export let label: string = ''; - export let value: string; + export let value: string | null; export let required = false; export let nullable = false; export let disabled = false; @@ -17,6 +17,7 @@ let error: string; let element: HTMLInputElement; + let previousValue: string | null = null; onMount(() => { if (element && autofocus) { @@ -24,14 +25,16 @@ } }); - let prevValue = ''; function handleNullChange(e: CustomEvent) { const isNull = e.detail; + if (isNull) { - prevValue = value; + if (value !== null) { + previousValue = value; + } value = null; } else { - value = prevValue; + value = previousValue; } } @@ -39,9 +42,7 @@ error = null; } - function onChange(event: CustomEvent) { - value = (event.target as HTMLInputElement).value; - } + $: isValueNull = value === null; @@ -51,20 +52,20 @@ {disabled} {readonly} {required} - {value} + bind:value {step} {type} helper={error} {leadingIcon} - on:change={onChange} autocomplete={autocomplete ? 'on' : 'off'}> - {#if nullable} - - {/if} + + {#if nullable} + + {/if} + diff --git a/src/lib/elements/forms/inputId.svelte b/src/lib/elements/forms/inputId.svelte index 909aaf094..9946c924a 100644 --- a/src/lib/elements/forms/inputId.svelte +++ b/src/lib/elements/forms/inputId.svelte @@ -12,11 +12,7 @@ const handleInvalid = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => { event.preventDefault(); - - if (event.currentTarget.validity.patternMismatch) { - error = true; - return; - } + error = true; // show on any error }; $: if (value) { diff --git a/src/lib/elements/forms/inputProjectId.svelte b/src/lib/elements/forms/inputProjectId.svelte index c9628485c..f9b479ce5 100644 --- a/src/lib/elements/forms/inputProjectId.svelte +++ b/src/lib/elements/forms/inputProjectId.svelte @@ -12,11 +12,7 @@ const handleInvalid = (event: Event & { currentTarget: EventTarget & HTMLInputElement }) => { event.preventDefault(); - - if (event.currentTarget.validity.patternMismatch) { - error = true; - return; - } + error = true; // show on any error }; $: if (value) { diff --git a/src/lib/helpers/faker.ts b/src/lib/helpers/faker.ts index 56987f879..421288bb3 100644 --- a/src/lib/helpers/faker.ts +++ b/src/lib/helpers/faker.ts @@ -198,15 +198,21 @@ function generateSingleValue( case 'integer': { const intAttr = column as Models.ColumnInteger; - const min = !isWithinSafeRange(intAttr.min) ? 0 : intAttr.min; - const max = !isWithinSafeRange(intAttr.max) ? 100 : intAttr.max; + const min = isWithinSafeRange(intAttr.min) ? intAttr.min : 0; + const fallbackMax = Math.max(min + 100, 100); + const max = isWithinSafeRange(intAttr.max) + ? intAttr.max + : Math.min(fallbackMax, Number.MAX_SAFE_INTEGER); return faker.number.int({ min, max }); } case 'double': { const floatAttr = column as Models.ColumnFloat; - const min = !isWithinSafeRange(floatAttr.min) ? 0 : floatAttr.min; - const max = !isWithinSafeRange(floatAttr.max) ? 100 : floatAttr.max; + const min = isWithinSafeRange(floatAttr.min) ? floatAttr.min : 0; + const fallbackMax = Math.max(min + 100, 100); + const max = isWithinSafeRange(floatAttr.max) + ? floatAttr.max + : Math.min(fallbackMax, Number.MAX_SAFE_INTEGER); const precision = 4; return faker.number.float({ min, max, fractionDigits: precision }); diff --git a/src/lib/helpers/numbers.ts b/src/lib/helpers/numbers.ts index ce1c0a0bb..a3ad303b5 100644 --- a/src/lib/helpers/numbers.ts +++ b/src/lib/helpers/numbers.ts @@ -18,21 +18,38 @@ export function toDecimals(num: number, decimals: number = 1): number { return parseFloat(num.toFixed(decimals)); } -export function formatNumberWithCommas(number: number): string { +export function formatNumberWithCommas(number: number, min: number = 0): string { if (isNaN(number)) return String(number); const formatter = new Intl.NumberFormat('en'); - return formatter.format(number); + return formatter.format(clampMin(number, min)); } -export function formatCurrency(number: number, locale = 'en-US', currency = 'USD'): string { +export function formatCurrency( + number: number, + locale = 'en-US', + currency = 'USD', + min: number = 0 +): string { if (isNaN(number)) return String(number); const formatter = new Intl.NumberFormat(locale, { style: 'currency', currency }); - return formatter.format(number); + return formatter.format(clampMin(number, min)); } export function isWithinSafeRange(val: number) { return Math.abs(val) < Number.MAX_SAFE_INTEGER; } + +/** + * Clamps a number to a minimum value + * + * @export + * @param {number} value + * @param {number} min + * @returns {number} + */ +export function clampMin(value: number, min: number = 0): number { + return Math.max(min, value || 0); +} diff --git a/src/lib/helpers/string.ts b/src/lib/helpers/string.ts index 91dd9a37c..6f867f86c 100644 --- a/src/lib/helpers/string.ts +++ b/src/lib/helpers/string.ts @@ -1,3 +1,5 @@ +import { clampMin } from './numbers'; + /** * Capitalizes the first letter of a string * @@ -45,8 +47,8 @@ const formatter = Intl.NumberFormat('en', { notation: 'compact' }); -export function formatNum(number: number): string { - return formatter.format(number); +export function formatNum(number: number, min: number = 0): string { + return formatter.format(clampMin(number, min)); } /** diff --git a/src/lib/images/promos/inversion-queries-dark.png b/src/lib/images/promos/inversion-queries-dark.png deleted file mode 100644 index 2b59c4ee2..000000000 Binary files a/src/lib/images/promos/inversion-queries-dark.png and /dev/null differ diff --git a/src/lib/images/promos/inversion-queries-light.png b/src/lib/images/promos/inversion-queries-light.png deleted file mode 100644 index 3063aca3c..000000000 Binary files a/src/lib/images/promos/inversion-queries-light.png and /dev/null differ diff --git a/src/lib/images/promos/spatial-columns-api-dark.png b/src/lib/images/promos/spatial-columns-api-dark.png deleted file mode 100644 index 232763d19..000000000 Binary files a/src/lib/images/promos/spatial-columns-api-dark.png and /dev/null differ diff --git a/src/lib/images/promos/spatial-columns-api-light.png b/src/lib/images/promos/spatial-columns-api-light.png deleted file mode 100644 index f62f20d4d..000000000 Binary files a/src/lib/images/promos/spatial-columns-api-light.png and /dev/null differ diff --git a/src/lib/images/promos/time-helper-queries-dark.png b/src/lib/images/promos/time-helper-queries-dark.png deleted file mode 100644 index 0adf4b231..000000000 Binary files a/src/lib/images/promos/time-helper-queries-dark.png and /dev/null differ diff --git a/src/lib/images/promos/time-helper-queries-light.png b/src/lib/images/promos/time-helper-queries-light.png deleted file mode 100644 index 466e6ac9f..000000000 Binary files a/src/lib/images/promos/time-helper-queries-light.png and /dev/null differ diff --git a/src/lib/images/promos/transactions-api-dark.png b/src/lib/images/promos/transactions-api-dark.png new file mode 100644 index 000000000..4fa749489 Binary files /dev/null and b/src/lib/images/promos/transactions-api-dark.png differ diff --git a/src/lib/images/promos/transactions-api-light.png b/src/lib/images/promos/transactions-api-light.png new file mode 100644 index 000000000..f9796c5e0 Binary files /dev/null and b/src/lib/images/promos/transactions-api-light.png differ diff --git a/src/lib/layout/footer.svelte b/src/lib/layout/footer.svelte index 2e4f9819c..54e8ec053 100644 --- a/src/lib/layout/footer.svelte +++ b/src/lib/layout/footer.svelte @@ -181,6 +181,11 @@ &.hide { display: none; } + + & :global(i) { + // because the `IconCloud` shows above floating action bars. + position: unset; + } } :global(main:has(.sub-navigation)) footer { diff --git a/src/lib/layout/notifications.svelte b/src/lib/layout/notifications.svelte index aa0132398..b6d6ffeff 100644 --- a/src/lib/layout/notifications.svelte +++ b/src/lib/layout/notifications.svelte @@ -11,13 +11,16 @@ {#each $notifications as notification (notification.id)} { return { label: button.name, - onClick: button.method + onClick: button.method, + isHtml: button.isHtml }; })} on:dismiss={() => dismissNotification(notification.id)} /> diff --git a/src/lib/layout/progress.svelte b/src/lib/layout/progress.svelte index a5b445fcc..8d34097f4 100644 --- a/src/lib/layout/progress.svelte +++ b/src/lib/layout/progress.svelte @@ -1,5 +1,4 @@ diff --git a/src/lib/layout/usageMultiple.svelte b/src/lib/layout/usageMultiple.svelte index 421b0892b..85f5091ca 100644 --- a/src/lib/layout/usageMultiple.svelte +++ b/src/lib/layout/usageMultiple.svelte @@ -4,7 +4,7 @@ import { Card, SecondaryTabs, SecondaryTabsItem } from '$lib/components'; import { page } from '$app/state'; import { type Models } from '@appwrite.io/console'; - import { formatNumberWithCommas } from '$lib/helpers/numbers'; + import { formatNumberWithCommas, clampMin } from '$lib/helpers/numbers'; import { Layout, Typography } from '@appwrite.io/pink-svelte'; export let title: string; @@ -41,7 +41,7 @@ {#if count} - {@const totalCount = total.reduce((a, b) => a + b, 0)} + {@const totalCount = clampMin(total.reduce((a, b) => a + b, 0))} {formatNumberWithCommas(totalCount)} diff --git a/src/lib/stores/navigation.ts b/src/lib/stores/navigation.ts deleted file mode 100644 index f35211058..000000000 --- a/src/lib/stores/navigation.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { writable } from 'svelte/store'; - -export const navigationCancelled = writable(false); diff --git a/src/lib/stores/notifications.ts b/src/lib/stores/notifications.ts index 97f6cfb36..c250820f7 100644 --- a/src/lib/stores/notifications.ts +++ b/src/lib/stores/notifications.ts @@ -1,13 +1,14 @@ import { writable } from 'svelte/store'; +import type { ComponentType } from 'svelte'; export type Notification = { id: number; - type: 'success' | 'error' | 'info' | 'warning'; + type?: 'success' | 'error' | 'info' | 'warning'; dismissible?: boolean; timeout?: number; message: string; title?: string; - icon?: string; + icon?: ComponentType; buttons?: Buttons[]; isHtml?: boolean; }; @@ -15,32 +16,52 @@ export type Notification = { export type Buttons = { method: () => void | Promise; name: string; + isHtml?: boolean; }; let counter = 0; +const activeTimeouts = new Map(); + export const notifications = writable([]); export const dismissNotification = (id: number) => { + const timeoutId = activeTimeouts.get(id); + + if (timeoutId) { + clearTimeout(timeoutId); + activeTimeouts.delete(id); + } + notifications.update((all) => all.filter((t) => t.id !== id)); }; export const dismissAllNotifications = () => { + activeTimeouts.forEach((timeoutId) => clearTimeout(timeoutId)); + activeTimeouts.clear(); + notifications.set([]); }; -export const addNotification = (notification: Omit) => { - const defaults = { +export const addNotification = (notification: Omit): number => { + const defaults: Notification = { id: counter++, type: 'info', dismissible: true, - timeout: 6000 + timeout: 6000, + ...notification }; - const n = { ...defaults, ...notification }; - notifications.update((all) => { - return [n, ...all.slice(0, 4)]; - }); + notifications.update((all) => [defaults, ...all.slice(0, 4)]); - if (n.timeout) setTimeout(() => dismissNotification(n.id), n.timeout); + if (defaults.timeout) { + const timeoutId = setTimeout(() => { + activeTimeouts.delete(defaults.id); + dismissNotification(defaults.id); + }, defaults.timeout); + + activeTimeouts.set(defaults.id, timeoutId); + } + + return defaults.id; }; diff --git a/src/lib/stores/preferences.ts b/src/lib/stores/preferences.ts index 723721410..fc6a864e2 100644 --- a/src/lib/stores/preferences.ts +++ b/src/lib/stores/preferences.ts @@ -224,6 +224,8 @@ function createPreferences() { delete teamPreferences?.displayNames?.[tableId]; delete teamPreferences?.columnOrder?.[tableId]; delete teamPreferences?.columnWidths?.[tableId]; + delete teamPreferences?.columnWidths?.[tableId + '#columns']; + delete teamPreferences?.columnWidths?.[tableId + '#indexes']; const removeTablePreferences = sdk.forConsole.teams.updatePrefs({ teamId: orgId, diff --git a/src/routes/(console)/+layout.svelte b/src/routes/(console)/+layout.svelte index a8e6e30c7..d3a77a8c4 100644 --- a/src/routes/(console)/+layout.svelte +++ b/src/routes/(console)/+layout.svelte @@ -45,7 +45,7 @@ import { headerAlert } from '$lib/stores/headerAlert'; import { UsageRates } from '$lib/components/billing'; import { canSeeProjects } from '$lib/stores/roles'; - import { BottomModalAlert, EmailVerificationBanner } from '$lib/components'; + import { BottomModalAlert } from '$lib/components'; import { IconAnnotation, IconBookOpen, @@ -346,8 +346,6 @@