Start working on using preact and TSX.
This commit is contained in:
parent
40bb264107
commit
a95c6d250f
File diff suppressed because one or more lines are too long
|
@ -50,7 +50,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"node": {
|
"node": {
|
||||||
"extensions": [".js", ".ts"]
|
"extensions": [".js", ".ts", ".tsx"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
dist
|
dist
|
||||||
|
.eslintcache
|
|
@ -29,6 +29,7 @@
|
||||||
"mini-css-extract-plugin": "^1.6.0",
|
"mini-css-extract-plugin": "^1.6.0",
|
||||||
"node-sass": "^6.0.0",
|
"node-sass": "^6.0.0",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
"preact": "^10.5.13",
|
||||||
"prettier": "^2.3.0",
|
"prettier": "^2.3.0",
|
||||||
"prismjs": "^1.23.0",
|
"prismjs": "^1.23.0",
|
||||||
"qr-scanner": "^1.2.0",
|
"qr-scanner": "^1.2.0",
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
npx eslint --cache -c .eslintrc.json "$@" --ext .js,.ts
|
npx eslint --cache -c .eslintrc.json "$@" --ext .js,.ts,.tsx
|
36
src/elements/ReactTile.tsx
Normal file
36
src/elements/ReactTile.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Component, JSX } from "preact";
|
||||||
|
|
||||||
|
export type TileParams = {
|
||||||
|
condition?: boolean;
|
||||||
|
color?: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon?: string;
|
||||||
|
iconText?: string;
|
||||||
|
onclick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Tile extends Component<TileParams, unknown> {
|
||||||
|
render(): JSX.Element {
|
||||||
|
if (this.props.condition == false) return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a class="uk-link-heading" onClick={this.props.onclick}>
|
||||||
|
<div class={"uk-padding-small uk-background-" + (this.props.color || "primary")}>
|
||||||
|
<p class="uk-h4">
|
||||||
|
{this.props.title}
|
||||||
|
{typeof this.props.icon == "string" && (
|
||||||
|
<span
|
||||||
|
class="uk-icon uk-margin-small-left"
|
||||||
|
uk-icon={`icon: ${this.props.icon}`}
|
||||||
|
role="img"
|
||||||
|
aria-label={this.props.iconText}
|
||||||
|
></span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<span class="uk-text-muted">{this.props.description}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,6 +107,9 @@ document.addEventListener(
|
||||||
Object.entries(translations).map(([k, v]) => [k, { translation: v }]),
|
Object.entries(translations).map(([k, v]) => [k, { translation: v }]),
|
||||||
),
|
),
|
||||||
interpolation: {
|
interpolation: {
|
||||||
|
escape: (str) => {
|
||||||
|
return str;
|
||||||
|
},
|
||||||
format: function (value: unknown, format, _): string {
|
format: function (value: unknown, format, _): string {
|
||||||
if (format === "until_date" && value instanceof Date)
|
if (format === "until_date" && value instanceof Date)
|
||||||
return formatDistance(new Date(), new Date(value), pageState.language);
|
return formatDistance(new Date(), new Date(value), pageState.language);
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
import { Page } from "../types/Page";
|
|
||||||
import { Tile } from "../elements/Tile";
|
|
||||||
import { lookupSelf } from "../api/sys/lookupSelf";
|
|
||||||
import { makeElement } from "z-makeelement";
|
|
||||||
import { prePageChecks, setErrorText } from "../pageUtils";
|
|
||||||
import i18next from "i18next";
|
|
||||||
|
|
||||||
export class HomePage extends Page {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
async render(): Promise<void> {
|
|
||||||
await this.router.setPageContent("");
|
|
||||||
if (!(await prePageChecks(this.router))) return;
|
|
||||||
|
|
||||||
this.state.baseMount = "";
|
|
||||||
this.state.secretPath = [];
|
|
||||||
this.state.secretItem = "";
|
|
||||||
this.state.secretVersion = null;
|
|
||||||
|
|
||||||
const homePageContent = makeElement({ tag: "div" });
|
|
||||||
await this.router.setPageContent(homePageContent);
|
|
||||||
const textList = makeElement({
|
|
||||||
tag: "ul",
|
|
||||||
class: "uk-nav",
|
|
||||||
children: [
|
|
||||||
makeElement({
|
|
||||||
tag: "li",
|
|
||||||
children: makeElement({
|
|
||||||
tag: "span",
|
|
||||||
html: i18next.t("home_vaulturl_text", { text: this.state.apiURL }),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
makeElement({
|
|
||||||
tag: "li",
|
|
||||||
children: makeElement({
|
|
||||||
tag: "a",
|
|
||||||
text: i18next.t("home_password_generator_btn"),
|
|
||||||
onclick: async () => {
|
|
||||||
await this.router.changePage("PW_GEN");
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
homePageContent.appendChild(textList);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const selfTokenInfo = await lookupSelf();
|
|
||||||
textList.appendChild(
|
|
||||||
makeElement({
|
|
||||||
tag: "li",
|
|
||||||
text: i18next.t("home_your_token_expires_in", {
|
|
||||||
date: new Date(selfTokenInfo.expire_time),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch (e: unknown) {
|
|
||||||
const error = e as Error;
|
|
||||||
setErrorText(error.message);
|
|
||||||
if (error.message == "permission denied") {
|
|
||||||
this.state.token = "";
|
|
||||||
await this.router.changePage("LOGIN");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
homePageContent.appendChild(
|
|
||||||
makeElement({
|
|
||||||
tag: "div",
|
|
||||||
class:
|
|
||||||
"uk-child-width-1-1@s uk-child-width-1-2@m uk-grid-small uk-grid-match uk-margin-top",
|
|
||||||
attributes: { "uk-grid": "" },
|
|
||||||
children: [
|
|
||||||
Tile({
|
|
||||||
title: i18next.t("home_secrets_title"),
|
|
||||||
description: i18next.t("home_secrets_description"),
|
|
||||||
icon: "file-edit",
|
|
||||||
onclick: async () => {
|
|
||||||
await this.router.changePage("SECRETS_HOME");
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
Tile({
|
|
||||||
title: i18next.t("home_access_title"),
|
|
||||||
description: i18next.t("home_access_description"),
|
|
||||||
icon: "users",
|
|
||||||
onclick: async () => {
|
|
||||||
await this.router.changePage("ACCESS_HOME");
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
get name(): string {
|
|
||||||
return i18next.t("home_page_title");
|
|
||||||
}
|
|
||||||
}
|
|
86
src/pages/Home.tsx
Normal file
86
src/pages/Home.tsx
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { Page } from "../types/Page";
|
||||||
|
import { Tile } from "../elements/ReactTile";
|
||||||
|
import { TokenInfo } from "../api/types/token";
|
||||||
|
import { lookupSelf } from "../api/sys/lookupSelf";
|
||||||
|
import { prePageChecks, setErrorText } from "../pageUtils";
|
||||||
|
import { render } from "preact";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
export class HomePage extends Page {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
async render(): Promise<void> {
|
||||||
|
await this.router.setPageContent("");
|
||||||
|
if (!(await prePageChecks(this.router))) return;
|
||||||
|
|
||||||
|
this.state.baseMount = "";
|
||||||
|
this.state.secretPath = [];
|
||||||
|
this.state.secretItem = "";
|
||||||
|
this.state.secretVersion = null;
|
||||||
|
|
||||||
|
let selfTokenInfo: TokenInfo;
|
||||||
|
try {
|
||||||
|
selfTokenInfo = await lookupSelf();
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as Error;
|
||||||
|
setErrorText(error.message);
|
||||||
|
if (error.message == "permission denied") {
|
||||||
|
this.state.token = "";
|
||||||
|
await this.router.changePage("LOGIN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(
|
||||||
|
<div>
|
||||||
|
<ul id="textList" class="uk-nav">
|
||||||
|
<li>
|
||||||
|
<span>{i18next.t("home_vaulturl_text", { text: this.state.apiURL })}</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
onClick={async () => {
|
||||||
|
await this.router.changePage("PW_GEN");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("home_password_generator_btn")}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span>
|
||||||
|
{i18next.t("home_your_token_expires_in", {
|
||||||
|
date: new Date(selfTokenInfo.expire_time),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div
|
||||||
|
class="uk-child-width-1-1@s uk-child-width-1-2@m uk-grid-small uk-grid-match uk-margin-top"
|
||||||
|
uk-grid
|
||||||
|
>
|
||||||
|
<Tile
|
||||||
|
title={i18next.t("home_secrets_title")}
|
||||||
|
description={i18next.t("home_secrets_description")}
|
||||||
|
icon="file-edit"
|
||||||
|
onclick={async () => {
|
||||||
|
await this.router.changePage("SECRETS_HOME");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tile
|
||||||
|
title={i18next.t("home_access_title")}
|
||||||
|
description={i18next.t("home_access_description")}
|
||||||
|
icon="users"
|
||||||
|
onclick={async () => {
|
||||||
|
await this.router.changePage("ACCESS_HOME");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
this.router.pageContentElement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return i18next.t("home_page_title");
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,8 @@
|
||||||
"strictBindCallApply": true,
|
"strictBindCallApply": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"moduleResolution": "node"
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "preact",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue