Skip to content

Commit b092bbb

Browse files
authored
Merge pull request #347 from appwrite/changelog
Add Changelog Page
2 parents 9634482 + 658eafb commit b092bbb

34 files changed

+700
-111
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
1212
"lint": "prettier --plugin-search-dir . --check . && eslint .",
1313
"format": "prettier --plugin-search-dir . --write .",
14+
"clean": "rm -rf node_modules && rm -rf .svelte_kit && pnpm i",
1415
"test:integration": "playwright test",
1516
"test:unit": "vitest",
1617
"icons:build": "node ./src/icons/build.js",

src/app.d.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// See https://kit.svelte.dev/docs/types#app
22
// for information about these interfaces
33
declare global {
4-
namespace App {
5-
// interface Error {}
6-
// interface Locals {}
7-
// interface PageData {}
8-
// interface Platform {}
9-
}
4+
namespace App {
5+
// interface Error {}
6+
// interface Locals {}
7+
interface PageData {
8+
changelogEntries: number;
9+
}
10+
// interface Platform {}
11+
}
1012
}
1113

1214
export {};
Lines changed: 49 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,58 @@
11
<script lang="ts">
2-
import { rect } from '$lib/actions';
3-
import { clamp } from '$lib/utils/clamp';
4-
import { onMount } from 'svelte';
5-
import { writable } from 'svelte/store';
6-
7-
export let percentage = 0;
8-
let easedPercentage = 0;
9-
10-
const elRect = writable<DOMRect | null>(null);
11-
$: y = $elRect ? clamp(0, easedPercentage, 1) * $elRect.height : 0;
12-
13-
onMount(() => {
14-
let frame: number | null = null;
15-
const ease = () => {
16-
easedPercentage += (percentage - easedPercentage);
17-
frame = window.requestAnimationFrame(ease);
18-
};
19-
ease();
20-
21-
return () => {
22-
frame && window.cancelAnimationFrame(frame);
23-
};
24-
});
2+
import { rect } from '$lib/actions';
3+
import { clamp } from '$lib/utils/clamp';
4+
import { onMount } from 'svelte';
5+
import { writable } from 'svelte/store';
6+
7+
export let percentage = 0;
8+
let easedPercentage = 0;
9+
10+
const elRect = writable<DOMRect | null>(null);
11+
$: y = $elRect ? clamp(0, easedPercentage, 1) * $elRect.height : 0;
12+
13+
onMount(() => {
14+
let frame: number | null = null;
15+
const ease = () => {
16+
easedPercentage += percentage - easedPercentage;
17+
frame = window.requestAnimationFrame(ease);
18+
};
19+
ease();
20+
21+
return () => {
22+
frame && window.cancelAnimationFrame(frame);
23+
};
24+
});
2525
</script>
2626

2727
<div
28-
class="scroll-indicator"
29-
use:rect={elRect}
30-
style:--y={`${y}px`}
31-
style:--percentage={`${easedPercentage * 100}%`}
28+
class="scroll-indicator"
29+
use:rect={elRect}
30+
style:--y={`${y}px`}
31+
style:--percentage={`${easedPercentage * 100}%`}
3232
>
33-
<div class="cursor" />
33+
<div class="aw-dot" />
3434
</div>
3535

