187 lines
7.6 KiB
TypeScript
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>
|
|
);
|
|
};
|