feat: backup key task

This commit is contained in:
Kieran 2023-11-23 13:43:23 +00:00
parent 3e8fe2ca95
commit 2c414c4a56
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
4 changed files with 100 additions and 22 deletions

View File

@ -0,0 +1,49 @@
import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom";
import { BaseUITask } from "@/Tasks";
import { MetadataCache } from "@snort/system";
import { LoginSession } from "@/Login";
import Icon from "@/Icons/Icon";
export class BackupKeyTask extends BaseUITask {
id = "backup-key";
noBaseStyle = true;
check(_: MetadataCache, session: LoginSession): boolean {
return !this.state.muted && session.type == "private_key";
}
render() {
return (
<div className="p card">
<div className="flex g12 bg-superdark p24 br">
<div>
<div className="p12 bg-dark circle">
<Icon name="key" size={21} />
</div>
</div>
<div className="flex flex-col g8">
<div className="font-semibold text-xl">
<FormattedMessage defaultMessage="Be sure to back up your keys!" id="1UWegE" />
</div>
<small>
<FormattedMessage defaultMessage="No keys, no {app}, There is no way to reset it if you don't back up. It only takes a minute." id="YR2I9M" values={{
app: CONFIG.appNameCapitalized
}} />
</small>
<div className="flex g8">
<Link to="/settings/keys">
<button>
<FormattedMessage defaultMessage="Back up now" id="rMgF34" />
</button>
</Link>
<button className="secondary" onClick={() => this.mute()}>
<FormattedMessage defaultMessage="Already backed up" id="j9xbzF" />
</button>
</div>
</div>
</div>
</div>
);
}
}

View File

@ -1,5 +1,5 @@
import "./TaskList.css";
import { useState } from "react";
import { useSyncExternalStore } from "react";
import { useUserProfile } from "@snort/system-react";
import useLogin from "@/Hooks/useLogin";
@ -9,37 +9,62 @@ import { DonateTask } from "./DonateTask";
import { Nip5Task } from "./Nip5Task";
import { RenewSubTask } from "./RenewSubscription";
import { NoticeZapPoolDefault } from "./NoticeZapPool";
import { BackupKeyTask } from "./BackupKey";
import { ExternalStore } from "@snort/shared";
const AllTasks: Array<UITask> = [new Nip5Task(), new DonateTask(), new NoticeZapPoolDefault()];
if (CONFIG.features.subscriptions) {
AllTasks.push(new RenewSubTask());
class TaskStore extends ExternalStore<Array<UITask>> {
#tasks: Array<UITask>;
constructor() {
super();
const AllTasks: Array<UITask> = [
new BackupKeyTask(),
new Nip5Task(),
new DonateTask(),
new NoticeZapPoolDefault()
];
if (CONFIG.features.subscriptions) {
AllTasks.push(new RenewSubTask());
}
AllTasks.forEach(a => a.load(() => {
this.notifyChange()
}));
this.#tasks = AllTasks;
}
takeSnapshot(): UITask[] {
return [...this.#tasks];
}
}
AllTasks.forEach(a => a.load());
const AllTasks = new TaskStore();
export const TaskList = () => {
const session = useLogin();
const user = useUserProfile(session.publicKey);
const [, setTick] = useState<number>(0);
const tasks = useSyncExternalStore(c => AllTasks.hook(c), () => AllTasks.snapshot());
function muteTask(t: UITask) {
t.mute();
setTick(x => (x += 1));
}
return (
<div className="task-list">
{AllTasks.filter(a => (user ? a.check(user, session) : false)).map(a => {
return (
<div key={a.id} className="card">
<div className="header">
<Icon name="lightbulb" />
<div className="close" onClick={() => muteTask(a)}>
<Icon name="close" size={14} />
{tasks.filter(a => (user ? a.check(user, session) : false)).map(a => {
if (a.noBaseStyle) {
return a.render();
} else {
return (
<div key={a.id} className="card">
<div className="header">
<Icon name="lightbulb" />
<div className="close" onClick={() => muteTask(a)}>
<Icon name="close" size={14} />
</div>
</div>
{a.render()}
</div>
{a.render()}
</div>
);
);
}
})}
</div>
);

View File

@ -3,12 +3,13 @@ import { LoginSession } from "@/Login";
export interface UITask {
id: string;
noBaseStyle: boolean;
/**
* Run checks to determine if this Task should be triggered for this user
*/
check(user: MetadataCache, session: LoginSession): boolean;
mute(): void;
load(): void;
load(cb: () => void): void;
render(): JSX.Element;
}
@ -19,9 +20,11 @@ export interface UITaskState {
}
export abstract class BaseUITask implements UITask {
#cb?: () => void;
protected state: UITaskState;
abstract id: string;
noBaseStyle = false;
abstract check(user: MetadataCache, session: LoginSession): boolean;
abstract render(): JSX.Element;
@ -34,7 +37,8 @@ export abstract class BaseUITask implements UITask {
this.#save();
}
load() {
load(cb: () => void) {
this.#cb = cb;
const state = window.localStorage.getItem(`task:${this.id}`);
if (state) {
this.state = JSON.parse(state);
@ -43,5 +47,6 @@ export abstract class BaseUITask implements UITask {
#save() {
window.localStorage.setItem(`task:${this.id}`, JSON.stringify(this.state));
this.#cb?.();
}
}

View File

@ -3,7 +3,6 @@ import { VitePWA } from "vite-plugin-pwa";
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from "vite";
import { vitePluginVersionMark } from "vite-plugin-version-mark";
import appConfig from "config";
export default defineConfig({
@ -27,13 +26,13 @@ export default defineConfig({
name: "snort",
ifGitSHA: true,
command: "git describe --always --tags",
}),
})
],
assetsInclude: ["**/*.md", "**/*.wasm"],
build: {
outDir: "build",
},
base: "",
clearScreen: false,
publicDir: appConfig.get("publicDir"),
resolve: {
alias: {