3636
<style lang="scss">
37-
.scroll-indicator {
38-
position: relative;
39-
40-
width: 1px;
41-
flex-shrink: 0;
42-
height: 100%;
43-
background: linear-gradient(
44-
to bottom,
45-
hsl(var(--aw-color-accent)) 0%,
46-
hsl(var(--aw-color-greyscale-700)) var(--percentage),
47-
hsl(var(--aw-color-greyscale-700)) 100%
48-
);
49-
border-radius: 100%;
50-
51-
.cursor {
52-
border-radius: 16px;
53-
border: 0.5px solid rgba(255, 255, 255, 0.16);
54-
background: linear-gradient(
55-
138deg,
56-
rgba(255, 255, 255, 0.13) 9.61%,
57-
rgba(255, 255, 255, 0) 105.41%
58-
);
59-
backdrop-filter: blur(100px);
60-
61-
width: 16px;
62-
height: 16px;
63-
64-
position: absolute;
65-
left: 50%;
66-
translate: -50% var(--y, 0);
67-
top: -8px;
68-
69-
&::after {
70-
content: '';
71-
background-color: white;
72-
border-radius: 100%;
73-
width: 4px;
74-
height: 4px;
75-
position: absolute;
76-
left: 50%;
77-
top: 50%;
78-
translate: -50% -50%;
79-
}
80-
}
81-
}
37+
.scroll-indicator {
38+
position: relative;
39+
40+
width: 1px;
41+
flex-shrink: 0;
42+
height: 100%;
43+
background: linear-gradient(
44+
to bottom,
45+
hsl(var(--aw-color-accent)) 0%,
46+
hsl(var(--aw-color-greyscale-700)) var(--percentage),
47+
hsl(var(--aw-color-greyscale-700)) 100%
48+
);
49+
border-radius: 100%;
50+
}
51+
52+
.aw-dot {
53+
position: absolute;
54+
left: 50%;
55+
translate: -50% var(--y, 0);
56+
top: -8px;
57+
}
8258
</style>

src/lib/components/Article.svelte

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts">
2+
import { formatDate } from '$lib/utils/date';
3+
24
export let title: string;
35
export let cover: string;
46
export let href: string;
@@ -24,11 +26,7 @@
2426
<h4 class="aw-sub-body-400 aw-u-color-text-primary">{author}</h4>
2527
<ul class="aw-metadata aw-caption-400 aw-is-not-mobile">
2628
<li>
27-
{Intl.DateTimeFormat('en-US', {
28-
day: '2-digit',
29-
month: 'short',
30-
year: 'numeric'
31-
}).format(date)}
29+
{formatDate(date)}
3230
</li>
3331
<li>{timeToRead} min</li>
3432
</ul>

