Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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>
67 changes: 60 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 @@ -91,6 +87,40 @@
{ label: 'Education', href: '/education' },
{ label: 'Partners', href: '/partners' }
],
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. Neon',
href: '/blog/post/appwrite-vs-neon-ai-backends'
},
{
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: 'Backend as a service (BaaS)',
href: '/blog/post/backend-as-a-service'
}
],
About: [
{ label: 'Company', href: '/company' },
{ label: 'Pricing', href: '/pricing' },
Expand Down Expand Up @@ -118,16 +148,29 @@
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--compare-col={title === 'Compare'}
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 +186,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 +216,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