stoat-for-desktop/components/modal/modals/Changelog.tsx

196 lines
5.2 KiB
TypeScript

import { For, Match, Switch, createSignal } from "solid-js";
import { Trans } from "@lingui-solid/solid/macro";
import { styled } from "styled-system/jsx";
import { CategoryButton, Column, Dialog, DialogProps } from "@revolt/ui";
import type { DialogAction } from "@revolt/ui/components/design/Dialog";
import { Markdown } from "@revolt/markdown";
import { Symbol } from "@revolt/ui/components/utils/Symbol";
import { Modals } from "../types";
/**
* Changelog element
*/
type Element =
| string
| {
type: "image";
src: string;
};
/**
* Changelog post
*/
export interface ChangelogPost {
icon: string;
date: Date;
link: string;
title: string;
content: Element[];
}
const ChangelogPosts: ChangelogPost[] = [
// {
// icon: "key",
// date: new Date("2022-06-12T20:39:16.674Z"),
// title: "Secure your account with 2FA",
// content: [
// "Two-factor authentication is now available to all users, you can now head over to settings to enable recovery codes and an authenticator app.",
// {
// type: "image",
// src: "https://autumn.revolt.chat/attachments/E21kwmuJGcASgkVLiSIW0wV3ggcaOWjW0TQF7cdFNY/image.png",
// },
// "Once enabled, you will be prompted on login.",
// {
// type: "image",
// src: "https://autumn.revolt.chat/attachments/LWRYoKR2tE1ggW_Lzm547P1pnrkNgmBaoCAfWvHE74/image.png",
// },
// "Other authentication methods coming later, stay tuned!",
// ],
// },
{
icon: "voice_chat",
date: new Date("2025-11-08T14:00:00.000Z"),
link: "https://stoat.chat/updates/web-0013",
title: "Stoat for Web: 0.0.13",
content: [
`This update brings a host of improvements to voice chats along with an improved messaging experience.
## Improved Voice Chats
A brand new user interface with picture-in-picture support to keep an eye on your voice chats while you multitask.`,
{
type: "image",
src: "https://stoat.chat/content/changelog/web-0.0.13/pip.gif",
},
`You can now also see who is in a voice chat and who is speaking, making it easier to keep track of conversations.
## Improved Text Editor
The text editor has been revamped to provide a smoother and more intuitive messaging experience. We've taken and improved the experience from our old web app, featuring rich text preview while not intruding on what you're typing.`,
{
type: "image",
src: "https://stoat.chat/content/changelog/web-0.0.13/text_editor.png",
},
`For an in-depth look at all the new features and improvements, check out our blog post by pressing read more!`,
],
},
];
export const CHANGELOG_MODAL_CONST = {
index: 0,
until: new Date("2026-01-01T00:00:00.000Z"),
};
/**
* Modal to display changelog
*/
export function ChangelogModal(
props: DialogProps & Modals & { type: "changelog" },
) {
const [log, setLog] = createSignal(props.initial);
/**
* Get the currently selected log
* @returns Log
*/
const currentLog = () =>
typeof log() !== "undefined" ? ChangelogPosts[log()!] : undefined;
const actions = () => {
const actionList: DialogAction[] = [
{
text: <Trans>Read More</Trans>,
onClick() {
window.open(currentLog()?.link, "_blank");
},
},
{ text: <Trans>Close</Trans> },
];
// if (currentLog()) {
// actionList.push({
// text: <Trans>View older updates</Trans>,
// onClick: () => {
// setLog(undefined);
// return false;
// },
// });
// }
return actionList;
};
return (
<Dialog
show={props.show}
onClose={props.onClose}
title={
<Switch fallback={<Trans>Changelog</Trans>}>
<Match when={currentLog()}>{currentLog()!.title}</Match>
</Switch>
}
actions={actions()}
>
<Switch
fallback={
<Column>
<For each={ChangelogPosts}>
{(entry, index) => {
/**
* Handle changing post
*/
const onClick = () => setLog(index());
return (
<CategoryButton
icon={<Symbol>{entry.icon}</Symbol>}
onClick={onClick}
>
{entry.title}
</CategoryButton>
);
}}
</For>
</Column>
}
>
<Match when={currentLog()}>
<RenderLog post={currentLog()!} />
</Match>
</Switch>
</Dialog>
);
}
/**
* Render a single changelog post
*/
function RenderLog(props: { post: ChangelogPost }) {
return (
<Column>
<For each={props.post.content}>
{(entry) => (
<Switch>
<Match when={typeof entry === "string"}>
<Markdown content={entry as string} />
</Match>
<Match when={typeof entry === "object" && entry.type === "image"}>
<Image src={(entry as { src: string }).src} loading="lazy" />
</Match>
</Switch>
)}
</For>
</Column>
);
}
/**
* Image wrapper
*/
const Image = styled("img", {
base: {
borderRadius: "var(--borderRadius-md)",
},
});