diff --git a/apps/desktop2/index.html b/apps/desktop2/index.html
index 2e9a0363..ea08f587 100644
--- a/apps/desktop2/index.html
+++ b/apps/desktop2/index.html
@@ -7,8 +7,7 @@
Lume Desktop
-
+
diff --git a/apps/desktop2/package.json b/apps/desktop2/package.json
index 525a8a82..59d38dc0 100644
--- a/apps/desktop2/package.json
+++ b/apps/desktop2/package.json
@@ -9,9 +9,14 @@
"preview": "vite preview"
},
"dependencies": {
+ "@lume/ark": "workspace:^",
+ "@lume/storage": "workspace:^",
"@tanstack/react-router": "^1.16.0",
+ "i18next": "^23.8.2",
+ "i18next-resources-to-backend": "^1.2.0",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-i18next": "^14.0.2"
},
"devDependencies": {
"@lume/tailwindcss": "workspace:^",
diff --git a/apps/desktop2/public/heading-en.png b/apps/desktop2/public/heading-en.png
new file mode 100644
index 00000000..fb740945
Binary files /dev/null and b/apps/desktop2/public/heading-en.png differ
diff --git a/apps/desktop2/public/heading-en@2x.png b/apps/desktop2/public/heading-en@2x.png
new file mode 100644
index 00000000..802c679b
Binary files /dev/null and b/apps/desktop2/public/heading-en@2x.png differ
diff --git a/apps/desktop2/public/heading-fr.png b/apps/desktop2/public/heading-fr.png
new file mode 100644
index 00000000..9a1cf82a
Binary files /dev/null and b/apps/desktop2/public/heading-fr.png differ
diff --git a/apps/desktop2/public/heading-fr@2x.png b/apps/desktop2/public/heading-fr@2x.png
new file mode 100644
index 00000000..16bf43fd
Binary files /dev/null and b/apps/desktop2/public/heading-fr@2x.png differ
diff --git a/apps/desktop2/public/heading-ja.png b/apps/desktop2/public/heading-ja.png
new file mode 100644
index 00000000..19134a08
Binary files /dev/null and b/apps/desktop2/public/heading-ja.png differ
diff --git a/apps/desktop2/public/heading-ja@2x.png b/apps/desktop2/public/heading-ja@2x.png
new file mode 100644
index 00000000..606cb59c
Binary files /dev/null and b/apps/desktop2/public/heading-ja@2x.png differ
diff --git a/apps/desktop2/src/app.tsx b/apps/desktop2/src/app.tsx
index 4b61b627..50a735ae 100644
--- a/apps/desktop2/src/app.tsx
+++ b/apps/desktop2/src/app.tsx
@@ -1,6 +1,11 @@
+import { ArkProvider } from "@lume/ark";
+import { StorageProvider } from "@lume/storage";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import React, { StrictMode } from "react";
import ReactDOM from "react-dom/client";
+import { I18nextProvider } from "react-i18next";
+import "./app.css";
+import i18n from "./i18n";
// Import the generated route tree
import { routeTree } from "./tree.gen";
@@ -21,8 +26,14 @@ const rootElement = document.getElementById("root")!;
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
-
-
- ,
+
+
+
+
+
+
+
+
+ ,
);
}
diff --git a/apps/desktop2/src/i18n.ts b/apps/desktop2/src/i18n.ts
new file mode 100644
index 00000000..fb75d366
--- /dev/null
+++ b/apps/desktop2/src/i18n.ts
@@ -0,0 +1,23 @@
+import { resolveResource } from "@tauri-apps/api/path";
+import { readTextFile } from "@tauri-apps/plugin-fs";
+import i18n from "i18next";
+import resourcesToBackend from "i18next-resources-to-backend";
+import { initReactI18next } from "react-i18next";
+
+i18n
+ .use(
+ resourcesToBackend(async (language: string) => {
+ const file_path = await resolveResource(`locales/${language}.json`);
+ return JSON.parse(await readTextFile(file_path));
+ }),
+ )
+ .use(initReactI18next)
+ .init({
+ lng: "en",
+ fallbackLng: "en",
+ interpolation: {
+ escapeValue: false,
+ },
+ });
+
+export default i18n;
diff --git a/apps/desktop2/src/routes/__root.tsx b/apps/desktop2/src/routes/__root.tsx
index b02bf17b..b88ac49e 100644
--- a/apps/desktop2/src/routes/__root.tsx
+++ b/apps/desktop2/src/routes/__root.tsx
@@ -3,14 +3,12 @@ import {
ScrollRestoration,
createRootRoute,
} from "@tanstack/react-router";
-import { TanStackRouterDevtools } from "@tanstack/router-devtools";
export const Route = createRootRoute({
component: () => (
<>
-
>
),
});
diff --git a/apps/desktop2/src/routes/auth/create.lazy.tsx b/apps/desktop2/src/routes/auth/create.lazy.tsx
new file mode 100644
index 00000000..e7478b94
--- /dev/null
+++ b/apps/desktop2/src/routes/auth/create.lazy.tsx
@@ -0,0 +1,52 @@
+import { useArk } from "@lume/ark";
+import { Keys } from "@lume/types";
+import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
+import { invoke } from "@tauri-apps/api/core";
+import { useEffect, useState } from "react";
+
+export const Route = createLazyFileRoute("/auth/create")({
+ component: Create,
+});
+
+function Create() {
+ const ark = useArk();
+ const navigate = useNavigate();
+
+ const [keys, setKeys] = useState(null);
+
+ const submit = async () => {
+ const save = await ark.save_account(keys);
+ if (save) {
+ navigate({ to: "/" });
+ } else {
+ console.log("create failed");
+ }
+ };
+
+ useEffect(() => {
+ async function genKeys() {
+ const cmd: Keys = await invoke("create_keys");
+ setKeys(cmd);
+ }
+
+ genKeys();
+ }, []);
+
+ return (
+
+
+
Backup your key
+
+ {keys ? : null}
+
+
+
+
+ );
+}
diff --git a/apps/desktop2/src/routes/auth/import.lazy.tsx b/apps/desktop2/src/routes/auth/import.lazy.tsx
new file mode 100644
index 00000000..2556b95c
--- /dev/null
+++ b/apps/desktop2/src/routes/auth/import.lazy.tsx
@@ -0,0 +1,55 @@
+import { useArk } from "@lume/ark";
+import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
+import { invoke } from "@tauri-apps/api/core";
+import { useState } from "react";
+
+export const Route = createLazyFileRoute("/auth/import")({
+ component: Import,
+});
+
+function Import() {
+ const ark = useArk();
+ const navigate = useNavigate();
+
+ const [key, setKey] = useState("");
+
+ const submit = async () => {
+ if (!key.startsWith("nsec1")) return;
+ if (key.length < 30) return;
+
+ const npub: string = await invoke("get_public_key", { nsec: key });
+ const keys = {
+ npub,
+ nsec: key,
+ };
+
+ const save = await ark.save_account(keys);
+ if (save) {
+ navigate({ to: "/" });
+ } else {
+ console.log("import failed");
+ }
+ };
+
+ return (
+
+
+
Import your key
+
+ setKey(e.target.value)}
+ />
+
+
+
+
+ );
+}
diff --git a/apps/desktop2/src/routes/index.lazy.tsx b/apps/desktop2/src/routes/index.lazy.tsx
deleted file mode 100644
index a5bee37f..00000000
--- a/apps/desktop2/src/routes/index.lazy.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { createLazyFileRoute } from "@tanstack/react-router";
-
-export const Route = createLazyFileRoute("/")({
- component: Index,
-});
-
-function Index() {
- return (
-
-
Welcome Home!
-
- );
-}
diff --git a/apps/desktop2/src/routes/index.tsx b/apps/desktop2/src/routes/index.tsx
new file mode 100644
index 00000000..d3370bd6
--- /dev/null
+++ b/apps/desktop2/src/routes/index.tsx
@@ -0,0 +1,25 @@
+import { createFileRoute, redirect } from "@tanstack/react-router";
+import { invoke } from "@tauri-apps/api/core";
+
+export const Route = createFileRoute("/")({
+ component: Index,
+ beforeLoad: async ({ location }) => {
+ const signer = await invoke("verify_signer");
+ if (!signer) {
+ throw redirect({
+ to: "/landing",
+ search: {
+ redirect: location.href,
+ },
+ });
+ }
+ },
+});
+
+function Index() {
+ return (
+
+
Welcome Home!
+
+ );
+}
diff --git a/apps/desktop2/src/routes/landing/index.tsx b/apps/desktop2/src/routes/landing/index.tsx
new file mode 100644
index 00000000..3fc0edc2
--- /dev/null
+++ b/apps/desktop2/src/routes/landing/index.tsx
@@ -0,0 +1,59 @@
+import { useStorage } from "@lume/storage";
+import { Link, createFileRoute } from "@tanstack/react-router";
+import { useTranslation } from "react-i18next";
+
+export const Route = createFileRoute("/landing/")({
+ component: Index,
+});
+
+function Index() {
+ const storage = useStorage();
+ const { t } = useTranslation();
+
+ return (
+
+
+
+
+
+
+
+ {t("welcome.title")}
+
+
+
+
+ {t("welcome.signup")}
+
+
+ {t("welcome.login")}
+
+
+
+
+
+ {t("welcome.footer")}{" "}
+
+ here
+
+
+
+
+
+ );
+}
diff --git a/apps/desktop2/src/tree.gen.ts b/apps/desktop2/src/tree.gen.ts
index 01afbeea..4fa60d03 100644
--- a/apps/desktop2/src/tree.gen.ts
+++ b/apps/desktop2/src/tree.gen.ts
@@ -13,24 +13,54 @@ import { createFileRoute } from '@tanstack/react-router'
// Import Routes
import { Route as rootRoute } from './routes/__root'
+import { Route as IndexImport } from './routes/index'
+import { Route as LandingIndexImport } from './routes/landing/index'
// Create Virtual Routes
-const IndexLazyImport = createFileRoute('/')()
+const AuthImportLazyImport = createFileRoute('/auth/import')()
+const AuthCreateLazyImport = createFileRoute('/auth/create')()
// Create/Update Routes
-const IndexLazyRoute = IndexLazyImport.update({
+const IndexRoute = IndexImport.update({
path: '/',
getParentRoute: () => rootRoute,
-} as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route))
+} as any)
+
+const LandingIndexRoute = LandingIndexImport.update({
+ path: '/landing/',
+ getParentRoute: () => rootRoute,
+} as any)
+
+const AuthImportLazyRoute = AuthImportLazyImport.update({
+ path: '/auth/import',
+ getParentRoute: () => rootRoute,
+} as any).lazy(() => import('./routes/auth/import.lazy').then((d) => d.Route))
+
+const AuthCreateLazyRoute = AuthCreateLazyImport.update({
+ path: '/auth/create',
+ getParentRoute: () => rootRoute,
+} as any).lazy(() => import('./routes/auth/create.lazy').then((d) => d.Route))
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
- preLoaderRoute: typeof IndexLazyImport
+ preLoaderRoute: typeof IndexImport
+ parentRoute: typeof rootRoute
+ }
+ '/auth/create': {
+ preLoaderRoute: typeof AuthCreateLazyImport
+ parentRoute: typeof rootRoute
+ }
+ '/auth/import': {
+ preLoaderRoute: typeof AuthImportLazyImport
+ parentRoute: typeof rootRoute
+ }
+ '/landing/': {
+ preLoaderRoute: typeof LandingIndexImport
parentRoute: typeof rootRoute
}
}
@@ -38,6 +68,11 @@ declare module '@tanstack/react-router' {
// Create and export the route tree
-export const routeTree = rootRoute.addChildren([IndexLazyRoute])
+export const routeTree = rootRoute.addChildren([
+ IndexRoute,
+ AuthCreateLazyRoute,
+ AuthImportLazyRoute,
+ LandingIndexRoute,
+])
/* prettier-ignore-end */
diff --git a/apps/desktop2/vite.config.ts b/apps/desktop2/vite.config.ts
index 5cd0f888..04c584f9 100644
--- a/apps/desktop2/vite.config.ts
+++ b/apps/desktop2/vite.config.ts
@@ -12,12 +12,7 @@ export default defineConfig({
promiseExportName: "__tla",
promiseImportName: (i) => `__tla_${i}`,
}),
- TanStackRouterVite({
- routesDirectory: "./src/routes",
- generatedRouteTree: "./src/tree.gen.ts",
- routeFileIgnorePrefix: "-",
- quoteStyle: "single",
- }),
+ TanStackRouterVite(),
],
build: {
outDir: "../../dist",
diff --git a/packages/ark/src/ark.ts b/packages/ark/src/ark.ts
index 3d75da4b..8ab44aa7 100644
--- a/packages/ark/src/ark.ts
+++ b/packages/ark/src/ark.ts
@@ -1,20 +1,35 @@
-import { type CurrentAccount, Event, Metadata } from "@lume/types";
+import { type CurrentAccount, Event, Keys, Metadata } from "@lume/types";
import { invoke } from "@tauri-apps/api/core";
export class Ark {
public account: CurrentAccount;
constructor() {
- this.account = { pubkey: "" };
+ this.account = { npub: "" };
}
- public async verify_signer() {
+ public async load_account() {
try {
- const cmd: string = await invoke("verify_signer");
+ const cmd: string = await invoke("load_account");
if (cmd) {
- this.account.pubkey = cmd;
+ this.account.npub = cmd;
+ }
+ } catch (e) {
+ console.error(String(e));
+ }
+ }
+
+ public async save_account(keys: Keys) {
+ try {
+ const cmd: boolean = await invoke("save_key", { nsec: keys.nsec });
+
+ if (cmd) {
+ await invoke("update_signer", { nsec: keys.nsec });
+ this.account.npub = keys.npub;
+
return true;
}
+
return false;
} catch (e) {
console.error(String(e));
diff --git a/packages/ark/src/provider.tsx b/packages/ark/src/provider.tsx
index e0cee303..450c7eb9 100644
--- a/packages/ark/src/provider.tsx
+++ b/packages/ark/src/provider.tsx
@@ -1,10 +1,21 @@
-import { PropsWithChildren, createContext, useContext, useMemo } from "react";
+import {
+ PropsWithChildren,
+ createContext,
+ useContext,
+ useEffect,
+ useMemo,
+} from "react";
import { Ark } from "./ark";
export const ArkContext = createContext(undefined);
export const ArkProvider = ({ children }: PropsWithChildren