Compare commits

..
10 Commits
Author SHA1 Message Date
Ren AmamiyaandGitHub 8cb3ef82b2 Merge pull request #67 from luminous-devs/main
v1.2.0
2023-08-13 11:19:51 +07:00
Ren AmamiyaandGitHub 025562f9f0 Merge pull request #61 from luminous-devs/main
v1.1.1
2023-07-27 09:26:26 +07:00
Ren AmamiyaandGitHub 595bcc9b3c Merge pull request #59 from luminous-devs/main
v1.1.0
2023-07-26 09:27:39 +07:00
Ren AmamiyaandGitHub 15991d07ab Merge pull request #53 from luminous-devs/main
update set password flow
2023-07-10 17:20:26 +07:00
Ren AmamiyaandGitHub 99fc1f0b10 Merge pull request #52 from luminous-devs/main
update gh action and fix migrate page
2023-07-10 15:34:03 +07:00
Ren AmamiyaandGitHub 1041e1ccd4 Merge pull request #50 from luminous-devs/main
v1.0.1
2023-07-10 14:40:17 +07:00
Ren AmamiyaandGitHub 2eeb2c896d Merge pull request #45 from luminous-devs/main
rebuild with code signing
2023-07-07 12:24:08 +07:00
Ren AmamiyaandGitHub caf8fb584a Merge pull request #33 from luminous-devs/main
v1.0.0
2023-07-06 08:18:01 +07:00
Ren AmamiyaandGitHub 58205713ab Merge pull request #32 from luminous-devs/main
test github action again
2023-07-05 17:40:35 +07:00
Ren AmamiyaandGitHub 33802d32f3 Merge pull request #31 from luminous-devs/main
test github action
2023-07-05 09:50:57 +07:00
421 changed files with 17680 additions and 19227 deletions
-1
View File
@@ -1 +0,0 @@
use flake
+13 -12
View File
@@ -1,5 +1,8 @@
name: 'Publish'
on: workflow_dispatch
name: 'publish'
on:
push:
branches:
- release
env:
CARGO_INCREMENTAL: 0
@@ -14,7 +17,7 @@ jobs:
settings:
- platform: 'macos-latest'
args: '--target universal-apple-darwin'
- platform: 'ubuntu-22.04'
- platform: 'ubuntu-20.04'
args: ''
- platform: 'windows-latest'
args: '--target x86_64-pc-windows-msvc'
@@ -29,14 +32,14 @@ jobs:
with:
targets: aarch64-apple-darwin
- name: install dependencies (ubuntu only)
if: matrix.settings.platform == 'ubuntu-22.04'
if: matrix.settings.platform == 'ubuntu-20.04'
run: |
sudo apt-get update
sudo apt-get install -y build-essential libssl-dev javascriptcoregtk-4.1 libayatana-appindicator3-dev libsoup-3.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev webkit2gtk-4.1 librsvg2-dev patchelf
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8.x.x
version: 7.x.x
run_install: false
- name: Setup node and cache for package data
uses: actions/setup-node@v3
@@ -51,20 +54,18 @@ jobs:
- uses: tauri-apps/tauri-action@dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tagName: v__VERSION__
releaseName: 'v__VERSION__'
tagName: v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version
releaseName: 'App v__VERSION__'
releaseBody: 'See the assets to download this version and install.'
releaseDraft: true
prerelease: false
args: ${{ matrix.settings.args }}
includeDebug: true
+1 -3
View File
@@ -14,11 +14,9 @@ out
*.local
.next
.vscode
pnpm-lock.yaml
*.db
*.db-journal
bun.lockb
.direnv
# Editor directories and files
.vscode/*
+6 -15
View File
@@ -8,12 +8,6 @@ Download Lume for your platform here: [https://github.com/luminous-devs/lume/rel
Supported platform: macOS, Windows and Linux
### Prerequisites
- PNPM or Bun (experiment)
- Tauri: https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-macos
### Develop
Clone project
@@ -28,23 +22,20 @@ Install packages
pnpm install
```
Run dev build
Run dev
```
pnpm tauri dev
```
Generate production build
Build
```
pnpm tauri build
```
#### Nix
(Advance) - Generate SQLite migration
Requirements:
1. [Install Nix](https://zero-to-flakes.com/install)
1. [Setup `direnv`](https://zero-to-flakes.com/direnv)
`cd` into the root folder of the project to enter `nix develop` shell. Run `direnv allow` (only once). Then run `pnpm` or `bun` (experimental) commands as described above.
```
pnpm add-migrate <migrate_name>
```
Generated
-130
View File
@@ -1,130 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1697723726,
"narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7c9cc5a6e5d38010801741ac830a3f8fd667a7a0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1681358109,
"narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1697940838,
"narHash": "sha256-eyk92QqAoRNC0V99KOcKcBZjLPixxNBS0PRc4KlSQVs=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "a3e829c06eadf848f13d109c7648570ce37ebccd",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
-71
View File
@@ -1,71 +0,0 @@
# Nix.flake to build Lume based on Tauri's Guides:
# Prerequisites -> Installing -> Setting Up Linux -> NixOS
# https://tauri.app/v1/guides/getting-started/prerequisites/#1-system-dependencies
#
# To build Rust backend of Tauri `rust-overlay` is used
# https://github.com/oxalica/rust-overlay
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay.url = "github:oxalica/rust-overlay";
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
libraries = with pkgs;[
webkitgtk
gtk3
cairo
gdk-pixbuf
glib
dbus
openssl_3
librsvg
];
packages = with pkgs; [
curl
wget
pkg-config
dbus
openssl_3
glib
gtk3
libsoup
webkitgtk
librsvg
];
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
extensions = [ "rust-src" ]; # needed by rust-analyzer
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = [
rustToolchain
pkgs.nodejs
pkgs.nodePackages.pnpm
pkgs.bun # experimental in Lume
] ++ packages;
shellHook =
''
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH
export XDG_DATA_DIRS=${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}:$XDG_DATA_DIRS
'';
# Avoid white screen running with Nix
# https://github.com/tauri-apps/tauri/issues/4315#issuecomment-1207755694
WEBKIT_DISABLE_COMPOSITING_MODE = 1;
};
});
}
+3 -3
View File
@@ -1,11 +1,11 @@
<html lang="en">
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lume</title>
</head>
<body class="relative cursor-default select-none overflow-hidden font-sans antialiased h-screen w-screen text-neutral-950 dark:text-neutral-50">
<body class="relative cursor-default select-none overflow-hidden font-sans antialiased h-screen w-screen text-white">
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+76 -87
View File
@@ -1,8 +1,7 @@
{
"name": "lume",
"description": "the communication app",
"private": true,
"version": "2.2.0",
"version": "1.2.0",
"scripts": {
"dev": "vite",
"build": "vite build",
@@ -18,109 +17,99 @@
"**/*.{ts, tsx, css, md, html, json}": "prettier --cache --write"
},
"dependencies": {
"@evilmartians/harmony": "^1.2.0",
"@getalby/sdk": "^2.7.0",
"@nostr-dev-kit/ndk": "^2.2.0",
"@nostr-fetch/adapter-ndk": "^0.13.1",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-avatar": "^1.0.4",
"@ctrl/magnet-link": "^3.1.2",
"@headlessui/react": "^1.7.16",
"@nostr-dev-kit/ndk": "^0.8.11",
"@nostr-fetch/adapter-ndk": "^0.11.0",
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-toolbar": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@tanstack/react-query": "^5.12.1",
"@tanstack/react-query-devtools": "^5.12.1",
"@tauri-apps/api": "2.0.0-alpha.11",
"@tauri-apps/cli": "2.0.0-alpha.17",
"@tauri-apps/plugin-autostart": "2.0.0-alpha.3",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-alpha.3",
"@tauri-apps/plugin-dialog": "2.0.0-alpha.3",
"@tauri-apps/plugin-fs": "2.0.0-alpha.3",
"@tauri-apps/plugin-http": "2.0.0-alpha.3",
"@tauri-apps/plugin-notification": "2.0.0-alpha.3",
"@tauri-apps/plugin-os": "2.0.0-alpha.4",
"@tauri-apps/plugin-process": "2.0.0-alpha.3",
"@tauri-apps/plugin-shell": "2.0.0-alpha.3",
"@tauri-apps/plugin-sql": "2.0.0-alpha.3",
"@tauri-apps/plugin-updater": "2.0.0-alpha.3",
"@tauri-apps/plugin-upload": "2.0.0-alpha.3",
"@tiptap/extension-character-count": "^2.1.13",
"@tiptap/extension-document": "^2.1.13",
"@tiptap/extension-image": "^2.1.13",
"@tiptap/extension-mention": "^2.1.13",
"@tiptap/extension-paragraph": "^2.1.13",
"@tiptap/extension-placeholder": "^2.1.13",
"@tiptap/extension-text": "^2.1.13",
"@tiptap/pm": "^2.1.13",
"@tiptap/react": "^2.1.13",
"@tiptap/starter-kit": "^2.1.13",
"@tiptap/suggestion": "^2.1.13",
"dayjs": "^1.11.10",
"framer-motion": "^10.16.12",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-tooltip": "^1.0.6",
"@tanstack/react-query": "^4.32.6",
"@tanstack/react-query-devtools": "^4.32.6",
"@tanstack/react-virtual": "3.0.0-beta.54",
"@tauri-apps/api": "2.0.0-alpha.5",
"@tauri-apps/plugin-app": "github:tauri-apps/tauri-plugin-app#v2",
"@tauri-apps/plugin-autostart": "github:tauri-apps/tauri-plugin-autostart#v2",
"@tauri-apps/plugin-clipboard-manager": "github:tauri-apps/tauri-plugin-clipboard-manager#v2",
"@tauri-apps/plugin-dialog": "github:tauri-apps/tauri-plugin-dialog#v2",
"@tauri-apps/plugin-fs": "github:tauri-apps/tauri-plugin-fs#v2",
"@tauri-apps/plugin-http": "github:tauri-apps/tauri-plugin-http#v2",
"@tauri-apps/plugin-notification": "github:tauri-apps/tauri-plugin-notification#v2",
"@tauri-apps/plugin-os": "github:tauri-apps/tauri-plugin-os#v2",
"@tauri-apps/plugin-process": "github:tauri-apps/tauri-plugin-process#v2",
"@tauri-apps/plugin-shell": "github:tauri-apps/tauri-plugin-shell#v2",
"@tauri-apps/plugin-sql": "github:tauri-apps/tauri-plugin-sql#v2",
"@tauri-apps/plugin-store": "github:tauri-apps/tauri-plugin-store#v2",
"@tauri-apps/plugin-stronghold": "github:tauri-apps/tauri-plugin-stronghold#v2",
"@tauri-apps/plugin-upload": "github:tauri-apps/tauri-plugin-upload#v2",
"@tauri-apps/plugin-window": "2.0.0-alpha.0",
"@tiptap/extension-image": "^2.0.4",
"@tiptap/extension-mention": "^2.0.4",
"@tiptap/extension-placeholder": "^2.0.4",
"@tiptap/pm": "^2.0.4",
"@tiptap/react": "^2.0.4",
"@tiptap/starter-kit": "^2.0.4",
"@tiptap/suggestion": "^2.0.4",
"@void-cat/api": "^1.0.7",
"cheerio": "1.0.0-rc.12",
"dayjs": "^1.11.9",
"destr": "^1.2.2",
"get-urls": "^11.0.0",
"html-to-text": "^9.0.5",
"idb-keyval": "^6.2.1",
"immer": "^10.0.2",
"light-bolt11-decoder": "^3.0.0",
"lru-cache": "^10.1.0",
"markdown-to-jsx": "^7.3.2",
"media-chrome": "^1.5.4",
"minidenticons": "^4.2.0",
"nanoid": "^5.0.3",
"nostr-fetch": "^0.13.1",
"nostr-tools": "^1.17.0",
"lru-cache": "^10.0.1",
"nostr-fetch": "^0.12.2",
"nostr-tools": "^1.14.0",
"qrcode.react": "^3.1.0",
"re-resizable": "^6.9.11",
"react": "^18.2.0",
"react-currency-input-field": "^3.6.12",
"react-dom": "^18.2.0",
"react-hook-form": "^7.48.2",
"react-hook-form": "^7.45.4",
"react-hotkeys-hook": "^4.4.1",
"react-router-dom": "^6.20.1",
"react-markdown": "^8.0.7",
"react-player": "^2.12.0",
"react-router-dom": "^6.15.0",
"react-string-replace": "^1.1.1",
"reactflow": "^11.10.1",
"sonner": "^1.2.4",
"tauri-controls": "github:reyamir/tauri-controls",
"react-virtuoso": "^4.5.0",
"remark-gfm": "^3.0.1",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.7",
"virtua": "^0.16.7",
"zustand": "^4.4.7"
"zustand": "^4.4.1"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/html-to-text": "^9.0.4",
"@types/node": "^20.10.2",
"@types/react": "^18.2.40",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.13.1",
"@typescript-eslint/parser": "^6.13.1",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.16",
"@tailwindcss/typography": "^0.5.9",
"@tauri-apps/cli": "2.0.0-alpha.10",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/html-to-text": "^9.0.1",
"@types/node": "^18.17.5",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@types/youtube-player": "^5.5.7",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"clsx": "^2.0.0",
"cross-env": "^7.0.3",
"csstype": "^3.1.2",
"encoding": "^0.1.13",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.33.2",
"eslint": "^8.47.0",
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"husky": "^8.0.3",
"lint-staged": "^15.1.0",
"postcss": "^8.4.31",
"prettier": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7",
"lint-staged": "^13.2.3",
"postcss": "^8.4.27",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.3.0",
"prop-types": "^15.8.1",
"tailwind-merge": "^1.14.0",
"tailwind-scrollbar": "^3.0.5",
"tailwindcss": "^3.3.5",
"typescript": "^5.3.2",
"vite": "^4.5.0",
"tailwindcss": "^3.3.3",
"typescript": "^4.9.5",
"vite": "^4.4.9",
"vite-plugin-top-level-await": "^1.3.1",
"vite-tsconfig-paths": "^4.2.1"
"vite-tsconfig-paths": "^4.2.0"
}
}
+3753 -2660
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

