Compare commits

..

11 Commits

Author SHA1 Message Date
Ren Amamiya
1553f5ced2 bump version 2023-09-09 07:21:02 +07:00
Ren Amamiya
41901b2174 fix hashtag step in onboarding 2023-09-08 17:02:01 +07:00
Ren Amamiya
177e4c1ff7 fix some bugs 2023-09-08 15:29:41 +07:00
Ren Amamiya
10036500cb small fixes 2023-09-08 09:22:09 +07:00
Ren Amamiya
a1fa777f8c fix logout function, prepare for multi-account support 2023-09-08 08:36:15 +07:00
Ren Amamiya
472925bb05 small fixes 2023-09-07 12:19:28 +07:00
Ren Amamiya
8eb11efb34 update notification 2023-09-07 11:34:26 +07:00
Ren Amamiya
48066a4018 bump version 2023-09-06 16:53:05 +07:00
Ren Amamiya
5c8850ea8f redesign widget list 2023-09-06 14:30:57 +07:00
Ren Amamiya
09aea3cff5 update widgets 2023-09-06 08:58:02 +07:00
Ren Amamiya
45c5a890b9 update tauri config 2023-09-06 07:46:31 +07:00
48 changed files with 632 additions and 454 deletions

View File

@@ -2,7 +2,7 @@
"name": "lume", "name": "lume",
"description": "the communication app", "description": "the communication app",
"private": true, "private": true,
"version": "1.2.2", "version": "1.2.4",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
@@ -20,7 +20,7 @@
"dependencies": { "dependencies": {
"@ctrl/magnet-link": "^3.1.2", "@ctrl/magnet-link": "^3.1.2",
"@getalby/sdk": "^2.4.0", "@getalby/sdk": "^2.4.0",
"@nostr-dev-kit/ndk": "^0.8.23", "@nostr-dev-kit/ndk": "^1.0.0",
"@nostr-fetch/adapter-ndk": "^0.12.2", "@nostr-fetch/adapter-ndk": "^0.12.2",
"@radix-ui/react-alert-dialog": "^1.0.4", "@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.3",
@@ -28,7 +28,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-popover": "^1.0.6", "@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-tooltip": "^1.0.6", "@radix-ui/react-tooltip": "^1.0.6",
"@tanstack/react-query": "^4.33.0", "@tanstack/react-query": "^4.35.0",
"@tanstack/react-virtual": "3.0.0-beta.54", "@tanstack/react-virtual": "3.0.0-beta.54",
"@tauri-apps/api": "^1.4.0", "@tauri-apps/api": "^1.4.0",
"@tiptap/extension-image": "^2.1.8", "@tiptap/extension-image": "^2.1.8",
@@ -54,10 +54,10 @@
"react-hook-form": "^7.46.1", "react-hook-form": "^7.46.1",
"react-hotkeys-hook": "^4.4.1", "react-hotkeys-hook": "^4.4.1",
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-player": "^2.12.0", "react-player": "^2.13.0",
"react-router-dom": "^6.15.0", "react-router-dom": "^6.15.0",
"react-textarea-autosize": "^8.5.3", "react-textarea-autosize": "^8.5.3",
"react-virtuoso": "^4.5.0", "react-virtuoso": "^4.5.1",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1", "tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
"tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1", "tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1",
@@ -67,7 +67,7 @@
"zustand": "^4.4.1" "zustand": "^4.4.1"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.9", "@tailwindcss/typography": "^0.5.10",
"@tauri-apps/cli": "^1.4.0", "@tauri-apps/cli": "^1.4.0",
"@trivago/prettier-plugin-sort-imports": "^4.2.0", "@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/html-to-text": "^9.0.1", "@types/html-to-text": "^9.0.1",

246
pnpm-lock.yaml generated
View File

@@ -8,11 +8,11 @@ dependencies:
specifier: ^2.4.0 specifier: ^2.4.0
version: 2.4.0 version: 2.4.0
'@nostr-dev-kit/ndk': '@nostr-dev-kit/ndk':
specifier: ^0.8.23 specifier: ^1.0.0
version: 0.8.23(typescript@5.2.2) version: 1.0.0(typescript@5.2.2)
'@nostr-fetch/adapter-ndk': '@nostr-fetch/adapter-ndk':
specifier: ^0.12.2 specifier: ^0.12.2
version: 0.12.2(@nostr-dev-kit/ndk@0.8.23)(nostr-fetch@0.13.0) version: 0.12.2(@nostr-dev-kit/ndk@1.0.0)(nostr-fetch@0.13.0)
'@radix-ui/react-alert-dialog': '@radix-ui/react-alert-dialog':
specifier: ^1.0.4 specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0)
@@ -32,8 +32,8 @@ dependencies:
specifier: ^1.0.6 specifier: ^1.0.6
version: 1.0.6(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) version: 1.0.6(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^4.33.0 specifier: ^4.35.0
version: 4.33.0(react-dom@18.2.0)(react@18.2.0) version: 4.35.0(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-virtual': '@tanstack/react-virtual':
specifier: 3.0.0-beta.54 specifier: 3.0.0-beta.54
version: 3.0.0-beta.54(react@18.2.0) version: 3.0.0-beta.54(react@18.2.0)
@@ -110,8 +110,8 @@ dependencies:
specifier: ^8.0.7 specifier: ^8.0.7
version: 8.0.7(@types/react@18.2.21)(react@18.2.0) version: 8.0.7(@types/react@18.2.21)(react@18.2.0)
react-player: react-player:
specifier: ^2.12.0 specifier: ^2.13.0
version: 2.12.0(react@18.2.0) version: 2.13.0(react@18.2.0)
react-router-dom: react-router-dom:
specifier: ^6.15.0 specifier: ^6.15.0
version: 6.15.0(react-dom@18.2.0)(react@18.2.0) version: 6.15.0(react-dom@18.2.0)(react@18.2.0)
@@ -119,8 +119,8 @@ dependencies:
specifier: ^8.5.3 specifier: ^8.5.3
version: 8.5.3(@types/react@18.2.21)(react@18.2.0) version: 8.5.3(@types/react@18.2.21)(react@18.2.0)
react-virtuoso: react-virtuoso:
specifier: ^4.5.0 specifier: ^4.5.1
version: 4.5.0(react-dom@18.2.0)(react@18.2.0) version: 4.5.1(react-dom@18.2.0)(react@18.2.0)
remark-gfm: remark-gfm:
specifier: ^3.0.1 specifier: ^3.0.1
version: 3.0.1 version: 3.0.1
@@ -145,8 +145,8 @@ dependencies:
devDependencies: devDependencies:
'@tailwindcss/typography': '@tailwindcss/typography':
specifier: ^0.5.9 specifier: ^0.5.10
version: 0.5.9(tailwindcss@3.3.3) version: 0.5.10(tailwindcss@3.3.3)
'@tauri-apps/cli': '@tauri-apps/cli':
specifier: ^1.4.0 specifier: ^1.4.0
version: 1.4.0 version: 1.4.0
@@ -312,8 +312,8 @@ packages:
chalk: 2.4.2 chalk: 2.4.2
js-tokens: 4.0.0 js-tokens: 4.0.0
/@babel/parser@7.22.15: /@babel/parser@7.22.16:
resolution: {integrity: sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==} resolution: {integrity: sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
@@ -331,7 +331,7 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/code-frame': 7.22.13 '@babel/code-frame': 7.22.13
'@babel/parser': 7.22.15 '@babel/parser': 7.22.16
'@babel/types': 7.22.15 '@babel/types': 7.22.15
dev: true dev: true
@@ -345,7 +345,7 @@ packages:
'@babel/helper-function-name': 7.22.5 '@babel/helper-function-name': 7.22.5
'@babel/helper-hoist-variables': 7.22.5 '@babel/helper-hoist-variables': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.22.15 '@babel/parser': 7.22.16
'@babel/types': 7.17.0 '@babel/types': 7.17.0
debug: 4.3.4 debug: 4.3.4
globals: 11.12.0 globals: 11.12.0
@@ -950,8 +950,8 @@ packages:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.15.0 fastq: 1.15.0
/@nostr-dev-kit/ndk@0.8.23(typescript@5.2.2): /@nostr-dev-kit/ndk@1.0.0(typescript@5.2.2):
resolution: {integrity: sha512-wX/9Cl02gCR0Kz25C/1xxGO47K13Ve1x8IISbkF/M3RTTXftYBvzB7bL8qwLaFaeqb02cMU0YVL+oKDrYzH/Ng==} resolution: {integrity: sha512-EY2MVHZgHp6RSudnLVoWYYP1BQvtb4nX3Vafoa4LzmME5dD8i0XLURdW29h7+87mluPdLTJLMI6wTZWEy2NFUQ==}
dependencies: dependencies:
'@noble/hashes': 1.3.2 '@noble/hashes': 1.3.2
'@noble/secp256k1': 2.0.0 '@noble/secp256k1': 2.0.0
@@ -970,6 +970,7 @@ packages:
node-fetch: 3.3.2 node-fetch: 3.3.2
nostr-tools: 1.14.2 nostr-tools: 1.14.2
tsd: 0.28.1 tsd: 0.28.1
typescript-lru-cache: 2.0.0
utf8-buffer: 1.0.0 utf8-buffer: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
- eslint-import-resolver-typescript - eslint-import-resolver-typescript
@@ -978,13 +979,13 @@ packages:
- typescript - typescript
dev: false dev: false
/@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@0.8.23)(nostr-fetch@0.13.0): /@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@1.0.0)(nostr-fetch@0.13.0):
resolution: {integrity: sha512-+7EVuxS5DDZvNo6qbfFp7xRHwIyjyi36hYkiQFDjbQ4gX5LKo9RIPB1P+1XGkOSDFshypTbovZCaFunscJ/zhQ==} resolution: {integrity: sha512-+7EVuxS5DDZvNo6qbfFp7xRHwIyjyi36hYkiQFDjbQ4gX5LKo9RIPB1P+1XGkOSDFshypTbovZCaFunscJ/zhQ==}
peerDependencies: peerDependencies:
'@nostr-dev-kit/ndk': ^0.7.5 '@nostr-dev-kit/ndk': ^0.7.5
nostr-fetch: ^0.12.2 nostr-fetch: ^0.12.2
dependencies: dependencies:
'@nostr-dev-kit/ndk': 0.8.23(typescript@5.2.2) '@nostr-dev-kit/ndk': 1.0.0(typescript@5.2.2)
'@nostr-fetch/kernel': 0.12.2 '@nostr-fetch/kernel': 0.12.2
nostr-fetch: 0.13.0 nostr-fetch: 0.13.0
dev: false dev: false
@@ -1695,15 +1696,15 @@ packages:
resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==}
dependencies: dependencies:
'@noble/curves': 1.1.0 '@noble/curves': 1.1.0
'@noble/hashes': 1.3.2 '@noble/hashes': 1.3.1
'@scure/base': 1.1.3 '@scure/base': 1.1.1
dev: false dev: false
/@scure/bip39@1.2.1: /@scure/bip39@1.2.1:
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
dependencies: dependencies:
'@noble/hashes': 1.3.2 '@noble/hashes': 1.3.1
'@scure/base': 1.1.3 '@scure/base': 1.1.1
dev: false dev: false
/@selderee/plugin-htmlparser2@0.11.0: /@selderee/plugin-htmlparser2@0.11.0:
@@ -1717,8 +1718,8 @@ packages:
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
dev: false dev: false
/@swc/core-darwin-arm64@1.3.82: /@swc/core-darwin-arm64@1.3.83:
resolution: {integrity: sha512-JfsyDW34gVKD3uE0OUpUqYvAD3yseEaicnFP6pB292THtLJb0IKBBnK50vV/RzEJtc1bR3g1kNfxo2PeurZTrA==} resolution: {integrity: sha512-Plz2IKeveVLivbXTSCC3OZjD2MojyKYllhPrn9RotkDIZEFRYJZtW5/Ik1tJW/2rzu5HVKuGYrDKdScVVTbOxQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
@@ -1726,8 +1727,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-darwin-x64@1.3.82: /@swc/core-darwin-x64@1.3.83:
resolution: {integrity: sha512-ogQWgNMq7qTpITjcP3dnzkFNj7bh6SwMr859GvtOTrE75H7L7jDWxESfH4f8foB/LGxBKiDNmxKhitCuAsZK4A==} resolution: {integrity: sha512-FBGVg5IPF/8jQ6FbK60iDUHjv0H5+LwfpJHKH6wZnRaYWFtm7+pzYgreLu3NTsm3m7/1a7t0+7KURwBGUaJCCw==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
@@ -1735,8 +1736,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-linux-arm-gnueabihf@1.3.82: /@swc/core-linux-arm-gnueabihf@1.3.83:
resolution: {integrity: sha512-7TMXG1lXlNhD0kUiEqs+YlGV4irAdBa2quuy+XI3oJf2fBK6dQfEq4xBy65B3khrorzQS3O0oDGQ+cmdpHExHA==} resolution: {integrity: sha512-EZcsuRYhGkzofXtzwDjuuBC/suiX9s7zeg2YYXOVjWwyebb6BUhB1yad3mcykFQ20rTLO9JUyIaiaMYDHGobqw==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
@@ -1744,8 +1745,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-linux-arm64-gnu@1.3.82: /@swc/core-linux-arm64-gnu@1.3.83:
resolution: {integrity: sha512-26JkOujbzcItPAmIbD5vHJxQVy5ihcSu3YHTKwope1h28sApZdtE7S3e2G3gsZRTIdsCQkXUtAQeqHxGWWR3pw==} resolution: {integrity: sha512-khI41szLHrCD/cFOcN4p2SYvZgHjhhHlcMHz5BksRrDyteSJKu0qtWRZITVom0N/9jWoAleoFhMnFTUs0H8IWA==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@@ -1753,8 +1754,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-linux-arm64-musl@1.3.82: /@swc/core-linux-arm64-musl@1.3.83:
resolution: {integrity: sha512-8Izj9tuuMpoc3cqiPBRtwqpO1BZ/+sfZVsEhLxrbOFlcSb8LnKyMle1g3JMMUwI4EU75RGVIzZMn8A6GOKdJbA==} resolution: {integrity: sha512-zgT7yNOdbjHcGAwvys79mbfNLK65KBlPJWzeig+Yk7I8TVzmaQge7B6ZS/gwF9/p+8TiLYo/tZ5aF2lqlgdSVw==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@@ -1762,8 +1763,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-linux-x64-gnu@1.3.82: /@swc/core-linux-x64-gnu@1.3.83:
resolution: {integrity: sha512-0GSrIBScQwTaPv46T2qB7XnDYxndRCpwH4HMjh6FN+I+lfPUhTSJKW8AonqrqT1TbpFIgvzQs7EnTsD7AnSCow==} resolution: {integrity: sha512-x+mH0Y3NC/G0YNlFmGi3vGD4VOm7IPDhh+tGrx6WtJp0BsShAbOpxtfU885rp1QweZe4qYoEmGqiEjE2WrPIdA==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@@ -1771,8 +1772,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-linux-x64-musl@1.3.82: /@swc/core-linux-x64-musl@1.3.83:
resolution: {integrity: sha512-KJUnaaepDKNzrEbwz4jv0iC3/t9x0NSoe06fnkAlhh2+NFKWKKJhVCOBTrpds8n7eylBDIXUlK34XQafjVMUdg==} resolution: {integrity: sha512-s5AYhAOmetUwUZwS5g9qb92IYgNHHBGiY2mTLImtEgpAeBwe0LPDj6WrujxCBuZnaS55mKRLLOuiMZE5TpjBNA==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@@ -1780,8 +1781,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-win32-arm64-msvc@1.3.82: /@swc/core-win32-arm64-msvc@1.3.83:
resolution: {integrity: sha512-TR3MHKhDYIyGyFcyl2d/p1ftceXcubAhX5wRSOdtOyr5+K/v3jbyCCqN7bbqO5o43wQVCwwR/drHleYyDZvg8Q==} resolution: {integrity: sha512-yw2rd/KVOGs95lRRB+killLWNaO1dy4uVa8Q3/4wb5txlLru07W1m041fZLzwOg/1Sh0TMjJgGxj0XHGR3ZXhQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
@@ -1789,8 +1790,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-win32-ia32-msvc@1.3.82: /@swc/core-win32-ia32-msvc@1.3.83:
resolution: {integrity: sha512-ZX4HzVVt6hs84YUg70UvyBJnBOIspmQQM0iXSzBvOikk3zRoN7BnDwQH4GScvevCEBuou60+i4I6d5kHLOfh8Q==} resolution: {integrity: sha512-POW+rgZ6KWqBpwPGIRd2/3pcf46P+UrKBm4HLt5IwbHvekJ4avIM8ixJa9kK0muJNVJcDpaZgxaU1ELxtJ1j8w==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
@@ -1798,8 +1799,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core-win32-x64-msvc@1.3.82: /@swc/core-win32-x64-msvc@1.3.83:
resolution: {integrity: sha512-4mJMnex21kbQoaHeAmHnVwQN9/XAfPszJ6n9HI7SVH+aAHnbBIR0M59/b50/CJMjTj5niUGk7EwQ3nhVNOG32g==} resolution: {integrity: sha512-CiWQtkFnZElXQUalaHp+Wacw0Jd+24ncRYhqaJ9YKnEQP1H82CxIIuQqLM8IFaLpn5dpY6SgzaeubWF46hjcLA==}
engines: {node: '>=10'} engines: {node: '>=10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@@ -1807,8 +1808,8 @@ packages:
dev: true dev: true
optional: true optional: true
/@swc/core@1.3.82: /@swc/core@1.3.83:
resolution: {integrity: sha512-jpC1a18HMH67018Ij2jh+hT7JBFu7ZKcQVfrZ8K6JuEY+kjXmbea07P9MbQUZbAe0FB+xi3CqEVCP73MebodJQ==} resolution: {integrity: sha512-PccHDgGQlFjpExgJxH91qA3a4aifR+axCFJ4RieCoiI0m5gURE4nBhxzTBY5YU/YKTBmPO8Gc5Q6inE3+NquWg==}
engines: {node: '>=10'} engines: {node: '>=10'}
requiresBuild: true requiresBuild: true
peerDependencies: peerDependencies:
@@ -1819,24 +1820,24 @@ packages:
dependencies: dependencies:
'@swc/types': 0.1.4 '@swc/types': 0.1.4
optionalDependencies: optionalDependencies:
'@swc/core-darwin-arm64': 1.3.82 '@swc/core-darwin-arm64': 1.3.83
'@swc/core-darwin-x64': 1.3.82 '@swc/core-darwin-x64': 1.3.83
'@swc/core-linux-arm-gnueabihf': 1.3.82 '@swc/core-linux-arm-gnueabihf': 1.3.83
'@swc/core-linux-arm64-gnu': 1.3.82 '@swc/core-linux-arm64-gnu': 1.3.83
'@swc/core-linux-arm64-musl': 1.3.82 '@swc/core-linux-arm64-musl': 1.3.83
'@swc/core-linux-x64-gnu': 1.3.82 '@swc/core-linux-x64-gnu': 1.3.83
'@swc/core-linux-x64-musl': 1.3.82 '@swc/core-linux-x64-musl': 1.3.83
'@swc/core-win32-arm64-msvc': 1.3.82 '@swc/core-win32-arm64-msvc': 1.3.83
'@swc/core-win32-ia32-msvc': 1.3.82 '@swc/core-win32-ia32-msvc': 1.3.83
'@swc/core-win32-x64-msvc': 1.3.82 '@swc/core-win32-x64-msvc': 1.3.83
dev: true dev: true
/@swc/types@0.1.4: /@swc/types@0.1.4:
resolution: {integrity: sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==} resolution: {integrity: sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==}
dev: true dev: true
/@tailwindcss/typography@0.5.9(tailwindcss@3.3.3): /@tailwindcss/typography@0.5.10(tailwindcss@3.3.3):
resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==} resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==}
peerDependencies: peerDependencies:
tailwindcss: '>=3.0.0 || insiders' tailwindcss: '>=3.0.0 || insiders'
dependencies: dependencies:
@@ -1847,12 +1848,12 @@ packages:
tailwindcss: 3.3.3 tailwindcss: 3.3.3
dev: true dev: true
/@tanstack/query-core@4.33.0: /@tanstack/query-core@4.35.0:
resolution: {integrity: sha512-qYu73ptvnzRh6se2nyBIDHGBQvPY1XXl3yR769B7B6mIDD7s+EZhdlWHQ67JI6UOTFRaI7wupnTnwJ3gE0Mr/g==} resolution: {integrity: sha512-4GMcKQuLZQi6RFBiBZNsLhl+hQGYScRZ5ZoVq8QAzfqz9M7vcGin/2YdSESwl7WaV+Qzsb5CZOAbMBes4lNTnA==}
dev: false dev: false
/@tanstack/react-query@4.33.0(react-dom@18.2.0)(react@18.2.0): /@tanstack/react-query@4.35.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-97nGbmDK0/m0B86BdiXzx3EW9RcDYKpnyL2+WwyuLHEgpfThYAnXFaMMmnTDuAO4bQJXEhflumIEUfKmP7ESGA==} resolution: {integrity: sha512-LLYDNnM9ewYHgjm2rzhk4KG/puN2rdoqCUD+N9+V7SwlsYwJk5ypX58rpkoZAhFyZ+KmFUJ7Iv2lIEOoUqydIg==}
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -1863,7 +1864,7 @@ packages:
react-native: react-native:
optional: true optional: true
dependencies: dependencies:
'@tanstack/query-core': 4.33.0 '@tanstack/query-core': 4.35.0
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
use-sync-external-store: 1.2.0(react@18.2.0) use-sync-external-store: 1.2.0(react@18.2.0)
@@ -2293,7 +2294,7 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@babel/generator': 7.17.7 '@babel/generator': 7.17.7
'@babel/parser': 7.22.15 '@babel/parser': 7.22.16
'@babel/traverse': 7.17.3 '@babel/traverse': 7.17.3
'@babel/types': 7.17.0 '@babel/types': 7.17.0
javascript-natural-sort: 0.7.1 javascript-natural-sort: 0.7.1
@@ -2455,7 +2456,7 @@ packages:
ignore: 5.2.4 ignore: 5.2.4
natural-compare: 1.4.0 natural-compare: 1.4.0
semver: 7.5.4 semver: 7.5.4
ts-api-utils: 1.0.2(typescript@5.2.2) ts-api-utils: 1.0.3(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -2552,7 +2553,7 @@ packages:
'@typescript-eslint/utils': 6.6.0(eslint@8.48.0)(typescript@5.2.2) '@typescript-eslint/utils': 6.6.0(eslint@8.48.0)(typescript@5.2.2)
debug: 4.3.4 debug: 4.3.4
eslint: 8.48.0 eslint: 8.48.0
ts-api-utils: 1.0.2(typescript@5.2.2) ts-api-utils: 1.0.3(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -2604,7 +2605,7 @@ packages:
globby: 11.1.0 globby: 11.1.0
is-glob: 4.0.3 is-glob: 4.0.3
semver: 7.5.4 semver: 7.5.4
ts-api-utils: 1.0.2(typescript@5.2.2) ts-api-utils: 1.0.3(typescript@5.2.2)
typescript: 5.2.2 typescript: 5.2.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -2670,7 +2671,7 @@ packages:
peerDependencies: peerDependencies:
vite: ^4 vite: ^4
dependencies: dependencies:
'@swc/core': 1.3.82 '@swc/core': 1.3.83
vite: 4.4.9(@types/node@20.5.9) vite: 4.4.9(@types/node@20.5.9)
transitivePeerDependencies: transitivePeerDependencies:
- '@swc/helpers' - '@swc/helpers'
@@ -2810,8 +2811,8 @@ packages:
get-intrinsic: 1.2.1 get-intrinsic: 1.2.1
dev: false dev: false
/array.prototype.flat@1.3.1: /array.prototype.flat@1.3.2:
resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -2819,8 +2820,8 @@ packages:
es-abstract: 1.22.1 es-abstract: 1.22.1
es-shim-unscopables: 1.0.0 es-shim-unscopables: 1.0.0
/array.prototype.flatmap@1.3.1: /array.prototype.flatmap@1.3.2:
resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -2828,8 +2829,8 @@ packages:
es-abstract: 1.22.1 es-abstract: 1.22.1
es-shim-unscopables: 1.0.0 es-shim-unscopables: 1.0.0
/array.prototype.tosorted@1.1.1: /array.prototype.tosorted@1.1.2:
resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
define-properties: 1.2.0 define-properties: 1.2.0
@@ -2838,13 +2839,14 @@ packages:
get-intrinsic: 1.2.1 get-intrinsic: 1.2.1
dev: true dev: true
/arraybuffer.prototype.slice@1.0.1: /arraybuffer.prototype.slice@1.0.2:
resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
array-buffer-byte-length: 1.0.0 array-buffer-byte-length: 1.0.0
call-bind: 1.0.2 call-bind: 1.0.2
define-properties: 1.2.0 define-properties: 1.2.0
es-abstract: 1.22.1
get-intrinsic: 1.2.1 get-intrinsic: 1.2.1
is-array-buffer: 3.0.2 is-array-buffer: 3.0.2
is-shared-array-buffer: 1.0.2 is-shared-array-buffer: 1.0.2
@@ -2872,7 +2874,7 @@ packages:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
browserslist: 4.21.10 browserslist: 4.21.10
caniuse-lite: 1.0.30001525 caniuse-lite: 1.0.30001528
fraction.js: 4.3.6 fraction.js: 4.3.6
normalize-range: 0.1.2 normalize-range: 0.1.2
picocolors: 1.0.0 picocolors: 1.0.0
@@ -2884,8 +2886,8 @@ packages:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
/axe-core@4.7.2: /axe-core@4.8.0:
resolution: {integrity: sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==} resolution: {integrity: sha512-ZtlVZobOeDQhb/y2lMK6mznDw7TJHDNcKx5/bbBkFvArIQ5CVFhSI6hWWQnMx9I8cNmNmZ30wpDyOC2E2nvgbQ==}
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
@@ -2924,8 +2926,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true hasBin: true
dependencies: dependencies:
caniuse-lite: 1.0.30001525 caniuse-lite: 1.0.30001528
electron-to-chromium: 1.4.508 electron-to-chromium: 1.4.511
node-releases: 2.0.13 node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10) update-browserslist-db: 1.0.11(browserslist@4.21.10)
dev: true dev: true
@@ -2959,8 +2961,8 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: false dev: false
/caniuse-lite@1.0.30001525: /caniuse-lite@1.0.30001528:
resolution: {integrity: sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q==} resolution: {integrity: sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==}
dev: true dev: true
/case-anything@2.1.13: /case-anything@2.1.13:
@@ -3284,8 +3286,8 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true dev: true
/electron-to-chromium@1.4.508: /electron-to-chromium@1.4.511:
resolution: {integrity: sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==} resolution: {integrity: sha512-udHyLfdy390CObLy3uFQitCBvK+WxWu6WZWQMBzO/npNiRy6tanDKR1c/F6OImfAiSt1ylgNszPJBxix2c0w3w==}
dev: true dev: true
/emoji-regex@8.0.0: /emoji-regex@8.0.0:
@@ -3323,7 +3325,7 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
array-buffer-byte-length: 1.0.0 array-buffer-byte-length: 1.0.0
arraybuffer.prototype.slice: 1.0.1 arraybuffer.prototype.slice: 1.0.2
available-typed-arrays: 1.0.5 available-typed-arrays: 1.0.5
call-bind: 1.0.2 call-bind: 1.0.2
es-set-tostringtag: 2.0.1 es-set-tostringtag: 2.0.1
@@ -3350,11 +3352,11 @@ packages:
object-keys: 1.1.1 object-keys: 1.1.1
object.assign: 4.1.4 object.assign: 4.1.4
regexp.prototype.flags: 1.5.0 regexp.prototype.flags: 1.5.0
safe-array-concat: 1.0.0 safe-array-concat: 1.0.1
safe-regex-test: 1.0.0 safe-regex-test: 1.0.0
string.prototype.trim: 1.2.7 string.prototype.trim: 1.2.8
string.prototype.trimend: 1.0.6 string.prototype.trimend: 1.0.7
string.prototype.trimstart: 1.0.6 string.prototype.trimstart: 1.0.7
typed-array-buffer: 1.0.0 typed-array-buffer: 1.0.0
typed-array-byte-length: 1.0.0 typed-array-byte-length: 1.0.0
typed-array-byte-offset: 1.0.0 typed-array-byte-offset: 1.0.0
@@ -3378,7 +3380,7 @@ packages:
has-symbols: 1.0.3 has-symbols: 1.0.3
internal-slot: 1.0.5 internal-slot: 1.0.5
iterator.prototype: 1.1.1 iterator.prototype: 1.1.1
safe-array-concat: 1.0.0 safe-array-concat: 1.0.1
dev: true dev: true
/es-set-tostringtag@2.0.1: /es-set-tostringtag@2.0.1:
@@ -3568,8 +3570,8 @@ packages:
'@typescript-eslint/parser': 5.62.0(eslint@8.48.0)(typescript@5.2.2) '@typescript-eslint/parser': 5.62.0(eslint@8.48.0)(typescript@5.2.2)
array-includes: 3.1.7 array-includes: 3.1.7
array.prototype.findlastindex: 1.2.3 array.prototype.findlastindex: 1.2.3
array.prototype.flat: 1.3.1 array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.1 array.prototype.flatmap: 1.3.2
debug: 3.2.7 debug: 3.2.7
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 8.48.0 eslint: 8.48.0
@@ -3599,9 +3601,9 @@ packages:
'@babel/runtime': 7.22.15 '@babel/runtime': 7.22.15
aria-query: 5.3.0 aria-query: 5.3.0
array-includes: 3.1.7 array-includes: 3.1.7
array.prototype.flatmap: 1.3.1 array.prototype.flatmap: 1.3.2
ast-types-flow: 0.0.7 ast-types-flow: 0.0.7
axe-core: 4.7.2 axe-core: 4.8.0
axobject-query: 3.2.1 axobject-query: 3.2.1
damerau-levenshtein: 1.0.8 damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2 emoji-regex: 9.2.2
@@ -3622,8 +3624,8 @@ packages:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies: dependencies:
array-includes: 3.1.7 array-includes: 3.1.7
array.prototype.flatmap: 1.3.1 array.prototype.flatmap: 1.3.2
array.prototype.tosorted: 1.1.1 array.prototype.tosorted: 1.1.2
doctrine: 2.1.0 doctrine: 2.1.0
es-iterator-helpers: 1.0.14 es-iterator-helpers: 1.0.14
eslint: 8.48.0 eslint: 8.48.0
@@ -4406,8 +4408,8 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: false dev: false
/jiti@1.19.3: /jiti@1.20.0:
resolution: {integrity: sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==} resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==}
hasBin: true hasBin: true
dev: true dev: true
@@ -4455,7 +4457,7 @@ packages:
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
dependencies: dependencies:
array-includes: 3.1.7 array-includes: 3.1.7
array.prototype.flat: 1.3.1 array.prototype.flat: 1.3.2
object.assign: 4.1.4 object.assign: 4.1.4
object.values: 1.1.7 object.values: 1.1.7
dev: true dev: true
@@ -5255,7 +5257,7 @@ packages:
pidtree: 0.3.1 pidtree: 0.3.1
read-pkg: 3.0.0 read-pkg: 3.0.0
shell-quote: 1.8.1 shell-quote: 1.8.1
string.prototype.padend: 3.1.4 string.prototype.padend: 3.1.5
dev: false dev: false
/npm-run-path@5.1.0: /npm-run-path@5.1.0:
@@ -5917,8 +5919,8 @@ packages:
- supports-color - supports-color
dev: false dev: false
/react-player@2.12.0(react@18.2.0): /react-player@2.13.0(react@18.2.0):
resolution: {integrity: sha512-rymLRz/2GJJD+Wc01S7S+i9pGMFYnNmQibR2gVE3KmHJCBNN8BhPAlOPTGZtn1uKpJ6p4RPLlzPQ1OLreXd8gw==} resolution: {integrity: sha512-gkY7ZdbVFztlKFFhCPcnDrFQm+L399b8fhWsKatZ+b2wpKJwfUHBXQFMRxqYQGT0ic1/wQ7D7EZEWy7ZBqk2pw==}
peerDependencies: peerDependencies:
react: '>=16.6.0' react: '>=16.6.0'
dependencies: dependencies:
@@ -6019,8 +6021,8 @@ packages:
- '@types/react' - '@types/react'
dev: false dev: false
/react-virtuoso@4.5.0(react-dom@18.2.0)(react@18.2.0): /react-virtuoso@4.5.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OMP6XrzJMMos1vbJZC16RxGW7utAxUMP7i5PNPi6epBNVH7nz+CF/DlmecNBep5wyjLud51dQ5epjb2A0w9W/Q==} resolution: {integrity: sha512-Jdo9M/T5PcDAczvmXAKwvb/BW0MCMr/cb+3j2m9192zlQgQ+syMdJR42i+Sk80ln5aSNaL1fnxleJzdKsCc4lw==}
engines: {node: '>=10'} engines: {node: '>=10'}
peerDependencies: peerDependencies:
react: '>=16 || >=17 || >= 18' react: '>=16 || >=17 || >= 18'
@@ -6182,8 +6184,8 @@ packages:
dependencies: dependencies:
glob: 7.2.3 glob: 7.2.3
/rollup@3.28.1: /rollup@3.29.0:
resolution: {integrity: sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==} resolution: {integrity: sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'} engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true hasBin: true
optionalDependencies: optionalDependencies:
@@ -6206,8 +6208,8 @@ packages:
mri: 1.2.0 mri: 1.2.0
dev: false dev: false
/safe-array-concat@1.0.0: /safe-array-concat@1.0.1:
resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==}
engines: {node: '>=0.4'} engines: {node: '>=0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -6379,8 +6381,8 @@ packages:
side-channel: 1.0.4 side-channel: 1.0.4
dev: true dev: true
/string.prototype.padend@3.1.4: /string.prototype.padend@3.1.5:
resolution: {integrity: sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==} resolution: {integrity: sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -6388,23 +6390,23 @@ packages:
es-abstract: 1.22.1 es-abstract: 1.22.1
dev: false dev: false
/string.prototype.trim@1.2.7: /string.prototype.trim@1.2.8:
resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
define-properties: 1.2.0 define-properties: 1.2.0
es-abstract: 1.22.1 es-abstract: 1.22.1
/string.prototype.trimend@1.0.6: /string.prototype.trimend@1.0.7:
resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
define-properties: 1.2.0 define-properties: 1.2.0
es-abstract: 1.22.1 es-abstract: 1.22.1
/string.prototype.trimstart@1.0.6: /string.prototype.trimstart@1.0.7:
resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
define-properties: 1.2.0 define-properties: 1.2.0
@@ -6514,7 +6516,7 @@ packages:
fast-glob: 3.3.1 fast-glob: 3.3.1
glob-parent: 6.0.2 glob-parent: 6.0.2
is-glob: 4.0.3 is-glob: 4.0.3
jiti: 1.19.3 jiti: 1.20.0
lilconfig: 2.1.0 lilconfig: 2.1.0
micromatch: 4.0.5 micromatch: 4.0.5
normalize-path: 3.0.0 normalize-path: 3.0.0
@@ -6595,8 +6597,8 @@ packages:
resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==}
dev: false dev: false
/ts-api-utils@1.0.2(typescript@5.2.2): /ts-api-utils@1.0.3(typescript@5.2.2):
resolution: {integrity: sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==} resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==}
engines: {node: '>=16.13.0'} engines: {node: '>=16.13.0'}
peerDependencies: peerDependencies:
typescript: '>=4.2.0' typescript: '>=4.2.0'
@@ -6736,6 +6738,10 @@ packages:
for-each: 0.3.3 for-each: 0.3.3
is-typed-array: 1.1.12 is-typed-array: 1.1.12
/typescript-lru-cache@2.0.0:
resolution: {integrity: sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==}
dev: false
/typescript@5.2.2: /typescript@5.2.2:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
@@ -6996,7 +7002,7 @@ packages:
'@types/node': 20.5.9 '@types/node': 20.5.9
esbuild: 0.18.20 esbuild: 0.18.20
postcss: 8.4.29 postcss: 8.4.29
rollup: 3.28.1 rollup: 3.29.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true

30
src-tauri/Cargo.lock generated
View File

@@ -533,9 +533,9 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.13.1" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@@ -545,9 +545,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -695,9 +695,9 @@ dependencies = [
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.28" version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877"
dependencies = [ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
@@ -2627,7 +2627,7 @@ dependencies = [
[[package]] [[package]]
name = "lume" name = "lume"
version = "1.2.2" version = "1.2.4"
dependencies = [ dependencies = [
"rust-argon2 1.0.0", "rust-argon2 1.0.0",
"serde", "serde",
@@ -2642,7 +2642,6 @@ dependencies = [
"tauri-plugin-stronghold", "tauri-plugin-stronghold",
"tauri-plugin-upload", "tauri-plugin-upload",
"webpage", "webpage",
"window-shadows",
"window-vibrancy", "window-vibrancy",
] ]
@@ -5709,9 +5708,9 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.3.3" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [ dependencies = [
"same-file", "same-file",
"winapi-util", "winapi-util",
@@ -5971,17 +5970,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window-shadows"
version = "0.2.1"
source = "git+https://github.com/tauri-apps/window-shadows?branch=dev#a9f5f1f7725609c71404539befca1f1a98095cd2"
dependencies = [
"cocoa 0.25.0",
"objc",
"raw-window-handle",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "window-vibrancy" name = "window-vibrancy"
version = "0.4.0" version = "0.4.0"

View File

@@ -1,22 +1,22 @@
[package] [package]
name = "lume" name = "lume"
version = "1.2.2" version = "1.2.4"
description = "the communication app" description = "the communication app"
authors = ["Ren Amamiya"] authors = ["Ren Amamiya"]
license = "GPL-3.0" license = "GPL-3.0"
repository = "https://github.com/luminous-devs/lume" repository = "https://github.com/luminous-devs/lume"
edition = "2021" edition = "2021"
rust-version = "1.71" rust-version = "1.66"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies] [build-dependencies]
tauri-build = { version = "1.4.0", features = [] } tauri-build = { version = "1.4", features = [] }
[dependencies] [dependencies]
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.4.0", features = [ tauri = { version = "1.4", features = [
"fs-read-dir", "fs-read-dir",
"fs-read-file", "fs-read-file",
"window-start-dragging", "window-start-dragging",
@@ -34,7 +34,6 @@ tauri = { version = "1.4.0", features = [
"fs-remove-file", "fs-remove-file",
"window-center", "window-center",
"dialog-all", "dialog-all",
"macos-private-api",
"http-multipart", "http-multipart",
] } ] }
tauri-plugin-sql = { git = "hhttps://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [ tauri-plugin-sql = { git = "hhttps://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [
@@ -46,7 +45,6 @@ tauri-plugin-stronghold = { git = "https://github.com/tauri-apps/plugins-workspa
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
window-vibrancy = { git = "https://github.com/tauri-apps/window-vibrancy", branch = "dev" } window-vibrancy = { git = "https://github.com/tauri-apps/window-vibrancy", branch = "dev" }
window-shadows = { git = "https://github.com/tauri-apps/window-shadows", branch = "dev" }
sqlx-cli = { version = "0.7.0", default-features = false, features = [ sqlx-cli = { version = "0.7.0", default-features = false, features = [
"sqlite", "sqlite",
] } ] }
@@ -54,8 +52,11 @@ rust-argon2 = "1.0"
webpage = { version = "1.6.0", features = ["serde"] } webpage = { version = "1.6.0", features = ["serde"] }
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem # by default Tauri runs in production mode
# DO NOT REMOVE!! # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = ["custom-protocol"]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = ["tauri/custom-protocol"] custom-protocol = ["tauri/custom-protocol"]
# Optimized for bundle size. If you want faster builds comment out/delete this section. # Optimized for bundle size. If you want faster builds comment out/delete this section.

View File

@@ -6,7 +6,6 @@
use tauri::Manager; use tauri::Manager;
use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_sql::{Migration, MigrationKind}; use tauri_plugin_sql::{Migration, MigrationKind};
use window_shadows::set_shadow;
use webpage::{Webpage, WebpageOptions}; use webpage::{Webpage, WebpageOptions};
use std::time::Duration; use std::time::Duration;
@@ -103,9 +102,6 @@ fn main() {
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None) apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None)
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS"); .expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
// set native shadow
set_shadow(&window, true).expect("Unsupported platform!");
Ok(()) Ok(())
}) })
.plugin( .plugin(

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": { "build": {
"beforeBuildCommand": "pnpm build", "beforeBuildCommand": "pnpm build",
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
@@ -8,7 +9,7 @@
}, },
"package": { "package": {
"productName": "Lume", "productName": "Lume",
"version": "1.2.2" "version": "1.2.4"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {
@@ -124,6 +125,7 @@
"csp": { "csp": {
"content-security-policy": "upgrade-insecure-requests" "content-security-policy": "upgrade-insecure-requests"
} }
} },
"macOSPrivateApi": true
} }
} }

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": { "tauri": {
"windows": [ "windows": [
{ {

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": { "tauri": {
"macOSPrivateApi": true,
"windows": [ "windows": [
{ {
"width": 400, "width": 400,

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": { "tauri": {
"windows": [ "windows": [
{ {

View File

@@ -1,3 +1,4 @@
import { message } from '@tauri-apps/api/dialog';
import { RouterProvider, createBrowserRouter, redirect } from 'react-router-dom'; import { RouterProvider, createBrowserRouter, redirect } from 'react-router-dom';
import { AuthCreateScreen } from '@app/auth/create'; import { AuthCreateScreen } from '@app/auth/create';
@@ -19,18 +20,20 @@ import './index.css';
async function Loader() { async function Loader() {
try { try {
const account = await checkActiveAccount(); const account = await checkActiveAccount();
const stronghold = sessionStorage.getItem('stronghold'); const stronghold = sessionStorage.getItem('stronghold');
const privkey = JSON.parse(stronghold).state.privkey || null; const privkey = JSON.parse(stronghold).state.privkey || null;
const onboarding = localStorage.getItem('onboarding'); const onboarding = localStorage.getItem('onboarding');
const step = JSON.parse(onboarding).state.step || null; const step = JSON.parse(onboarding).state.step || null;
if (step) {
return redirect(step);
}
if (!account) { if (!account) {
return redirect('/auth/welcome'); return redirect('/auth/welcome');
} else { } else {
if (step) {
return redirect(step);
}
if (!privkey) { if (!privkey) {
return redirect('/auth/unlock'); return redirect('/auth/unlock');
} }
@@ -38,7 +41,7 @@ async function Loader() {
return null; return null;
} catch (e) { } catch (e) {
throw new Error('App failed to load'); await message(e, { title: 'An unexpected error has occurred', type: 'error' });
} }
} }

View File

@@ -67,7 +67,7 @@ export function CreateStep1Screen() {
useEffect(() => { useEffect(() => {
// save current step, if user close app and reopen it // save current step, if user close app and reopen it
setStep('/auth/create/step-1'); setStep('/auth/create');
}, []); }, []);
return ( return (

View File

@@ -63,7 +63,7 @@ export function CreateStep2Screen() {
const dir = await appConfigDir(); const dir = await appConfigDir();
const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, data.password); const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, data.password);
db.secureDB = stronghold; if (!db.secureDB) db.secureDB = stronghold;
// save privkey to secure storage // save privkey to secure storage
await db.secureSave(pubkey, privkey); await db.secureSave(pubkey, privkey);

View File

@@ -79,7 +79,7 @@ export function ImportStep1Screen() {
useEffect(() => { useEffect(() => {
// save current step, if user close app and reopen it // save current step, if user close app and reopen it
setStep('/auth/import/step-1'); setStep('/auth/import');
}, []); }, []);
return ( return (

View File

@@ -63,7 +63,7 @@ export function ImportStep2Screen() {
const dir = await appConfigDir(); const dir = await appConfigDir();
const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, data.password); const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, data.password);
db.secureDB = stronghold; if (!db.secureDB) db.secureDB = stronghold;
// save privkey to secure storage // save privkey to secure storage
await db.secureSave(pubkey, privkey); await db.secureSave(pubkey, privkey);

View File

@@ -1,3 +1,4 @@
import { message } from '@tauri-apps/api/dialog';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
@@ -52,12 +53,12 @@ export function OnboardStep2Screen() {
setLoading(true); setLoading(true);
for (const tag of tags) { for (const tag of tags) {
await db.createWidget(WidgetKinds.hashtag, tag, tag.replace('#', '')); await db.createWidget(WidgetKinds.global.hashtag, tag, tag.replace('#', ''));
} }
navigate('/auth/onboarding/step-3', { replace: true }); navigate('/auth/onboarding/step-3', { replace: true });
} catch { } catch (e) {
console.log('error'); await message(e, { title: 'Error', type: 'error' });
} }
}; };

View File

@@ -49,6 +49,8 @@ export function OnboardStep3Screen() {
} }
); );
const relaysAsArray = Array.from(data?.keys() || []);
const toggleRelay = (relay: string) => { const toggleRelay = (relay: string) => {
if (relays.has(relay)) { if (relays.has(relay)) {
setRelays((prev) => { setRelays((prev) => {
@@ -61,8 +63,9 @@ export function OnboardStep3Screen() {
}; };
const submit = async (skip?: boolean) => { const submit = async (skip?: boolean) => {
setLoading(true);
try { try {
setLoading(true);
if (!skip) { if (!skip) {
for (const relay of relays) { for (const relay of relays) {
await db.createRelay(relay); await db.createRelay(relay);
@@ -87,8 +90,6 @@ export function OnboardStep3Screen() {
} }
}; };
const relaysAsArray = Array.from(data?.keys() || []);
useEffect(() => { useEffect(() => {
// save current step, if user close app and reopen it // save current step, if user close app and reopen it
setStep('/auth/onboarding/step-3'); setStep('/auth/onboarding/step-3');
@@ -118,10 +119,11 @@ export function OnboardStep3Screen() {
<LoaderIcon className="h-4 w-4 animate-spin text-white" /> <LoaderIcon className="h-4 w-4 animate-spin text-white" />
</div> </div>
) : relaysAsArray.length === 0 ? ( ) : relaysAsArray.length === 0 ? (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center px-6">
<p className="text-center text-white/50"> <p className="text-center text-white/50">
Can&apos;t found any relays, you can skip this step and use default relays Lume couldn&apos;t find any relays from your follows.
instead <br />
You can skip this step and use default relays instead.
</p> </p>
</div> </div>
) : ( ) : (
@@ -145,7 +147,7 @@ export function OnboardStep3Screen() {
)) ))
)} )}
{relays.size > 5 && ( {relays.size > 5 && (
<div className="sticky bottom-0 left-0 inline-flex w-full items-center justify-center bg-white/10 px-4 py-2 backdrop-blur-2xl backdrop-blur-xl"> <div className="sticky bottom-0 left-0 inline-flex w-full items-center justify-center bg-white/10 px-4 py-2 backdrop-blur-2xl">
<p className="text-sm text-orange-400"> <p className="text-sm text-orange-400">
Using too much relay can cause high resource usage Using too much relay can cause high resource usage
</p> </p>
@@ -178,7 +180,7 @@ export function OnboardStep3Screen() {
onClick={() => submit(true)} onClick={() => submit(true)}
className="inline-flex h-11 w-full items-center justify-center rounded-lg px-6 font-medium leading-none text-white backdrop-blur-xl hover:bg-white/10 focus:outline-none" className="inline-flex h-11 w-full items-center justify-center rounded-lg px-6 font-medium leading-none text-white backdrop-blur-xl hover:bg-white/10 focus:outline-none"
> >
Skip, use default relays Skip, use Lume default relays
</button> </button>
</div> </div>
</div> </div>

View File

@@ -1,32 +1,25 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useMemo } from 'react'; import { Link } from 'react-router-dom';
import { NotiContent } from '@app/notifications/components/content';
import { NotiUser } from '@app/notifications/components/user'; import { NotiUser } from '@app/notifications/components/user';
import { formatCreatedAt } from '@utils/createdAt'; import { formatCreatedAt } from '@utils/createdAt';
import { parser } from '@utils/parser';
export function NotiMention({ event }: { event: NDKEvent }) { export function NotiMention({ event }: { event: NDKEvent }) {
const createdAt = formatCreatedAt(event.created_at); const createdAt = formatCreatedAt(event.created_at);
const content = useMemo(() => parser(event), [event]); const rootId = event.tags.find((el) => el[0])?.[1];
return ( return (
<div className="h-min w-full px-3 py-1.5"> <Link to={`/notes/text/${rootId}`} className="h-min w-full px-3">
<div className="relative overflow-hidden rounded-xl bg-white/10 px-3 pt-3 backdrop-blur-xl"> <div className="group flex items-center justify-between rounded-xl px-3 py-3 hover:bg-white/10">
<div className="flex items-start justify-between"> <div className="flex items-center gap-2">
<div className="flex items-start gap-1">
<NotiUser pubkey={event.pubkey} /> <NotiUser pubkey={event.pubkey} />
<p className="leading-none text-white/50">has reply you post · {createdAt}</p> <p className="leading-none text-white/50">has mention you · {createdAt}</p>
</div>
</div>
<div className="f- relative z-10 -mt-6 flex gap-3">
<div className="h-11 w-11 shrink-0" />
<div className="mb-2 mt-3 w-full cursor-default rounded-lg bg-white/10 px-3 py-3 backdrop-blur-xl">
<NotiContent content={content} />
</div>
</div>
</div> </div>
<span className="hidden text-sm font-semibold text-fuchsia-500 group-hover:block">
View
</span>
</div> </div>
</Link>
); );
} }

View File

@@ -1,30 +1,27 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { Link } from 'react-router-dom';
import { SimpleNote } from '@app/notifications/components/simpleNote';
import { NotiUser } from '@app/notifications/components/user'; import { NotiUser } from '@app/notifications/components/user';
import { formatCreatedAt } from '@utils/createdAt'; import { formatCreatedAt } from '@utils/createdAt';
export function NotiReaction({ event }: { event: NDKEvent }) { export function NotiReaction({ event }: { event: NDKEvent }) {
const root = event.tags.find((e) => e[0] === 'e')?.[1];
const createdAt = formatCreatedAt(event.created_at); const createdAt = formatCreatedAt(event.created_at);
const rootId = event.tags.find((el) => el[0])?.[1];
return ( return (
<div className="h-min w-full px-3 py-1.5"> <Link to={`/notes/text/${rootId}`} className="h-min w-full px-3">
<div className="relative overflow-hidden rounded-xl bg-white/10 px-3 pt-3 backdrop-blur-xl"> <div className="group flex items-center justify-between rounded-xl px-3 py-3 hover:bg-white/10">
<div className="flex items-start justify-between"> <div className="flex items-center gap-2">
<div className="flex items-start gap-1">
<NotiUser pubkey={event.pubkey} /> <NotiUser pubkey={event.pubkey} />
<p className="leading-none text-white/50"> <p className="leading-none text-white/50">
reacted {event.content} · {createdAt} reacted {event.content} · {createdAt}
</p> </p>
</div> </div>
<span className="hidden text-sm font-semibold text-fuchsia-500 group-hover:block">
View
</span>
</div> </div>
<div className="relative z-10 -mt-6 flex gap-3"> </Link>
<div className="h-11 w-11 shrink-0" />
<div className="flex-1">{root && <SimpleNote id={root} />}</div>
</div>
</div>
</div>
); );
} }

View File

@@ -1,6 +1,6 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { Link } from 'react-router-dom';
import { SimpleNote } from '@app/notifications/components/simpleNote';
import { NotiUser } from '@app/notifications/components/user'; import { NotiUser } from '@app/notifications/components/user';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
@@ -10,29 +10,24 @@ import { formatCreatedAt } from '@utils/createdAt';
export function NotiRepost({ event }: { event: NDKEvent }) { export function NotiRepost({ event }: { event: NDKEvent }) {
const { db } = useStorage(); const { db } = useStorage();
const root = event.tags.find((e) => e[0] === 'e')?.[1];
const createdAt = formatCreatedAt(event.created_at); const createdAt = formatCreatedAt(event.created_at);
const rootId = event.tags.find((el) => el[0])?.[1];
return ( return (
<div className="h-min w-full px-3 py-1.5"> <Link to={`/notes/text/${rootId}`} className="h-min w-full px-3">
<div className="relative overflow-hidden rounded-xl bg-white/10 px-3 pt-3 backdrop-blur-xl"> <div className="group flex items-center justify-between rounded-xl px-3 py-3 hover:bg-white/10">
<div className="flex items-start justify-between"> <div className="flex items-center gap-2">
<div className="flex items-start gap-1">
<NotiUser pubkey={event.pubkey} /> <NotiUser pubkey={event.pubkey} />
<p className="leading-none text-white/50"> <p className="leading-none text-white/50">
repost{' '} repost{' '}
{event.pubkey !== db.account.pubkey {event.pubkey !== db.account.pubkey ? 'a post that mention you' : 'your post'}{' '}
? 'a post that mention you'
: 'your post'}{' '}
· {createdAt} · {createdAt}
</p> </p>
</div> </div>
<span className="hidden text-sm font-semibold text-fuchsia-500 group-hover:block">
View
</span>
</div> </div>
<div className="relative z-10 -mt-6 flex gap-3"> </Link>
<div className="h-11 w-11 shrink-0" />
<div className="flex-1">{root && <SimpleNote id={root} />}</div>
</div>
</div>
</div>
); );
} }

View File

@@ -18,13 +18,13 @@ export function NotiUser({ pubkey }: { pubkey: string }) {
} }
return ( return (
<div className="flex shrink-0 items-start justify-start gap-3"> <div className="flex shrink-0 items-center justify-start gap-2">
<Image <Image
src={user?.picture || user?.image} src={user?.picture || user?.image}
alt={pubkey} alt={pubkey}
className="h-11 w-11 shrink-0 rounded-lg object-cover" className="h-8 w-8 shrink-0 rounded-md object-cover"
/> />
<span className="max-w-[10rem] flex-1 truncate font-medium leading-none text-white"> <span className="max-w-[10rem] truncate font-medium leading-none text-white">
{user?.name || user?.display_name || displayNpub(pubkey, 16)} {user?.name || user?.display_name || displayNpub(pubkey, 16)}
</span> </span>
</div> </div>

View File

@@ -1,28 +1,24 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'; import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query'; import { useCallback, useEffect } from 'react';
import { useCallback } from 'react';
import { NotiMention } from '@app/notifications/components/mention'; import { NotiMention } from '@app/notifications/components/mention';
import { NotiReaction } from '@app/notifications/components/reaction'; import { NotiReaction } from '@app/notifications/components/reaction';
import { NotiRepost } from '@app/notifications/components/repost'; import { NotiRepost } from '@app/notifications/components/repost';
import { useStorage } from '@libs/storage/provider';
import { LoaderIcon } from '@shared/icons'; import { LoaderIcon } from '@shared/icons';
import { TitleBar } from '@shared/titleBar'; import { TitleBar } from '@shared/titleBar';
import { useActivities } from '@stores/activities';
import { useNostr } from '@utils/hooks/useNostr'; import { useNostr } from '@utils/hooks/useNostr';
export function NotificationScreen() { export function NotificationScreen() {
const { db } = useStorage();
const { fetchActivities } = useNostr(); const { fetchActivities } = useNostr();
const { status, data } = useQuery( const [activities, setActivities, clearTotalNewActivities] = useActivities((state) => [
['notifications', db.account.pubkey], state.activities,
async () => { state.setActivities,
return await fetchActivities(); state.clearTotalNewActivities,
}, ]);
{ refetchOnWindowFocus: false }
);
const renderItem = useCallback( const renderItem = useCallback(
(event: NDKEvent) => { (event: NDKEvent) => {
@@ -37,24 +33,34 @@ export function NotificationScreen() {
return null; return null;
} }
}, },
[data] [activities]
); );
useEffect(() => {
async function getActivities() {
const events = await fetchActivities();
setActivities(events);
// clear total new activities
clearTotalNewActivities();
}
getActivities();
}, []);
return ( return (
<div className="scrollbar-hide h-full w-full overflow-y-auto bg-white/10 backdrop-blur-xl"> <div className="scrollbar-hide h-full w-full overflow-y-auto bg-white/10 backdrop-blur-xl">
<div className="grid h-full grid-cols-3"> <div className="grid h-full grid-cols-3">
<div className="col-span-2 flex flex-col border-r border-white/5"> <div className="col-span-2 flex flex-col border-r border-white/5">
<TitleBar title="Activities in the last 24 hours" /> <TitleBar title="Activities in the last 24 hours" />
<div className="flex h-full flex-col gap-1.5">
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
{status === 'loading' ? ( {!activities ? (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<div className="flex flex-col items-center gap-1.5"> <div className="flex flex-col items-center gap-1.5">
<LoaderIcon className="h-5 w-5 animate-spin text-white" /> <LoaderIcon className="h-5 w-5 animate-spin text-white" />
<p className="text-sm font-medium text-white/50">Loading</p> <p className="text-sm font-medium text-white/50">Loading</p>
</div> </div>
</div> </div>
) : data?.length < 1 ? ( ) : activities.length < 1 ? (
<div className="flex h-full w-full flex-col items-center justify-center"> <div className="flex h-full w-full flex-col items-center justify-center">
<p className="mb-1 text-4xl">🎉</p> <p className="mb-1 text-4xl">🎉</p>
<p className="font-medium text-white/50"> <p className="font-medium text-white/50">
@@ -62,12 +68,11 @@ export function NotificationScreen() {
</p> </p>
</div> </div>
) : ( ) : (
data.map((event) => renderItem(event)) activities.map((event) => renderItem(event))
)} )}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
); );
} }

View File

@@ -2,9 +2,17 @@ import { useCallback } from 'react';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import {
ArticleIcon,
FileIcon,
FollowsIcon,
GroupFeedsIcon,
HashtagIcon,
TrendingIcon,
} from '@shared/icons';
import { TitleBar } from '@shared/titleBar'; import { TitleBar } from '@shared/titleBar';
import { DefaultWidgets, useWidgets } from '@stores/widgets'; import { DefaultWidgets, WidgetKinds, useWidgets } from '@stores/widgets';
import { Widget, WidgetGroup, WidgetGroupItem } from '@utils/types'; import { Widget, WidgetGroup, WidgetGroupItem } from '@utils/types';
@@ -20,16 +28,61 @@ export function WidgetList({ params }: { params: Widget }) {
removeWidget(db, params.id); removeWidget(db, params.id);
}; };
const renderIcon = useCallback(
(kind: number) => {
switch (kind) {
case WidgetKinds.tmp.xfeed:
return <GroupFeedsIcon className="h-5 w-5 text-white" />;
case WidgetKinds.local.follows:
return <FollowsIcon className="h-5 w-5 text-white" />;
case WidgetKinds.local.files:
case WidgetKinds.global.files:
return <FileIcon className="h-5 w-5 text-white" />;
case WidgetKinds.local.articles:
case WidgetKinds.global.articles:
return <ArticleIcon className="h-5 w-5 text-white" />;
case WidgetKinds.tmp.xhashtag:
return <HashtagIcon className="h-5 w-4 text-white" />;
case WidgetKinds.nostrBand.trendingAccounts:
case WidgetKinds.nostrBand.trendingNotes:
return <TrendingIcon className="h-5 w-4 text-white" />;
default:
return '';
}
},
[DefaultWidgets]
);
const renderItem = useCallback( const renderItem = useCallback(
(row: WidgetGroup) => { (row: WidgetGroup) => {
return ( return (
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<h3 className="font-medium text-white/50">{row.title}</h3> <h3 className="font-medium text-white/50">{row.title}</h3>
<div className="grid grid-cols-3 gap-6"> <div className="flex flex-col divide-y divide-white/5 overflow-hidden rounded-xl bg-white/10">
{row.data.map((item, index) => ( {row.data.map((item, index) => (
<button onClick={() => openWidget(item)} key={index}> <button
<div className="inline-flex aspect-square h-full w-full transform-gpu flex-col items-center justify-center gap-2.5 rounded-2xl bg-white/5 hover:bg-white/10"> onClick={() => openWidget(item)}
<h5 className="text-sm font-medium">{item.title}</h5> key={index}
className="flex items-center gap-2.5 px-4 hover:bg-white/10"
>
{item.icon ? (
<div className="h-10 w-10 shrink-0 rounded-md">
<img
src={item.icon}
alt={item.title}
className="h-10 w-10 object-cover"
/>
</div>
) : (
<div className="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-white/10">
{renderIcon(item.kind)}
</div>
)}
<div className="inline-flex h-16 w-full flex-col items-start justify-center gap-1">
<h5 className="line-clamp-1 font-medium leading-none">{item.title}</h5>
<p className="line-clamp-1 text-xs leading-none text-white/50">
{item.description}
</p>
</div> </div>
</button> </button>
))} ))}
@@ -41,11 +94,27 @@ export function WidgetList({ params }: { params: Widget }) {
); );
return ( return (
<div className="relative h-full shrink-0 grow-0 basis-[400px] overflow-hidden"> <div className="relative h-full shrink-0 grow-0 basis-[400px] overflow-hidden bg-white/10">
<TitleBar id={params.id} title="Add widget" /> <TitleBar id={params.id} title="Add widget" />
<div className="flex flex-col gap-8 px-3"> <div className="flex flex-col gap-6 px-3">
{DefaultWidgets.map((row: WidgetGroup) => renderItem(row))} {DefaultWidgets.map((row: WidgetGroup) => renderItem(row))}
</div> </div>
<div className="mt-6 px-3">
<div className="border-t border-white/5 pt-6">
<button
type="button"
disabled
className="inline-flex h-14 w-full items-center justify-center gap-2.5 rounded-xl bg-white/5 text-sm font-medium text-white/50"
>
Build your own widget{' '}
<div className="-rotate-3 transform rounded-md border border-white/20 bg-white/10 px-1.5 py-1">
<span className="bg-gradient-to-t from-fuchsia-200 via-red-200 to-orange-300 bg-clip-text text-xs text-transparent">
Coming soon
</span>
</div>
</button>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -21,6 +21,7 @@ import {
XfeedsWidget, XfeedsWidget,
XhashtagWidget, XhashtagWidget,
} from '@shared/widgets'; } from '@shared/widgets';
import { LocalFollowsWidget } from '@shared/widgets/local/follows';
import { WidgetKinds, useWidgets } from '@stores/widgets'; import { WidgetKinds, useWidgets } from '@stores/widgets';
@@ -40,6 +41,8 @@ export function SpaceScreen() {
switch (widget.kind) { switch (widget.kind) {
case WidgetKinds.local.network: case WidgetKinds.local.network:
return <LocalNetworkWidget key={widget.id} />; return <LocalNetworkWidget key={widget.id} />;
case WidgetKinds.local.follows:
return <LocalFollowsWidget key={widget.id} params={widget} />;
case WidgetKinds.local.feeds: case WidgetKinds.local.feeds:
return <LocalFeedsWidget key={widget.id} params={widget} />; return <LocalFeedsWidget key={widget.id} params={widget} />;
case WidgetKinds.local.files: case WidgetKinds.local.files:

View File

@@ -15,46 +15,51 @@ export function SplashScreen() {
const { fetchUserData, prefetchEvents } = useNostr(); const { fetchUserData, prefetchEvents } = useNostr();
const [isLoading, setIsLoading] = useState<boolean>(true); const [isLoading, setIsLoading] = useState<boolean>(true);
const [errorMessage, setErrorMessage] = useState<null | string>(null);
const skip = async () => { const skip = async () => {
await invoke('close_splashscreen'); await invoke('close_splashscreen');
}; };
const prefetch = async () => { const prefetch = async () => {
const onboarding = localStorage.getItem('onboarding');
const step = JSON.parse(onboarding).state.step || null;
if (step) await invoke('close_splashscreen');
try { try {
const user = await fetchUserData(); const user = await fetchUserData();
const data = await prefetchEvents(); const data = await prefetchEvents();
if (user.status === 'ok' && data.status === 'ok') { if (user.status === 'ok' && data.status === 'ok') {
// update last login = current time
await db.updateLastLogin(); await db.updateLastLogin();
// close splash screen and open main app screen
await invoke('close_splashscreen'); await invoke('close_splashscreen');
} else {
setIsLoading(false);
setErrorMessage(user.message);
console.log('fetch failed, error: ', user.message);
} }
} catch (e) { } catch (e) {
setIsLoading(false); setIsLoading(false);
setErrorMessage(e); await message(e, {
await message(`Something wrong: ${e}`, { title: 'An unexpected error has occurred',
title: 'Lume',
type: 'error', type: 'error',
}); });
} }
}; };
useEffect(() => { useEffect(() => {
if (ndk) { async function initial() {
if (!db.account) invoke('close_splashscreen'); if (!db.account) {
await invoke('close_splashscreen');
} else {
const onboarding = localStorage.getItem('onboarding');
const step = JSON.parse(onboarding).state.step || null;
if (step) {
await invoke('close_splashscreen');
} else {
console.log('prefetching...'); console.log('prefetching...');
prefetch(); prefetch();
} }
}
}
if (ndk) {
initial();
}
}, [ndk, db.account]); }, [ndk, db.account]);
return ( return (
@@ -75,15 +80,14 @@ export function SplashScreen() {
) : ( ) : (
<div className="mt-2 flex flex-col gap-1 text-center"> <div className="mt-2 flex flex-col gap-1 text-center">
<h3 className="text-lg font-semibold leading-none text-white"> <h3 className="text-lg font-semibold leading-none text-white">
Something wrong! An unexpected error has occurred
</h3> </h3>
<p className="text-sm text-white/50">{errorMessage}</p>
<button <button
type="button" type="button"
onClick={skip} onClick={skip}
className="mx-auto mt-4 inline-flex h-10 w-max items-center justify-center rounded-md bg-white/10 px-8 text-sm font-medium leading-none text-white backdrop-blur-xl hover:bg-white/20" className="mx-auto mt-4 inline-flex h-10 w-max items-center justify-center rounded-md bg-white/10 px-8 text-sm font-medium leading-none text-white backdrop-blur-xl hover:bg-white/20"
> >
Skip Skip this step
</button> </button>
</div> </div>
)} )}

View File

@@ -17,40 +17,32 @@ export const NDKInstance = () => {
const cacheAdapter = useMemo(() => new TauriAdapter(), [ndk]); const cacheAdapter = useMemo(() => new TauriAdapter(), [ndk]);
// TODO: fully support NIP-11 // TODO: fully support NIP-11
async function verifyRelays(relays: string[]) { async function getExplicitRelays() {
try { try {
const urls: string[] = relays.map((relay) => {
if (relay.startsWith('ws')) {
return relay.replace('ws', 'http');
}
if (relay.startsWith('wss')) {
return relay.replace('wss', 'https');
}
});
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort('timeout'), 10000); const timeoutId = setTimeout(() => controller.abort('timeout'), 10000);
const requests = urls.map((url) => // get relays
fetch(url, { const relays = (await db.getExplicitRelayUrls()) ?? FULL_RELAYS;
const requests = relays.map((relay) => {
const url = new URL(relay);
return fetch(`https://${url.hostname + url.pathname}`, {
headers: { Accept: 'application/nostr+json' }, headers: { Accept: 'application/nostr+json' },
signal: controller.signal, signal: controller.signal,
}) });
); });
const responses = await Promise.all(requests); const responses = await Promise.all(requests);
const errors = responses.filter((response) => !response.ok); const errors = responses.filter((response) => !response.ok);
if (errors.length > 0) { if (errors.length > 0) throw errors.map((response) => Error(response.statusText));
throw errors.map((response) => Error(response.statusText));
}
const verifiedRelays: string[] = responses.map((res) => { const verifiedRelays: string[] = responses.map((res) => {
if (res.url.startsWith('http')) { const url = new URL(res.url);
return res.url.replace('htto', 'ws'); if (url.protocol === 'http:') return `ws://${url.hostname + url.pathname}`;
} if (url.protocol === 'https:') return `wss://${url.hostname + url.pathname}`;
if (res.url.startsWith('https')) {
return res.url.replace('https', 'wss');
}
}); });
// clear timeout // clear timeout
@@ -59,31 +51,19 @@ export const NDKInstance = () => {
// return all validate relays // return all validate relays
return verifiedRelays; return verifiedRelays;
} catch (e) { } catch (e) {
console.error('verify relay failed with error: ', e); await message(e, { title: 'Cannot connect to relays', type: 'error' });
} }
} }
async function initNDK() { async function initNDK() {
let explicitRelayUrls: string[]; const explicitRelayUrls = await getExplicitRelays();
const explicitRelayUrlsFromDB = await db.getExplicitRelayUrls(); const instance = new NDK({
explicitRelayUrls,
console.log('relays in db: ', explicitRelayUrlsFromDB); cacheAdapter,
console.log('ndk cache adapter: ', cacheAdapter); // outboxRelayUrls: ['wss://purplepag.es'],
// enableOutboxModel: true,
if (explicitRelayUrlsFromDB) {
explicitRelayUrls = await verifyRelays(explicitRelayUrlsFromDB);
} else {
explicitRelayUrls = await verifyRelays(FULL_RELAYS);
}
if (explicitRelayUrls.length < 1) {
await message('Something is wrong. No relays have been found.', {
title: 'Lume',
type: 'error',
}); });
}
const instance = new NDK({ explicitRelayUrls, cacheAdapter });
try { try {
await instance.connect(10000); await instance.connect(10000);
} catch (error) { } catch (error) {

View File

@@ -291,6 +291,15 @@ export class LumeStorage {
); );
} }
public async accountLogout() {
await this.db.execute("UPDATE accounts SET is_active = '0' WHERE id = $1;", [
this.account.id,
]);
this.account = null;
return true;
}
public async close() { public async close() {
return this.db.close(); return this.db.close();
} }

View File

@@ -6,6 +6,8 @@ import { useStorage } from '@libs/storage/provider';
import { AccountMoreActions } from '@shared/accounts/more'; import { AccountMoreActions } from '@shared/accounts/more';
import { Image } from '@shared/image'; import { Image } from '@shared/image';
import { useActivities } from '@stores/activities';
import { useNostr } from '@utils/hooks/useNostr'; import { useNostr } from '@utils/hooks/useNostr';
import { useProfile } from '@utils/hooks/useProfile'; import { useProfile } from '@utils/hooks/useProfile';
import { sendNativeNotification } from '@utils/notification'; import { sendNativeNotification } from '@utils/notification';
@@ -16,6 +18,8 @@ export function ActiveAccount() {
const { status, user } = useProfile(db.account.pubkey); const { status, user } = useProfile(db.account.pubkey);
const { sub } = useNostr(); const { sub } = useNostr();
const addActivity = useActivities((state) => state.addActivity);
useEffect(() => { useEffect(() => {
const filter: NDKFilter = { const filter: NDKFilter = {
'#p': [db.account.pubkey], '#p': [db.account.pubkey],
@@ -30,6 +34,8 @@ export function ActiveAccount() {
}; };
sub(filter, async (event) => { sub(filter, async (event) => {
addActivity(event);
switch (event.kind) { switch (event.kind) {
case NDKKind.Text: case NDKKind.Text:
return await sendNativeNotification('Mention'); return await sendNativeNotification('Mention');

View File

@@ -3,6 +3,7 @@ import { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { HorizontalDotsIcon } from '@shared/icons'; import { HorizontalDotsIcon } from '@shared/icons';
import { Logout } from '@shared/logout';
export function AccountMoreActions({ pubkey }: { pubkey: string }) { export function AccountMoreActions({ pubkey }: { pubkey: string }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -44,9 +45,7 @@ export function AccountMoreActions({ pubkey }: { pubkey: string }) {
</Link> </Link>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Item asChild> <DropdownMenu.Item asChild>
<button className="inline-flex h-10 items-center rounded-lg px-2 text-sm font-medium text-white hover:bg-white/10"> <Logout />
Logout
</button>
</DropdownMenu.Item> </DropdownMenu.Item>
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Portal> </DropdownMenu.Portal>

View File

@@ -125,7 +125,7 @@ export function Composer() {
autoCapitalize="off" autoCapitalize="off"
className={twMerge( className={twMerge(
'scrollbar-hide markdown max-h-[500px] overflow-y-auto break-all pr-2 outline-none', 'scrollbar-hide markdown max-h-[500px] overflow-y-auto break-all pr-2 outline-none',
expand ? 'min-h-[500px]' : 'min-h-[120px]' expand ? 'min-h-[500px]' : reply.id ? 'min-h-min' : 'min-h-[120px]'
)} )}
/> />
{reply.id && ( {reply.id && (

View File

@@ -0,0 +1,22 @@
import { SVGProps } from 'react';
export function ArticleIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M16.25 12V4.75a1 1 0 00-1-1H3.75a1 1 0 00-1 1v13a2.5 2.5 0 002.5 2.5H18.5M16.25 12v5.75a2.5 2.5 0 005 0V13a1 1 0 00-1-1h-4zm-9.5 3.75h5.5m-5.5-8h5.5v4.5h-5.5v-4.5z"
></path>
</svg>
);
}

View File

@@ -13,10 +13,9 @@ export function FileIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement
<path <path
stroke="currentColor" stroke="currentColor"
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5" strokeWidth="1.5"
d="M8.75 6.75h6.5m-6.5 4h6.5m-6.5 4h2.5m-5.5 6.5h12.5a1 1 0 001-1V3.75a1 1 0 00-1-1H5.75a1 1 0 00-1 1v16.5a1 1 0 001 1z" d="M12.75 3.25v5a1 1 0 001 1h5m-10 4h3.5m-3.5 4h6.5m-9.5-14.5h6.586a1 1 0 01.707.293l5.914 5.914a1 1 0 01.293.707V20.25a1 1 0 01-1 1H5.75a1 1 0 01-1-1V3.75a1 1 0 011-1z"
/> ></path>
</svg> </svg>
); );
} }

View File

@@ -0,0 +1,22 @@
import { SVGProps } from 'react';
export function FollowsIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M17.75 19.25h3.673c.581 0 1.045-.496.947-1.07-.531-3.118-2.351-5.43-5.37-5.43-.446 0-.866.05-1.26.147M11.25 7a3.25 3.25 0 11-6.5 0 3.25 3.25 0 016.5 0zm8.5.5a2.75 2.75 0 11-5.5 0 2.75 2.75 0 015.5 0zM1.87 19.18c.568-3.68 2.647-6.43 6.13-6.43 3.482 0 5.561 2.75 6.13 6.43.088.575-.375 1.07-.956 1.07H2.825c-.58 0-1.043-.495-.955-1.07z"
></path>
</svg>
);
}

View File

@@ -0,0 +1,22 @@
import { SVGProps } from 'react';
export function GroupFeedsIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
{...props}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M14.425 13.18c3.361-1.396 7.598.605 8.454 6.003.09.573-.372 1.067-.952 1.067H16.75M10.75 7a3.25 3.25 0 11-6.5 0 3.25 3.25 0 016.5 0zm9 0a3.25 3.25 0 11-6.5 0 3.25 3.25 0 016.5 0zm-6.966 13.25H2.072c-.58 0-1.042-.497-.951-1.07 1.362-8.573 11.252-8.573 12.614 0 .091.573-.371 1.07-.951 1.07z"
></path>
</svg>
);
}

View File

@@ -16,7 +16,7 @@ export function HashtagIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElem
strokeLinejoin="round" strokeLinejoin="round"
strokeWidth="1.5" strokeWidth="1.5"
d="M8.75 3.75l-2 16.5m10.5-16.5l-2 16.5M3.75 7.75h16.5m0 8.5H3.75" d="M8.75 3.75l-2 16.5m10.5-16.5l-2 16.5M3.75 7.75h16.5m0 8.5H3.75"
/> ></path>
</svg> </svg>
); );
} }

View File

@@ -58,3 +58,6 @@ export * from './chevronUp';
export * from './secure'; export * from './secure';
export * from './verified'; export * from './verified';
export * from './mention'; export * from './mention';
export * from './groupFeeds';
export * from './article';
export * from './follows';

View File

@@ -1,75 +1,65 @@
import * as Dialog from '@radix-ui/react-dialog'; import * as AlertDialog from '@radix-ui/react-alert-dialog';
import { useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom';
import { CancelIcon, LogoutIcon } from '@shared/icons'; import { useStorage } from '@libs/storage/provider';
import { useStronghold } from '@stores/stronghold';
export function Logout() { export function Logout() {
const queryClient = useQueryClient(); const { db } = useStorage();
const navigate = useNavigate();
const clearPrivkey = useStronghold((state) => state.clearPrivkey);
const logout = async () => { const logout = async () => {
// reset database // remove account
// await removeAll(); db.accountLogout();
// reset react query // clear privkey in session storage
queryClient.clear(); clearPrivkey();
// redirect to welcome screen
navigate('/auth/welcome');
}; };
return ( return (
<Dialog.Root> <AlertDialog.Root>
<Dialog.Trigger asChild> <AlertDialog.Trigger asChild>
<button <button
type="button" type="button"
aria-label="Logout" className="inline-flex h-10 items-center rounded-lg px-2 text-sm font-medium text-white hover:bg-white/10"
className="inline-flex h-9 w-9 transform items-center justify-center rounded-md bg-white/20 active:translate-y-1"
> >
<LogoutIcon className="h-4 w-4 text-white" /> Logout
</button> </button>
</Dialog.Trigger> </AlertDialog.Trigger>
<Dialog.Portal className="relative z-10"> <AlertDialog.Portal className="relative z-10">
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" /> <AlertDialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center"> <AlertDialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10 backdrop-blur-xl"> <div className="relative h-min w-full max-w-md rounded-xl bg-white/10 backdrop-blur-xl">
<div className="h-min w-full shrink-0 border-b border-white/10 bg-white/5 px-5 py-6"> <div className="flex flex-col gap-2 border-b border-white/5 px-5 py-4">
<div className="flex flex-col gap-2"> <AlertDialog.Title className="text-lg font-semibold leading-none text-white">
<div className="flex items-center justify-between">
<Dialog.Title className="text-lg font-semibold leading-none text-white">
Are you sure! Are you sure!
</Dialog.Title> </AlertDialog.Title>
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10"> <AlertDialog.Description className="text-sm leading-tight text-white/50">
<CancelIcon className="h-4 w-4 text-white/50" /> You can always log back in at any time. If you just want to switch
</Dialog.Close> accounts, you can do that by adding an existing account.
</AlertDialog.Description>
</div> </div>
<Dialog.Description className="text-sm leading-tight text-white/50"> <div className="flex justify-end gap-2 px-5 py-3">
<p className="mb-2"> <AlertDialog.Cancel asChild>
When logout, all local data will be wiped, and restart app then you <button className="inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium leading-none text-white outline-none hover:bg-white/10 hover:backdrop-blur-xl">
need to start onboarding process again when you log in.
</p>
<p>
In the next version, Lume will support multi account, then you can
switch between all account s instead of logout
</p>
</Dialog.Description>
</div>
</div>
<div className="flex h-full w-full flex-col items-end justify-center overflow-y-auto px-5 py-2.5">
<div className="flex items-center gap-2">
<button
type="button"
className="inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-white/50 backdrop-blur-xl hover:bg-white/10"
>
Cancel Cancel
</button> </button>
</AlertDialog.Cancel>
<button <button
type="button" type="button"
onClick={() => logout()} onClick={() => logout()}
className="inline-flex h-9 items-center justify-center rounded-md bg-red-500 px-3 text-sm font-medium text-white hover:bg-red-600" className="inline-flex h-9 items-center justify-center rounded-md bg-white/10 px-4 text-sm font-medium leading-none text-white outline-none hover:bg-fuchsia-500"
> >
Confirm Logout
</button> </button>
</div> </div>
</div> </div>
</div> </AlertDialog.Content>
</Dialog.Content> </AlertDialog.Portal>
</Dialog.Portal> </AlertDialog.Root>
</Dialog.Root>
); );
} }

View File

@@ -4,19 +4,29 @@ import { twMerge } from 'tailwind-merge';
import { ChatsList } from '@app/chats/components/list'; import { ChatsList } from '@app/chats/components/list';
import { useStorage } from '@libs/storage/provider';
import { ActiveAccount } from '@shared/accounts/active'; import { ActiveAccount } from '@shared/accounts/active';
import { ComposerModal } from '@shared/composer'; import { ComposerModal } from '@shared/composer';
import { Frame } from '@shared/frame'; import { Frame } from '@shared/frame';
import { BellIcon, NavArrowDownIcon, SpaceIcon } from '@shared/icons'; import { BellIcon, NavArrowDownIcon, SpaceIcon } from '@shared/icons';
import { useActivities } from '@stores/activities';
import { useSidebar } from '@stores/sidebar'; import { useSidebar } from '@stores/sidebar';
import { compactNumber } from '@utils/number';
export function Navigation() { export function Navigation() {
const { db } = useStorage();
const [totalNewActivities] = useActivities((state) => [state.totalNewActivities]);
const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]); const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]);
return ( return (
<Frame className="relative flex h-full w-[232px] flex-col" lighter> <Frame className="relative flex h-full w-[232px] flex-col" lighter>
{db.platform === 'darwin' ? (
<div data-tauri-drag-region className="h-11 w-full shrink-0" /> <div data-tauri-drag-region className="h-11 w-full shrink-0" />
) : null}
<div className="scrollbar-hide flex h-full flex-1 flex-col gap-6 overflow-y-auto pb-32"> <div className="scrollbar-hide flex h-full flex-1 flex-col gap-6 overflow-y-auto pb-32">
<div className="flex flex-col pr-2"> <div className="flex flex-col pr-2">
<ComposerModal /> <ComposerModal />
@@ -42,17 +52,26 @@ export function Navigation() {
preventScrollReset={true} preventScrollReset={true}
className={({ isActive }) => className={({ isActive }) =>
twMerge( twMerge(
'flex h-10 items-center gap-2.5 rounded-r-lg border-l-2 pl-4 pr-2', 'flex h-10 items-center justify-between rounded-r-lg border-l-2 pl-4 pr-2',
isActive isActive
? 'border-fuchsia-500 bg-white/5 text-white' ? 'border-fuchsia-500 bg-white/5 text-white'
: 'border-transparent text-white/80' : 'border-transparent text-white/80'
) )
} }
> >
<div className="flex items-center gap-2.5">
<span className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded bg-white/10 backdrop-blur-xl"> <span className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded bg-white/10 backdrop-blur-xl">
<BellIcon className="h-4 w-4 text-white" /> <BellIcon className="h-4 w-4 text-white" />
</span> </span>
Notifications Notifications
</div>
{totalNewActivities > 0 ? (
<div className="inline-flex h-5 w-8 items-center justify-center rounded bg-fuchsia-500">
<span className="text-xs font-medium text-white">
{compactNumber.format(totalNewActivities)}
</span>
</div>
) : null}
</NavLink> </NavLink>
</div> </div>
<Collapsible.Root open={chats} onOpenChange={toggleChats}> <Collapsible.Root open={chats} onOpenChange={toggleChats}>

View File

@@ -46,26 +46,26 @@ export function NoteRepost({ id, pubkey }: { id: string; pubkey: string }) {
<AlertDialog.Portal className="relative z-10"> <AlertDialog.Portal className="relative z-10">
<AlertDialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" /> <AlertDialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
<AlertDialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center"> <AlertDialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10 backdrop-blur-xl"> <div className="relative h-min w-full max-w-md rounded-xl bg-white/10 backdrop-blur-xl">
<div className="flex flex-col gap-2 border-b border-white/5 px-5 py-4"> <div className="flex flex-col gap-2 border-b border-white/5 px-5 py-4">
<AlertDialog.Title className="text-lg font-semibold leading-none text-white"> <AlertDialog.Title className="text-lg font-semibold leading-none text-white">
Confirm repost this post? Confirm repost this post?
</AlertDialog.Title> </AlertDialog.Title>
<AlertDialog.Description className="text-sm leading-none text-white/50"> <AlertDialog.Description className="text-sm leading-tight text-white/50">
Reposted post will be visible to your followers, and you cannot undo this Reposted post will be visible to your followers, and you cannot undo this
action. action.
</AlertDialog.Description> </AlertDialog.Description>
</div> </div>
<div className="flex justify-end gap-6 px-5 py-3"> <div className="flex justify-end gap-2 px-5 py-3">
<AlertDialog.Cancel asChild> <AlertDialog.Cancel asChild>
<button className="inline-flex h-11 items-center justify-center rounded-lg bg-white/10 px-4 font-medium leading-none text-white outline-none backdrop-blur-xl"> <button className="inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium leading-none text-white outline-none hover:bg-white/10 hover:backdrop-blur-xl">
Cancel Cancel
</button> </button>
</AlertDialog.Cancel> </AlertDialog.Cancel>
<button <button
type="button" type="button"
onClick={() => submit()} onClick={() => submit()}
className="inline-flex h-11 items-center justify-center rounded-lg bg-fuchsia-500 px-4 font-medium leading-none text-white outline-none" className="inline-flex h-9 items-center justify-center rounded-md bg-white/10 px-4 text-sm font-medium leading-none text-white outline-none hover:bg-fuchsia-500"
> >
Yes, repost Yes, repost
</button> </button>

View File

@@ -19,7 +19,7 @@ import { useEvent } from '@utils/hooks/useEvent';
export function Repost({ event }: { event: NDKEvent }) { export function Repost({ event }: { event: NDKEvent }) {
// @ts-expect-error, root_id isn't exist on NDKEvent // @ts-expect-error, root_id isn't exist on NDKEvent
const { status, data } = useEvent(event.root_id, event.content); const { status, data } = useEvent(event.root_id ?? event.id, event.content);
const renderKind = useCallback( const renderKind = useCallback(
(repostEvent: NDKEvent) => { (repostEvent: NDKEvent) => {

View File

@@ -42,7 +42,7 @@ export function TextNote({ content }: { content: string }) {
const cleanURL = new URL(href); const cleanURL = new URL(href);
cleanURL.search = ''; cleanURL.search = '';
return ( return (
<Link to={href} target="_blank"> <Link to={href} target="_blank" className="line-clamp-1">
{cleanURL.hostname + cleanURL.pathname} {cleanURL.hostname + cleanURL.pathname}
</Link> </Link>
); );

View File

@@ -58,31 +58,26 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
if (status === 'error') { if (status === 'error') {
const noteLink = `https://nostr.com/${nip19.noteEncode(id)}`; const noteLink = `https://nostr.com/${nip19.noteEncode(id)}`;
return ( return (
<div className="relative mb-5 flex flex-col"> <div className="relative mt-3 flex flex-col">
<div className="relative z-10 flex items-start gap-3"> <div className="relative z-10 flex items-center gap-3">
<div className="inline-flex h-11 w-11 items-end justify-center rounded-lg bg-black pb-1"> <div className="inline-flex h-6 w-6 items-end justify-center rounded bg-black pb-1">
<img src="/lume.png" alt="lume" className="h-auto w-1/3" /> <img src="/lume.png" alt="lume" className="h-auto w-1/3" />
</div> </div>
<h5 className="truncate font-semibold leading-none text-white"> <h5 className="truncate font-semibold leading-none text-white">
Lume <span className="text-green-500">(System)</span> Lume <span className="text-green-500">(System)</span>
</h5> </h5>
</div> </div>
<div className="-mt-6 flex items-start gap-3"> <div className="mt-1">
<div className="w-11 shrink-0" />
<div>
<div className="relative z-20 mt-1 flex-1 select-text">
<div className="mb-1 select-text rounded-lg bg-white/5 p-1.5 text-sm"> <div className="mb-1 select-text rounded-lg bg-white/5 p-1.5 text-sm">
Lume cannot find this post with your current relays, but you can view it Lume cannot find this post with your current relays, but you can view it via
via nostr.com.{' '} nostr.com.{' '}
<Link to={noteLink} className="text-fuchsia-500"> <Link to={noteLink} className="text-fuchsia-500">
Learn more Learn more
</Link> </Link>
</div> </div>
</div>
<LinkPreview urls={[noteLink]} /> <LinkPreview urls={[noteLink]} />
</div> </div>
</div> </div>
</div>
); );
} }

View File

@@ -17,9 +17,9 @@ import {
import { NoteSkeleton } from '@shared/notes/skeleton'; import { NoteSkeleton } from '@shared/notes/skeleton';
import { TitleBar } from '@shared/titleBar'; import { TitleBar } from '@shared/titleBar';
import { DBEvent } from '@utils/types'; import { DBEvent, Widget } from '@utils/types';
export function LocalFollowsWidget() { export function LocalFollowsWidget({ params }: { params: Widget }) {
const { db } = useStorage(); const { db } = useStorage();
const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({ useInfiniteQuery({
@@ -116,7 +116,7 @@ export function LocalFollowsWidget() {
return ( return (
<div className="relative shrink-0 grow-0 basis-[400px] bg-white/10 backdrop-blur-xl"> <div className="relative shrink-0 grow-0 basis-[400px] bg-white/10 backdrop-blur-xl">
<TitleBar title="👋 Network" /> <TitleBar id={params.id} title="Follows" />
<div ref={parentRef} className="scrollbar-hide h-full overflow-y-auto pb-20"> <div ref={parentRef} className="scrollbar-hide h-full overflow-y-auto pb-20">
{status === 'loading' ? ( {status === 'loading' ? (
<div className="px-3 py-1.5"> <div className="px-3 py-1.5">

27
src/stores/activities.ts Normal file
View File

@@ -0,0 +1,27 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { create } from 'zustand';
interface ActivitiesState {
activities: Array<NDKEvent>;
totalNewActivities: number;
setActivities: (events: NDKEvent[]) => void;
addActivity: (event: NDKEvent) => void;
clearTotalNewActivities: () => void;
}
export const useActivities = create<ActivitiesState>((set) => ({
activities: null,
totalNewActivities: 0,
setActivities: (events: NDKEvent[]) => {
set(() => ({ activities: events }));
},
addActivity: (event: NDKEvent) => {
set((state) => ({
activities: state.activities ? [event, ...state.activities] : [event],
totalNewActivities: state.totalNewActivities++,
}));
},
clearTotalNewActivities: () => {
set(() => ({ totalNewActivities: 0 }));
},
}));

View File

@@ -4,6 +4,7 @@ import { createJSONStorage, persist } from 'zustand/middleware';
interface StrongholdState { interface StrongholdState {
privkey: null | string; privkey: null | string;
setPrivkey: (privkey: string) => void; setPrivkey: (privkey: string) => void;
clearPrivkey: () => void;
} }
export const useStronghold = create<StrongholdState>()( export const useStronghold = create<StrongholdState>()(
@@ -13,6 +14,9 @@ export const useStronghold = create<StrongholdState>()(
setPrivkey: (privkey: string) => { setPrivkey: (privkey: string) => {
set({ privkey: privkey }); set({ privkey: privkey });
}, },
clearPrivkey: () => {
set({ privkey: null });
},
}), }),
{ {
name: 'stronghold', name: 'stronghold',

View File

@@ -3,7 +3,7 @@ import { createJSONStorage, persist } from 'zustand/middleware';
import { LumeStorage } from '@libs/storage/instance'; import { LumeStorage } from '@libs/storage/instance';
import { Widget } from '@utils/types'; import { Widget, WidgetGroup } from '@utils/types';
interface WidgetState { interface WidgetState {
widgets: null | Array<Widget>; widgets: null | Array<Widget>;
@@ -39,25 +39,29 @@ export const WidgetKinds = {
}, },
}; };
export const DefaultWidgets = [ export const DefaultWidgets: Array<WidgetGroup> = [
{ {
title: 'Network / Follows', title: 'Network / Follows',
data: [ data: [
{ {
kind: WidgetKinds.tmp.xfeed, kind: WidgetKinds.tmp.xfeed,
title: 'Group feeds', title: 'Group feeds',
description: 'All posts from specific people you want to keep up with',
}, },
{ {
kind: WidgetKinds.local.files, kind: WidgetKinds.local.files,
title: 'Files', title: 'Files',
description: 'All files shared by people in your network',
}, },
{ {
kind: WidgetKinds.local.articles, kind: WidgetKinds.local.articles,
title: 'Articles', title: 'Articles',
description: 'All articles shared by people in your network',
}, },
{ {
kind: WidgetKinds.local.follows, kind: WidgetKinds.local.follows,
title: 'Follows', title: 'Follows',
description: 'All posts from people you are following',
}, },
], ],
}, },
@@ -67,27 +71,32 @@ export const DefaultWidgets = [
{ {
kind: WidgetKinds.tmp.xhashtag, kind: WidgetKinds.tmp.xhashtag,
title: 'Hashtag', title: 'Hashtag',
description: 'All posts have a specific hashtag',
}, },
{ {
kind: WidgetKinds.global.files, kind: WidgetKinds.global.files,
title: 'Files', title: 'Files',
description: 'All files shared by people in your current relay set',
}, },
{ {
kind: WidgetKinds.global.articles, kind: WidgetKinds.global.articles,
title: 'Articles', title: 'Articles',
description: 'All articles shared by people in your current relay set',
}, },
], ],
}, },
{ {
title: 'Trending (nostr.band)', title: 'nostr.band',
data: [ data: [
{ {
kind: WidgetKinds.nostrBand.trendingAccounts, kind: WidgetKinds.nostrBand.trendingAccounts,
title: 'Accounts', title: 'Accounts',
description: 'Trending accounts from the last 24 hours',
}, },
{ {
kind: WidgetKinds.nostrBand.trendingNotes, kind: WidgetKinds.nostrBand.trendingNotes,
title: 'Notes', title: 'Notes',
description: 'Trending notes from the last 24 hours',
}, },
], ],
}, },

View File

@@ -12,7 +12,7 @@ export function useEvent(id: string, embed?: string) {
const { status, data } = useQuery( const { status, data } = useQuery(
['event', id], ['event', id],
async () => { async () => {
// return embed event (nostr.band api) // return embed event (nostr.band api) or repost
if (embed) { if (embed) {
const event: NDKEvent = JSON.parse(embed); const event: NDKEvent = JSON.parse(embed);
return event; return event;

View File

@@ -3,17 +3,20 @@ import { EventPointer } from 'nostr-tools/lib/nip19';
import { RichContent } from '@utils/types'; import { RichContent } from '@utils/types';
function isURL(str: string) { function isURL(string: string) {
const pattern = new RegExp( try {
'^(https?:\\/\\/)?' + // protocol const url = new URL(string);
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name if (url.protocol.length > 0) {
'((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address if (url.protocol === 'https:' || url.protocol === 'http:') {
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path return true;
'(\\?[;&a-z\\d%_.~+=-]*)?' + // query string } else {
'(\\#[-a-z\\d_]*)?$', // fragment locator return false;
'i' }
); }
return !!pattern.test(str); return true;
} catch (e) {
return false;
}
} }
export function parser(eventContent: string) { export function parser(eventContent: string) {
@@ -32,18 +35,18 @@ export function parser(eventContent: string) {
const url = new URL(word); const url = new URL(word);
url.search = ''; url.search = '';
if (url.toString().match(/\.(jpg|jpeg|gif|png|webp|avif)$/)) { if (url.pathname.match(/\.(jpg|jpeg|gif|png|webp|avif)$/)) {
// image url // image url
content.images.push(word); content.images.push(word);
// remove url from original content // remove url from original content
return word.replace(word, ''); return word.replace(word, '');
} }
if (url.toString().match(/\.(mp4|mov|webm|wmv|flv|mts|avi|ogv|mkv|mp3|m3u8)$/)) { if (url.pathname.match(/\.(mp4|mov|webm|wmv|flv|mts|avi|ogv|mkv|mp3|m3u8)$/)) {
// video // video
content.videos.push(word); content.videos.push(word);
// remove url from original content // remove url from original content
word = word.replace(word, ''); return word.replace(word, '');
} }
content.links.push(url.toString()); content.links.push(url.toString());

View File

@@ -44,7 +44,9 @@ export interface WidgetGroup {
export interface WidgetGroupItem { export interface WidgetGroupItem {
title: string; title: string;
description: string;
kind: number; kind: number;
icon?: string;
} }
export interface Widget { export interface Widget {