stoat-for-desktop/components/markdown/plugins/timestamps.tsx

129 lines
2.9 KiB
TypeScript

import { createEffect, createSignal } from "solid-js";
import { type Dayjs } from "dayjs";
import type { Handler } from "mdast-util-to-hast";
import { Plugin } from "unified";
import { visit } from "unist-util-visit";
import { dayjs, timeLocale } from "@revolt/i18n/dayjs";
import { time as Time } from "../elements";
export function RenderTimestamp(props: { format: string; date: Dayjs }) {
/**
* Format a time string
*/
function format() {
switch (props.format) {
case "t":
return props.date.format("hh:mm");
case "T":
return props.date.format("hh:mm:ss");
case "R":
return props.date.fromNow();
case "D":
return props.date.format("DD MMMM YYYY");
case "F":
return props.date.format("dddd, DD MMMM YYYY hh:mm");
default:
return props.date.format("DD MMMM YYYY hh:mm");
}
}
// Signal for current value
const [value, setValue] = createSignal(format());
const update = () => setValue(format());
createEffect(() => {
// Update every second if we are rendering relative time
if (props.format === "R") {
const interval = setInterval(update, 1000);
return () => clearInterval(interval);
}
});
return (
<time
class={Time()}
use:floating={{
tooltip: {
placement: "top",
content: props.date.toString(),
},
}}
>
{value()}
</time>
);
}
/**
* Regex for matching timestamps
*/
export const RE_TIMESTAMP = /<t:([0-9]+)(?::(\w))?>/g;
export const remarkTimestamps: Plugin = () => (tree) => {
visit(
tree,
"text",
(
node: { type: "text"; value: string },
idx,
parent: { children: unknown[] },
) => {
const elements = node.value.split(RE_TIMESTAMP);
if (elements.length === 1) return; // no matches
// Generate initial node
const newNodes: (
| { type: "text"; value: string }
| {
type: "timestamp";
format: string;
date: Dayjs;
}
)[] = [
{
type: "text",
value: elements.shift()!,
},
];
// Process all timestamps
for (let i = 0; i < elements.length / 3; i++) {
// Process timestamp
const date = dayjs
.unix(parseInt(elements[i * 3]))
.locale(timeLocale()[1]);
// Insert components
newNodes.push({
type: "timestamp",
format: elements[i * 3 + 1],
date,
});
newNodes.push({
type: "text",
value: elements[i * 3 + 2],
});
}
parent.children.splice(idx, 1, ...newNodes);
return idx + newNodes.length;
},
);
};
export const timestampHandler: Handler = (h, node) => {
return {
type: "element" as const,
tagName: "timestamp",
children: [],
properties: {
format: node.format,
date: node.date,
},
};
};