TeaWeb/shared/js/ui/modal/whats-new/Renderer.tsx

187 lines
7.6 KiB
TypeScript

import * as React from "react";
import {useState} from "react";
import * as dompurify from "dompurify";
import {ChangeLog, ChangeLogEntry, ChangeSet} from "tc-shared/update/ChangeLog";
import {Translatable, VariadicTranslatable} from "tc-shared/ui/react-elements/i18n";
import {guid} from "tc-shared/crypto/uid";
const {Remarkable} = require("remarkable");
const cssStyle = require("./Renderer.scss");
const mdRenderer = new Remarkable();
const brElementUuid = guid();
export interface DisplayableChangeList extends ChangeLog {
title: React.ReactElement<Translatable>;
url: string;
}
mdRenderer.renderer.rules.link_open = function (tokens, idx, options /*, env */) {
const title = tokens[idx].title ? (`title="${tokens[idx].title}"`) : '';
const target = options.linkTarget ? (`target="${options.linkTarget}"`) : '';
let href = `<a href="${tokens[idx].href}" ${title} ${target} ></a>`;
href = dompurify.sanitize(href);
if (href.substr(-4) !== "</a>") {
return "<-- invalid link open... -->";
}
return href.substr(0, href.length - 4);
};
const ChangeSetRenderer = (props: { set: ChangeSet }) => {
return (
<React.Fragment>
{props.set.title ? <li key={"title"}>{props.set.title}</li> : undefined}
<ul>
{props.set.changes.map((change, index) => typeof change === "string" ?
<li key={index} dangerouslySetInnerHTML={{
__html: mdRenderer.renderInline(change.replace(/\n/g, brElementUuid)).replace(new RegExp(brElementUuid, "g"), "<br />")
}}/> : <ChangeSetRenderer set={change} key={index}/>)}
</ul>
</React.Fragment>
);
};
const ChangeLogEntryRenderer = React.memo((props: { entry: ChangeLogEntry }) => (
<li>
<b>{props.entry.timestamp}</b>
<ChangeSetRenderer set={props.entry}/>
</li>
));
const DisplayableChangeListRenderer = (props: { list: DisplayableChangeList | undefined, visible: boolean }) => (
<div className={cssStyle.body + " " + (!props.visible ? cssStyle.hidden : "")}>
<div className={cssStyle.changeList}>
<ul>
{props.list?.changes.map((value, index) => <ChangeLogEntryRenderer entry={value} key={index}/>)}
</ul>
</div>
<div className={cssStyle.containerBrowse}>
<a href={props.list?.url} target={"_blank"}><Translatable>Open full Change Log</Translatable></a>
</div>
</div>
);
const ChangeListRenderer = (props: { left?: DisplayableChangeList, right?: DisplayableChangeList, defaultSelected: "right" | "left" | "none" }) => {
const [selected, setSelected] = useState<"left" | "right" | "none">(props.defaultSelected);
return (
<div className={cssStyle.changes}>
<div
className={cssStyle.header + " " + (selected === "left" ? cssStyle.selectedLeft : selected === "right" ? cssStyle.selectedRight : "")}>
<div className={cssStyle.left + " " + (props.left ? "" : cssStyle.hidden)}
onClick={() => setSelected("left")}>
<a>{props.left?.title}</a>
</div>
<div className={cssStyle.right + " " + (props.right ? "" : cssStyle.hidden)}
onClick={() => setSelected("right")}>
<a>{props.right?.title}</a>
</div>
</div>
<DisplayableChangeListRenderer list={props.left} visible={selected === "left"}/>
<DisplayableChangeListRenderer list={props.right} visible={selected === "right"}/>
</div>
)
};
export const WhatsNew = (props: { changesUI?: ChangeLog, changesClient?: ChangeLog }) => {
let subtitleLong, infoText;
let changesUI = props.changesUI ? Object.assign({
title: <Translatable>UI Change Log</Translatable>,
url: "https://github.com/TeaSpeak/TeaWeb/blob/master/ChangeLog.md"
}, props.changesUI) : undefined;
let changesClient = props.changesClient ? Object.assign({
title: <Translatable>Client Change Log</Translatable>,
url: "https://github.com/TeaSpeak/TeaClient/blob/master/ChangeLog.txt"
}, props.changesClient) : undefined;
let versionUIDate = props.changesUI?.currentVersion, versionNativeDate = props.changesClient?.currentVersion;
if (__build.target === "web") {
subtitleLong = (
<Translatable key={"sub-web"}>We've successfully updated the web client for you.</Translatable>
);
infoText = (
<VariadicTranslatable key={"info-web"}
text={"The web client has been updated to the version from {}."}
>
{versionUIDate}
</VariadicTranslatable>
);
} else if (props.changesUI && props.changesClient) {
subtitleLong = (
<Translatable
key={"sub-native-client-ui"}
>
We've successfully updated the native client and its UI for you.
</Translatable>
);
infoText = (
<React.Fragment key={"info-native-client-ui"}>
<VariadicTranslatable
text={"The native client has been updated to the version from {}."}>{versionNativeDate}</VariadicTranslatable>
<VariadicTranslatable
text={"Its UI has been updated to the version {}."}>{versionUIDate}</VariadicTranslatable>
</React.Fragment>
);
} else if (props.changesClient) {
subtitleLong = (
<Translatable key={"sub-native-client"}>
We've successfully updated the native client for you.
</Translatable>
);
infoText = (
<VariadicTranslatable
key={"info-native-client"}
text={"The native client has been updated to the version {}."}>
{versionNativeDate}
</VariadicTranslatable>
);
} else if (props.changesUI) {
subtitleLong = (
<Translatable
key={"sub-native-ui"}
>
We've successfully updated the native clients UI for you.
</Translatable>
);
infoText = (
<VariadicTranslatable key={"info-native-ui"}
text={"The native clients UI has been updated to the version from {}."}
>
{versionUIDate}
</VariadicTranslatable>
);
}
const changes = [changesClient, changesUI].filter(e => !!e);
return (
<div className={cssStyle.container}>
<div className={cssStyle.info}>
<div className={cssStyle.logo}>
<img alt={tr("TeaSpeak logo")} src="img/teaspeak_cup_animated.png"/>
</div>
<div className={cssStyle.text}>
<h1><Translatable>Welcome back!</Translatable></h1>
<h2 className={cssStyle.subtitleLong}>{subtitleLong}</h2>
<h2 className={cssStyle.subtitleShort}><Translatable>The client has been updated.</Translatable>
</h2>
<p>
<Translatable>While you've been away, resting, we did some work.</Translatable> <br/>
{infoText} <br/>
<Translatable>A list of changes, bugfixes and new features can be found bellow.</Translatable>
</p>
<a><Translatable>Enjoy!</Translatable></a>
</div>
</div>
<ChangeListRenderer
defaultSelected={"right"}
right={changes[0]}
left={changes[1]}
/>
</div>
);
};