···11---
22-import Welcome from '../components/Welcome.astro';
33-import Layout from '../layouts/Layout.astro';
22+import Layout from "../layouts/Layout.astro";
33+import DesktopNavigation from "../components/Navigation/DektopNavigation.astro";
44+import { getCollection, render } from "astro:content";
55+import Markdown from "../layouts/Markdown.astro";
4655-// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build
66-// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.
77+const about = await getCollection("about");
88+console.log(about);
99+1010+const { Content } = await render(about[0]);
711---
812913<Layout>
1010- <Welcome />
1414+ <DesktopNavigation
1515+ items={["about", "links", "feed", "blog"]}
1616+ active="about"
1717+ />
1818+1919+ <div class="mt-8 mx-4">
2020+ <Markdown>
2121+ <Content />
2222+ </Markdown>
2323+ </div>
1124</Layout>
···11+// keep a cache of formatter per locale, to avoid re-creating them (GC)
22+const formatters = new Map<string, Intl.RelativeTimeFormat>();
33+44+// get the Intl.RelativeTimeFormat formatter for the given locale
55+export function getFormatter(locale: string) {
66+ if (formatters.has(locale)) {
77+ return formatters.get(locale)!;
88+ }
99+ const formatter = new Intl.RelativeTimeFormat(locale, { numeric: 'always', style: 'narrow' });
1010+ formatters.set(locale, formatter);
1111+ return formatter;
1212+}
+6
src/components/post/relative-time/index.ts
···11+// adapted from https://github.com/CaptainCodeman/svelte-relative-time
22+33+export * from './action';
44+export type { Callback } from './render';
55+export { register, unregister } from './state';
66+export { default } from './RelativeTime.svelte';
+80
src/components/post/relative-time/render.ts
···11+export type Callback = (result: {
22+ seconds: number;
33+ count: number;
44+ units: Intl.RelativeTimeFormatUnit;
55+ text: string;
66+}) => void;
77+88+export interface RenderState {
99+ date: Date | number;
1010+ callback: Callback;
1111+ formatter: Intl.RelativeTimeFormat;
1212+}
1313+1414+// Array reprsenting one minute, hour, day, week, month, etc in seconds
1515+const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
1616+1717+// Array equivalent to the above but in the string representation of the units
1818+const formatUnits: Intl.RelativeTimeFormatUnit[] = [
1919+ 'seconds',
2020+ 'minutes',
2121+ 'hours',
2222+ 'days',
2323+ 'weeks',
2424+ 'months',
2525+ 'years'
2626+];
2727+2828+// function to render relative time into
2929+export function render(state: RenderState, now: number) {
3030+ const { date, callback, formatter } = state;
3131+3232+ // Allow dates or times to be passed
3333+ const timeMs = typeof date === 'number' ? date : date.getTime();
3434+3535+ // Get the amount of seconds between the given date and now
3636+ const delta = timeMs - now;
3737+ const seconds = Math.round(delta / 1000);
3838+3939+ // Grab the ideal cutoff unit
4040+ const unitIndex = cutoffs.findIndex((cutoff) => cutoff > Math.abs(seconds));
4141+4242+ // units
4343+ const units = formatUnits[unitIndex];
4444+4545+ // Get the divisor to divide from the seconds. E.g. if our unit is 'day' our divisor
4646+ // is one day in seconds, so we can divide our seconds by this to get the # of days
4747+ const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1;
4848+4949+ // count of units
5050+ const count = Math.round(seconds / divisor);
5151+5252+ // Intl.RelativeTimeFormat do its magic
5353+ callback({
5454+ seconds: seconds,
5555+ count,
5656+ units,
5757+ text: formatter.format(count, units).replace('ago', '')
5858+ });
5959+6060+ // calculate time to next update, taking account rounding (e.g. it goes from 2 minutes to 1 minute at 90 seconds)
6161+ // and also the changeover from units (59 seconds shouldn't show as 1 minute, so at 61 seconds we schedule the next
6262+ // update for 1 second time)
6363+6464+ const divisorMs = divisor * 1000;
6565+6666+ let updateIn;
6767+6868+ if (unitIndex) {
6969+ updateIn = divisorMs / 2 - (Math.abs(delta) % divisorMs);
7070+ if (updateIn < 0) {
7171+ updateIn += divisorMs;
7272+ }
7373+ } else {
7474+ updateIn = divisorMs - (Math.abs(delta) % divisorMs);
7575+ }
7676+7777+ const updateAt = now + updateIn;
7878+7979+ return updateAt;
8080+}
+60
src/components/post/relative-time/state.ts
···11+import { getFormatter } from './formatter';
22+import { render } from './render';
33+import type { Callback, RenderState } from './render';
44+55+interface UpdateState extends RenderState {
66+ update: number;
77+}
88+99+// keep track of each instance
1010+const instances = new Map<Object, UpdateState>();
1111+1212+// we use a single timer for efficiency and to keep updates in sync
1313+let updateInterval: number | NodeJS.Timeout;
1414+1515+// register or update instance
1616+export function register(
1717+ instance: Object,
1818+ date: Date | number,
1919+ locale: string,
2020+ live: boolean,
2121+ callback: Callback
2222+) {
2323+ // get the formatter for the given locale, we do this here so we don't keep having to look it up on each tick
2424+ const formatter = getFormatter(locale);
2525+2626+ // create state to render
2727+ const state = { date, callback, formatter };
2828+2929+ // initial render is immediate, so works for SSR
3030+ const update = render(state, Date.now());
3131+3232+ // if it's to update live, we keep a track and schedule the next update
3333+ if (live) {
3434+ instances.set(instance, { ...state, update });
3535+ } else {
3636+ instances.delete(instance);
3737+ }
3838+3939+ // start the clock ticking if there are any live instances
4040+ if (instances.size) {
4141+ updateInterval =
4242+ updateInterval ||
4343+ setInterval(() => {
4444+ const now = Date.now();
4545+ for (const state of instances.values()) {
4646+ if (state.update <= now) {
4747+ state.update = render(state, now);
4848+ }
4949+ }
5050+ }, 1000);
5151+ }
5252+}
5353+5454+export function unregister(instance: Object) {
5555+ instances.delete(instance);
5656+ if (instances.size === 0) {
5757+ clearInterval(updateInterval);
5858+ updateInterval = 0;
5959+ }
6060+}