src/lib/components/FooterNav.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
rel: 'noopener noreferrer'
6969
},
7070
{ label: 'Contact us', href: '/contact-us' },
71-
{ label: 'Assets', href: '/assets' }
71+
{ label: 'Assets', href: '/assets' },
72+
{ label: 'Changelog', href: '/changelog' }
7273
],
7374
Policies: [
7475
{ label: 'Terms', href: '/terms' },

src/lib/layouts/Main.svelte

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
export type NavLink = {
55
label: string;
66
href: string;
7+
showBadge?: boolean;
78
};
89
export const isHeaderHidden = writable(false);
910
export const isMobileNavOpen = writable(false);
11+
12+
const initialized = writable(false);
1013
</script>
1114

1215
<script lang="ts">
@@ -15,8 +18,10 @@
1518
import { BANNER_KEY } from '$lib/constants';
1619
import { isVisible } from '$lib/utils/isVisible';
1720
import { createScrollInfo } from '$lib/utils/scroll';
21+
import { hasNewChangelog } from '$routes/changelog/utils';
1822
import { addEventListener } from '@melt-ui/svelte/internal/helpers';
1923
import { onMount } from 'svelte';
24+
import { page } from '$app/stores';
2025
2126
export let omitMainId = false;
2227
let theme: 'light' | 'dark' | null = 'dark';
@@ -75,6 +80,9 @@
7580
}
7681
7782
onMount(() => {
83+
setTimeout(() => {
84+
$initialized = true;
85+
}, 1000);
7886
return setupThemeObserver();
7987
});
8088
@@ -91,6 +99,11 @@
9199
label: 'Blog',
92100
href: '/blog'
93101
},
102+
{
103+
label: 'Changelog',
104+
href: '/changelog',
105+
showBadge: hasNewChangelog() && !$page.url.pathname.includes('/changelog')
106+
},
94107
{
95108
label: 'Pricing',
96109
href: '/pricing'
@@ -204,9 +217,15 @@
204217
</a>
205218
<nav class="aw-main-header-nav" aria-label="Main">
206219
<ul class="aw-main-header-nav-list">
207-
{#each navLinks as { label, href }}
220+
{#each navLinks as navLink}
208221
<li class="aw-main-header-nav-item">
209-
<a class="aw-link" {href}>{label}</a>
222+
<a
223+
class="aw-link"
224+
href={navLink.href}
225+
data-initialized={$initialized ? '' : undefined}
226+
data-badge={navLink.showBadge ? '' : undefined}
227+
>{navLink.label}
228+
</a>
210229
</li>
211230
{/each}
212231
</ul>
@@ -242,3 +261,40 @@
242261
<slot />
243262
</main>
244263
</div>
264+
265+
<style lang="scss">
266+
.nav-badge {
267+
margin-inline-start: 0.5rem;
268+
padding-inline: 0.375rem;
269+
}
270+
271+
@keyframes scale-in {
272+
0% {
273+
transform: scale(0);
274+
}
275+
100% {
276+
transform: scale(1);
277+
}
278+
}
279+
280+
[data-badge] {
281+
position: relative;
282+
283+
&::after {
284+
content: '';
285+
position: absolute;
286+
background-color: hsl(var(--aw-color-accent));
287+
border-radius: 100%;
288+
width: 0.375rem;
289+
height: 0.375rem;
290+
291+
inset-block-start: -2px;
292+
inset-inline-end: -4px;
293+
translate: 100%;
294+
}
295+
296+
&:not([data-initialized])::after {
297+
animation: scale-in 0.2s ease-out;
298+
}
299+
}
300+
</style>

src/lib/utils/date.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export const formatDate = (date: string): string => {
2-
const dt = new Date(date);
3-
const month = dt.toLocaleString('default', { month: 'short' });
4-
const day = dt.getDate();
5-
const year = dt.getFullYear();
6-
return `${month} ${day}, ${year}`;
2+
const dt = new Date(date);
3+
const month = dt.toLocaleString('default', { month: 'short' });
4+
const day = dt.getDate();
5+
const year = dt.getFullYear();
6+
return `${month} ${day}, ${year}`;
77
};

src/lib/utils/is.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function isNumeric(value: unknown): boolean {
2+
if (typeof value === 'number' && !isNaN(value)) {
3+
return true;
4+
}
5+
if (typeof value === 'string' && value.trim() !== '') {
6+
return !isNaN(Number(value));
7+
}
8+
return false;
9+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script lang="ts" context="module">
2+
import { hasContext, setContext } from 'svelte';
3+
4+
export type ChangelogData = {
5+
title: string;
6+
description?: string;
7+
date: string;
8+
cover?: string;
9+
};
10+
11+
const CONTEXT_KEY = Symbol('changelog');
12+
13+
function initContext() {
14+
setContext(CONTEXT_KEY, null);
15+
}
16+
17+
export function isInsideChangelog() {
18+
return hasContext(CONTEXT_KEY);
19+
}
20+
</script>
21+
22+
<script lang="ts">
23+
initContext();
24+
</script>
25+
26+
<!-- Empty so it can be displayed inline -->
27+
<slot />

src/markdoc/nodes/Item.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<script lang="ts">
2+
import { isInsideChangelog } from '$markdoc/layouts/Changelog.svelte';
23
import { getContext } from 'svelte';
34
45
const isDocs = getContext('isDocs') ?? false;
6+
const inChangelog = isInsideChangelog();
57
</script>
68

7-
<li><p class:aw-paragraph-lg={!isDocs}><slot /></p></li>
9+
<li><p class:aw-paragraph-lg={!isDocs && !inChangelog}><slot /></p></li>

0 commit comments

Comments
 (0)