+1469 -1580
View File
File diff suppressed because it is too large Load Diff
+32 -26
View File
@@ -1,46 +1,52 @@
[package]
name = "lume"
version = "2.2.0"
description = "the communication app"
version = "1.2.0"
description = "nostr client"
authors = ["Ren Amamiya"]
license = "GPL-3.0"
repository = "https://github.com/luminous-devs/lume"
license = ""
repository = ""
edition = "2021"
rust-version = "1.66"
rust-version = "1.71"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "2.0.0-alpha", features = [] }
tauri-build = { version = "2.0.0-alpha.6", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "2.0.0-alpha", features = [
tauri = { version = "2.0.0-alpha.10", features = [
"macos-private-api",
"native-tls-vendored",
"system-tray",
] }
tauri-plugin-cli = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-clipboard-manager = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-fs = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-http = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-notification = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-os = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-process = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-stronghold = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-theme = { git = "https://github.com/wyhaya/tauri-plugin-theme" }
tauri-plugin-sql = { git = "hhttps://github.com/tauri-apps/plugins-workspace", branch = "v2", features = [
"sqlite",
] }
tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-http = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-fs = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-clipboard-manager = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-notification = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-app = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-process = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-os = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-window = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
window-vibrancy = { git = "https://github.com/tauri-apps/window-vibrancy", branch = "dev" }
sqlx-cli = { version = "0.7.0", default-features = false, features = [
"sqlite",
] }
webpage = { version = "1.6.0", features = ["serde"] }
keyring = "2"
rust-argon2 = "1.0"
rand = "0.8.5"
[dependencies.tauri-plugin-sql]
git = "https://github.com/tauri-apps/plugins-workspace"
branch = "v2"
features = ["sqlite"]
[features]
# by default Tauri runs in production mode
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

@@ -1,29 +1,45 @@
-- Add migration script here
-- create accounts table
-- is_active (multi-account feature), value:
-- 0: false
-- 1: true
CREATE TABLE
accounts (
id TEXT NOT NULL PRIMARY KEY,
id INTEGER NOT NULL PRIMARY KEY,
npub TEXT NOT NULL UNIQUE,
pubkey TEXT NOT NULL UNIQUE,
follows TEXT,
circles TEXT,
privkey TEXT NOT NULL,
follows JSON,
is_active INTEGER NOT NULL DEFAULT 0,
last_login_at NUMBER NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- create notes table
CREATE TABLE
events (
id TEXT NOT NULL PRIMARY KEY,
notes (
id INTEGER NOT NULL PRIMARY KEY,
event_id TEXT NOT NULL UNIQUE,
account_id INTEGER NOT NULL,
event TEXT NOT NULL,
author TEXT NOT NULL,
kind NUMBER NOT NULL DEFAULt 1,
root_id TEXT,
reply_id TEXT,
pubkey TEXT NOT NULL,
kind INTEGER NOT NULL DEFAULT 1,
tags JSON,
content TEXT NOT NULL,
created_at INTEGER NOT NULL,
parent_id TEXT,
FOREIGN KEY (account_id) REFERENCES accounts (id)
);
-- create channels table
CREATE TABLE
channels (
id INTEGER NOT NULL PRIMARY KEY,
event_id TEXT NOT NULL UNIQUE,
name TEXT,
about TEXT,
picture TEXT,
created_at INTEGER NOT NULL
);
-- create settings table
CREATE TABLE
settings (
@@ -33,23 +49,11 @@ CREATE TABLE
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- create metadata table
CREATE TABLE
widgets (
id INTEGER NOT NULL PRIMARY KEY,
account_id INTEGER NOT NULL,
kind INTEGER NOT NULL,
title TEXT NOT NULL,
metadata (
id TEXT NOT NULL PRIMARY KEY,
pubkey TEXT NOT NULL,
content TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts (id)
);
CREATE TABLE
relays (
id INTEGER NOT NULL PRIMARY KEY,
account_id INTEGER NOT NULL,
relay TEXT NOT NULL UNIQUE,
purpose TEXT NOT NULL DEFAULT '',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts (id)
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
@@ -0,0 +1,12 @@
-- Add migration script here
-- create chats table
CREATE TABLE
chats (
id INTEGER NOT NULL PRIMARY KEY,
event_id TEXT NOT NULL UNIQUE,
receiver_pubkey INTEGER NOT NULL,
sender_pubkey TEXT NOT NULL,
content TEXT NOT NULL,
tags JSON,
created_at INTEGER NOT NULL
);
@@ -0,0 +1,14 @@
-- Add migration script here
INSERT INTO
settings (key, value)
VALUES
("last_login", "0"),
(
"relays",
'["wss://relayable.org","wss://relay.damus.io","wss://relay.nostr.band/all","wss://relay.nostrgraph.net","wss://nostr.mutinywallet.com"]'
),
("auto_start", "0"),
("cache_time", "86400000"),
("compose_shortcut", "meta+n"),
("add_imageblock_shortcut", "meta+i"),
("add_feedblock_shortcut", "meta+f")
@@ -0,0 +1,3 @@
-- Add migration script here
-- add pubkey to channel
ALTER TABLE channels ADD pubkey TEXT NOT NULL DEFAULT '';
@@ -0,0 +1,38 @@
-- Add migration script here
INSERT
OR IGNORE INTO channels (
event_id,
pubkey,
name,
about,
picture,
created_at
)
VALUES
(
"e3cadf5beca1b2af1cddaa41a633679bedf263e3de1eb229c6686c50d85df753",
"126103bfddc8df256b6e0abfd7f3797c80dcc4ea88f7c2f87dd4104220b4d65f",
"lume-general",
"General channel for Lume",
"https://void.cat/d/UNyxBmAh1MUx5gQTX95jyf.webp",
1681898574
);
INSERT
OR IGNORE INTO channels (
event_id,
pubkey,
name,
about,
picture,
created_at
)
VALUES
(
"25e5c82273a271cb1a840d0060391a0bf4965cafeb029d5ab55350b418953fbb",
"ed1d0e1f743a7d19aa2dfb0162df73bacdbc699f67cc55bb91a98c35f7deac69",
"Nostr",
"",
"https://cloudflare-ipfs.com/ipfs/QmTN4Eas9atUULVbEAbUU8cowhtvK7g3t7jfKztY7wc8eP?.png",
1661333723
);
@@ -0,0 +1,11 @@
-- Add migration script here
-- create blacklist table
CREATE TABLE
blacklist (
id INTEGER NOT NULL PRIMARY KEY,
account_id INTEGER NOT NULL,
content TEXT NOT NULL UNIQUE,
status INTEGER NOT NULL DEFAULT 0,
kind INTEGER NOT NULL,
FOREIGN KEY (account_id) REFERENCES accounts (id)
);
@@ -0,0 +1,11 @@
-- Add migration script here
CREATE TABLE
blocks (
id INTEGER NOT NULL PRIMARY KEY,
account_id INTEGER NOT NULL,
kind INTEGER NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts (id)
);
@@ -0,0 +1,15 @@
-- Add migration script here
CREATE TABLE
channel_messages (
id INTEGER NOT NULL PRIMARY KEY,
channel_id TEXT NOT NULL,
event_id TEXT NOT NULL UNIQUE,
pubkey TEXT NOT NULL,
kind INTEGER NOT NULL,
content TEXT NOT NULL,
tags JSON,
mute BOOLEAN DEFAULT 0,
hide BOOLEAN DEFAULT 0,
created_at INTEGER NOT NULL,
FOREIGN KEY (channel_id) REFERENCES channels (event_id)
);
@@ -0,0 +1,13 @@
-- Add migration script here
CREATE TABLE
replies (
id INTEGER NOT NULL PRIMARY KEY,
parent_id TEXT NOT NULL,
event_id TEXT NOT NULL UNIQUE,
pubkey TEXT NOT NULL,
kind INTEGER NOT NULL DEFAULT 1,
tags JSON,
content TEXT NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (parent_id) REFERENCES notes (event_id)
);
@@ -0,0 +1,6 @@
-- Add migration script here
DROP TABLE IF EXISTS blacklist;
DROP TABLE IF EXISTS channel_messages;
DROP TABLE IF EXISTS channels;
@@ -0,0 +1,6 @@
-- Add migration script here
UPDATE settings
SET
value = '["wss://relayable.org","wss://relay.damus.io","wss://relay.nostr.band/all","wss://nostr.mutinywallet.com"]'
WHERE
key = 'relays';
@@ -0,0 +1,2 @@
-- Add migration script here
ALTER TABLE accounts ADD network JSON;
@@ -0,0 +1,10 @@
-- Add migration script here
CREATE TABLE
relays (
id INTEGER NOT NULL PRIMARY KEY,
account_id INTEGER NOT NULL,
relay TEXT NOT NULL,
purpose TEXT NOT NULL DEFAULT '',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts (id)
);
@@ -0,0 +1,3 @@
-- Add migration script here
ALTER TABLE blocks
RENAME TO widgets;
@@ -1,27 +0,0 @@
-- Add migration script here
CREATE TABLE
ndk_users (
pubkey TEXT NOT NULL PRIMARY KEY,
profile TEXT,
createdAt NUMBER
);
CREATE TABLE
ndk_events (
id TEXT NOT NULL PRIMARY KEY,
pubkey TEXT,
content TEXT,
kind NUMBER,
createdAt NUMBER,
relay TEXT,
event TEXT
);
CREATE TABLE
ndk_eventtags (
id TEXT NOT NULL PRIMARY KEY,
eventId TEXT,
tag TEXT,
value TEXT,
tagValue TEXT
);
@@ -1,5 +0,0 @@
ALTER TABLE accounts DROP COLUMN follows;
ALTER TABLE accounts DROP COLUMN circles;
ALTER TABLE accounts DROP COLUMN last_login_at;
DROP TABLE IF EXISTS events;
DROP TABLE IF EXISTS relays;
+141 -124
View File
@@ -3,12 +3,11 @@
windows_subsystem = "windows"
)]
use keyring::Entry;
use std::time::Duration;
// use rand::distributions::{Alphanumeric, DistString};
use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_sql::{Migration, MigrationKind};
use tauri_plugin_theme::ThemePlugin;
use webpage::{Webpage, WebpageOptions};
use window_vibrancy::{apply_mica, apply_vibrancy, NSVisualEffectMaterial};
#[derive(Clone, serde::Serialize)]
struct Payload {
@@ -16,109 +15,22 @@ struct Payload {
cwd: String,
}
#[derive(serde::Serialize)]
struct OpenGraphResponse {
title: String,
description: String,
url: String,
image: String,
}
async fn fetch_opengraph(url: String) -> OpenGraphResponse {
let options = WebpageOptions {
allow_insecure: false,
max_redirections: 3,
timeout: Duration::from_secs(15),
useragent: "lume - desktop app".to_string(),
..Default::default()
};
let result = match Webpage::from_url(&url, options) {
Ok(webpage) => webpage,
Err(_) => {
return OpenGraphResponse {
title: "".to_string(),
description: "".to_string(),
url: "".to_string(),
image: "".to_string(),
#[tauri::command]
async fn close_splashscreen(window: tauri::Window) {
// Close splashscreen
if let Some(splashscreen) = window.get_window("splashscreen") {
splashscreen.close().unwrap();
}
}
};
let html = result.html;
return OpenGraphResponse {
title: html
.opengraph
.properties
.get("title")
.cloned()
.unwrap_or_default(),
description: html
.opengraph
.properties
.get("description")
.cloned()
.unwrap_or_default(),
url: html
.opengraph
.properties
.get("url")
.cloned()
.unwrap_or_default(),
image: html
.opengraph
.images
.get(0)
.and_then(|i| Some(i.url.clone()))
.unwrap_or_default(),
};
}
#[tauri::command]
async fn opengraph(url: String) -> OpenGraphResponse {
let result = fetch_opengraph(url).await;
return result;
}
#[tauri::command]
fn secure_save(key: String, value: String) -> Result<(), ()> {
let entry = Entry::new("lume", &key).expect("Failed to create entry");
let _ = entry.set_password(&value);
Ok(())
}
#[tauri::command]
fn secure_load(key: String) -> Result<String, String> {
let entry = Entry::new("lume", &key).expect("Failed to create entry");
if let Ok(password) = entry.get_password() {
Ok(password)
} else {
Err("not found".to_string())
}
}
#[tauri::command]
fn secure_remove(key: String) -> Result<(), ()> {
let entry = Entry::new("lume", &key).expect("Failed to create entry");
let _ = entry.delete_password();
Ok(())
// Show main window
window.get_window("main").unwrap().show().unwrap();
}
fn main() {
let mut ctx = tauri::generate_context!();
tauri::Builder::default()
.setup(|app| {
#[cfg(desktop)]
app
.handle()
.plugin(tauri_plugin_updater::Builder::new().build())?;
Ok(())
})
.plugin(
tauri_plugin_sql::Builder::default()
.add_migrations(
"sqlite:lume_v2.db",
"sqlite:lume.db",
vec![
Migration {
version: 20230418013219,
@@ -127,42 +39,147 @@ fn main() {
kind: MigrationKind::Up,
},
Migration {
version: 20231028083224,
description: "add ndk cache table",
sql: include_str!("../migrations/20231028083224_add_ndk_cache_table.sql"),
version: 20230418080146,
description: "create chats",
sql: include_str!("../migrations/20230418080146_create_chats.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20231130105202,
description: "clean up table",
sql: include_str!("../migrations/20231130105202_clean_up_table.sql"),
version: 20230420040005,
description: "insert last login to settings",
sql: include_str!("../migrations/20230420040005_insert_last_login_to_settings.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230425023912,
description: "add pubkey to channel",
sql: include_str!("../migrations/20230425023912_add_pubkey_to_channel.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230425024708,
description: "add default channels",
sql: include_str!("../migrations/20230425024708_add_default_channels.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230425050745,
description: "create blacklist",
sql: include_str!("../migrations/20230425050745_add_blacklist_model.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230521092300,
description: "create block",
sql: include_str!("../migrations/20230521092300_add_block_model.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230617003135,
description: "add channel messages",
sql: include_str!("../migrations/20230617003135_add_channel_messages.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230619082415,
description: "add replies",
sql: include_str!("../migrations/20230619082415_add_replies.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230718072634,
description: "clean up",
sql: include_str!("../migrations/20230718072634_clean_up_old_tables.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230725010250,
description: "update default relays",
sql: include_str!("../migrations/20230725010250_update_default_relays.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230804083544,
description: "add network to accounts",
sql: include_str!("../migrations/20230804083544_add_network_to_account.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230808085847,
description: "add relays",
sql: include_str!("../migrations/20230808085847_add_relays_table.sql"),
kind: MigrationKind::Up,
},
Migration {
version: 20230811074423,
description: "rename blocks to widgets",
sql: include_str!("../migrations/20230811074423_rename_blocks_to_widgets.sql"),
kind: MigrationKind::Up,
},
],
)
.build(),
)
.plugin(ThemePlugin::init(ctx.config_mut()))
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_window_state::Builder::default().build())
.plugin(
tauri_plugin_stronghold::Builder::new(|password| {
let config = argon2::Config {
lanes: 2,
mem_cost: 50_000,
time_cost: 30,
thread_mode: argon2::ThreadMode::from_threads(2),
variant: argon2::Variant::Argon2id,
..Default::default()
};
// let salt = Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
let key = argon2::hash_raw(
password.as_ref(),
b"LUME_NEED_RUST_DEVELOPER_HELP_MAKE_SALT_RANDOM",
&config,
)
.expect("failed to hash password");
key.to_vec()
})
.build(),
)
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec![]),
Some(vec!["--flag1", "--flag2"]),
))
.invoke_handler(tauri::generate_handler![
opengraph,
secure_save,
secure_load,
secure_remove
])
.run(ctx)
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
println!("{}, {argv:?}, {cwd}", app.package_info().name);
app
.emit_all("single-instance", Payload { args: argv, cwd })
.unwrap();
}))
.plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_app::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_window::init())
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_shell::init())
.setup(|app| {
let window = app.get_window("main").unwrap();
#[cfg(target_os = "macos")]
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None)
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
#[cfg(target_os = "windows")]
apply_mica(&window, None, None)
.expect("Unsupported platform! 'apply_blur' is only supported on Windows");
Ok(())
})
.invoke_handler(tauri::generate_handler![close_splashscreen])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
+53 -21
View File
@@ -1,15 +1,14 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "pnpm run build",
"beforeDevCommand": "pnpm run dev",
"beforeBuildCommand": "pnpm build",
"beforeDevCommand": "pnpm dev",
"devPath": "http://localhost:3000",
"distDir": "../dist",
"withGlobalTauri": true
},
"package": {
"productName": "Lume",
"version": "2.2.0"
"version": "1.2.0"
},
"plugins": {
"fs": {
@@ -45,12 +44,15 @@
"tauri": {
"bundle": {
"active": true,
"appimage": {
"bundleMediaFramework": true
},
"category": "SocialNetworking",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"resources": [],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
@@ -58,36 +60,66 @@
"icons/icon.icns",
"icons/icon.ico"
],
"copyright": "",
"identifier": "com.lume.nu",
"longDescription": "The communication app build on Nostr Protocol",
"shortDescription": "",
"targets": "all",
"updater": {
"active": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU3OTdCMkM3RjU5QzE2NzkKUldSNUZwejF4N0tYNTVHYjMrU0JkL090SlEyNUVLYU5TM2hTU3RXSWtEWngrZWJ4a0pydUhXZHEK",
"windows": {
"installMode": "quiet"
}
},
"appimage": {
"bundleMediaFramework": true
},
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"license": "../LICENSE",
"minimumSystemVersion": "10.15.0",
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"updater": {
"active": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU4RjAzODFBREQ4MkM3RTEKUldUaHg0TGRHamp3NkI5bnhoOEVjanlHWFNzQ2Q3NDhubFFLUmJpSHJ1L2FqNnB3alF1Y2R3U3gK",
"windows": {
"installMode": "passive"
}
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": "upgrade-insecure-requests"
},
"systemTray": {
"iconAsTemplate": true,
"iconPath": "icons/icon.png"
},
"windows": [
{
"width": 1080,
"height": 800,
"minWidth": 1080,
"minHeight": 800,
"resizable": true,
"theme": "Dark",
"title": "Lume",
"titleBarStyle": "Overlay",
"transparent": true,
"center": true,
"fullscreen": false,
"hiddenTitle": true,
"visible": false
},
{
"width": 400,
"height": 500,
"decorations": true,
"hiddenTitle": true,
"center": true,
"resizable": false,
"titleBarStyle": "Overlay",
"label": "splashscreen",
"url": "splashscreen"
}
],
"macOSPrivateApi": true
}
}
-21
View File
@@ -1,21 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"windows": [
{
"width": 1080,
"height": 800,
"minWidth": 560,
"minHeight": 800,
"resizable": true,
"title": "Lume",
"center": true,
"fullscreen": false,
"hiddenTitle": true,
"fileDropEnabled": true,
"decorations": false,
"transparent": false
}
]
}
}
-25
View File
@@ -1,25 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"windows": [
{
"width": 1080,
"height": 800,
"minWidth": 560,
"minHeight": 800,
"resizable": true,
"title": "Lume",
"titleBarStyle": "Overlay",
"center": true,
"fullscreen": false,
"hiddenTitle": true,
"fileDropEnabled": true,
"decorations": true,
"transparent": true,
"windowEffects": {
"effects": ["sidebar"]
}
}
]
}
}
-24
View File
@@ -1,24 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"windows": [
{
"width": 1080,
"height": 800,
"minWidth": 560,
"minHeight": 800,
"resizable": true,
"title": "Lume",
"center": true,
"fullscreen": false,
"hiddenTitle": true,
"fileDropEnabled": true,
"decorations": false,
"transparent": true,
"windowEffects": {
"effects": ["micaLight", "micaDark"]
}
}
]
}
}
-66
View File
@@ -1,66 +0,0 @@
/* @import 'reactflow/dist/style.css'; */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.break-p {
word-break: break-word;
word-wrap: break-word;
overflow-wrap: break-word;
}
.prose :where(iframe):not(:where([class~="not-prose"] *)) {
@apply aspect-video w-full h-auto mx-auto;
}
}
html {
font-size: 14px;
}
a {
@apply cursor-default no-underline !important;
}
button {
@apply cursor-default focus:outline-none;
}
input::-ms-reveal,
input::-ms-clear {
display: none;
}
::-webkit-input-placeholder {
line-height: normal;
}
.border {
background-clip: padding-box;
}
.player {
--brand-color: #f5f5f5;
--focus-color: #4e9cf6;
--audio-brand: var(--brand-color);
--audio-focus-ring-color: var(--focus-color);
--audio-border-radius: 2px;
--video-brand: var(--brand-color);
--video-focus-ring-color: var(--focus-color);
--video-border-radius: 8px;
@apply w-full;
}
.player[data-view-type='video'] {
@apply aspect-video;
}
.ProseMirror p.is-empty::before {
@apply text-neutral-600 dark:text-neutral-400 float-left h-0 pointer-events-none content-[attr(data-placeholder)];
}
.ProseMirror img.ProseMirror-selectednode {
@apply outline-blue-500;
}
+138 -204
View File
@@ -1,61 +1,71 @@
import { message } from '@tauri-apps/plugin-dialog';
import { fetch } from '@tauri-apps/plugin-http';
import { RouterProvider, createBrowserRouter, defer, redirect } from 'react-router-dom';
import { ReactFlowProvider } from 'reactflow';
import { RouterProvider, createBrowserRouter, redirect } from 'react-router-dom';
import { ChatsScreen } from '@app/chats';
import { AuthCreateScreen } from '@app/auth/create';
import { AuthImportScreen } from '@app/auth/import';
import { OnboardingScreen } from '@app/auth/onboarding';
import { ErrorScreen } from '@app/error';
import { ExploreScreen } from '@app/explore';
import { useStorage } from '@libs/storage/provider';
import { getActiveAccount } from '@libs/storage';
import { AppLayout } from '@shared/appLayout';
import { AuthLayout } from '@shared/authLayout';
import { LoaderIcon } from '@shared/icons';
import { AppLayout } from '@shared/layouts/app';
import { AuthLayout } from '@shared/layouts/auth';
import { NewLayout } from '@shared/layouts/new';
import { NoteLayout } from '@shared/layouts/note';
import { SettingsLayout } from '@shared/layouts/settings';
import { SettingsLayout } from '@shared/settingsLayout';
import './app.css';
import './index.css';
export default function App() {
const { db } = useStorage();
const appLoader = async () => {
const account = await getActiveAccount();
const stronghold = sessionStorage.getItem('stronghold');
const privkey = JSON.parse(stronghold).state.privkey || null;
const onboarding = localStorage.getItem('onboarding');
const step = JSON.parse(onboarding).state.step || null;
const accountLoader = async () => {
try {
// redirect to welcome screen if none user exist
const totalAccount = await db.checkAccount();
if (totalAccount === 0) return redirect('/auth/welcome');
if (step) {
return redirect(step);
}
if (!account) {
return redirect('/auth/welcome');
}
if (account && account.privkey.length > 35) {
return redirect('/auth/migrate');
}
if (account && !privkey) {
return redirect('/auth/unlock');
}
return null;
} catch (e) {
await message(e, { title: 'An unexpected error has occurred', type: 'error' });
}
};
};
const relayLoader = async ({ params }) => {
return defer({
relay: fetch(`https://${params.url}`, {
method: 'GET',
headers: {
Accept: 'application/nostr+json',
},
}).then((res) => res.json()),
});
};
const router = createBrowserRouter([
const router = createBrowserRouter([
{
path: '/',
element: <AppLayout />,
errorElement: <ErrorScreen />,
loader: accountLoader,
loader: appLoader,
children: [
{
path: '',
async lazy() {
const { HomeScreen } = await import('@app/home');
return { Component: HomeScreen };
const { SpaceScreen } = await import('@app/space');
return { Component: SpaceScreen };
},
},
{
path: 'trending',
async lazy() {
const { TrendingScreen } = await import('@app/trending');
return { Component: TrendingScreen };
},
},
{
path: 'events/:id',
async lazy() {
const { EventScreen } = await import('@app/events');
return { Component: EventScreen };
},
},
{
@@ -66,112 +76,25 @@ export default function App() {
},
},
{
path: 'nwc',
path: 'chats/:pubkey',
async lazy() {
const { NWCScreen } = await import('@app/nwc');
return { Component: NWCScreen };
},
},
{
path: 'relays',
async lazy() {
const { RelaysScreen } = await import('@app/relays');
return { Component: RelaysScreen };
},
},
{
path: 'relays/:url',
loader: relayLoader,
async lazy() {
const { RelayScreen } = await import('@app/relays/relay');
return { Component: RelayScreen };
},
},
{
path: 'explore',
element: (
<ReactFlowProvider>
<ExploreScreen />
</ReactFlowProvider>
),
errorElement: <ErrorScreen />,
},
{
path: 'chats',
element: <ChatsScreen />,
errorElement: <ErrorScreen />,
children: [
{
path: 'chat/:pubkey',
async lazy() {
const { ChatScreen } = await import('@app/chats/chat');
const { ChatScreen } = await import('@app/chats');
return { Component: ChatScreen };
},
},
],
},
],
},
{
path: '/new',
element: <NewLayout />,
path: '/splashscreen',
errorElement: <ErrorScreen />,
children: [
{
path: '',
async lazy() {
const { NewPostScreen } = await import('@app/new/post');
return { Component: NewPostScreen };
const { SplashScreen } = await import('@app/splash');
return { Component: SplashScreen };
},
},
{
path: 'article',
async lazy() {
const { NewArticleScreen } = await import('@app/new/article');
return { Component: NewArticleScreen };
},
},
{
path: 'file',
async lazy() {
const { NewFileScreen } = await import('@app/new/file');
return { Component: NewFileScreen };
},
},
{
path: 'privkey',
async lazy() {
const { NewPrivkeyScreen } = await import('@app/new/privkey');
return { Component: NewPrivkeyScreen };
},
},
],
},
{
path: '/notes',
element: <NoteLayout />,
errorElement: <ErrorScreen />,
children: [
{
path: 'text/:id',
async lazy() {
const { TextNoteScreen } = await import('@app/notes/text');
return { Component: TextNoteScreen };
},
},
{
path: 'article/:id',
async lazy() {
const { ArticleNoteScreen } = await import('@app/notes/article');
return { Component: ArticleNoteScreen };
},
},
],
},
{
path: '/auth',
element: <AuthLayout />,
errorElement: <ErrorScreen />,
children: [
{
path: 'welcome',
@@ -181,66 +104,105 @@ export default function App() {
},
},
{
path: 'create',
path: 'import',
element: <AuthImportScreen />,
children: [
{
path: '',
async lazy() {
const { CreateAccountScreen } = await import('@app/auth/create');
return { Component: CreateAccountScreen };
const { ImportStep1Screen } = await import('@app/auth/import/step-1');
return { Component: ImportStep1Screen };
},
},
{
path: 'import',
path: 'step-2',
async lazy() {
const { ImportAccountScreen } = await import('@app/auth/import');
return { Component: ImportAccountScreen };
const { ImportStep2Screen } = await import('@app/auth/import/step-2');
return { Component: ImportStep2Screen };
},
},
{
path: 'step-3',
async lazy() {
const { ImportStep3Screen } = await import('@app/auth/import/step-3');
return { Component: ImportStep3Screen };
},
},
],
},
{
path: 'create',
element: <AuthCreateScreen />,
children: [
{
path: '',
async lazy() {
const { CreateStep1Screen } = await import('@app/auth/create/step-1');
return { Component: CreateStep1Screen };
},
},
{
path: 'step-2',
async lazy() {
const { CreateStep2Screen } = await import('@app/auth/create/step-2');
return { Component: CreateStep2Screen };
},
},
{
path: 'step-3',
async lazy() {
const { CreateStep3Screen } = await import('@app/auth/create/step-3');
return { Component: CreateStep3Screen };
},
},
],
},
{
path: 'onboarding',
element: <OnboardingScreen />,
children: [
{
path: '',
async lazy() {
const { OnboardingScreen } = await import('@app/auth/onboarding');
return { Component: OnboardingScreen };
const { OnboardStep1Screen } = await import('@app/auth/onboarding/step-1');
return { Component: OnboardStep1Screen };
},
},
{
path: 'follow',
path: 'step-2',
async lazy() {
const { FollowScreen } = await import('@app/auth/follow');
return { Component: FollowScreen };
const { OnboardStep2Screen } = await import('@app/auth/onboarding/step-2');
return { Component: OnboardStep2Screen };
},
},
{
path: 'finish',
path: 'step-3',
async lazy() {
const { FinishScreen } = await import('@app/auth/finish');
return { Component: FinishScreen };
const { OnboardStep3Screen } = await import('@app/auth/onboarding/step-3');
return { Component: OnboardStep3Screen };
},
},
],
},
{
path: 'unlock',
async lazy() {
const { UnlockScreen } = await import('@app/auth/unlock');
return { Component: UnlockScreen };
},
},
{
path: 'tutorials/note',
path: 'migrate',
async lazy() {
const { TutorialNoteScreen } = await import('@app/auth/tutorials/note');
return { Component: TutorialNoteScreen };
const { MigrateScreen } = await import('@app/auth/migrate');
return { Component: MigrateScreen };
},
},
{
path: 'tutorials/widget',
path: 'reset',
async lazy() {
const { TutorialWidgetScreen } = await import('@app/auth/tutorials/widget');
return { Component: TutorialWidgetScreen };
},
},
{
path: 'tutorials/posting',
async lazy() {
const { TutorialPostingScreen } = await import('@app/auth/tutorials/posting');
return { Component: TutorialPostingScreen };
},
},
{
path: 'tutorials/finish',
async lazy() {
const { TutorialFinishScreen } = await import('@app/auth/tutorials/finish');
return { Component: TutorialFinishScreen };
const { ResetScreen } = await import('@app/auth/reset');
return { Component: ResetScreen };
},
},
],
@@ -248,66 +210,38 @@ export default function App() {
{
path: '/settings',
element: <SettingsLayout />,
errorElement: <ErrorScreen />,
children: [
{
path: '',
async lazy() {
const { UserSettingScreen } = await import('@app/settings');
return { Component: UserSettingScreen };
},
},
{
path: 'edit-profile',
async lazy() {
const { EditProfileScreen } = await import('@app/settings/editProfile');
return { Component: EditProfileScreen };
},
},
{
path: 'edit-contact',
async lazy() {
const { EditContactScreen } = await import('@app/settings/editContact');
return { Component: EditContactScreen };
},
},
{
path: 'general',
async lazy() {
const { GeneralSettingScreen } = await import('@app/settings/general');
return { Component: GeneralSettingScreen };
const { GeneralSettingsScreen } = await import('@app/settings/general');
return { Component: GeneralSettingsScreen };
},
},
{
path: 'backup',
path: 'shortcuts',
async lazy() {
const { BackupSettingScreen } = await import('@app/settings/backup');
return { Component: BackupSettingScreen };
const { ShortcutsSettingsScreen } = await import('@app/settings/shortcuts');
return { Component: ShortcutsSettingsScreen };
},
},
{
path: 'advanced',
path: 'account',
async lazy() {
const { AdvancedSettingScreen } = await import('@app/settings/advanced');
return { Component: AdvancedSettingScreen };
},
},
{
path: 'about',
async lazy() {
const { AboutScreen } = await import('@app/settings/about');
return { Component: AboutScreen };
const { AccountSettingsScreen } = await import('@app/settings/account');
return { Component: AccountSettingsScreen };
},
},
],
},
]);
]);
export default function App() {
return (
<RouterProvider
router={router}
fallbackElement={
<div className="flex h-full w-full items-center justify-center">
<div className="flex h-full w-full items-center justify-center bg-black/90">
<LoaderIcon className="h-6 w-6 animate-spin text-white" />
</div>
}
+43
View File
@@ -0,0 +1,43 @@
import { Image } from '@shared/image';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useProfile } from '@utils/hooks/useProfile';
import { displayNpub } from '@utils/shortenKey';
export function User({ pubkey, fallback }: { pubkey: string; fallback?: string }) {
const { status, user } = useProfile(pubkey, fallback);
if (status === 'loading') {
return (
<div className="flex items-center gap-2">
<div className="relative h-10 w-10 shrink-0 animate-pulse rounded-md bg-white/10" />
<div className="flex w-full flex-1 flex-col items-start gap-1 text-start">
<span className="h-4 w-1/2 animate-pulse rounded bg-white/10" />
<span className="h-3 w-1/3 animate-pulse rounded bg-white/10" />
</div>
</div>
);
}
return (
<div className="flex items-center gap-2">
<div className="relative h-10 w-10 shrink rounded-md">
<Image
src={user?.picture || user?.image}
fallback={DEFAULT_AVATAR}
alt={pubkey}
className="h-10 w-10 rounded-md object-cover"
/>
</div>
<div className="flex w-full flex-1 flex-col items-start text-start">
<span className="truncate font-medium leading-tight text-white">
{user?.name || user?.display_name || user?.nip05}
</span>
<span className="max-w-[15rem] truncate text-base leading-tight text-white/50">
{user?.nip05?.toLowerCase() || displayNpub(pubkey, 16)}
</span>
</div>
</div>
);
}
+39
View File
@@ -0,0 +1,39 @@
import { Image } from '@shared/image';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useProfile } from '@utils/hooks/useProfile';
import { displayNpub } from '@utils/shortenKey';
export function UserRelay({ pubkey }: { pubkey: string }) {
const { status, user } = useProfile(pubkey);
if (status === 'loading') {
return (
<div className="flex items-center gap-2">
<div className="relative h-10 w-10 shrink-0 animate-pulse rounded-md bg-white/10" />
<div className="flex w-full flex-1 flex-col items-start gap-1 text-start">
<span className="h-4 w-1/2 animate-pulse rounded bg-white/10" />
<span className="h-3 w-1/3 animate-pulse rounded bg-white/10" />
</div>
</div>
);
}
return (
<div className="inline-flex items-center gap-2 text-white/50">
<span className="text-sm">Use by</span>
<div className="inline-flex items-center gap-1">
<Image
src={user?.picture || user?.image}
fallback={DEFAULT_AVATAR}
alt={pubkey}
className="h-5 w-5 shrink-0 rounded object-cover"
/>
<span className="truncate text-sm font-medium leading-none text-white">
{user?.name || user?.display_name || user?.nip05 || displayNpub(pubkey, 16)}
</span>
</div>
</div>
);
}
-321
View File
@@ -1,321 +0,0 @@
import { NDKEvent, NDKKind, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
import { downloadDir } from '@tauri-apps/api/path';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { save } from '@tauri-apps/plugin-dialog';
import { writeTextFile } from '@tauri-apps/plugin-fs';
import { motion } from 'framer-motion';
import { minidenticon } from 'minidenticons';
import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider';
import { AvatarUploader } from '@shared/avatarUploader';
import { ArrowLeftIcon, InfoIcon, LoaderIcon } from '@shared/icons';
import { User } from '@shared/user';
export function CreateAccountScreen() {
const [picture, setPicture] = useState('');
const [downloaded, setDownloaded] = useState(false);
const [loading, setLoading] = useState(false);
const [keys, setKeys] = useState<null | {
npub: string;
nsec: string;
pubkey: string;
privkey: string;
}>(null);
const {
register,
handleSubmit,
formState: { isDirty, isValid },
} = useForm();
const { db } = useStorage();
const { ndk } = useNDK();
const navigate = useNavigate();
const svgURI =
'data:image/svg+xml;utf8,' +
encodeURIComponent(minidenticon('lume new account', 90, 50));
const onSubmit = async (data: { name: string; about: string }) => {
try {
setLoading(true);
const profile = {
...data,
name: data.name,
display_name: data.name,
bio: data.about,
picture: picture,
avatar: picture,
};
const userPrivkey = generatePrivateKey();
const userPubkey = getPublicKey(userPrivkey);
const userNpub = nip19.npubEncode(userPubkey);
const userNsec = nip19.nsecEncode(userPrivkey);
const signer = new NDKPrivateKeySigner(userPrivkey);
ndk.signer = signer;
const event = new NDKEvent(ndk);
event.content = JSON.stringify(profile);
event.kind = NDKKind.Metadata;
event.pubkey = userPubkey;
event.tags = [];
const publish = await event.publish();
if (publish) {
await db.createAccount(userNpub, userPubkey);
await db.secureSave(userPubkey, userPrivkey);
const relayListEvent = new NDKEvent(ndk);
relayListEvent.kind = NDKKind.RelayList;
relayListEvent.tags = [...ndk.pool.relays.values()].map((item) => [
'r',
item.url,
]);
await relayListEvent.publish();
setKeys({
npub: userNpub,
nsec: userNsec,
pubkey: userPubkey,
privkey: userPrivkey,
});
setLoading(false);
} else {
toast('Create account failed');
setLoading(false);
}
} catch (e) {
return toast.error(e);
}
};
const copyNsec = async () => {
await writeText(keys.nsec);
};
const download = async () => {
try {
const downloadPath = await downloadDir();
const fileName = `nostr_keys_${new Date().toISOString()}.txt`;
const filePath = await save({
defaultPath: downloadPath + '/' + fileName,
});
if (filePath) {
await writeTextFile(
filePath,
`Nostr account, generated by Lume (lume.nu)\nPublic key: ${keys.npub}\nPrivate key: ${keys.nsec}`
);
setDownloaded(true);
} // else { user cancel action }
} catch (e) {
return toast.error(e);
}
};
return (
<div className="relative flex h-full w-full items-center justify-center">
<div className="absolute left-[8px] top-2">
{!keys ? (
<button
onClick={() => navigate(-1)}
className="group inline-flex items-center gap-2 text-sm font-medium"
>
<div className="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-neutral-200 text-neutral-800 group-hover:bg-neutral-300 dark:bg-neutral-800 dark:text-neutral-200 dark:group-hover:bg-neutral-700">
<ArrowLeftIcon className="h-4 w-4" />
</div>
Back
</button>
) : null}
</div>
<div className="mx-auto flex w-full max-w-md flex-col gap-10">
<h1 className="text-center text-2xl font-semibold">
Let&apos;s set up your account.
</h1>
<div className="flex flex-col gap-3">
{!keys ? (
<div className="rounded-xl bg-neutral-50 p-3 dark:bg-neutral-950">
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col">
<input type={'hidden'} {...register('picture')} value={picture} />
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-1">
<span className="font-semibold">Avatar</span>
<div className="flex h-36 w-full flex-col items-center justify-center gap-3 rounded-lg bg-neutral-100 dark:bg-neutral-900">
{picture.length > 0 ? (
<img
src={picture}
alt="user's avatar"
className="h-14 w-14 rounded-xl object-cover"
/>
) : (
<img
src={svgURI}
alt="user's avatar"
className="h-14 w-14 rounded-xl bg-black dark:bg-white"
/>
)}
<AvatarUploader setPicture={setPicture} />
</div>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="name" className="font-semibold">
Name *
</label>
<input
type={'text'}
{...register('name', {
required: true,
minLength: 1,
})}
spellCheck={false}
className="h-11 rounded-lg border-transparent bg-neutral-100 px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
/>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="about" className="font-semibold">
Bio
</label>
<textarea
{...register('about')}
spellCheck={false}
className="relative h-24 w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-2 !outline-none placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
/>
</div>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-2 rounded-lg bg-blue-100 p-3 text-sm text-blue-800 dark:bg-blue-900 dark:text-blue-200">
<InfoIcon className="h-8 w-8" />
<p>
There are many more settings you can configure from the
&quot;Settings&quot; screen. Be sure to visit it later.
</p>
</div>
<button
type="submit"
disabled={!isDirty || !isValid}
className="inline-flex h-11 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600 disabled:opacity-50"
>
{loading ? (
<LoaderIcon className="h-4 w-4 animate-spin" />
) : (
'Create and Continue'
)}
</button>
</div>
</div>
</form>
</div>
) : (
<>
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{
opacity: 1,
y: 0,
}}
className="rounded-xl bg-neutral-50 p-3 dark:bg-neutral-950"
>
<User pubkey={keys.pubkey} variant="simple" />
</motion.div>
<motion.div
initial={{ opacity: 0, y: 80 }}
animate={{
opacity: 1,
y: 0,
}}
className="rounded-xl bg-neutral-50 p-3 dark:bg-neutral-950"
>
<div className="flex flex-col gap-1.5">
<h5 className="font-semibold">Backup account</h5>
<div>
<p className="mb-2 select-text text-sm text-neutral-800 dark:text-neutral-200">
Your private key is your password. If you lose this key, you will
lose access to your account! Copy it and keep it in a safe place.{' '}
<span className="text-red-500">
There is no way to reset your private key.
</span>
</p>
<p className="select-text text-sm text-neutral-800 dark:text-neutral-200">
Public key is used for sharing with other people so that they can
find you using the public key.
</p>
</div>
<div className="mt-3 flex flex-col gap-3">
<div className="flex flex-col gap-1">
<label htmlFor="nsec" className="text-sm font-semibold">
Private key
</label>
<div className="relative w-full">
<input
readOnly
value={
keys.nsec.substring(0, 10) + '**************************'
}
className="h-11 w-full rounded-lg border-transparent bg-neutral-100 px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
/>
<div className="absolute right-0 top-0 inline-flex h-11 items-center justify-center px-2">
<button
type="button"
onClick={copyNsec}
className="rounded-md bg-neutral-200 px-2 py-1 text-sm font-medium hover:bg-neutral-400 dark:bg-neutral-700 dark:hover:bg-neutral-600"
>
Copy
</button>
</div>
</div>
</div>
<div className="flex flex-col gap-1">
<label htmlFor="nsec" className="text-sm font-semibold">
Public key
</label>
<input
readOnly
value={keys.npub}
className="h-11 w-full rounded-lg border-transparent bg-neutral-100 px-3 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800"
/>
</div>
</div>
{!downloaded ? (
<button
type="button"
onClick={() => download()}
className="mt-1 inline-flex h-11 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600"
>
Download account keys
</button>
) : null}
</div>
</motion.div>
</>
)}
{downloaded ? (
<motion.button
initial={{ opacity: 0, y: 50 }}
animate={{
opacity: 1,
y: 0,
}}
className="inline-flex h-11 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600"
type="button"
onClick={() => navigate('/auth/onboarding')}
>
Finish
</motion.button>
) : null}
</div>
</div>
</div>
);
}
+22
View File
@@ -0,0 +1,22 @@
import { useEffect } from 'react';
import { Outlet } from 'react-router-dom';
import { useOnboarding } from '@stores/onboarding';
import { useStronghold } from '@stores/stronghold';
export function AuthCreateScreen() {
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
const setPrivkey = useStronghold((state) => state.setPrivkey);
useEffect(() => {
if (step) {
setPrivkey(tmpPrivkey);
}
}, [tmpPrivkey]);
return (
<div className="flex h-full w-full items-center justify-center">
<Outlet />
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More