Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
025562f9f0 | ||
|
|
595bcc9b3c | ||
|
|
15991d07ab | ||
|
|
99fc1f0b10 | ||
|
|
1041e1ccd4 | ||
|
|
2eeb2c896d | ||
|
|
caf8fb584a | ||
|
|
58205713ab | ||
|
|
33802d32f3 |
@@ -1,3 +0,0 @@
|
||||
/**/node_modules/*
|
||||
node_modules/
|
||||
dist/
|
||||
25
.github/workflows/main.yml
vendored
@@ -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
|
||||
|
||||
4
.gitignore
vendored
@@ -14,11 +14,9 @@ out
|
||||
*.local
|
||||
.next
|
||||
.vscode
|
||||
pnpm-lock.yaml
|
||||
*.db
|
||||
*.db-journal
|
||||
bun.lockb
|
||||
|
||||
.direnv
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
||||
21
README.md
@@ -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>
|
||||
```
|
||||
|
||||
130
flake.lock
generated
@@ -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
flake.nix
@@ -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;
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -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="cursor-default select-none overflow-hidden font-sans antialiased h-screen w-screen dark:bg-black dark:text-zinc-100">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
151
package.json
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"name": "lume",
|
||||
"description": "the communication app",
|
||||
"private": true,
|
||||
"version": "2.1.5",
|
||||
"version": "1.1.1",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
@@ -18,107 +17,83 @@
|
||||
"**/*.{ts, tsx, css, md, html, json}": "prettier --cache --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@evilmartians/harmony": "^1.1.0",
|
||||
"@getalby/sdk": "^2.7.0",
|
||||
"@nostr-dev-kit/ndk": "^2.1.1",
|
||||
"@nostr-fetch/adapter-ndk": "^0.13.1",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^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.8.7",
|
||||
"@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.12",
|
||||
"@tiptap/extension-document": "^2.1.12",
|
||||
"@tiptap/extension-image": "^2.1.12",
|
||||
"@tiptap/extension-mention": "^2.1.12",
|
||||
"@tiptap/extension-paragraph": "^2.1.12",
|
||||
"@tiptap/extension-placeholder": "^2.1.12",
|
||||
"@tiptap/extension-text": "^2.1.12",
|
||||
"@tiptap/pm": "^2.1.12",
|
||||
"@tiptap/react": "^2.1.12",
|
||||
"@tiptap/starter-kit": "^2.1.12",
|
||||
"@tiptap/suggestion": "^2.1.12",
|
||||
"dayjs": "^1.11.10",
|
||||
"framer-motion": "^10.16.5",
|
||||
"@headlessui/react": "^1.7.15",
|
||||
"@nostr-dev-kit/ndk": "^0.7.7",
|
||||
"@nostr-fetch/adapter-ndk": "^0.11.0",
|
||||
"@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.0",
|
||||
"@tanstack/react-query-devtools": "^4.32.0",
|
||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||
"@tauri-apps/api": "^1.4.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",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"dayjs": "^1.11.9",
|
||||
"destr": "^1.2.2",
|
||||
"framer-motion": "^10.13.1",
|
||||
"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.3",
|
||||
"minidenticons": "^4.2.0",
|
||||
"nanoid": "^5.0.3",
|
||||
"nostr-fetch": "^0.13.1",
|
||||
"nostr-tools": "^1.17.0",
|
||||
"nostr-fetch": "^0.12.2",
|
||||
"nostr-tools": "^1.13.1",
|
||||
"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.2",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"react-router-dom": "^6.20.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-player": "^2.12.0",
|
||||
"react-router-dom": "^6.14.2",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"reactflow": "^11.10.1",
|
||||
"sonner": "^1.2.3",
|
||||
"tailwind-scrollbar": "^3.0.5",
|
||||
"tauri-controls": "github:reyamir/tauri-controls",
|
||||
"react-virtuoso": "^4.4.2",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tauri-plugin-autostart-api": "github:tauri-apps/tauri-plugin-autostart#v1",
|
||||
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql",
|
||||
"tauri-plugin-stronghold-api": "github:tauri-apps/tauri-plugin-stronghold#v1",
|
||||
"tauri-plugin-upload-api": "github:tauri-apps/tauri-plugin-upload#v1",
|
||||
"tippy.js": "^6.3.7",
|
||||
"tiptap-markdown": "^0.8.4",
|
||||
"virtua": "^0.16.7",
|
||||
"zustand": "^4.4.6"
|
||||
"zustand": "^4.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/youtube-player": "^5.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"clsx": "^2.0.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@tauri-apps/cli": "^1.4.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
||||
"@types/html-to-text": "^9.0.1",
|
||||
"@types/node": "^18.17.1",
|
||||
"@types/react": "^18.2.17",
|
||||
"@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",
|
||||
"cross-env": "^7.0.3",
|
||||
"csstype": "^3.1.2",
|
||||
"encoding": "^0.1.13",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-react": "^7.33.0",
|
||||
"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",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.3.2",
|
||||
"vite": "^4.5.0",
|
||||
"vite-tsconfig-paths": "^4.2.1"
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.4.7",
|
||||
"vite-plugin-top-level-await": "^1.3.1",
|
||||
"vite-tsconfig-paths": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
6675
pnpm-lock.yaml
generated
BIN
public/anime.jpg
|
Before Width: | Height: | Size: 56 KiB |
BIN
public/art.jpg
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 398 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 986 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 27 KiB |
BIN
public/ghost.png
|
Before Width: | Height: | Size: 1.0 MiB |
BIN
public/icon.png
|
Before Width: | Height: | Size: 29 KiB |
BIN
public/movie.jpg
|
Before Width: | Height: | Size: 24 KiB |
BIN
public/music.jpg
|
Before Width: | Height: | Size: 30 KiB |
BIN
public/nsfw.jpg
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 26 KiB |
BIN
public/zap.png
|
Before Width: | Height: | Size: 341 KiB |
3544
src-tauri/Cargo.lock
generated
@@ -1,46 +1,60 @@
|
||||
[package]
|
||||
name = "lume"
|
||||
version = "2.1.5"
|
||||
description = "the communication app"
|
||||
version = "1.1.1"
|
||||
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.57"
|
||||
|
||||
# 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 = "1.2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "2.0.0-alpha", features = [
|
||||
"macos-private-api",
|
||||
"native-tls-vendored",
|
||||
] }
|
||||
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-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-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 = { version = "1.2", features = [
|
||||
"fs-remove-file",
|
||||
"fs-write-file",
|
||||
"window-create",
|
||||
"path-all",
|
||||
"fs-read-dir",
|
||||
"fs-read-file",
|
||||
"clipboard-read-text",
|
||||
"clipboard-write-text",
|
||||
"dialog-open",
|
||||
"http-all",
|
||||
"http-multipart",
|
||||
"notification-all",
|
||||
"os-all",
|
||||
"process-relaunch",
|
||||
"shell-open",
|
||||
"system-tray",
|
||||
"updater",
|
||||
"window-close",
|
||||
"window-start-dragging",
|
||||
] }
|
||||
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tauri-plugin-stronghold = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
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 = "v1"
|
||||
features = ["sqlite"]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2.7"
|
||||
cocoa = "0.24.1"
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 731 B |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 709 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 921 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 536 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 828 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
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
|
||||
);
|
||||
12
src-tauri/migrations/20230418080146_create_chats.sql
Normal file
@@ -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 '';
|
||||
38
src-tauri/migrations/20230425024708_add_default_channels.sql
Normal file
@@ -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
|
||||
);
|
||||
11
src-tauri/migrations/20230425050745_add_blacklist_model.sql
Normal file
@@ -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)
|
||||
);
|
||||
11
src-tauri/migrations/20230521092300_add_block_model.sql
Normal file
@@ -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)
|
||||
);
|
||||
15
src-tauri/migrations/20230617003135_add_channel_messages.sql
Normal file
@@ -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)
|
||||
);
|
||||
13
src-tauri/migrations/20230619082415_add_replies.sql
Normal file
@@ -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';
|
||||
@@ -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
|
||||
);
|
||||
@@ -3,12 +3,19 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use keyring::Entry;
|
||||
use std::time::Duration;
|
||||
#[cfg(target_os = "macos")]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
||||
// use rand::distributions::{Alphanumeric, DistString};
|
||||
use tauri::{Manager, WindowEvent};
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||
use tauri_plugin_theme::ThemePlugin;
|
||||
use webpage::{Webpage, WebpageOptions};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use window_ext::WindowExt;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod window_ext;
|
||||
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
struct Payload {
|
||||
@@ -16,110 +23,35 @@ 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(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut ctx = tauri::generate_context!();
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
#[cfg(desktop)]
|
||||
app
|
||||
.handle()
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())?;
|
||||
#[cfg(target_os = "macos")]
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
main_window.position_traffic_lights(13.0, 17.0); // set inset for traffic lights (macos)
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.plugin(ThemePlugin::init(ctx.config_mut()))
|
||||
.on_window_event(|e| {
|
||||
#[cfg(target_os = "macos")]
|
||||
let apply_offset = || {
|
||||
let win = e.window();
|
||||
// keep inset for traffic lights when window resize (macos)
|
||||
win.position_traffic_lights(13.0, 17.0);
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
match e.event() {
|
||||
WindowEvent::Resized(..) => apply_offset(),
|
||||
WindowEvent::ThemeChanged(..) => apply_offset(),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.plugin(
|
||||
tauri_plugin_sql::Builder::default()
|
||||
.add_migrations(
|
||||
"sqlite:lume_v2.db",
|
||||
"sqlite:lume.db",
|
||||
vec![
|
||||
Migration {
|
||||
version: 20230418013219,
|
||||
@@ -128,35 +60,103 @@ 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: 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,
|
||||
},
|
||||
],
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.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_upload::init())
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
60
src-tauri/src/window_ext.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use tauri::{Runtime, Window};
|
||||
|
||||
pub trait WindowExt {
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_transparent_titlebar(&self, transparent: bool);
|
||||
fn position_traffic_lights(&self, x: f64, y: f64);
|
||||
}
|
||||
|
||||
impl<R: Runtime> WindowExt for Window<R> {
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_transparent_titlebar(&self, transparent: bool) {
|
||||
use cocoa::appkit::{NSWindow, NSWindowTitleVisibility};
|
||||
|
||||
let window = self.ns_window().unwrap() as cocoa::base::id;
|
||||
|
||||
unsafe {
|
||||
window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
|
||||
|
||||
if transparent {
|
||||
window.setTitlebarAppearsTransparent_(cocoa::base::YES);
|
||||
} else {
|
||||
window.setTitlebarAppearsTransparent_(cocoa::base::NO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn position_traffic_lights(&self, x: f64, y: f64) {
|
||||
use cocoa::appkit::{NSView, NSWindow, NSWindowButton};
|
||||
use cocoa::foundation::NSRect;
|
||||
|
||||
let window = self.ns_window().unwrap() as cocoa::base::id;
|
||||
|
||||
unsafe {
|
||||
let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
|
||||
let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
|
||||
let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
|
||||
|
||||
let title_bar_container_view = close.superview().superview();
|
||||
|
||||
let close_rect: NSRect = msg_send![close, frame];
|
||||
let button_height = close_rect.size.height;
|
||||
|
||||
let title_bar_frame_height = button_height + y;
|
||||
let mut title_bar_rect = NSView::frame(title_bar_container_view);
|
||||
title_bar_rect.size.height = title_bar_frame_height;
|
||||
title_bar_rect.origin.y = NSView::frame(window).size.height - title_bar_frame_height;
|
||||
let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect];
|
||||
|
||||
let window_buttons = vec![close, miniaturize, zoom];
|
||||
let space_between = NSView::frame(miniaturize).origin.x - NSView::frame(close).origin.x;
|
||||
|
||||
for (i, button) in window_buttons.into_iter().enumerate() {
|
||||
let mut rect: NSRect = NSView::frame(button);
|
||||
rect.origin.x = x + (i as f64 * space_between);
|
||||
button.setFrameOrigin(rect.origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +1,148 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"build": {
|
||||
"beforeBuildCommand": "pnpm run build",
|
||||
"beforeDevCommand": "pnpm run dev",
|
||||
"devPath": "http://localhost:3000",
|
||||
"distDir": "../dist",
|
||||
"withGlobalTauri": true
|
||||
},
|
||||
"package": {
|
||||
"productName": "Lume",
|
||||
"version": "2.1.5"
|
||||
},
|
||||
"plugins": {
|
||||
"fs": {
|
||||
"scope": [
|
||||
"$APPDATA/*",
|
||||
"$DATA/*",
|
||||
"$LOCALDATA/*",
|
||||
"$DESKTOP/*",
|
||||
"$DOCUMENT/*",
|
||||
"$DOWNLOAD/*",
|
||||
"$HOME/*",
|
||||
"$PICTURE/*",
|
||||
"$PUBLIC/*",
|
||||
"$VIDEO/*"
|
||||
]
|
||||
},
|
||||
"http": {
|
||||
"scope": [
|
||||
"http://**/",
|
||||
"https://**/"
|
||||
]
|
||||
},
|
||||
"shell": {
|
||||
"open": true
|
||||
},
|
||||
"updater": {
|
||||
"endpoints": [
|
||||
"https://lus.reya3772.workers.dev/v1/{{target}}/{{arch}}/{{current_version}}",
|
||||
"https://lus.reya3772.workers.dev/{{target}}/{{current_version}}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "SocialNetworking",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"resources": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"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
|
||||
},
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"license": "../LICENSE",
|
||||
"minimumSystemVersion": "10.15.0",
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"macOSPrivateApi": true
|
||||
}
|
||||
}
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"beforeBuildCommand": "pnpm build",
|
||||
"devPath": "http://localhost:3000",
|
||||
"distDir": "../dist",
|
||||
"withGlobalTauri": true
|
||||
},
|
||||
"package": {
|
||||
"productName": "Lume",
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false,
|
||||
"app": {
|
||||
"all": false
|
||||
},
|
||||
"os": {
|
||||
"all": true
|
||||
},
|
||||
"http": {
|
||||
"all": true,
|
||||
"request": true,
|
||||
"scope": ["http://**", "https://**"]
|
||||
},
|
||||
"fs": {
|
||||
"all": false,
|
||||
"readFile": true,
|
||||
"readDir": true,
|
||||
"writeFile": true,
|
||||
"removeFile": true,
|
||||
"scope": [
|
||||
"$APPDATA/*",
|
||||
"$DATA/*",
|
||||
"$LOCALDATA/*",
|
||||
"$DESKTOP/*",
|
||||
"$DOCUMENT/*",
|
||||
"$DOWNLOAD/*",
|
||||
"$HOME/*",
|
||||
"$PICTURE/*",
|
||||
"$PUBLIC/*",
|
||||
"$VIDEO/*"
|
||||
]
|
||||
},
|
||||
"path": {
|
||||
"all": true
|
||||
},
|
||||
"shell": {
|
||||
"all": false,
|
||||
"open": true
|
||||
},
|
||||
"clipboard": {
|
||||
"all": false,
|
||||
"writeText": true,
|
||||
"readText": true
|
||||
},
|
||||
"dialog": {
|
||||
"all": false,
|
||||
"open": true
|
||||
},
|
||||
"notification": {
|
||||
"all": true
|
||||
},
|
||||
"window": {
|
||||
"startDragging": true,
|
||||
"close": true,
|
||||
"create": true
|
||||
},
|
||||
"process": {
|
||||
"all": false,
|
||||
"exit": false,
|
||||
"relaunch": true,
|
||||
"relaunchDangerousAllowSymlinkMacos": false
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "SocialNetworking",
|
||||
"copyright": "",
|
||||
"appimage": {
|
||||
"bundleMediaFramework": true
|
||||
},
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "com.lume.nu",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": "upgrade-insecure-requests"
|
||||
},
|
||||
"updater": {
|
||||
"active": true,
|
||||
"dialog": true,
|
||||
"endpoints": [
|
||||
"https://lus.reya3772.workers.dev/v1/{{target}}/{{arch}}/{{current_version}}",
|
||||
"https://lus.reya3772.workers.dev/{{target}}/{{current_version}}"
|
||||
],
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU4RjAzODFBREQ4MkM3RTEKUldUaHg0TGRHamp3NkI5bnhoOEVjanlHWFNzQ2Q3NDhubFFLUmJpSHJ1L2FqNnB3alF1Y2R3U3gK",
|
||||
"windows": {
|
||||
"installMode": "passive"
|
||||
}
|
||||
},
|
||||
"systemTray": {
|
||||
"iconPath": "icons/icon.png",
|
||||
"iconAsTemplate": true
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"title": "Lume",
|
||||
"theme": "Dark",
|
||||
"titleBarStyle": "Overlay",
|
||||
"hiddenTitle": true,
|
||||
"transparent": false,
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"width": 1080,
|
||||
"height": 800,
|
||||
"minWidth": 1080,
|
||||
"minHeight": 720
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
70
src/app.css
@@ -1,70 +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;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.player[data-view-type='video'] {
|
||||
aspect-ratio: 16 /9;
|
||||
}
|
||||
|
||||
.ProseMirror p.is-empty::before {
|
||||
@apply text-neutral-600 dark:text-neutral-400;
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ProseMirror img.ProseMirror-selectednode {
|
||||
@apply outline-blue-500;
|
||||
}
|
||||
390
src/app.tsx
@@ -1,302 +1,116 @@
|
||||
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 } from 'react-router-dom';
|
||||
|
||||
import { AuthCreateScreen } from '@app/auth/create';
|
||||
import { CreateStep1Screen } from '@app/auth/create/step-1';
|
||||
import { CreateStep2Screen } from '@app/auth/create/step-2';
|
||||
import { CreateStep3Screen } from '@app/auth/create/step-3';
|
||||
import { CreateStep4Screen } from '@app/auth/create/step-4';
|
||||
import { CreateStep5Screen } from '@app/auth/create/step-5';
|
||||
import { AuthImportScreen } from '@app/auth/import';
|
||||
import { ImportStep1Screen } from '@app/auth/import/step-1';
|
||||
import { ImportStep2Screen } from '@app/auth/import/step-2';
|
||||
import { ImportStep3Screen } from '@app/auth/import/step-3';
|
||||
import { MigrateScreen } from '@app/auth/migrate';
|
||||
import { OnboardingScreen } from '@app/auth/onboarding';
|
||||
import { ChatsScreen } from '@app/chats';
|
||||
import { ResetScreen } from '@app/auth/reset';
|
||||
import { UnlockScreen } from '@app/auth/unlock';
|
||||
import { WelcomeScreen } from '@app/auth/welcome';
|
||||
import { ChannelScreen } from '@app/channel';
|
||||
import { ChatScreen } from '@app/chats';
|
||||
import { ErrorScreen } from '@app/error';
|
||||
import { ExploreScreen } from '@app/explore';
|
||||
import { NoteScreen } from '@app/note';
|
||||
import { Root } from '@app/root';
|
||||
import { AccountSettingsScreen } from '@app/settings/account';
|
||||
import { GeneralSettingsScreen } from '@app/settings/general';
|
||||
import { ShortcutsSettingsScreen } from '@app/settings/shortcuts';
|
||||
import { SpaceScreen } from '@app/space';
|
||||
import { TrendingScreen } from '@app/trending';
|
||||
import { UserScreen } from '@app/users';
|
||||
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
import { AppLayout } from '@shared/appLayout';
|
||||
import { AuthLayout } from '@shared/authLayout';
|
||||
import { Protected } from '@shared/protected';
|
||||
import { SettingsLayout } from '@shared/settingsLayout';
|
||||
|
||||
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 './index.css';
|
||||
|
||||
import './app.css';
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: (
|
||||
<Protected>
|
||||
<Root />
|
||||
</Protected>
|
||||
),
|
||||
errorElement: <ErrorScreen />,
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
element: <AuthLayout />,
|
||||
children: [
|
||||
{ path: 'welcome', element: <WelcomeScreen /> },
|
||||
{ path: 'onboarding', element: <OnboardingScreen /> },
|
||||
{
|
||||
path: 'import',
|
||||
element: <AuthImportScreen />,
|
||||
children: [
|
||||
{ path: '', element: <ImportStep1Screen /> },
|
||||
{ path: 'step-2', element: <ImportStep2Screen /> },
|
||||
{ path: 'step-3', element: <ImportStep3Screen /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
element: <AuthCreateScreen />,
|
||||
children: [
|
||||
{ path: '', element: <CreateStep1Screen /> },
|
||||
{ path: 'step-2', element: <CreateStep2Screen /> },
|
||||
{ path: 'step-3', element: <CreateStep3Screen /> },
|
||||
{ path: 'step-4', element: <CreateStep4Screen /> },
|
||||
{ path: 'step-5', element: <CreateStep5Screen /> },
|
||||
],
|
||||
},
|
||||
{ path: 'unlock', element: <UnlockScreen /> },
|
||||
{ path: 'migrate', element: <MigrateScreen /> },
|
||||
{ path: 'reset', element: <ResetScreen /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/app',
|
||||
element: (
|
||||
<Protected>
|
||||
<AppLayout />
|
||||
</Protected>
|
||||
),
|
||||
children: [
|
||||
{ path: 'space', element: <SpaceScreen /> },
|
||||
{ path: 'trending', element: <TrendingScreen /> },
|
||||
{ path: 'note/:id', element: <NoteScreen /> },
|
||||
{ path: 'users/:pubkey', element: <UserScreen /> },
|
||||
{ path: 'chats/:pubkey', element: <ChatScreen /> },
|
||||
{ path: 'channel/:id', element: <ChannelScreen /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
element: (
|
||||
<Protected>
|
||||
<SettingsLayout />
|
||||
</Protected>
|
||||
),
|
||||
children: [
|
||||
{ path: 'general', element: <GeneralSettingsScreen /> },
|
||||
{ path: 'shortcuts', element: <ShortcutsSettingsScreen /> },
|
||||
{ path: 'account', element: <AccountSettingsScreen /> },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
export default function App() {
|
||||
const { db } = useStorage();
|
||||
|
||||
const accountLoader = async () => {
|
||||
try {
|
||||
// redirect to welcome screen if none user exist
|
||||
const totalAccount = await db.checkAccount();
|
||||
if (totalAccount === 0) return redirect('/auth/welcome');
|
||||
|
||||
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([
|
||||
{
|
||||
path: '/',
|
||||
element: <AppLayout />,
|
||||
errorElement: <ErrorScreen />,
|
||||
loader: accountLoader,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
async lazy() {
|
||||
const { HomeScreen } = await import('@app/home');
|
||||
return { Component: HomeScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'users/:pubkey',
|
||||
async lazy() {
|
||||
const { UserScreen } = await import('@app/users');
|
||||
return { Component: UserScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'nwc',
|
||||
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');
|
||||
return { Component: ChatScreen };
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/new',
|
||||
element: <NewLayout />,
|
||||
errorElement: <ErrorScreen />,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
async lazy() {
|
||||
const { NewPostScreen } = await import('@app/new/post');
|
||||
return { Component: NewPostScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
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',
|
||||
async lazy() {
|
||||
const { WelcomeScreen } = await import('@app/auth/welcome');
|
||||
return { Component: WelcomeScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
async lazy() {
|
||||
const { CreateAccountScreen } = await import('@app/auth/create');
|
||||
return { Component: CreateAccountScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'import',
|
||||
async lazy() {
|
||||
const { ImportAccountScreen } = await import('@app/auth/import');
|
||||
return { Component: ImportAccountScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'onboarding',
|
||||
element: <OnboardingScreen />,
|
||||
errorElement: <ErrorScreen />,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
async lazy() {
|
||||
const { OnboardingListScreen } = await import(
|
||||
'@app/auth/onboarding/list'
|
||||
);
|
||||
return { Component: OnboardingListScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'enrich',
|
||||
async lazy() {
|
||||
const { OnboardEnrichScreen } = await import(
|
||||
'@app/auth/onboarding/enrich'
|
||||
);
|
||||
return { Component: OnboardEnrichScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'hashtag',
|
||||
async lazy() {
|
||||
const { OnboardHashtagScreen } = await import(
|
||||
'@app/auth/onboarding/hashtag'
|
||||
);
|
||||
return { Component: OnboardHashtagScreen };
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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 };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'backup',
|
||||
async lazy() {
|
||||
const { BackupSettingScreen } = await import('@app/settings/backup');
|
||||
return { Component: BackupSettingScreen };
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'advanced',
|
||||
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 };
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<RouterProvider
|
||||
router={router}
|
||||
fallbackElement={
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<LoaderIcon className="h-6 w-6 animate-spin text-white" />
|
||||
</div>
|
||||
}
|
||||
fallbackElement={<p>Loading..</p>}
|
||||
future={{ v7_startTransition: true }}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import { isPermissionGranted, requestPermission } from '@tauri-apps/plugin-notification';
|
||||
|
||||
import { CheckCircleIcon } from '@shared/icons';
|
||||
|
||||
import { useOnboarding } from '@stores/onboarding';
|
||||
|
||||
export function AllowNotification() {
|
||||
const [notification, setNotification] = useOnboarding((state) => [
|
||||
state.notification,
|
||||
state.toggleNotification,
|
||||
]);
|
||||
|
||||
const allow = async () => {
|
||||
let permissionGranted = await isPermissionGranted();
|
||||
if (!permissionGranted) {
|
||||
const permission = await requestPermission();
|
||||
permissionGranted = permission === 'granted';
|
||||
}
|
||||
if (permissionGranted) {
|
||||
setNotification();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<h5 className="font-semibold">Allow notification</h5>
|
||||
<p className="text-sm">
|
||||
By allowing Lume to send notifications in your OS settings, you will receive
|
||||
notification messages when someone interacts with you or your content.
|
||||
</p>
|
||||
</div>
|
||||
{notification ? (
|
||||
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
|
||||
<CheckCircleIcon className="h-4 w-4" />
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={allow}
|
||||
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
|
||||
>
|
||||
Allow
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
||||
|
||||
import { useOnboarding } from '@stores/onboarding';
|
||||
|
||||
export function Circle() {
|
||||
const { db } = useStorage();
|
||||
const { ndk } = useNDK();
|
||||
|
||||
const [circle, setCircle] = useOnboarding((state) => [
|
||||
state.circle,
|
||||
state.toggleCircle,
|
||||
]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const enableLinks = async () => {
|
||||
setLoading(true);
|
||||
|
||||
const users = ndk.getUser({ pubkey: db.account.pubkey });
|
||||
const follows = await users.follows();
|
||||
|
||||
if (follows.size === 0) {
|
||||
setLoading(false);
|
||||
return toast('You need to follow at least 1 account');
|
||||
}
|
||||
|
||||
const lru = new LRUCache<string, string, void>({ max: 300 });
|
||||
const followsAsArr = [];
|
||||
|
||||
// add user's follows to lru
|
||||
follows.forEach((user) => {
|
||||
lru.set(user.pubkey, user.pubkey);
|
||||
followsAsArr.push(user.pubkey);
|
||||
});
|
||||
|
||||
// get follows from follows
|
||||
const events = await ndk.fetchEvents({
|
||||
kinds: [NDKKind.Contacts],
|
||||
authors: followsAsArr,
|
||||
limit: 300,
|
||||
});
|
||||
|
||||
events.forEach((event: NDKEvent) => {
|
||||
event.tags.forEach((tag) => {
|
||||
if (tag[0] === 'p') lru.set(tag[1], tag[1]);
|
||||
});
|
||||
});
|
||||
|
||||
// get lru values
|
||||
const circleList = [...lru.values()] as string[];
|
||||
|
||||
// update db
|
||||
await db.updateAccount('follows', JSON.stringify(followsAsArr));
|
||||
await db.updateAccount('circles', JSON.stringify(circleList));
|
||||
|
||||
db.account.follows = followsAsArr;
|
||||
db.account.circles = circleList;
|
||||
|
||||
// clear lru
|
||||
lru.clear();
|
||||
|
||||
// done
|
||||
await db.createSetting('circles', '1');
|
||||
setCircle();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="rounded-xl bg-neutral-100 p-3 text-neutral-800 dark:bg-neutral-900 dark:text-neutral-200">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<h5 className="font-semibold">Enable Circle</h5>
|
||||
<p className="text-sm">
|
||||
Beside newsfeed from your follows, you will see more content from all people
|
||||
that followed by your follows.
|
||||
</p>
|
||||
</div>
|
||||
{circle ? (
|
||||
<div className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-teal-500 text-white">
|
||||
<CheckCircleIcon className="h-4 w-4" />
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={enableLinks}
|
||||
className="mt-1 inline-flex h-9 w-24 shrink-0 items-center justify-center rounded-lg bg-neutral-200 font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-800"
|
||||
>
|
||||
{loading ? <LoaderIcon className="h-4 w-4 animate-spin" /> : 'Enable'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||