Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .optimize-cache.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
"static/assets/logotype/white.png": "a6c0a516cafa38798b578597a50e1edc967494e986ee1a74e9170e3fa45c67fc",
"static/assets/visuals/auth.jpg": "a310ed768b63e73eb09452352ffa5a59638b4223aebbcaac80bdb232b24de64f",
"static/assets/visuals/auth.png": "6634419bdf4972706d08a03a148fbf2f0053377dca85ffd7d95ecc3e016a009b",
"static/assets/visuals/dashboard-screenshot.png": "931eec1e7232b5754684e2652999648f3fada876b3d39351360a5bb87008367c",
"static/assets/visuals/dashboard.jpg": "fbb2bae3d39f500337d6c4bca64ddaaea163a177ab3db77641ecd7050c43c10e",
"static/assets/visuals/dashboard.png": "544b8af8bdb33546d5746fac6427c815248b2c321c4f28c3be7870c040b265b3",
"static/assets/visuals/dashboard.png": "931eec1e7232b5754684e2652999648f3fada876b3d39351360a5bb87008367c",
"static/assets/visuals/databases.jpg": "0de2e6b470f9903db803120d03e1b3abe21728802564d2702989a48128bd0abc",
"static/assets/visuals/databases.png": "d50ce8119e31f7b8099e9b98fe38a7066adcf421eb061756db900b5e60f257fe",
"static/assets/visuals/functions.jpg": "cb9dc9f434329949a8a0bd1302033e4106d4e6f16e05dada696529800e7236b3",
Expand Down
1 change: 1 addition & 0 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const securityheaders: Handle = async ({ event, resolve }) => {
'https://*.appwrite.io',
'https://*.appwrite.org',
'https://*.appwrite.network',
'https://status.appwrite.online',
'https://*.sentry.io',
'https://*.plausible.io',
'https://plausible.io',
Expand Down
180 changes: 180 additions & 0 deletions src/lib/components/FooterCloudStatusBadge.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<script lang="ts">
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import { trackEvent } from '$lib/actions/analytics';
import {
fetchAppwriteCloudAggregateState,
type AppwriteCloudAggregateState
} from '$lib/status/fetch-appwrite-cloud-aggregate-state';

const STATUS_PAGE_URL = 'https://status.appwrite.online';
const REFRESH_MS = 60_000;

let aggregateState: AppwriteCloudAggregateState = 'operational';

function labelFor(state: AppwriteCloudAggregateState): string {
switch (state) {
case 'maintenance':
return 'Maintenance in progress';
case 'downtime':
return 'Major service disruption';
case 'degraded':
return 'Some services affected';
default:
return 'All systems normal';
}
}

onMount(() => {
if (!browser) {
return;
}

let cancelled = false;

const refresh = () => {
void fetchAppwriteCloudAggregateState().then((next) => {
if (!cancelled) {
aggregateState = next;
}
});
};

/** Defer first fetch until idle (or short deadline) so it never competes with first paint. */
let idleId: number | undefined;
let fallbackId: ReturnType<typeof setTimeout> | undefined;
if (typeof requestIdleCallback !== 'undefined') {
idleId = requestIdleCallback(() => refresh(), { timeout: 2000 });
} else {
fallbackId = setTimeout(() => refresh(), 0);
}

const interval = window.setInterval(refresh, REFRESH_MS);

return () => {
cancelled = true;
window.clearInterval(interval);
if (idleId !== undefined && typeof cancelIdleCallback !== 'undefined') {
cancelIdleCallback(idleId);
}
if (fallbackId !== undefined) {
clearTimeout(fallbackId);
}
};
});
Comment on lines +61 to +64
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant browser guard inside onMount

onMount is a client-only lifecycle hook in SvelteKit — it never executes during SSR. The if (!browser) { return; } check on line 62 is therefore always false and can never early-return. The import of browser from $app/environment can be dropped entirely from this file.

</script>

<a
class="web-footer-cloud-status web-icon-button is-more-content"
href={STATUS_PAGE_URL}
target="_blank"
rel="noopener noreferrer"
data-state={aggregateState}
aria-label="{labelFor(aggregateState)}. Opens the public status page in a new tab."
onclick={() => trackEvent('footer-cloud-status-badge-click')}
>
<span class="web-footer-cloud-status__mark" aria-hidden="true">
{#if aggregateState === 'operational'}
<span class="web-icon-check web-footer-cloud-status__glyph"></span>
{:else if aggregateState === 'maintenance'}
<svg
class="web-footer-cloud-status__svg"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<path
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
fill="currentColor"
/>
</svg>
{:else}
<svg class="web-footer-cloud-status__svg" viewBox="0 0 24 24" aria-hidden="true">
<path
fill="currentColor"
d="M12 3 2 20h20L12 3zm0 3.5L17.5 18h-11L12 6.5zM11 10h2v5h-2v-5zm0 6h2v2h-2v-2z"
/>
</svg>
{/if}
</span>
<span class="web-footer-cloud-status__label">{labelFor(aggregateState)}</span>
</a>

<style lang="scss">
@use '$scss/abstract' as *;

/* Same chrome as `.web-icon-button.is-more-content`, extended for label + status tint */
.web-footer-cloud-status.web-icon-button.is-more-content {
position: relative;
display: inline-flex;
max-inline-size: 100%;
align-items: center;
gap: pxToRem(6);
min-block-size: pxToRem(26);
padding-block: pxToRem(1);
padding-inline: pxToRem(5) pxToRem(7);
text-decoration: none;
inline-size: fit-content;
/* Slightly smaller than footer link copy so the chip reads as secondary */
font-size: calc(var(--web-footer-nav-link-font-size, var(--text-caption)) * 0.9);
font-weight: var(--font-weight-regular);
line-height: var(--web-footer-nav-link-line-height, var(--text-caption--line-height));
letter-spacing: var(
--web-footer-nav-link-letter-spacing,
var(--text-caption--letter-spacing)
);
/*
* `web-icon.css` matches `[class*=' web-icon-']` — that substring appears inside the
* token `web-icon-button`, so the whole anchor wrongly gets `font-family: web-icon !important`.
* Reset to body UI font; keep icon font only on the glyph span.
*/
font-family: var(--web-font-family-inter), arial, sans-serif !important;

.web-footer-cloud-status__label {
white-space: nowrap;
}

.web-footer-cloud-status__mark {
display: flex;
width: pxToRem(16);
height: pxToRem(16);
flex-shrink: 0;
align-items: center;
justify-content: center;
border-radius: pxToRem(6);
background-color: hsl(var(--web-color-smooth));
color: hsl(var(--web-color-secondary));
}

.web-footer-cloud-status__glyph {
font-family: 'web-icon' !important;
font-size: pxToRem(9);
line-height: 1;
}

.web-footer-cloud-status__svg {
width: pxToRem(10);
height: pxToRem(10);
}

&[data-state='operational'] .web-footer-cloud-status__mark {
background-color: hsl(var(--web-color-mint-500) / 0.14);
color: hsl(var(--web-color-mint-500));
}

&[data-state='degraded'] .web-footer-cloud-status__mark {
background-color: hsl(var(--web-color-yellow-500) / 0.14);
color: hsl(var(--web-color-yellow-700));
}

&[data-state='downtime'] .web-footer-cloud-status__mark {
background-color: hsl(var(--web-color-red-500) / 0.14);
color: hsl(var(--web-color-red-700));
}

&[data-state='maintenance'] .web-footer-cloud-status__mark {
background-color: hsl(var(--web-color-blue-500) / 0.14);
color: hsl(var(--web-color-blue-700));
}
}
</style>
66 changes: 59 additions & 7 deletions src/lib/components/FooterNav.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@
{ label: 'Docs', href: '/docs' },
{ label: 'Integrations', href: '/integrations' },
{ label: 'Community', href: '/community' },
{
label: 'Backend as a service (BaaS)',
href: '/blog/post/backend-as-a-service'
},
{ label: 'Init', href: '/init' },
{ label: 'Threads', href: '/threads' },
{ label: 'Changelog', href: '/changelog' },
Expand Down Expand Up @@ -85,6 +81,40 @@
// rel: 'noopener noreferrer'
// }
],
Compare: [
{
label: 'Appwrite vs. Supabase',
href: '/blog/post/appwrite-compared-to-supabase'
},
{
label: 'Appwrite vs. Firebase',
href: '/blog/post/open-source-firebase-alternative'
},
{
label: 'Appwrite vs. Vercel',
href: '/blog/post/open-source-vercel-alternative'
},
{
label: 'Appwrite vs. Netlify',
href: '/blog/post/open-source-netlify-alternative'
},
{
label: 'Appwrite vs. Cloudinary',
href: '/blog/post/appwrite-vs-cloudinary'
},
{
label: 'Appwrite vs. Auth0',
href: '/blog/post/appwrite-vs-auth0'
},
{
label: 'Appwrite vs. Neon',
href: '/blog/post/appwrite-vs-neon-ai-backends'
},
{
label: 'Backend as a service (BaaS)',
href: '/blog/post/backend-as-a-service'
}
],
Programs: [
{ label: 'Heroes', href: '/heroes' },
{ label: 'Startups', href: '/startups' },
Expand Down Expand Up @@ -118,16 +148,28 @@
class="web-footer-nav relative mt-24"
class:web-u-sep-block-start={!noBorder}
>
<img class="web-logo" src="/images/logos/appwrite.svg" alt="appwrite" height="24" width="130" />
<img
class="web-logo web-is-only-mobile"
src="/images/logos/appwrite.svg"
alt="appwrite"
height="24"
width="130"
/>
<ul class="web-footer-nav-main-list" use:melt={$root}>
{#each Object.entries(links) as [title, items]}
<li class="web-footer-nav-main-item web-is-not-mobile">
<li
class="web-footer-nav-main-item web-is-not-mobile"
class:web-footer-nav-main-item--with-bottom-logo={title === 'About'}
>
<h2
class="web-footer-nav-main-title web-is-not-mobile text-caption font-medium uppercase"
>
{title}
</h2>
<ul class="web-footer-nav-secondary-list text-sub-body">
<ul
class="web-footer-nav-secondary-list text-sub-body"
class:web-footer-nav-secondary-list--compare={title === 'Compare'}
>
{#each items as { href, label, target, rel }}
<li>
<a
Expand All @@ -143,6 +185,15 @@
</li>
{/each}
</ul>
{#if title === 'About'}
<img
class="web-logo web-is-not-mobile"
src="/images/logos/appwrite.svg"
alt="appwrite"
height="24"
width="130"
/>
{/if}
</li>
<li
class="web-footer-nav-main-item web-is-only-mobile"
Expand All @@ -164,6 +215,7 @@
{#if $isSelected(title)}
<ul
class="web-footer-nav-secondary-list text-sub-body"
class:web-footer-nav-secondary-list--compare={title === 'Compare'}
use:melt={$content({ value: title })}
transition:slide={{ duration: 250 }}
>
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/LogoList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@
height: 41
},
{
src: '/images/logos/trusted-by/k-collect.avif',
src: '/images/logos/trusted-by/k-collect.svg',
alt: 'K-collect',
width: 127,
width: 110,
height: 35
},
{
Expand Down
Loading
Loading