forked from Kieran/snort
feat: backup key task
This commit is contained in:
parent
3e8fe2ca95
commit
2c414c4a56
49
packages/app/src/Tasks/BackupKey.tsx
Normal file
49
packages/app/src/Tasks/BackupKey.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import "./TaskList.css";
|
import "./TaskList.css";
|
||||||
import { useState } from "react";
|
import { useSyncExternalStore } from "react";
|
||||||
import { useUserProfile } from "@snort/system-react";
|
import { useUserProfile } from "@snort/system-react";
|
||||||
|
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
@ -9,37 +9,62 @@ import { DonateTask } from "./DonateTask";
|
|||||||
import { Nip5Task } from "./Nip5Task";
|
import { Nip5Task } from "./Nip5Task";
|
||||||
import { RenewSubTask } from "./RenewSubscription";
|
import { RenewSubTask } from "./RenewSubscription";
|
||||||
import { NoticeZapPoolDefault } from "./NoticeZapPool";
|
import { NoticeZapPoolDefault } from "./NoticeZapPool";
|
||||||
|
import { BackupKeyTask } from "./BackupKey";
|
||||||
|
import { ExternalStore } from "@snort/shared";
|
||||||
|
|
||||||
const AllTasks: Array<UITask> = [new Nip5Task(), new DonateTask(), new NoticeZapPoolDefault()];
|
class TaskStore extends ExternalStore<Array<UITask>> {
|
||||||
if (CONFIG.features.subscriptions) {
|
#tasks: Array<UITask>;
|
||||||
AllTasks.push(new RenewSubTask());
|
|
||||||
|
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 = () => {
|
export const TaskList = () => {
|
||||||
const session = useLogin();
|
const session = useLogin();
|
||||||
const user = useUserProfile(session.publicKey);
|
const user = useUserProfile(session.publicKey);
|
||||||
const [, setTick] = useState<number>(0);
|
const tasks = useSyncExternalStore(c => AllTasks.hook(c), () => AllTasks.snapshot());
|
||||||
|
|
||||||
function muteTask(t: UITask) {
|
function muteTask(t: UITask) {
|
||||||
t.mute();
|
t.mute();
|
||||||
setTick(x => (x += 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="task-list">
|
<div className="task-list">
|
||||||
{AllTasks.filter(a => (user ? a.check(user, session) : false)).map(a => {
|
{tasks.filter(a => (user ? a.check(user, session) : false)).map(a => {
|
||||||
return (
|
if (a.noBaseStyle) {
|
||||||
<div key={a.id} className="card">
|
return a.render();
|
||||||
<div className="header">
|
} else {
|
||||||
<Icon name="lightbulb" />
|
return (
|
||||||
<div className="close" onClick={() => muteTask(a)}>
|
<div key={a.id} className="card">
|
||||||
<Icon name="close" size={14} />
|
<div className="header">
|
||||||
|
<Icon name="lightbulb" />
|
||||||
|
<div className="close" onClick={() => muteTask(a)}>
|
||||||
|
<Icon name="close" size={14} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{a.render()}
|
||||||
</div>
|
</div>
|
||||||
{a.render()}
|
);
|
||||||
</div>
|
}
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,12 +3,13 @@ import { LoginSession } from "@/Login";
|
|||||||
|
|
||||||
export interface UITask {
|
export interface UITask {
|
||||||
id: string;
|
id: string;
|
||||||
|
noBaseStyle: boolean;
|
||||||
/**
|
/**
|
||||||
* Run checks to determine if this Task should be triggered for this user
|
* Run checks to determine if this Task should be triggered for this user
|
||||||
*/
|
*/
|
||||||
check(user: MetadataCache, session: LoginSession): boolean;
|
check(user: MetadataCache, session: LoginSession): boolean;
|
||||||
mute(): void;
|
mute(): void;
|
||||||
load(): void;
|
load(cb: () => void): void;
|
||||||
render(): JSX.Element;
|
render(): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,9 +20,11 @@ export interface UITaskState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseUITask implements UITask {
|
export abstract class BaseUITask implements UITask {
|
||||||
|
#cb?: () => void;
|
||||||
protected state: UITaskState;
|
protected state: UITaskState;
|
||||||
|
|
||||||
abstract id: string;
|
abstract id: string;
|
||||||
|
noBaseStyle = false;
|
||||||
abstract check(user: MetadataCache, session: LoginSession): boolean;
|
abstract check(user: MetadataCache, session: LoginSession): boolean;
|
||||||
abstract render(): JSX.Element;
|
abstract render(): JSX.Element;
|
||||||
|
|
||||||
@ -34,7 +37,8 @@ export abstract class BaseUITask implements UITask {
|
|||||||
this.#save();
|
this.#save();
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load(cb: () => void) {
|
||||||
|
this.#cb = cb;
|
||||||
const state = window.localStorage.getItem(`task:${this.id}`);
|
const state = window.localStorage.getItem(`task:${this.id}`);
|
||||||
if (state) {
|
if (state) {
|
||||||
this.state = JSON.parse(state);
|
this.state = JSON.parse(state);
|
||||||
@ -43,5 +47,6 @@ export abstract class BaseUITask implements UITask {
|
|||||||
|
|
||||||
#save() {
|
#save() {
|
||||||
window.localStorage.setItem(`task:${this.id}`, JSON.stringify(this.state));
|
window.localStorage.setItem(`task:${this.id}`, JSON.stringify(this.state));
|
||||||
|
this.#cb?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { VitePWA } from "vite-plugin-pwa";
|
|||||||
import { visualizer } from "rollup-plugin-visualizer";
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { vitePluginVersionMark } from "vite-plugin-version-mark";
|
import { vitePluginVersionMark } from "vite-plugin-version-mark";
|
||||||
|
|
||||||
import appConfig from "config";
|
import appConfig from "config";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@ -27,13 +26,13 @@ export default defineConfig({
|
|||||||
name: "snort",
|
name: "snort",
|
||||||
ifGitSHA: true,
|
ifGitSHA: true,
|
||||||
command: "git describe --always --tags",
|
command: "git describe --always --tags",
|
||||||
}),
|
})
|
||||||
],
|
],
|
||||||
assetsInclude: ["**/*.md", "**/*.wasm"],
|
assetsInclude: ["**/*.md", "**/*.wasm"],
|
||||||
build: {
|
build: {
|
||||||
outDir: "build",
|
outDir: "build",
|
||||||
},
|
},
|
||||||
base: "",
|
clearScreen: false,
|
||||||
publicDir: appConfig.get("publicDir"),
|
publicDir: appConfig.get("publicDir"),
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
Loading…
Reference in New Issue
Block a user