mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 11:43:30 +00:00
Merge pull request #3 from reyamir/feat/note-processor
Implemented note processor
This commit is contained in:
commit
4d07ff94fb
11
.prettierrc
11
.prettierrc
@ -8,16 +8,7 @@
|
||||
"endOfLine": "lf",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": true,
|
||||
"importOrder": [
|
||||
"^@layouts/(.*)$",
|
||||
"^@pages/(.*)$",
|
||||
"^@components/(.*)$",
|
||||
"^@utils/(.*)$",
|
||||
"^@stores/(.*)$",
|
||||
"^@assets/(.*)$",
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"^[./]"
|
||||
],
|
||||
"importOrder": ["^@layouts/(.*)$", "^@pages/(.*)$", "^@components/(.*)$", "^@utils/(.*)$", "^@stores/(.*)$", "<THIRD_PARTY_MODULES>", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
|
||||
|
@ -9,6 +9,9 @@ module.exports = removeImports({
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
},
|
||||
webpack: (config) => {
|
||||
config.experiments = { ...config.experiments, topLevelAwait: true };
|
||||
return config;
|
||||
|
22
package.json
22
package.json
@ -12,11 +12,10 @@
|
||||
"**/*": "prettier --write --ignore-unknown"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nanostores/persistent": "^0.7.0",
|
||||
"@nanostores/react": "^0.4.1",
|
||||
"@radix-ui/react-dialog": "^1.0.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.3",
|
||||
"@radix-ui/react-icons": "^1.2.0",
|
||||
"@rehooks/local-storage": "^2.4.4",
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"@uiw/react-markdown-preview": "^4.1.9",
|
||||
"@uiw/react-md-editor": "^3.20.5",
|
||||
@ -27,8 +26,8 @@
|
||||
"nanostores": "^0.7.4",
|
||||
"next": "^13.2.1",
|
||||
"next-remove-imports": "^1.0.10",
|
||||
"nostr-react": "^0.6.4",
|
||||
"nostr-tools": "^1.6.0",
|
||||
"nostr-relaypool": "^0.5.3",
|
||||
"nostr-tools": "^1.7.1",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -37,25 +36,26 @@
|
||||
"react-player": "^2.11.2",
|
||||
"react-virtuoso": "^4.1.0",
|
||||
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql",
|
||||
"unique-names-generator": "^4.7.1"
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"ws": "^8.12.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@tauri-apps/cli": "^1.2.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.1.0",
|
||||
"@types/node": "^18.14.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
||||
"@typescript-eslint/parser": "^5.53.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"csstype": "^3.1.1",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-config-next": "^13.2.1",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"husky": "^8.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.1.2",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.8.4",
|
||||
|
397
pnpm-lock.yaml
397
pnpm-lock.yaml
@ -1,40 +1,39 @@
|
||||
lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@nanostores/persistent': ^0.7.0
|
||||
'@nanostores/react': ^0.4.1
|
||||
'@radix-ui/react-dialog': ^1.0.2
|
||||
'@radix-ui/react-dropdown-menu': ^2.0.3
|
||||
'@radix-ui/react-icons': ^1.2.0
|
||||
'@rehooks/local-storage': ^2.4.4
|
||||
'@tailwindcss/typography': ^0.5.9
|
||||
'@tauri-apps/api': ^1.2.0
|
||||
'@tauri-apps/cli': ^1.2.3
|
||||
'@trivago/prettier-plugin-sort-imports': ^4.1.0
|
||||
'@types/node': ^18.14.1
|
||||
'@trivago/prettier-plugin-sort-imports': ^4.1.1
|
||||
'@types/node': ^18.14.2
|
||||
'@types/react': ^18.0.28
|
||||
'@types/react-dom': ^18.0.11
|
||||
'@typescript-eslint/eslint-plugin': ^5.53.0
|
||||
'@typescript-eslint/parser': ^5.53.0
|
||||
'@typescript-eslint/eslint-plugin': ^5.54.0
|
||||
'@typescript-eslint/parser': ^5.54.0
|
||||
'@uiw/react-markdown-preview': ^4.1.9
|
||||
'@uiw/react-md-editor': ^3.20.5
|
||||
autoprefixer: ^10.4.13
|
||||
bitcoin-address-validation: ^2.2.1
|
||||
boring-avatars: ^1.7.0
|
||||
csstype: ^3.1.1
|
||||
eslint: ^8.34.0
|
||||
eslint: ^8.35.0
|
||||
eslint-config-next: ^13.2.1
|
||||
eslint-config-prettier: ^8.6.0
|
||||
eslint-plugin-react: ^7.32.2
|
||||
eslint-plugin-react-hooks: ^4.6.0
|
||||
framer-motion: ^9.1.7
|
||||
husky: ^8.0.0
|
||||
husky: ^8.0.3
|
||||
lint-staged: ^13.1.2
|
||||
moment: ^2.29.4
|
||||
nanostores: ^0.7.4
|
||||
next: ^13.2.1
|
||||
next-remove-imports: ^1.0.10
|
||||
nostr-react: ^0.6.4
|
||||
nostr-tools: ^1.6.0
|
||||
nostr-relaypool: ^0.5.3
|
||||
nostr-tools: ^1.7.1
|
||||
postcss: ^8.4.21
|
||||
prettier: ^2.8.4
|
||||
prettier-plugin-tailwindcss: ^0.2.3
|
||||
@ -50,13 +49,13 @@ specifiers:
|
||||
tauri-plugin-sql-api: github:tauri-apps/tauri-plugin-sql
|
||||
typescript: ^4.9.5
|
||||
unique-names-generator: ^4.7.1
|
||||
ws: ^8.12.1
|
||||
|
||||
dependencies:
|
||||
'@nanostores/persistent': 0.7.0_nanostores@0.7.4
|
||||
'@nanostores/react': 0.4.1_nkfnbc2tpc77iht7asm3uqwau4
|
||||
'@radix-ui/react-dialog': 1.0.2_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
'@radix-ui/react-dropdown-menu': 2.0.3_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
'@radix-ui/react-icons': 1.2.0_react@18.2.0
|
||||
'@rehooks/local-storage': 2.4.4_react@18.2.0
|
||||
'@tauri-apps/api': 1.2.0
|
||||
'@uiw/react-markdown-preview': 4.1.9_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
'@uiw/react-md-editor': 3.20.5_zula6vjvt3wdocc4mwcxqa6nzi
|
||||
@ -67,8 +66,8 @@ dependencies:
|
||||
nanostores: 0.7.4
|
||||
next: 13.2.1_biqbaboplfbrettd7655fr4n2y
|
||||
next-remove-imports: 1.0.10
|
||||
nostr-react: 0.6.4_react@18.2.0
|
||||
nostr-tools: 1.6.0
|
||||
nostr-relaypool: 0.5.3_ws@8.12.1
|
||||
nostr-tools: 1.7.1
|
||||
qrcode.react: 3.1.0_react@18.2.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
@ -78,28 +77,29 @@ dependencies:
|
||||
react-virtuoso: 4.1.0_biqbaboplfbrettd7655fr4n2y
|
||||
tauri-plugin-sql-api: github.com/tauri-apps/tauri-plugin-sql/abd8759ef49e1ba441540a2260b453d43d86c7ee
|
||||
unique-names-generator: 4.7.1
|
||||
ws: 8.12.1
|
||||
|
||||
devDependencies:
|
||||
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.7
|
||||
'@tauri-apps/cli': 1.2.3
|
||||
'@trivago/prettier-plugin-sort-imports': 4.1.0_prettier@2.8.4
|
||||
'@types/node': 18.14.1
|
||||
'@trivago/prettier-plugin-sort-imports': 4.1.1_prettier@2.8.4
|
||||
'@types/node': 18.14.2
|
||||
'@types/react': 18.0.28
|
||||
'@types/react-dom': 18.0.11
|
||||
'@typescript-eslint/eslint-plugin': 5.53.0_ny4s7qc6yg74faf3d6xty2ofzy
|
||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/eslint-plugin': 5.54.0_6mj2wypvdnknez7kws2nfdgupi
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
autoprefixer: 10.4.13_postcss@8.4.21
|
||||
csstype: 3.1.1
|
||||
eslint: 8.34.0
|
||||
eslint-config-next: 13.2.1_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
eslint-config-prettier: 8.6.0_eslint@8.34.0
|
||||
eslint-plugin-react: 7.32.2_eslint@8.34.0
|
||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.34.0
|
||||
eslint: 8.35.0
|
||||
eslint-config-next: 13.2.1_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
eslint-config-prettier: 8.6.0_eslint@8.35.0
|
||||
eslint-plugin-react: 7.32.2_eslint@8.35.0
|
||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.35.0
|
||||
husky: 8.0.3
|
||||
lint-staged: 13.1.2
|
||||
postcss: 8.4.21
|
||||
prettier: 2.8.4
|
||||
prettier-plugin-tailwindcss: 0.2.3_3p4xqifn6m4d44r76wgcnqfi3i
|
||||
prettier-plugin-tailwindcss: 0.2.3_zmkqdpv3ldc45e6wei6qtrbrca
|
||||
prop-types: 15.8.1
|
||||
tailwindcss: 3.2.7_postcss@8.4.21
|
||||
typescript: 4.9.5
|
||||
@ -350,8 +350,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@eslint/eslintrc/1.4.1:
|
||||
resolution: { integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== }
|
||||
/@eslint/eslintrc/2.0.0:
|
||||
resolution: { integrity: sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
@ -367,6 +367,11 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@eslint/js/8.35.0:
|
||||
resolution: { integrity: sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
dev: true
|
||||
|
||||
/@floating-ui/core/0.7.3:
|
||||
resolution: { integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg== }
|
||||
dev: false
|
||||
@ -411,6 +416,15 @@ packages:
|
||||
resolution: { integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== }
|
||||
dev: true
|
||||
|
||||
/@jest/source-map/29.4.3:
|
||||
resolution: { integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== }
|
||||
engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 }
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.17
|
||||
callsites: 3.1.0
|
||||
graceful-fs: 4.2.10
|
||||
dev: false
|
||||
|
||||
/@jridgewell/gen-mapping/0.1.1:
|
||||
resolution: { integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== }
|
||||
engines: { node: '>=6.0.0' }
|
||||
@ -449,27 +463,6 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: false
|
||||
|
||||
/@nanostores/persistent/0.7.0_nanostores@0.7.4:
|
||||
resolution: { integrity: sha512-4PAInL/T1hbftZUJ0cmgdFHBMalUoq7BUXFBy7QfyMv/8X3LPTYNh/yxspL7+J+XM3UNvVI7IFRMMs6FBasjhQ== }
|
||||
engines: { node: ^14.0.0 || ^16.0.0 || >=18.0.0 }
|
||||
peerDependencies:
|
||||
nanostores: ^0.7.0
|
||||
dependencies:
|
||||
nanostores: 0.7.4
|
||||
dev: false
|
||||
|
||||
/@nanostores/react/0.4.1_nkfnbc2tpc77iht7asm3uqwau4:
|
||||
resolution: { integrity: sha512-lsv0CYrMxczbXtoV/mxFVEoL/uVjEjseoP89srO/5yNAOkJka+dSFS7LYyWEbuvCPO7EgbtkvRpO5V+OztKQOw== }
|
||||
engines: { node: ^14.0.0 || ^16.0.0 || >=18.0.0 }
|
||||
peerDependencies:
|
||||
nanostores: ^0.7.0
|
||||
react: '>=18.0.0'
|
||||
dependencies:
|
||||
nanostores: 0.7.4
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0_react@18.2.0
|
||||
dev: false
|
||||
|
||||
/@next/env/13.2.1:
|
||||
resolution: { integrity: sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ== }
|
||||
dev: false
|
||||
@ -991,6 +984,14 @@ packages:
|
||||
'@babel/runtime': 7.21.0
|
||||
dev: false
|
||||
|
||||
/@rehooks/local-storage/2.4.4_react@18.2.0:
|
||||
resolution: { integrity: sha512-zE+kfOkG59n/1UTxdmbwktIosclr67Nlbf2MzUJ9mNtCSypVscNHeD1qT6JCSo5Pjj8DO893IKWNLJqKKzDL/Q== }
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@rushstack/eslint-patch/1.2.0:
|
||||
resolution: { integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== }
|
||||
dev: true
|
||||
@ -1134,8 +1135,8 @@ packages:
|
||||
'@tauri-apps/cli-win32-x64-msvc': 1.2.3
|
||||
dev: true
|
||||
|
||||
/@trivago/prettier-plugin-sort-imports/4.1.0_prettier@2.8.4:
|
||||
resolution: { integrity: sha512-aTr6QPFaPAAzPRFn9yWB/9yKi3ZAFqfGpxIGLPWuQfYJFGUed+W3KKwxntsoCiNvNE2iuKOg6haMo5KG8WXltg== }
|
||||
/@trivago/prettier-plugin-sort-imports/4.1.1_prettier@2.8.4:
|
||||
resolution: { integrity: sha512-dQ2r2uzNr1x6pJsuh/8x0IRA3CBUB+pWEW3J/7N98axqt7SQSm+2fy0FLNXvXGg77xEDC7KHxJlHfLYyi7PDcw== }
|
||||
peerDependencies:
|
||||
'@vue/compiler-sfc': 3.x
|
||||
prettier: 2.x
|
||||
@ -1183,8 +1184,8 @@ packages:
|
||||
resolution: { integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== }
|
||||
dev: false
|
||||
|
||||
/@types/node/18.14.1:
|
||||
resolution: { integrity: sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== }
|
||||
/@types/node/18.14.2:
|
||||
resolution: { integrity: sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA== }
|
||||
dev: true
|
||||
|
||||
/@types/parse5/6.0.3:
|
||||
@ -1222,8 +1223,8 @@ packages:
|
||||
resolution: { integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== }
|
||||
dev: false
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.53.0_ny4s7qc6yg74faf3d6xty2ofzy:
|
||||
resolution: { integrity: sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw== }
|
||||
/@typescript-eslint/eslint-plugin/5.54.0_6mj2wypvdnknez7kws2nfdgupi:
|
||||
resolution: { integrity: sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^5.0.0
|
||||
@ -1233,12 +1234,12 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/scope-manager': 5.53.0
|
||||
'@typescript-eslint/type-utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/scope-manager': 5.54.0
|
||||
'@typescript-eslint/type-utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
debug: 4.3.4
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
grapheme-splitter: 1.0.4
|
||||
ignore: 5.2.4
|
||||
natural-compare-lite: 1.4.0
|
||||
@ -1250,8 +1251,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm:
|
||||
resolution: { integrity: sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ== }
|
||||
/@typescript-eslint/parser/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: { integrity: sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
@ -1260,26 +1261,26 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.53.0
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
||||
'@typescript-eslint/scope-manager': 5.54.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||
debug: 4.3.4
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
typescript: 4.9.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager/5.53.0:
|
||||
resolution: { integrity: sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w== }
|
||||
/@typescript-eslint/scope-manager/5.54.0:
|
||||
resolution: { integrity: sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/visitor-keys': 5.53.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/visitor-keys': 5.54.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm:
|
||||
resolution: { integrity: sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw== }
|
||||
/@typescript-eslint/type-utils/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: { integrity: sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
@ -1288,23 +1289,23 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
||||
'@typescript-eslint/utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||
'@typescript-eslint/utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
debug: 4.3.4
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
tsutils: 3.21.0_typescript@4.9.5
|
||||
typescript: 4.9.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types/5.53.0:
|
||||
resolution: { integrity: sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A== }
|
||||
/@typescript-eslint/types/5.54.0:
|
||||
resolution: { integrity: sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/5.53.0_typescript@4.9.5:
|
||||
resolution: { integrity: sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w== }
|
||||
/@typescript-eslint/typescript-estree/5.54.0_typescript@4.9.5:
|
||||
resolution: { integrity: sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@ -1312,8 +1313,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/visitor-keys': 5.53.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/visitor-keys': 5.54.0
|
||||
debug: 4.3.4
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
@ -1324,31 +1325,31 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm:
|
||||
resolution: { integrity: sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g== }
|
||||
/@typescript-eslint/utils/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: { integrity: sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
'@types/semver': 7.3.13
|
||||
'@typescript-eslint/scope-manager': 5.53.0
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
||||
eslint: 8.34.0
|
||||
'@typescript-eslint/scope-manager': 5.54.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||
eslint: 8.35.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0_eslint@8.34.0
|
||||
eslint-utils: 3.0.0_eslint@8.35.0
|
||||
semver: 7.3.8
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys/5.53.0:
|
||||
resolution: { integrity: sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w== }
|
||||
/@typescript-eslint/visitor-keys/5.54.0:
|
||||
resolution: { integrity: sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
@ -1609,7 +1610,7 @@ packages:
|
||||
postcss: ^8.1.0
|
||||
dependencies:
|
||||
browserslist: 4.21.5
|
||||
caniuse-lite: 1.0.30001457
|
||||
caniuse-lite: 1.0.30001458
|
||||
fraction.js: 4.2.0
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.0
|
||||
@ -1714,8 +1715,8 @@ packages:
|
||||
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001457
|
||||
electron-to-chromium: 1.4.310
|
||||
caniuse-lite: 1.0.30001458
|
||||
electron-to-chromium: 1.4.313
|
||||
node-releases: 2.0.10
|
||||
update-browserslist-db: 1.0.10_browserslist@4.21.5
|
||||
|
||||
@ -1729,15 +1730,14 @@ packages:
|
||||
/callsites/3.1.0:
|
||||
resolution: { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== }
|
||||
engines: { node: '>=6' }
|
||||
dev: true
|
||||
|
||||
/camelcase-css/2.0.1:
|
||||
resolution: { integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== }
|
||||
engines: { node: '>= 6' }
|
||||
dev: true
|
||||
|
||||
/caniuse-lite/1.0.30001457:
|
||||
resolution: { integrity: sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA== }
|
||||
/caniuse-lite/1.0.30001458:
|
||||
resolution: { integrity: sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== }
|
||||
|
||||
/ccount/2.0.1:
|
||||
resolution: { integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== }
|
||||
@ -2030,8 +2030,8 @@ packages:
|
||||
resolution: { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== }
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium/1.4.310:
|
||||
resolution: { integrity: sha512-/xlATgfwkm5uDDwLw5nt/MNEf7c1oazLURMZLy39vOioGYyYzLWIDT8fZMJak6qTiAJ7udFTy7JG7ziyjNutiA== }
|
||||
/electron-to-chromium/1.4.313:
|
||||
resolution: { integrity: sha512-QckB9OVqr2oybjIrbMI99uF+b9+iTja5weFe0ePbqLb5BHqXOJUO1SG6kDj/1WtWPRIBr51N153AEq8m7HuIaA== }
|
||||
|
||||
/emoji-regex/8.0.0:
|
||||
resolution: { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== }
|
||||
@ -2144,7 +2144,7 @@ packages:
|
||||
engines: { node: '>=12' }
|
||||
dev: false
|
||||
|
||||
/eslint-config-next/13.2.1_7kw3g6rralp5ps6mg3uyzz6azm:
|
||||
/eslint-config-next/13.2.1_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: { integrity: sha512-2GAx7EjSiCzJN6H2L/v1kbYrNiwQxzkyjy6eWSjuhAKt+P6d3nVNHGy9mON8ZcYd72w/M8kyMjm4UB9cvijgrw== }
|
||||
peerDependencies:
|
||||
eslint: ^7.23.0 || ^8.0.0
|
||||
@ -2155,27 +2155,27 @@ packages:
|
||||
dependencies:
|
||||
'@next/eslint-plugin-next': 13.2.1
|
||||
'@rushstack/eslint-patch': 1.2.0
|
||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
eslint: 8.34.0
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
eslint: 8.35.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
eslint-import-resolver-typescript: 3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm
|
||||
eslint-plugin-import: 2.27.5_2hqppaeqs2axgzqg6vttejknky
|
||||
eslint-plugin-jsx-a11y: 6.7.1_eslint@8.34.0
|
||||
eslint-plugin-react: 7.32.2_eslint@8.34.0
|
||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.34.0
|
||||
eslint-import-resolver-typescript: 3.5.3_yckic57kx266ph64dhq6ozvb54
|
||||
eslint-plugin-import: 2.27.5_tqrcrxlenpngfto46ddarus52y
|
||||
eslint-plugin-jsx-a11y: 6.7.1_eslint@8.35.0
|
||||
eslint-plugin-react: 7.32.2_eslint@8.35.0
|
||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.35.0
|
||||
typescript: 4.9.5
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-config-prettier/8.6.0_eslint@8.34.0:
|
||||
/eslint-config-prettier/8.6.0_eslint@8.35.0:
|
||||
resolution: { integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== }
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
eslint: '>=7.0.0'
|
||||
dependencies:
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-node/0.3.7:
|
||||
@ -2188,7 +2188,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-typescript/3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm:
|
||||
/eslint-import-resolver-typescript/3.5.3_yckic57kx266ph64dhq6ozvb54:
|
||||
resolution: { integrity: sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== }
|
||||
engines: { node: ^14.18.0 || >=16.0.0 }
|
||||
peerDependencies:
|
||||
@ -2197,8 +2197,8 @@ packages:
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
enhanced-resolve: 5.12.0
|
||||
eslint: 8.34.0
|
||||
eslint-plugin-import: 2.27.5_2hqppaeqs2axgzqg6vttejknky
|
||||
eslint: 8.35.0
|
||||
eslint-plugin-import: 2.27.5_tqrcrxlenpngfto46ddarus52y
|
||||
get-tsconfig: 4.4.0
|
||||
globby: 13.1.3
|
||||
is-core-module: 2.11.0
|
||||
@ -2208,7 +2208,7 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-module-utils/2.7.4_yfzt44nswbaazp63chcrlz6vvq:
|
||||
/eslint-module-utils/2.7.4_igrub7c6rucg6hjc3uqgumd66y:
|
||||
resolution: { integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== }
|
||||
engines: { node: '>=4' }
|
||||
peerDependencies:
|
||||
@ -2229,16 +2229,16 @@ packages:
|
||||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
debug: 3.2.7
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
eslint-import-resolver-typescript: 3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm
|
||||
eslint-import-resolver-typescript: 3.5.3_yckic57kx266ph64dhq6ozvb54
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-import/2.27.5_2hqppaeqs2axgzqg6vttejknky:
|
||||
/eslint-plugin-import/2.27.5_tqrcrxlenpngfto46ddarus52y:
|
||||
resolution: { integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== }
|
||||
engines: { node: '>=4' }
|
||||
peerDependencies:
|
||||
@ -2248,15 +2248,15 @@ packages:
|
||||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
array-includes: 3.1.6
|
||||
array.prototype.flat: 1.3.1
|
||||
array.prototype.flatmap: 1.3.1
|
||||
debug: 3.2.7
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
eslint-import-resolver-node: 0.3.7
|
||||
eslint-module-utils: 2.7.4_yfzt44nswbaazp63chcrlz6vvq
|
||||
eslint-module-utils: 2.7.4_igrub7c6rucg6hjc3uqgumd66y
|
||||
has: 1.0.3
|
||||
is-core-module: 2.11.0
|
||||
is-glob: 4.0.3
|
||||
@ -2264,14 +2264,14 @@ packages:
|
||||
object.values: 1.1.6
|
||||
resolve: 1.22.1
|
||||
semver: 6.3.0
|
||||
tsconfig-paths: 3.14.1
|
||||
tsconfig-paths: 3.14.2
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-jsx-a11y/6.7.1_eslint@8.34.0:
|
||||
/eslint-plugin-jsx-a11y/6.7.1_eslint@8.35.0:
|
||||
resolution: { integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== }
|
||||
engines: { node: '>=4.0' }
|
||||
peerDependencies:
|
||||
@ -2286,7 +2286,7 @@ packages:
|
||||
axobject-query: 3.1.1
|
||||
damerau-levenshtein: 1.0.8
|
||||
emoji-regex: 9.2.2
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
has: 1.0.3
|
||||
jsx-ast-utils: 3.3.3
|
||||
language-tags: 1.0.5
|
||||
@ -2296,16 +2296,16 @@ packages:
|
||||
semver: 6.3.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react-hooks/4.6.0_eslint@8.34.0:
|
||||
/eslint-plugin-react-hooks/4.6.0_eslint@8.35.0:
|
||||
resolution: { integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== }
|
||||
engines: { node: '>=10' }
|
||||
peerDependencies:
|
||||
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
|
||||
dependencies:
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react/7.32.2_eslint@8.34.0:
|
||||
/eslint-plugin-react/7.32.2_eslint@8.35.0:
|
||||
resolution: { integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== }
|
||||
engines: { node: '>=4' }
|
||||
peerDependencies:
|
||||
@ -2315,7 +2315,7 @@ packages:
|
||||
array.prototype.flatmap: 1.3.1
|
||||
array.prototype.tosorted: 1.1.1
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
estraverse: 5.3.0
|
||||
jsx-ast-utils: 3.3.3
|
||||
minimatch: 3.1.2
|
||||
@ -2345,13 +2345,13 @@ packages:
|
||||
estraverse: 5.3.0
|
||||
dev: true
|
||||
|
||||
/eslint-utils/3.0.0_eslint@8.34.0:
|
||||
/eslint-utils/3.0.0_eslint@8.35.0:
|
||||
resolution: { integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== }
|
||||
engines: { node: ^10.0.0 || ^12.0.0 || >= 14.0.0 }
|
||||
peerDependencies:
|
||||
eslint: '>=5'
|
||||
dependencies:
|
||||
eslint: 8.34.0
|
||||
eslint: 8.35.0
|
||||
eslint-visitor-keys: 2.1.0
|
||||
dev: true
|
||||
|
||||
@ -2365,12 +2365,13 @@ packages:
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
dev: true
|
||||
|
||||
/eslint/8.34.0:
|
||||
resolution: { integrity: sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg== }
|
||||
/eslint/8.35.0:
|
||||
resolution: { integrity: sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw== }
|
||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@eslint/eslintrc': 1.4.1
|
||||
'@eslint/eslintrc': 2.0.0
|
||||
'@eslint/js': 8.35.0
|
||||
'@humanwhocodes/config-array': 0.11.8
|
||||
'@humanwhocodes/module-importer': 1.0.1
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
@ -2381,7 +2382,7 @@ packages:
|
||||
doctrine: 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 7.1.1
|
||||
eslint-utils: 3.0.0_eslint@8.34.0
|
||||
eslint-utils: 3.0.0_eslint@8.35.0
|
||||
eslint-visitor-keys: 3.3.0
|
||||
espree: 9.4.1
|
||||
esquery: 1.4.2
|
||||
@ -2734,7 +2735,6 @@ packages:
|
||||
|
||||
/graceful-fs/4.2.10:
|
||||
resolution: { integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== }
|
||||
dev: true
|
||||
|
||||
/grapheme-splitter/1.0.4:
|
||||
resolution: { integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== }
|
||||
@ -3187,53 +3187,18 @@ packages:
|
||||
resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== }
|
||||
dev: true
|
||||
|
||||
/isomorphic-ws/5.0.0_ws@8.12.1:
|
||||
resolution: { integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== }
|
||||
peerDependencies:
|
||||
ws: '*'
|
||||
dependencies:
|
||||
ws: 8.12.1
|
||||
dev: false
|
||||
|
||||
/javascript-natural-sort/0.7.1:
|
||||
resolution: { integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== }
|
||||
dev: true
|
||||
|
||||
/jotai/1.13.1_react@18.2.0:
|
||||
resolution: { integrity: sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw== }
|
||||
engines: { node: '>=12.20.0' }
|
||||
peerDependencies:
|
||||
'@babel/core': '*'
|
||||
'@babel/template': '*'
|
||||
jotai-devtools: '*'
|
||||
jotai-immer: '*'
|
||||
jotai-optics: '*'
|
||||
jotai-redux: '*'
|
||||
jotai-tanstack-query: '*'
|
||||
jotai-urql: '*'
|
||||
jotai-valtio: '*'
|
||||
jotai-xstate: '*'
|
||||
jotai-zustand: '*'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
'@babel/core':
|
||||
optional: true
|
||||
'@babel/template':
|
||||
optional: true
|
||||
jotai-devtools:
|
||||
optional: true
|
||||
jotai-immer:
|
||||
optional: true
|
||||
jotai-optics:
|
||||
optional: true
|
||||
jotai-redux:
|
||||
optional: true
|
||||
jotai-tanstack-query:
|
||||
optional: true
|
||||
jotai-urql:
|
||||
optional: true
|
||||
jotai-valtio:
|
||||
optional: true
|
||||
jotai-xstate:
|
||||
optional: true
|
||||
jotai-zustand:
|
||||
optional: true
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/js-sdsl/4.3.0:
|
||||
resolution: { integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== }
|
||||
dev: true
|
||||
@ -3447,7 +3412,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.10
|
||||
escape-string-regexp: 5.0.0
|
||||
unist-util-is: 5.2.0
|
||||
unist-util-is: 5.2.1
|
||||
unist-util-visit-parents: 5.1.3
|
||||
dev: false
|
||||
|
||||
@ -3530,7 +3495,7 @@ packages:
|
||||
resolution: { integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg== }
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.10
|
||||
unist-util-is: 5.2.0
|
||||
unist-util-is: 5.2.1
|
||||
dev: false
|
||||
|
||||
/mdast-util-to-hast/12.3.0:
|
||||
@ -3923,7 +3888,7 @@ packages:
|
||||
dependencies:
|
||||
'@next/env': 13.2.1
|
||||
'@swc/helpers': 0.4.14
|
||||
caniuse-lite: 1.0.30001457
|
||||
caniuse-lite: 1.0.30001458
|
||||
postcss: 8.4.14
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
@ -3960,31 +3925,19 @@ packages:
|
||||
engines: { node: '>=0.10.0' }
|
||||
dev: true
|
||||
|
||||
/nostr-react/0.6.4_react@18.2.0:
|
||||
resolution: { integrity: sha512-esRgmhTP5kPQ8ufs8cFAQxxJtMmzuba/k2QfXevG/ejHP3IMa41pb82qi8V0aPzY3KJ0Nr54x0OSa39d2InKzA== }
|
||||
engines: { node: '>=12' }
|
||||
peerDependencies:
|
||||
react: '>=16'
|
||||
/nostr-relaypool/0.5.3_ws@8.12.1:
|
||||
resolution: { integrity: sha512-1INGKleOTuUTFUs3RnnZrew4+G/idLUewh44WBtmTTJ9g+kRiQtMMaBGTVUpf9621nBNleEVOB8p3XSNcaX3FQ== }
|
||||
dependencies:
|
||||
jotai: 1.13.1_react@18.2.0
|
||||
nostr-tools: 1.6.0
|
||||
react: 18.2.0
|
||||
'@jest/source-map': 29.4.3
|
||||
isomorphic-ws: 5.0.0_ws@8.12.1
|
||||
nostr-tools: 1.7.1
|
||||
safe-stable-stringify: 2.4.2
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- '@babel/template'
|
||||
- jotai-devtools
|
||||
- jotai-immer
|
||||
- jotai-optics
|
||||
- jotai-redux
|
||||
- jotai-tanstack-query
|
||||
- jotai-urql
|
||||
- jotai-valtio
|
||||
- jotai-xstate
|
||||
- jotai-zustand
|
||||
- ws
|
||||
dev: false
|
||||
|
||||
/nostr-tools/1.6.0:
|
||||
resolution: { integrity: sha512-qjjJQ7YxJUMzgS24eVlxkZ87PKJtU6dlH04OzVuK6w+GSPL+VdUZkMe2lfSpnb7OkCrDIzmbFbtx+Q4LXdU2xw== }
|
||||
/nostr-tools/1.7.1:
|
||||
resolution: { integrity: sha512-r72KpbLVz6Gaqei6LIj6m+cyp24eF3myiIMlmv93WCgDFCI5w72u+OrZzjSrJaeE94vYoEJfOF16/2Rl5o5z5w== }
|
||||
dependencies:
|
||||
'@noble/hashes': 1.0.0
|
||||
'@noble/secp256k1': 1.7.1
|
||||
@ -4336,7 +4289,7 @@ packages:
|
||||
engines: { node: '>= 0.8.0' }
|
||||
dev: true
|
||||
|
||||
/prettier-plugin-tailwindcss/0.2.3_3p4xqifn6m4d44r76wgcnqfi3i:
|
||||
/prettier-plugin-tailwindcss/0.2.3_zmkqdpv3ldc45e6wei6qtrbrca:
|
||||
resolution: { integrity: sha512-s2N5Dh7Ao5KTV1mao5ZBnn8EKtUcDPJEkGViZIjI0Ij9TTI5zgTz4IHOxW33jOdjHKa8CSjM88scelUiC5TNRQ== }
|
||||
engines: { node: '>=12.17.0' }
|
||||
peerDependencies:
|
||||
@ -4388,7 +4341,7 @@ packages:
|
||||
prettier-plugin-twig-melody:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@trivago/prettier-plugin-sort-imports': 4.1.0_prettier@2.8.4
|
||||
'@trivago/prettier-plugin-sort-imports': 4.1.1_prettier@2.8.4
|
||||
prettier: 2.8.4
|
||||
dev: true
|
||||
|
||||
@ -4824,6 +4777,11 @@ packages:
|
||||
is-regex: 1.1.4
|
||||
dev: true
|
||||
|
||||
/safe-stable-stringify/2.4.2:
|
||||
resolution: { integrity: sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA== }
|
||||
engines: { node: '>=10' }
|
||||
dev: false
|
||||
|
||||
/scheduler/0.23.0:
|
||||
resolution: { integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== }
|
||||
dependencies:
|
||||
@ -5146,8 +5104,8 @@ packages:
|
||||
resolution: { integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== }
|
||||
dev: false
|
||||
|
||||
/tsconfig-paths/3.14.1:
|
||||
resolution: { integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== }
|
||||
/tsconfig-paths/3.14.2:
|
||||
resolution: { integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== }
|
||||
dependencies:
|
||||
'@types/json5': 0.0.29
|
||||
json5: 1.0.2
|
||||
@ -5233,7 +5191,7 @@ packages:
|
||||
resolution: { integrity: sha512-RynicUM/vbOSTSiUK+BnaK9XMfmQUh6gyi7L6taNgc7FIf84GukXVV3ucGzEN/PhUUkdP5hb1MmXc+3cvPUm5Q== }
|
||||
dependencies:
|
||||
'@types/unist': 2.0.6
|
||||
unist-util-is: 5.2.0
|
||||
unist-util-is: 5.2.1
|
||||
unist-util-visit-parents: 5.1.3
|
||||
dev: false
|
||||
|
||||
@ -5241,8 +5199,10 @@ packages:
|
||||
resolution: { integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A== }
|
||||
dev: false
|
||||
|
||||
/unist-util-is/5.2.0:
|
||||
resolution: { integrity: sha512-Glt17jWwZeyqrFqOK0pF1Ded5U3yzJnFr8CG1GMjCWTp9zDo2p+cmD6pWbZU8AgM5WU3IzRv6+rBwhzsGh6hBQ== }
|
||||
/unist-util-is/5.2.1:
|
||||
resolution: { integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== }
|
||||
dependencies:
|
||||
'@types/unist': 2.0.6
|
||||
dev: false
|
||||
|
||||
/unist-util-position/4.0.4:
|
||||
@ -5261,14 +5221,14 @@ packages:
|
||||
resolution: { integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== }
|
||||
dependencies:
|
||||
'@types/unist': 2.0.6
|
||||
unist-util-is: 5.2.0
|
||||
unist-util-is: 5.2.1
|
||||
dev: false
|
||||
|
||||
/unist-util-visit/4.1.2:
|
||||
resolution: { integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== }
|
||||
dependencies:
|
||||
'@types/unist': 2.0.6
|
||||
unist-util-is: 5.2.0
|
||||
unist-util-is: 5.2.1
|
||||
unist-util-visit-parents: 5.1.3
|
||||
dev: false
|
||||
|
||||
@ -5331,14 +5291,6 @@ packages:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/use-sync-external-store/1.2.0_react@18.2.0:
|
||||
resolution: { integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== }
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/util-deprecate/1.0.2:
|
||||
resolution: { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== }
|
||||
dev: true
|
||||
@ -5447,6 +5399,19 @@ packages:
|
||||
resolution: { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== }
|
||||
dev: true
|
||||
|
||||
/ws/8.12.1:
|
||||
resolution: { integrity: sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== }
|
||||
engines: { node: '>=10.0.0' }
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/xtend/4.0.2:
|
||||
resolution: { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== }
|
||||
engines: { node: '>=0.4' }
|
||||
|
168
src-tauri/Cargo.lock
generated
168
src-tauri/Cargo.lock
generated
@ -465,36 +465,6 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||
|
||||
[[package]]
|
||||
name = "curl"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
|
||||
dependencies = [
|
||||
"curl-sys",
|
||||
"libc",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"socket2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.59+curl-7.86.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
@ -1393,6 +1363,30 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libappindicator"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8"
|
||||
dependencies = [
|
||||
"glib",
|
||||
"gtk",
|
||||
"gtk-sys",
|
||||
"libappindicator-sys",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libappindicator-sys"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa"
|
||||
dependencies = [
|
||||
"gtk-sys",
|
||||
"libloading",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
@ -1408,6 +1402,16 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.24.2"
|
||||
@ -1419,18 +1423,6 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "line-wrap"
|
||||
version = "0.1.1"
|
||||
@ -1485,7 +1477,6 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-sql",
|
||||
"webpage",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1504,7 +1495,7 @@ dependencies = [
|
||||
"dirs-next",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
"time 0.3.17",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1530,18 +1521,6 @@ dependencies = [
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever_rcdom"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
|
||||
dependencies = [
|
||||
"html5ever",
|
||||
"markup5ever",
|
||||
"tendril",
|
||||
"xml5ever",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@ -1795,25 +1774,6 @@ dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
@ -2068,7 +2028,7 @@ dependencies = [
|
||||
"line-wrap",
|
||||
"quick-xml 0.26.0",
|
||||
"serde",
|
||||
"time 0.3.17",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2421,15 +2381,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
|
||||
dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
@ -2917,6 +2868,7 @@ dependencies = [
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dirs-next",
|
||||
"dispatch",
|
||||
"gdk",
|
||||
"gdk-pixbuf",
|
||||
@ -2930,6 +2882,7 @@ dependencies = [
|
||||
"instant",
|
||||
"jni",
|
||||
"lazy_static",
|
||||
"libappindicator",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk",
|
||||
@ -3047,7 +3000,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
"time",
|
||||
"uuid 1.3.0",
|
||||
"walkdir",
|
||||
]
|
||||
@ -3069,7 +3022,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tauri-plugin-sql"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=fix/sql-types#f1a7136b1e0b145ea5c5fb9d336cac0cb1dc6265"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=dev#8f34eb83e4f9a8c72fd3823a066c94f861f2d021"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
@ -3220,17 +3173,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.17"
|
||||
@ -3540,12 +3482,6 @@ version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
@ -3675,18 +3611,6 @@ dependencies = [
|
||||
"system-deps 6.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpage"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d261bbae112cb48a95d3cc9e8873a4e40933bc54ae8eddc1eef70e952dd3b232"
|
||||
dependencies = [
|
||||
"curl",
|
||||
"html5ever",
|
||||
"markup5ever_rcdom",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
@ -4051,15 +3975,3 @@ checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9234163818fd8e2418fcde330655e757900d4236acd8cc70fef345ef91f6d865"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"time 0.1.45",
|
||||
]
|
||||
|
@ -16,12 +16,11 @@ tauri-build = { version = "1.2", features = [] }
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.2", features = ["clipboard-all", "notification-all", "shell-open", "window-start-dragging"] }
|
||||
webpage = "1.5.0"
|
||||
tauri = { version = "1.2", features = ["clipboard-all", "notification-all", "shell-open", "system-tray", "window-start-dragging"] }
|
||||
|
||||
[dependencies.tauri-plugin-sql]
|
||||
git = "https://github.com/tauri-apps/plugins-workspace"
|
||||
branch = "fix/sql-types"
|
||||
branch = "dev"
|
||||
features = ["sqlite"]
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 106 KiB |
70
src-tauri/migrations/20230226004139_create_tables.sql
Normal file
70
src-tauri/migrations/20230226004139_create_tables.sql
Normal file
@ -0,0 +1,70 @@
|
||||
-- Add migration script here
|
||||
-- create relays
|
||||
CREATE TABLE
|
||||
relays (
|
||||
id INTEGER PRIMARY KEY,
|
||||
relay_url TEXT NOT NULL,
|
||||
relay_status INTEGER NOT NULL DEFAULT 1,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
relays (relay_url, relay_status)
|
||||
VALUES
|
||||
("wss://relay.damus.io", "1"),
|
||||
("wss://relay.uselume.xyz", "0"),
|
||||
("wss://nostr-pub.wellorder.net", "1"),
|
||||
("wss://nostr.bongbong.com", "1"),
|
||||
("wss://nostr.zebedee.cloud", "1"),
|
||||
("wss://nostr.fmt.wiz.biz", "1"),
|
||||
("wss://nostr.walletofsatoshi.com", "1"),
|
||||
("wss://relay.snort.social", "1"),
|
||||
("wss://offchain.pub", "1"),
|
||||
("wss://nos.lol", "1");
|
||||
|
||||
-- create accounts
|
||||
CREATE TABLE
|
||||
accounts (
|
||||
id TEXT PRIMARY KEY,
|
||||
privkey TEXT NOT NULL,
|
||||
npub TEXT NOT NULL,
|
||||
nsec TEXT NOT NULL,
|
||||
is_active INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSON
|
||||
);
|
||||
|
||||
-- create follows
|
||||
CREATE TABLE
|
||||
follows (
|
||||
id INTEGER PRIMARY KEY,
|
||||
pubkey TEXT NOT NULL,
|
||||
account TEXT NOT NULL,
|
||||
kind INTEGER NOT NULL DEFAULT 0,
|
||||
metadata JSON
|
||||
);
|
||||
|
||||
-- create index for pubkey in follows
|
||||
CREATE UNIQUE INDEX index_pubkey ON follows (pubkey);
|
||||
|
||||
-- create cache profiles
|
||||
CREATE TABLE
|
||||
cache_profiles (
|
||||
id TEXT PRIMARY KEY,
|
||||
metadata JSON,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- create cache notes
|
||||
CREATE TABLE
|
||||
cache_notes (
|
||||
id TEXT PRIMARY KEY,
|
||||
pubkey TEXT NOT NULL,
|
||||
created_at TEXT,
|
||||
kind INTEGER NOT NULL DEFAULT 1,
|
||||
tags TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
relay TEXT,
|
||||
is_multi BOOLEAN DEFAULT 0
|
||||
);
|
@ -7,48 +7,15 @@
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use tauri::{Manager, WindowEvent};
|
||||
use webpage::{Webpage, WebpageOptions};
|
||||
use tauri::{Manager, SystemTray, WindowEvent};
|
||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||
use window_ext::WindowExt;
|
||||
|
||||
mod window_ext;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct OpenGraphResponse {
|
||||
title: String,
|
||||
description: String,
|
||||
url: String,
|
||||
image: String,
|
||||
}
|
||||
|
||||
async fn fetch_opengraph(url: String) -> OpenGraphResponse {
|
||||
let options = WebpageOptions {
|
||||
allow_insecure: false,
|
||||
max_redirections: 3,
|
||||
timeout: Duration::from_secs(30),
|
||||
useragent: "lume - desktop app".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
let result = Webpage::from_url(&url, options).expect("Could not read from URL");
|
||||
let html = result.html;
|
||||
|
||||
return OpenGraphResponse {
|
||||
title: html.opengraph.properties["title"].to_string(),
|
||||
description: html.opengraph.properties["description"].to_string(),
|
||||
url: html.opengraph.properties["url"].to_string(),
|
||||
image: html.opengraph.images[0].url.to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn opengraph(url: String) -> OpenGraphResponse {
|
||||
let result = fetch_opengraph(url).await;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let tray = SystemTray::new();
|
||||
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
@ -57,8 +24,20 @@ fn main() {
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![opengraph])
|
||||
.plugin(tauri_plugin_sql::Builder::default().build())
|
||||
.system_tray(tray)
|
||||
.plugin(
|
||||
tauri_plugin_sql::Builder::default()
|
||||
.add_migrations(
|
||||
"sqlite:lume.db",
|
||||
vec![Migration {
|
||||
version: 1,
|
||||
description: "create default tables",
|
||||
sql: include_str!("../migrations/20230226004139_create_tables.sql"),
|
||||
kind: MigrationKind::Up,
|
||||
}],
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.on_window_event(|e| {
|
||||
let apply_offset = || {
|
||||
let win = e.window();
|
||||
|
@ -37,13 +37,7 @@
|
||||
"depends": []
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||
"identifier": "com.uselume.xyz",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
@ -68,6 +62,10 @@
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"systemTray": {
|
||||
"iconPath": "icons/icon.png",
|
||||
"iconAsTemplate": true
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"theme": "Dark",
|
||||
|
@ -16,12 +16,7 @@ export const Account = memo(function Account({ user, current }: { user: any; cur
|
||||
current === user.pubkey ? 'ring-1 ring-fuchsia-500 ring-offset-4 ring-offset-black' : ''
|
||||
}`}>
|
||||
{userData?.picture !== undefined ? (
|
||||
<Image
|
||||
src={userData.picture}
|
||||
alt="user's avatar"
|
||||
fill={true}
|
||||
className="rounded-full object-cover"
|
||||
/>
|
||||
<Image src={userData.picture} alt="user's avatar" fill={true} className="rounded-full object-cover" />
|
||||
) : (
|
||||
<div className="h-11 w-11 animate-pulse rounded-full bg-zinc-700" />
|
||||
)}
|
||||
|
@ -1,19 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Account } from '@components/accountBar/account';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import LumeSymbol from '@assets/icons/Lume';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { PlusIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import Link from 'next/link';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
export default function AccountBar() {
|
||||
const [users, setUsers] = useState([]);
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
|
||||
const getAccounts = useCallback(async () => {
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
@ -30,7 +27,7 @@ export default function AccountBar() {
|
||||
<div className="flex h-full flex-col items-center justify-between px-2 pt-12 pb-4">
|
||||
<div className="flex flex-col gap-4">
|
||||
{users.map((user, index) => (
|
||||
<Account key={index} user={user} current={$currentUser.pubkey} />
|
||||
<Account key={index} user={user} current={currentUser.pubkey} />
|
||||
))}
|
||||
<Link
|
||||
href="/onboarding"
|
||||
|
57
src/components/contexts/database.tsx
Normal file
57
src/components/contexts/database.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { writeStorage } from '@rehooks/local-storage';
|
||||
import { createContext, useEffect, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
export const DatabaseContext = createContext({});
|
||||
|
||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
||||
|
||||
export default function DatabaseProvider({ children }: { children: React.ReactNode }) {
|
||||
const [done, setDone] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const getRelays = async () => {
|
||||
const arr = [];
|
||||
const result: any[] = await db.select('SELECT relay_url FROM relays WHERE relay_status = "1"');
|
||||
|
||||
result.forEach((item: { relay_url: string }) => {
|
||||
arr.push(item.relay_url);
|
||||
});
|
||||
|
||||
writeStorage('relays', arr);
|
||||
};
|
||||
|
||||
const getAccount = async () => {
|
||||
const result = await db.select(`SELECT * FROM accounts LIMIT 1`);
|
||||
writeStorage('current-user', result[0]);
|
||||
|
||||
return result[0];
|
||||
};
|
||||
|
||||
const getFollows = async (id: string) => {
|
||||
const arr = [];
|
||||
const result: any[] = await db.select(`SELECT pubkey FROM follows WHERE account = "${id}"`);
|
||||
|
||||
result.forEach((item: { pubkey: string }) => {
|
||||
arr.push(item.pubkey);
|
||||
});
|
||||
|
||||
writeStorage('follows', arr);
|
||||
};
|
||||
|
||||
getRelays().catch(console.error);
|
||||
getAccount()
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
getFollows(res.id).catch(console.error);
|
||||
}
|
||||
setDone(true);
|
||||
})
|
||||
.catch(console.error);
|
||||
}, []);
|
||||
|
||||
if (done === true) {
|
||||
return <DatabaseContext.Provider value={{ db }}>{children}</DatabaseContext.Provider>;
|
||||
}
|
||||
}
|
11
src/components/contexts/relay.tsx
Normal file
11
src/components/contexts/relay.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { RelayPool } from 'nostr-relaypool';
|
||||
import { createContext, useMemo } from 'react';
|
||||
|
||||
export const RelayContext = createContext({});
|
||||
|
||||
export default function RelayProvider({ relays, children }: { relays: any; children: React.ReactNode }) {
|
||||
const value = useMemo(() => new RelayPool(relays, { useEventCache: true }), [relays]);
|
||||
|
||||
return <RelayContext.Provider value={value}>{children}</RelayContext.Provider>;
|
||||
}
|
@ -1,25 +1,28 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { dateToUnix } from '@utils/getDate';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import * as commands from '@uiw/react-md-editor/lib/commands';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { dateToUnix, useNostr } from 'nostr-react';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
import { useState } from 'react';
|
||||
import { useContext, useState } from 'react';
|
||||
|
||||
const MDEditor = dynamic(() => import('@uiw/react-md-editor').then((mod) => mod.default), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function CreatePost() {
|
||||
const { publish } = useNostr();
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const pubkey = $currentUser.pubkey;
|
||||
const privkey = $currentUser.privkey;
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const pubkey = currentUser.pubkey;
|
||||
const privkey = currentUser.privkey;
|
||||
|
||||
const postButton = {
|
||||
name: 'post',
|
||||
@ -27,9 +30,7 @@ export default function CreatePost() {
|
||||
buttonProps: { className: 'cta-btn', 'aria-label': 'Post a message' },
|
||||
icon: (
|
||||
<div className="relative inline-flex h-10 w-16 transform cursor-pointer overflow-hidden rounded bg-zinc-900 px-2.5 ring-zinc-500/50 ring-offset-zinc-900 will-change-transform focus:outline-none focus:ring-1 focus:ring-offset-2 active:translate-y-1">
|
||||
<span className="absolute inset-px z-10 inline-flex items-center justify-center rounded bg-zinc-900 text-zinc-200">
|
||||
Post
|
||||
</span>
|
||||
<span className="absolute inset-px z-10 inline-flex items-center justify-center rounded bg-zinc-900 text-zinc-200">Post</span>
|
||||
<span className="absolute inset-0 z-0 scale-x-[2.0] blur before:absolute before:inset-0 before:top-1/2 before:aspect-square before:animate-disco before:bg-gradient-conic before:from-gray-300 before:via-fuchsia-600 before:to-orange-600"></span>
|
||||
</div>
|
||||
),
|
||||
@ -44,11 +45,10 @@ export default function CreatePost() {
|
||||
pubkey: pubkey,
|
||||
tags: [],
|
||||
};
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privkey);
|
||||
|
||||
publish(event);
|
||||
relayPool.publish(event, relays);
|
||||
setValue('');
|
||||
}
|
||||
},
|
||||
|
@ -3,30 +3,22 @@ import ActiveLink from '@components/activeLink';
|
||||
import CreatePost from '@components/navigatorBar/createPost';
|
||||
import { ProfileMenu } from '@components/navigatorBar/profileMenu';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { PlusIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
|
||||
export default function NavigatorBar() {
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const profile =
|
||||
$currentUser.metadata !== undefined
|
||||
? JSON.parse($currentUser.metadata)
|
||||
: { display_name: null, username: null };
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const profile = JSON.parse(currentUser.metadata);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col flex-wrap justify-between overflow-hidden px-2 pt-3 pb-4">
|
||||
{/* main */}
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* Create post */}
|
||||
<div className="flex flex-col rounded-lg bg-zinc-900 ring-1 ring-white/10">
|
||||
<div className="flex flex-col p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<h5 className="font-semibold leading-tight text-zinc-100">
|
||||
{profile.display_name || ''}
|
||||
</h5>
|
||||
<ProfileMenu pubkey={$currentUser.pubkey} />
|
||||
<h5 className="font-semibold leading-tight text-zinc-100">{profile.display_name || ''}</h5>
|
||||
<ProfileMenu pubkey={currentUser.pubkey} />
|
||||
</div>
|
||||
<span className="text-sm leading-tight text-zinc-500">@{profile.username || ''}</span>
|
||||
</div>
|
||||
@ -38,9 +30,7 @@ export default function NavigatorBar() {
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<h3 className="text-sm font-bold text-zinc-400">Newsfeed</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||
<button type="button" className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||
<PlusIcon className="h-3 w-3 text-zinc-400 group-hover:text-zinc-100" />
|
||||
</button>
|
||||
</div>
|
||||
@ -65,9 +55,7 @@ export default function NavigatorBar() {
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<h3 className="text-sm font-bold text-zinc-400">Direct Messages</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||
<button type="button" className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||
<PlusIcon className="h-3 w-3 text-zinc-400 group-hover:text-zinc-100" />
|
||||
</button>
|
||||
</div>
|
||||
@ -77,17 +65,3 @@ export default function NavigatorBar() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* Channels
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<h3 className="text-sm font-bold text-zinc-400">Channels</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||
<PlusIcon className="h-4 w-4 text-zinc-400 group-hover:text-zinc-100" />
|
||||
</button>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
*/
|
||||
|
@ -1,46 +1,46 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { dateToUnix } from '@utils/getDate';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { HeartFilledIcon, HeartIcon } from '@radix-ui/react-icons';
|
||||
import { dateToUnix, useNostr, useNostrEvents } from 'nostr-react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
import { useState } from 'react';
|
||||
import { useContext, useState } from 'react';
|
||||
|
||||
export default function Reaction({ eventID, eventPubkey }: { eventID: string; eventPubkey: string }) {
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
|
||||
export default function Reaction({
|
||||
eventID,
|
||||
eventPubkey,
|
||||
}: {
|
||||
eventID: string;
|
||||
eventPubkey: string;
|
||||
}) {
|
||||
const { publish } = useNostr();
|
||||
const [reaction, setReaction] = useState(0);
|
||||
const [isReact, setIsReact] = useState(false);
|
||||
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const pubkey = $currentUser.pubkey;
|
||||
const privkey = $currentUser.privkey;
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const pubkey = currentUser.pubkey;
|
||||
const privkey = currentUser.privkey;
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
'#e': [eventID],
|
||||
since: 0,
|
||||
kinds: [7],
|
||||
limit: 20,
|
||||
},
|
||||
});
|
||||
|
||||
onEvent((rawMetadata) => {
|
||||
try {
|
||||
const content = rawMetadata.content;
|
||||
if (content === '🤙' || content === '+') {
|
||||
/*
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
'#e': [eventID],
|
||||
since: 0,
|
||||
kinds: [7],
|
||||
limit: 10,
|
||||
},
|
||||
],
|
||||
relays,
|
||||
(event: any) => {
|
||||
if (event.content === '🤙' || event.content === '+') {
|
||||
setReaction(reaction + 1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err, rawMetadata);
|
||||
},
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
console.log(events, relayURL);
|
||||
}
|
||||
});
|
||||
);
|
||||
*/
|
||||
|
||||
const handleReaction = (e: any) => {
|
||||
e.stopPropagation();
|
||||
@ -58,22 +58,16 @@ export default function Reaction({
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privkey);
|
||||
|
||||
publish(event);
|
||||
relayPool.publish(event, relays);
|
||||
|
||||
setIsReact(true);
|
||||
setReaction(reaction + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={(e) => handleReaction(e)}
|
||||
className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
||||
<button onClick={(e) => handleReaction(e)} className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
||||
<div className="rounded-lg p-1 group-hover:bg-zinc-600">
|
||||
{isReact ? (
|
||||
<HeartFilledIcon className="h-4 w-4 group-hover:text-red-400" />
|
||||
) : (
|
||||
<HeartIcon className="h-4 w-4 text-zinc-500" />
|
||||
)}
|
||||
{isReact ? <HeartFilledIcon className="h-4 w-4 group-hover:text-red-400" /> : <HeartIcon className="h-4 w-4 text-zinc-500" />}
|
||||
</div>
|
||||
<span>{reaction}</span>
|
||||
</button>
|
||||
|
@ -1,22 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { ChatBubbleIcon } from '@radix-ui/react-icons';
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function Reply({ eventID }: { eventID: string }) {
|
||||
const { events } = useNostrEvents({
|
||||
filter: {
|
||||
'#e': [eventID],
|
||||
since: 0,
|
||||
kinds: [1],
|
||||
limit: 10,
|
||||
},
|
||||
});
|
||||
export default function Reply() {
|
||||
const [count] = useState(0);
|
||||
|
||||
return (
|
||||
<button className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
||||
<div className="rounded-lg p-1 group-hover:bg-zinc-600">
|
||||
<ChatBubbleIcon className="h-4 w-4 group-hover:text-orange-400" />
|
||||
</div>
|
||||
<span>{events.length || 0}</span>
|
||||
<span>{count}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
@ -1,88 +1,65 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||
|
||||
import { truncate } from '@utils/truncate';
|
||||
|
||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
||||
import Avatar from 'boring-avatars';
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import Moment from 'react-moment';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
||||
|
||||
export const User = memo(function User({ pubkey, time }: { pubkey: string; time: any }) {
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
const [profile, setProfile] = useState({ picture: null, name: null, username: null });
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
authors: [pubkey],
|
||||
kinds: [0],
|
||||
},
|
||||
});
|
||||
const insertCacheProfile = useCallback(
|
||||
async (event) => {
|
||||
const metadata: any = JSON.parse(event.content);
|
||||
|
||||
onEvent(async (rawMetadata) => {
|
||||
try {
|
||||
const metadata: any = JSON.parse(rawMetadata.content);
|
||||
if (profile.picture === null || profile.name === null) {
|
||||
setProfile(metadata);
|
||||
await db.execute(
|
||||
`INSERT OR IGNORE INTO cache_profiles (pubkey, metadata) VALUES ("${pubkey}", '${JSON.stringify(
|
||||
metadata
|
||||
)}')`
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err, rawMetadata);
|
||||
}
|
||||
});
|
||||
await db.execute(`INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES ("${pubkey}", '${JSON.stringify(metadata)}')`);
|
||||
setProfile(metadata);
|
||||
},
|
||||
[db, pubkey]
|
||||
);
|
||||
|
||||
const getCacheProfile = useCallback(async () => {
|
||||
const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`);
|
||||
return result;
|
||||
}, [db, pubkey]);
|
||||
|
||||
useEffect(() => {
|
||||
const initialProfile = async () => {
|
||||
const result: any = await db.select(
|
||||
`SELECT metadata FROM cache_profiles WHERE pubkey = "${pubkey}"`
|
||||
);
|
||||
db.close;
|
||||
return result;
|
||||
};
|
||||
|
||||
initialProfile()
|
||||
getCacheProfile()
|
||||
.then((res) => {
|
||||
if (res[0] !== undefined) {
|
||||
setProfile(JSON.parse(res[0].metadata));
|
||||
} else {
|
||||
fetch(`https://rbr.bio/${pubkey}/metadata.json`).then((res) =>
|
||||
res.json().then((res) => {
|
||||
// update state
|
||||
setProfile(JSON.parse(res.content));
|
||||
// save profile to database
|
||||
insertCacheProfile(res);
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
}, [pubkey]);
|
||||
}, [getCacheProfile, insertCacheProfile, pubkey]);
|
||||
|
||||
return (
|
||||
<div className="relative flex items-start gap-4">
|
||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-full border border-white/10">
|
||||
{profile.picture ? (
|
||||
<ImageWithFallback
|
||||
src={profile.picture}
|
||||
alt={pubkey}
|
||||
fill={true}
|
||||
className="rounded-full object-cover"
|
||||
/>
|
||||
<ImageWithFallback src={profile.picture} alt={pubkey} fill={true} className="rounded-full object-cover" />
|
||||
) : (
|
||||
<Avatar
|
||||
size={44}
|
||||
name={pubkey}
|
||||
variant="beam"
|
||||
colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']}
|
||||
/>
|
||||
<Avatar size={44} name={pubkey} variant="beam" colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-1 items-start justify-between">
|
||||
<div className="flex w-full justify-between">
|
||||
<div className="flex items-baseline gap-2 text-sm">
|
||||
<span className="font-bold leading-tight">
|
||||
{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}
|
||||
</span>
|
||||
<span className="font-bold leading-tight">{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}</span>
|
||||
<span className="leading-tight text-zinc-500">·</span>
|
||||
<Moment fromNow unix className="text-zinc-500">
|
||||
{time}
|
||||
|
@ -1,30 +1,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { truncate } from '@utils/truncate';
|
||||
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { memo, useState } from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
|
||||
export const UserRepost = memo(function UserRepost({ pubkey }: { pubkey: string }) {
|
||||
const [profile, setProfile] = useState({ picture: null, name: null });
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
authors: [pubkey],
|
||||
kinds: [0],
|
||||
},
|
||||
});
|
||||
|
||||
// #TODO: save response to DB
|
||||
onEvent((rawMetadata) => {
|
||||
try {
|
||||
const metadata: any = JSON.parse(rawMetadata.content);
|
||||
if (metadata) {
|
||||
setProfile(metadata);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err, rawMetadata);
|
||||
}
|
||||
});
|
||||
useEffect(() => {
|
||||
fetch(`https://rbr.bio/${pubkey}/metadata.json`).then((res) =>
|
||||
res.json().then((res) => {
|
||||
// update state
|
||||
setProfile(JSON.parse(res.content));
|
||||
})
|
||||
);
|
||||
}, [pubkey]);
|
||||
|
||||
return (
|
||||
<div className="text-zinc-400">
|
||||
|
@ -5,86 +5,34 @@ import { truncate } from '@utils/truncate';
|
||||
|
||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
||||
import Avatar from 'boring-avatars';
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
||||
|
||||
export const UserWithUsername = memo(function UserWithUsername({ pubkey }: { pubkey: string }) {
|
||||
const [profile, setProfile] = useState({ picture: null, name: null, username: null });
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
authors: [pubkey],
|
||||
kinds: [0],
|
||||
},
|
||||
});
|
||||
|
||||
onEvent(async (rawMetadata) => {
|
||||
try {
|
||||
const metadata: any = JSON.parse(rawMetadata.content);
|
||||
if (profile.picture === null || profile.name === null) {
|
||||
setProfile(metadata);
|
||||
await db.execute(
|
||||
`INSERT OR IGNORE INTO cache_profiles (pubkey, metadata) VALUES ("${pubkey}", '${JSON.stringify(
|
||||
metadata
|
||||
)}')`
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err, rawMetadata);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const initialProfile = async () => {
|
||||
const result: any = await db.select(
|
||||
`SELECT metadata FROM cache_profiles WHERE pubkey = "${pubkey}"`
|
||||
);
|
||||
db.close;
|
||||
return result;
|
||||
};
|
||||
|
||||
initialProfile()
|
||||
.then((res) => {
|
||||
if (res[0] !== undefined) {
|
||||
setProfile(JSON.parse(res[0].metadata));
|
||||
}
|
||||
fetch(`https://rbr.bio/${pubkey}/metadata.json`).then((res) =>
|
||||
res.json().then((res) => {
|
||||
// update state
|
||||
setProfile(JSON.parse(res.content));
|
||||
})
|
||||
.catch(console.error);
|
||||
);
|
||||
}, [pubkey]);
|
||||
|
||||
return (
|
||||
<div className="relative flex items-start gap-2">
|
||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-full border border-white/10">
|
||||
{profile.picture ? (
|
||||
<ImageWithFallback
|
||||
src={profile.picture}
|
||||
alt={pubkey}
|
||||
fill={true}
|
||||
className="rounded-full object-cover"
|
||||
/>
|
||||
<ImageWithFallback src={profile.picture} alt={pubkey} fill={true} className="rounded-full object-cover" />
|
||||
) : (
|
||||
<Avatar
|
||||
size={44}
|
||||
name={pubkey}
|
||||
variant="beam"
|
||||
colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']}
|
||||
/>
|
||||
<Avatar size={44} name={pubkey} variant="beam" colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']} />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-1 items-start justify-between">
|
||||
<div className="flex w-full justify-between">
|
||||
<div className="flex flex-col gap-1 text-sm">
|
||||
<span className="font-bold leading-tight">
|
||||
{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}
|
||||
</span>
|
||||
<span className="text-zinc-500">
|
||||
{profile.username ? profile.username : truncate(pubkey, 16, ' .... ')}
|
||||
</span>
|
||||
<span className="font-bold leading-tight">{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}</span>
|
||||
<span className="text-zinc-500">{profile.username ? profile.username : truncate(pubkey, 16, ' .... ')}</span>
|
||||
</div>
|
||||
<div>
|
||||
<DotsHorizontalIcon className="h-4 w-4 text-zinc-500" />
|
||||
|
102
src/components/note/connector.tsx
Normal file
102
src/components/note/connector.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { dateToUnix, hoursAgo } from '@utils/getDate';
|
||||
|
||||
import { ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { memo, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
export const NoteConnector = memo(function NoteConnector({
|
||||
setParentReload,
|
||||
setHasNewNote,
|
||||
currentDate,
|
||||
}: {
|
||||
setParentReload: any;
|
||||
setHasNewNote: any;
|
||||
currentDate: any;
|
||||
}) {
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
|
||||
const [follows]: any = useLocalStorage('follows');
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
|
||||
const [reload, setReload] = useState(false);
|
||||
const timeout = useRef(null);
|
||||
|
||||
const reloadNewsfeed = () => {
|
||||
setParentReload(true);
|
||||
setReload(true);
|
||||
timeout.current = setTimeout(() => {
|
||||
setReload(false);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const insertDB = useCallback(
|
||||
async (event: any) => {
|
||||
await db.execute(
|
||||
`INSERT OR IGNORE INTO
|
||||
cache_notes
|
||||
(id, pubkey, created_at, kind, tags, content) VALUES
|
||||
("${event.id}", "${event.pubkey}", "${event.created_at}", "${event.kind}", '${JSON.stringify(event.tags)}', "${event.content}");`
|
||||
);
|
||||
},
|
||||
[db]
|
||||
);
|
||||
|
||||
const fetchEvent = useCallback(() => {
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
kinds: [1],
|
||||
authors: follows,
|
||||
since: dateToUnix(hoursAgo(12, currentDate)),
|
||||
},
|
||||
],
|
||||
relays,
|
||||
(event: any) => {
|
||||
// show trigger update newer event
|
||||
if (event.created_at > dateToUnix(currentDate)) {
|
||||
setHasNewNote(true);
|
||||
}
|
||||
// insert event to local database
|
||||
insertDB(event).catch(console.error);
|
||||
},
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
console.log(events, relayURL);
|
||||
}
|
||||
);
|
||||
}, [relayPool, follows, currentDate, relays, insertDB, setHasNewNote]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchEvent();
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout.current);
|
||||
};
|
||||
}, [fetchEvent]);
|
||||
|
||||
return (
|
||||
<div className="relative flex h-12 items-center justify-between border-b border-zinc-800 px-6 shadow-input">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-zinc-500"># following</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={() => reloadNewsfeed()} className={`${reload ? 'animate-spin' : ''} rounded-full p-1 hover:bg-zinc-800`}>
|
||||
<ReloadIcon className="h-3.5 w-3.5 text-zinc-500" />
|
||||
</button>
|
||||
<div className="inline-flex items-center gap-1 rounded-full border border-zinc-700 bg-zinc-800 px-2.5 py-1">
|
||||
{/* #TODO: get user network status */}
|
||||
<span className="relative flex h-1.5 w-1.5">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
|
||||
<span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-green-500"></span>
|
||||
</span>
|
||||
<p className="text-xs font-medium text-zinc-500">Online</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
@ -1,18 +1,33 @@
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
import { Content } from '@components/note/content';
|
||||
import NoteReply from '@components/note/modal/noteReply';
|
||||
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { memo } from 'react';
|
||||
import useLocalStorage from '@rehooks/local-storage';
|
||||
import { memo, useContext, useState } from 'react';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const Modal = ({ event }: { event: any }) => {
|
||||
const { events } = useNostrEvents({
|
||||
filter: {
|
||||
'#e': [event.id],
|
||||
since: event.created_at,
|
||||
kinds: [1],
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
const [events, setEvents] = useState([]);
|
||||
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
'#e': [event.id],
|
||||
since: event.created_at,
|
||||
kinds: [1],
|
||||
},
|
||||
],
|
||||
relays,
|
||||
(event: any) => {
|
||||
setEvents((events) => [event, ...events]);
|
||||
},
|
||||
});
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
console.log(events, relayURL);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full items-center justify-center p-4">
|
||||
|
@ -1,49 +1,47 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
import { UserRepost } from '@components/note/atoms/userRepost';
|
||||
import { Content } from '@components/note/content';
|
||||
import { Placeholder } from '@components/note/placeholder';
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { LoopIcon } from '@radix-ui/react-icons';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { memo } from 'react';
|
||||
|
||||
const Modal = dynamic(() => import('@components/note/modal'), {
|
||||
ssr: false,
|
||||
loading: () => <></>,
|
||||
});
|
||||
import useLocalStorage from '@rehooks/local-storage';
|
||||
import { memo, useContext, useState } from 'react';
|
||||
|
||||
export const Repost = memo(function Repost({ root, user }: { root: any; user: string }) {
|
||||
const { events } = useNostrEvents({
|
||||
filter: {
|
||||
ids: [root[0][1]],
|
||||
since: 0,
|
||||
kinds: [1],
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
const [events, setEvents] = useState([]);
|
||||
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
ids: [root[0][1]],
|
||||
since: 0,
|
||||
kinds: [1],
|
||||
},
|
||||
],
|
||||
relays,
|
||||
(event: any) => {
|
||||
setEvents((events) => [event, ...events]);
|
||||
},
|
||||
});
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
console.log(events, relayURL);
|
||||
}
|
||||
);
|
||||
|
||||
if (events !== null && Object.keys(events).length > 0) {
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>
|
||||
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
||||
<div className="flex items-center gap-1 pl-8 text-sm">
|
||||
<LoopIcon className="h-4 w-4 text-zinc-400" />
|
||||
<div className="ml-2">
|
||||
<UserRepost pubkey={user} />
|
||||
</div>
|
||||
</div>
|
||||
{events[0].content && <Content data={events[0]} />}
|
||||
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
||||
<div className="flex items-center gap-1 pl-8 text-sm">
|
||||
<LoopIcon className="h-4 w-4 text-zinc-400" />
|
||||
<div className="ml-2">
|
||||
<UserRepost pubkey={user} />
|
||||
</div>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
|
||||
<Dialog.Content className="fixed inset-0 overflow-y-auto">
|
||||
{events[0].content && <Modal event={events[0]} />}
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Trigger>
|
||||
</Dialog.Root>
|
||||
</div>
|
||||
{events[0].content && <Content data={events[0]} />}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
|
@ -1,30 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Content } from '@components/note/content';
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { memo } from 'react';
|
||||
|
||||
const Modal = dynamic(() => import('@components/note/modal'), {
|
||||
ssr: false,
|
||||
loading: () => <></>,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const Single = memo(function Single({ event }: { event: any }) {
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger asChild>
|
||||
<div className="flex h-min min-h-min w-full cursor-pointer select-text flex-col border-b border-zinc-800 py-4 px-6 hover:bg-zinc-800">
|
||||
<Content data={event} />
|
||||
</div>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
|
||||
<Dialog.Content className="fixed inset-0 overflow-y-auto">
|
||||
<Modal event={event} />
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<div className="flex h-min min-h-min w-full cursor-pointer select-text flex-col border-b border-zinc-800 py-4 px-6 hover:bg-zinc-800">
|
||||
<Content data={event} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,53 +0,0 @@
|
||||
import { Placeholder } from '@components/note/placeholder';
|
||||
import { Repost } from '@components/note/repost';
|
||||
import { Single } from '@components/note/single';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function Thread({ data }: { data: any }) {
|
||||
const ItemContent = useCallback(
|
||||
(index: string | number) => {
|
||||
const event = data[index];
|
||||
|
||||
if (event.content.includes('#[0]') && event.tags[0][0] == 'e') {
|
||||
// type: repost
|
||||
return <Repost root={event.tags} user={event.pubkey} />;
|
||||
} else {
|
||||
// type: default
|
||||
return <Single event={event} />;
|
||||
}
|
||||
},
|
||||
[data]
|
||||
);
|
||||
|
||||
const computeItemKey = useCallback(
|
||||
(index) => {
|
||||
return data[index].id;
|
||||
},
|
||||
[data]
|
||||
);
|
||||
|
||||
return (
|
||||
<Virtuoso
|
||||
data={data}
|
||||
itemContent={ItemContent}
|
||||
components={{
|
||||
EmptyPlaceholder: () => <Placeholder />,
|
||||
ScrollSeekPlaceholder: () => <Placeholder />,
|
||||
}}
|
||||
computeItemKey={computeItemKey}
|
||||
scrollSeekConfiguration={{
|
||||
enter: (velocity) => Math.abs(velocity) > 800,
|
||||
exit: (velocity) => Math.abs(velocity) < 500,
|
||||
}}
|
||||
overscan={800}
|
||||
increaseViewportBy={1000}
|
||||
className="scrollbar-hide relative h-full w-full rounded-lg"
|
||||
style={{
|
||||
contain: 'strict',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
@ -2,12 +2,10 @@
|
||||
import AccountBar from '@components/accountBar';
|
||||
import ActiveLink from '@components/activeLink';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
|
||||
export default function UserLayout({ children }: { children: React.ReactNode }) {
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-row">
|
||||
@ -27,13 +25,13 @@ export default function UserLayout({ children }: { children: React.ReactNode })
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 text-zinc-500">
|
||||
<ActiveLink
|
||||
href={`/profile/${$currentUser.pubkey}`}
|
||||
href={`/profile/${currentUser.pubkey}`}
|
||||
activeClassName="ring-1 ring-white/10 dark:bg-zinc-900 dark:text-white"
|
||||
className="flex h-10 items-center gap-1 rounded-lg px-2.5 text-sm font-medium hover:bg-zinc-900">
|
||||
<span>Personal Page</span>
|
||||
</ActiveLink>
|
||||
<ActiveLink
|
||||
href={`/profile/update?pubkey=${$currentUser.pubkey}`}
|
||||
href={`/profile/update?pubkey=${currentUser.pubkey}`}
|
||||
activeClassName="ring-1 ring-white/10 dark:bg-zinc-900 dark:text-white"
|
||||
className="flex h-10 items-center gap-1 rounded-lg px-2.5 text-sm font-medium hover:bg-zinc-900">
|
||||
<span>Update Profile</span>
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { relays } from '@stores/relays';
|
||||
import DatabaseProvider from '@components/contexts/database';
|
||||
import RelayProvider from '@components/contexts/relay';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import type { NextPage } from 'next';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { NostrProvider } from 'nostr-react';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { ReactElement, ReactNode } from 'react';
|
||||
|
||||
import '../App.css';
|
||||
|
||||
@ -21,12 +21,12 @@ type AppPropsWithLayout = AppProps & {
|
||||
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
// Use the layout defined at the page level, if available
|
||||
const getLayout = Component.getLayout ?? ((page) => page);
|
||||
// Get relays
|
||||
const $relays = useStore(relays);
|
||||
// Get relays from localstorage
|
||||
const [relays] = useLocalStorage('relays');
|
||||
|
||||
return (
|
||||
<NostrProvider relayUrls={$relays} debug={false}>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</NostrProvider>
|
||||
<DatabaseProvider>
|
||||
<RelayProvider relays={relays}>{getLayout(<Component {...pageProps} />)}</RelayProvider>
|
||||
</DatabaseProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,54 +1,113 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import NewsFeedLayout from '@layouts/newsfeedLayout';
|
||||
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { NoteConnector } from '@components/note/connector';
|
||||
import { Placeholder } from '@components/note/placeholder';
|
||||
import { Thread } from '@components/thread';
|
||||
import { Repost } from '@components/note/repost';
|
||||
import { Single } from '@components/note/single';
|
||||
|
||||
import { hoursAgo } from '@utils/getDate';
|
||||
import { dateToUnix } from '@utils/getDate';
|
||||
|
||||
import { follows } from '@stores/follows';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { dateToUnix, useNostrEvents } from 'nostr-react';
|
||||
import {
|
||||
JSXElementConstructor,
|
||||
ReactElement,
|
||||
ReactFragment,
|
||||
ReactPortal,
|
||||
Suspense,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { writeStorage } from '@rehooks/local-storage';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useRef } from 'react';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
|
||||
export default function Page() {
|
||||
const $follows = useStore(follows);
|
||||
const now = useRef(new Date());
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
|
||||
const { events } = useNostrEvents({
|
||||
filter: {
|
||||
authors: $follows,
|
||||
since: dateToUnix(hoursAgo(6, now.current)),
|
||||
kinds: [1],
|
||||
limit: 100,
|
||||
const [data, setData] = useState([]);
|
||||
const [parentReload, setParentReload] = useState(false);
|
||||
const [hasNewNote, setHasNewNote] = useState(false);
|
||||
|
||||
const now = useRef(new Date());
|
||||
const limit = useRef(30);
|
||||
const offset = useRef(0);
|
||||
|
||||
const loadMore = useCallback(async () => {
|
||||
offset.current += limit.current;
|
||||
// next query
|
||||
const result = await db.select(
|
||||
`SELECT * FROM
|
||||
cache_notes
|
||||
WHERE created_at <= ${dateToUnix(now.current)}
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ${limit.current} OFFSET ${offset.current}`
|
||||
);
|
||||
setData((data) => [...data, ...result]);
|
||||
}, [db]);
|
||||
|
||||
const ItemContent = useCallback(
|
||||
(index: string | number) => {
|
||||
const event = data[index];
|
||||
if (event.content.includes('#[0]') && event.tags[0][0] == 'e') {
|
||||
// type: repost
|
||||
return <Repost root={event.tags} user={event.pubkey} />;
|
||||
} else {
|
||||
// type: default
|
||||
return <Single event={event} />;
|
||||
}
|
||||
},
|
||||
});
|
||||
[data]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
const result = await db.select(
|
||||
`SELECT * FROM cache_notes WHERE created_at <= ${dateToUnix(now.current)} ORDER BY created_at DESC LIMIT ${limit.current}`
|
||||
);
|
||||
if (result) {
|
||||
setData(result);
|
||||
writeStorage('settings', new Date());
|
||||
}
|
||||
};
|
||||
|
||||
getData().catch(console.error);
|
||||
}, [db, parentReload]);
|
||||
|
||||
const computeItemKey = useCallback(
|
||||
(index: string | number) => {
|
||||
return data[index].id;
|
||||
},
|
||||
[data]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<Suspense fallback={<Placeholder />}>
|
||||
<Thread data={events} />
|
||||
</Suspense>
|
||||
<div className="relative h-full w-full">
|
||||
<NoteConnector setParentReload={setParentReload} setHasNewNote={setHasNewNote} currentDate={now.current} />
|
||||
{hasNewNote && (
|
||||
<div className="fixed top-10 left-1/2 z-50 -translate-x-1/2 transform">
|
||||
<button>Load newest</button>
|
||||
</div>
|
||||
)}
|
||||
<Virtuoso
|
||||
data={data}
|
||||
itemContent={ItemContent}
|
||||
components={{
|
||||
EmptyPlaceholder: () => <Placeholder />,
|
||||
ScrollSeekPlaceholder: () => <Placeholder />,
|
||||
}}
|
||||
computeItemKey={computeItemKey}
|
||||
scrollSeekConfiguration={{
|
||||
enter: (velocity) => Math.abs(velocity) > 800,
|
||||
exit: (velocity) => Math.abs(velocity) < 500,
|
||||
}}
|
||||
endReached={loadMore}
|
||||
overscan={800}
|
||||
increaseViewportBy={1000}
|
||||
className="relative h-full w-full"
|
||||
style={{
|
||||
contain: 'strict',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
|
@ -1,50 +1,18 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import NewsFeedLayout from '@layouts/newsfeedLayout';
|
||||
|
||||
import { Placeholder } from '@components/note/placeholder';
|
||||
import { Thread } from '@components/thread';
|
||||
|
||||
import { hoursAgo } from '@utils/getDate';
|
||||
|
||||
import { dateToUnix, useNostrEvents } from 'nostr-react';
|
||||
import {
|
||||
JSXElementConstructor,
|
||||
ReactElement,
|
||||
ReactFragment,
|
||||
ReactPortal,
|
||||
Suspense,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const now = useRef(new Date());
|
||||
|
||||
const { events } = useNostrEvents({
|
||||
filter: {
|
||||
until: dateToUnix(now.current),
|
||||
since: dateToUnix(hoursAgo(1, now.current)),
|
||||
kinds: [1],
|
||||
limit: 10,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<Suspense fallback={<Placeholder />}>
|
||||
<Thread data={events} />
|
||||
</Suspense>
|
||||
<p>Global</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
|
@ -2,108 +2,31 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import FullLayout from '@layouts/fullLayout';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { follows } from '@stores/follows';
|
||||
|
||||
import LumeSymbol from '@assets/icons/Lume';
|
||||
|
||||
import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/api/notification';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRouter } from 'next/router';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useEffect, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const initDB = useCallback(async () => {
|
||||
if (db) {
|
||||
await db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS accounts (id INTEGER PRIMARY KEY, privkey TEXT NOT NULL, pubkey TEXT NOT NULL, npub TEXT, nsec TEXT, current INTEGER DEFAULT "0" NOT NULL, metadata JSON, UNIQUE(privkey));'
|
||||
);
|
||||
await db.execute('CREATE TABLE IF NOT EXISTS follows (id INTEGER PRIMARY KEY, pubkey TEXT NOT NULL, account TEXT, UNIQUE(pubkey));');
|
||||
await db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS note_reactions (id INTEGER PRIMARY KEY, reaction_id TEXT NOT NULL, e TEXT, p TEXT, UNIQUE(reaction_id));'
|
||||
);
|
||||
await db.execute('CREATE TABLE IF NOT EXISTS note_replies (id INTEGER PRIMARY KEY, reply_id TEXT NOT NULL, e TEXT, p TEXT, UNIQUE(reply_id));');
|
||||
await db.execute('CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, event_id TEXT, event JSON, UNIQUE(event_id));');
|
||||
await db.execute('CREATE TABLE IF NOT EXISTS cache_profiles (id INTEGER PRIMARY KEY, pubkey TEXT, metadata JSON, UNIQUE(pubkey));');
|
||||
await db.execute('CREATE TABLE IF NOT EXISTS block_pubkeys (id INTEGER PRIMARY KEY, pubkey TEXT, UNIQUE(pubkey));');
|
||||
await db.close();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const notification = useCallback(async () => {
|
||||
// NOTE: notification don't work in dev mode (only affect MacOS)
|
||||
// ref: https://github.com/tauri-apps/tauri/issues/4965
|
||||
let permissionGranted = await isPermissionGranted();
|
||||
if (!permissionGranted) {
|
||||
const permission = await requestPermission();
|
||||
permissionGranted = permission === 'granted';
|
||||
}
|
||||
if (permissionGranted) {
|
||||
sendNotification({ title: 'Lume', body: 'Nostr is awesome' });
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getAccount = useCallback(async () => {
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
const result = await db.select(`SELECT * FROM accounts WHERE current = "1" ORDER BY id ASC LIMIT 1`);
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
const getFollows = useCallback(async (account) => {
|
||||
const arr = [];
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
const result: any = await db.select(`SELECT pubkey FROM follows WHERE account = "${account.pubkey}"`);
|
||||
|
||||
result.forEach((item: { pubkey: string }) => {
|
||||
arr.push(item.pubkey);
|
||||
});
|
||||
|
||||
return arr;
|
||||
}, []);
|
||||
|
||||
// Explain:
|
||||
// Step 1: check DB tables, if not exist then create new table. #TODO: move this function to Rust code
|
||||
// Step 2: request allow notification from system
|
||||
// Step 3: get first account. #TODO: get last used account instead (part of multi account feature)
|
||||
// Step 4: get follows by account
|
||||
useEffect(() => {
|
||||
initDB()
|
||||
.then(() => notification())
|
||||
.then(() => {
|
||||
getAccount()
|
||||
.then((res: any) => {
|
||||
if (res.length === 0) {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding');
|
||||
}, 1500);
|
||||
} else {
|
||||
// store current user in localstorage
|
||||
currentUser.set(res[0]);
|
||||
getFollows(res[0])
|
||||
.then(async (res) => {
|
||||
// store follows in localstorage
|
||||
follows.set(res);
|
||||
// redirect to newsfeed
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/feed/following');
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
})
|
||||
.catch(console.error);
|
||||
}, [getAccount, getFollows, initDB, notification, router]);
|
||||
if (!currentUser) {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding');
|
||||
}, 1500);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/feed/following');
|
||||
}, 1500);
|
||||
}
|
||||
}, [currentUser, router]);
|
||||
|
||||
return (
|
||||
<div className="relative flex h-full flex-col items-center justify-between">
|
||||
|
@ -2,107 +2,41 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage, writeStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { dateToUnix, useNostr } from 'nostr-react';
|
||||
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
|
||||
|
||||
const config: Config = {
|
||||
dictionaries: [names],
|
||||
};
|
||||
|
||||
const defaultAvatars = [
|
||||
'https://bafybeidfsbrzqbvontmucteomoz2rkrxugu462l5hyhh6uioslkfzzs4oq.ipfs.w3s.link/avatar-11.png',
|
||||
'https://bafybeid7mrvznbnd6r2ju2iu7lsxkcikufys6z6ssy5ldxrxq5qh3yqf4u.ipfs.w3s.link/avatar-12.png',
|
||||
'https://bafybeih5gpwu53ohui6p7scekjpxjk2d4lusq2jqohqhjsvhfkeu56ea4e.ipfs.w3s.link/avatar-13.png',
|
||||
'https://bafybeibpbvrpuphkerjygdbnh26av5brqggzunbbbmfl3ozlvcn2mj6zxa.ipfs.w3s.link/avatar-14.png',
|
||||
'https://bafybeia4ue4loinuflu7y5q3xu6hcvt653mzw5yorw25oarf2wqksig4ma.ipfs.w3s.link/avatar-15.png',
|
||||
'https://bafybeib3gzl6n2bebiru2cpkdljmlzbtqfsl6xcnqtabxt6jrpj7l7ltm4.ipfs.w3s.link/avatar-16.png',
|
||||
];
|
||||
|
||||
const defaultBanners = [
|
||||
'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg',
|
||||
'https://bafybeiderllqadxsikh3envikobmyka3uwgojriwh6epctqartq2loswyi.ipfs.w3s.link/banner-2.jpg',
|
||||
'https://bafybeiba4tifde2kczvd26vxhbb5jpqi3wmgvccpkcrle4hse2cqrwlwiy.ipfs.w3s.link/banner-3.jpg',
|
||||
'https://bafybeifqpny2eom7ccvmaguxxxfajutmn5h3fotaasga7gce2xfx37p6oy.ipfs.w3s.link/banner-4.jpg',
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
const { publish } = useNostr();
|
||||
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
|
||||
const [relays] = useLocalStorage('relays');
|
||||
|
||||
const [type, setType] = useState('password');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [privKey] = useState(() => generatePrivateKey());
|
||||
const [name] = useState(() => uniqueNamesGenerator(config).toString());
|
||||
const [avatar] = useState(
|
||||
() => defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]
|
||||
);
|
||||
const [banner] = useState(
|
||||
() => defaultBanners[Math.floor(Math.random() * defaultBanners.length)]
|
||||
);
|
||||
|
||||
const pubKey = getPublicKey(privKey);
|
||||
const npub = nip19.npubEncode(pubKey);
|
||||
const nsec = nip19.nsecEncode(privKey);
|
||||
|
||||
// auto-generated profile
|
||||
const data = {
|
||||
display_name: name,
|
||||
name: name,
|
||||
username: name.toLowerCase(),
|
||||
picture: avatar,
|
||||
banner: banner,
|
||||
};
|
||||
|
||||
const createAccount = async () => {
|
||||
setLoading(true);
|
||||
|
||||
// publish account to relays
|
||||
const event: any = {
|
||||
content: JSON.stringify(data),
|
||||
created_at: dateToUnix(),
|
||||
kind: 0,
|
||||
pubkey: pubKey,
|
||||
tags: [],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privKey);
|
||||
publish(event);
|
||||
|
||||
// save account to database
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
await db.execute(
|
||||
`INSERT INTO accounts (privkey, pubkey, npub, nsec, current, metadata) VALUES ("${privKey}", "${pubKey}", "${npub}", "${nsec}", "1", '${JSON.stringify(
|
||||
data
|
||||
)}')`
|
||||
);
|
||||
await db.close();
|
||||
|
||||
// set currentUser in global state
|
||||
currentUser.set({
|
||||
metadata: JSON.stringify(data),
|
||||
npub: npub,
|
||||
privkey: privKey,
|
||||
pubkey: pubKey,
|
||||
});
|
||||
|
||||
// redirect to pre-follow
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding/following');
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const showNsec = () => {
|
||||
const showPrivateKey = () => {
|
||||
if (type === 'password') {
|
||||
setType('text');
|
||||
} else {
|
||||
@ -110,19 +44,69 @@ export default function Page() {
|
||||
}
|
||||
};
|
||||
|
||||
// auto-generated profile
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
display_name: name,
|
||||
name: name,
|
||||
username: name.toLowerCase(),
|
||||
picture: 'https://bafybeidfsbrzqbvontmucteomoz2rkrxugu462l5hyhh6uioslkfzzs4oq.ipfs.w3s.link/avatar-11.png',
|
||||
banner: 'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg',
|
||||
}),
|
||||
[name]
|
||||
);
|
||||
|
||||
const insertDB = useCallback(async () => {
|
||||
await db.execute(
|
||||
`INSERT INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubKey}", "${privKey}", "${npub}", "${nsec}", '${JSON.stringify(data)}')`
|
||||
);
|
||||
}, [data, db, npub, nsec, privKey, pubKey]);
|
||||
|
||||
const createAccount = async () => {
|
||||
setLoading(true);
|
||||
|
||||
// build event
|
||||
const event: any = {
|
||||
content: JSON.stringify(data),
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 0,
|
||||
pubkey: pubKey,
|
||||
tags: [],
|
||||
};
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, privKey);
|
||||
|
||||
insertDB()
|
||||
.then(() => {
|
||||
// publish to relays
|
||||
relayPool.publish(event, relays);
|
||||
// set currentUser in global state
|
||||
writeStorage('current-user', {
|
||||
metadata: JSON.stringify(data),
|
||||
npub: npub,
|
||||
privkey: privKey,
|
||||
pubkey: pubKey,
|
||||
});
|
||||
// redirect to pre-follow
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/onboarding/create/pre-follows');
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-between px-8">
|
||||
<div>{/* spacer */}</div>
|
||||
<motion.div layoutId="form">
|
||||
<div className="mb-8 flex flex-col gap-3">
|
||||
<motion.h1
|
||||
layoutId="title"
|
||||
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Create new key
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
Lume will generate key with default profile for you, you can edit it later, and please
|
||||
store your key safely so you can restore your account or use other client
|
||||
Lume will generate key with default profile for you, you can edit it later, and please store your key safely so you can restore your
|
||||
account or use other client
|
||||
</motion.h2>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
@ -146,7 +130,7 @@ export default function Page() {
|
||||
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-600"
|
||||
/>
|
||||
<button
|
||||
onClick={() => showNsec()}
|
||||
onClick={() => showPrivateKey()}
|
||||
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700">
|
||||
{type === 'password' ? (
|
||||
<EyeClosedIcon className="h-5 w-5 text-zinc-500 group-hover:text-zinc-200" />
|
||||
@ -157,19 +141,12 @@ export default function Page() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-sm font-semibold text-zinc-400">
|
||||
Default Profile (you can change it later)
|
||||
</label>
|
||||
<label className="text-sm font-semibold text-zinc-400">Default Profile (you can change it later)</label>
|
||||
<div className="relative max-w-sm shrink-0 before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:opacity-0 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-blue-500/100 dark:focus-within:after:shadow-blue-500/20">
|
||||
<div className="relative max-w-sm rounded-lg border border-black/5 px-3.5 py-4 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-600">
|
||||
<div className="flex space-x-4">
|
||||
<div className="relative h-10 w-10 rounded-full">
|
||||
<Image
|
||||
className="inline-block rounded-full"
|
||||
src={data.picture}
|
||||
alt=""
|
||||
fill={true}
|
||||
/>
|
||||
<Image className="inline-block rounded-full" src={data.picture} alt="" fill={true} />
|
||||
</div>
|
||||
<div className="flex-1 space-y-4 py-1">
|
||||
<div className="flex items-center gap-2">
|
||||
@ -193,18 +170,8 @@ export default function Page() {
|
||||
<motion.div layoutId="action" className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{loading === true ? (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
@ -226,13 +193,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
@ -2,29 +2,29 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
|
||||
import { truncate } from '@utils/truncate';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
|
||||
import data from '@assets/directory.json';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { CheckCircledIcon } from '@radix-ui/react-icons';
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useState } from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react';
|
||||
|
||||
const shuffle = (arr: { name: string; avatar: string; npub: string }[]) => [...arr].sort(() => Math.random() - 0.5);
|
||||
|
||||
export default function Page() {
|
||||
const db: any = useContext(DatabaseContext);
|
||||
const router = useRouter();
|
||||
const shuffle = (arr) => [...arr].sort(() => Math.random() - 0.5);
|
||||
|
||||
const [follow, setFollow] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [list] = useState(shuffle(data));
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
|
||||
const followUser = (e) => {
|
||||
const npub = e.currentTarget.getAttribute('data-npub');
|
||||
@ -32,15 +32,12 @@ export default function Page() {
|
||||
};
|
||||
|
||||
const insertDB = async () => {
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
await db.execute(
|
||||
`INSERT INTO follows (pubkey, account) VALUES ("${$currentUser.pubkey}", "${$currentUser.pubkey}")`
|
||||
);
|
||||
// self follow
|
||||
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${currentUser.pubkey}", "${currentUser.pubkey}", "0")`);
|
||||
// follow selected
|
||||
follow.forEach(async (npub) => {
|
||||
const { data } = nip19.decode(npub);
|
||||
await db.execute(
|
||||
`INSERT INTO follows (pubkey, account) VALUES ("${data}", "${$currentUser.pubkey}")`
|
||||
);
|
||||
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${data}", "${currentUser.pubkey}", "0")`);
|
||||
});
|
||||
};
|
||||
|
||||
@ -60,14 +57,11 @@ export default function Page() {
|
||||
<div>{/* spacer */}</div>
|
||||
<motion.div layoutId="form" className="flex flex-col">
|
||||
<div className="mb-8 flex flex-col gap-3">
|
||||
<motion.h1
|
||||
layoutId="title"
|
||||
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Choose 10 people you want to following
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
For better experiences, you should follow the people you care about to personalize your
|
||||
newsfeed, otherwise you will be very bored
|
||||
For better experiences, you should follow the people you care about to personalize your newsfeed, otherwise you will be very bored
|
||||
</motion.h2>
|
||||
</div>
|
||||
<div className="h-full w-full shrink">
|
||||
@ -81,27 +75,14 @@ export default function Page() {
|
||||
follow.includes(item.npub) ? 'bg-zinc-800' : ''
|
||||
}`}>
|
||||
<div className="relative h-10 w-10 flex-shrink-0">
|
||||
<Image
|
||||
className="rounded-full object-cover"
|
||||
src={item.avatar}
|
||||
alt={item.name}
|
||||
fill={true}
|
||||
/>
|
||||
<Image className="rounded-full object-cover" src={item.avatar} alt={item.name} fill={true} />
|
||||
</div>
|
||||
<div className="inline-flex flex-1 items-center justify-between">
|
||||
<div>
|
||||
<p className="truncate text-sm font-medium text-zinc-200">{item.name}</p>
|
||||
<p className="text-sm leading-tight text-zinc-500">
|
||||
{truncate(item.npub, 16, ' .... ')}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
{follow.includes(item.npub) ? (
|
||||
<CheckCircledIcon className="h-4 w-4 text-green-500" />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<p className="text-sm leading-tight text-zinc-500">{truncate(item.npub, 16, ' .... ')}</p>
|
||||
</div>
|
||||
<div>{follow.includes(item.npub) ? <CheckCircledIcon className="h-4 w-4 text-green-500" /> : <></>}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@ -111,18 +92,8 @@ export default function Page() {
|
||||
<motion.div layoutId="action" className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{loading === true ? (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
@ -145,13 +116,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
@ -1,125 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import {
|
||||
JSXElementConstructor,
|
||||
ReactElement,
|
||||
ReactFragment,
|
||||
ReactPortal,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
export default function Page() {
|
||||
const [follows, setFollows] = useState([null]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { pubkey }: any = router.query;
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
authors: [pubkey],
|
||||
kinds: [3],
|
||||
},
|
||||
});
|
||||
|
||||
onEvent((rawMetadata) => {
|
||||
try {
|
||||
setFollows(rawMetadata.tags);
|
||||
} catch (err) {
|
||||
console.error(err, rawMetadata);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
const insertDB = async () => {
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
follows.forEach(async (item) => {
|
||||
if (item) {
|
||||
await db.execute(
|
||||
`INSERT OR IGNORE INTO follows (pubkey, account) VALUES ("${item[1]}", "${pubkey}")`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (follows !== null && follows.length > 0) {
|
||||
insertDB()
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push('/');
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [follows, pubkey, router]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-between px-8">
|
||||
<div>{/* spacer */}</div>
|
||||
<motion.div layoutId="form">
|
||||
<div className="mb-8 flex flex-col gap-3">
|
||||
<motion.h1
|
||||
layoutId="title"
|
||||
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Fetching your follows...
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
Not only profile, every nostr client can sync your follows list when you move to a new
|
||||
client, so please keep your key safely (again)
|
||||
</motion.h2>
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div layoutId="action" className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{loading === true ? (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
<OnboardingLayout>{page}</OnboardingLayout>
|
||||
</BaseLayout>
|
||||
);
|
||||
};
|
@ -1,134 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useNostrEvents } from 'nostr-react';
|
||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||
import {
|
||||
JSXElementConstructor,
|
||||
ReactElement,
|
||||
ReactFragment,
|
||||
ReactPortal,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
const { privkey }: any = router.query;
|
||||
|
||||
const [account, setAccount] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const pubkey = privkey ? getPublicKey(privkey) : null;
|
||||
const npub = privkey ? nip19.npubEncode(pubkey) : null;
|
||||
const nsec = privkey ? nip19.nsecEncode(privkey) : null;
|
||||
|
||||
const { onEvent } = useNostrEvents({
|
||||
filter: {
|
||||
authors: [pubkey],
|
||||
kinds: [0],
|
||||
},
|
||||
});
|
||||
|
||||
onEvent((rawMetadata) => {
|
||||
try {
|
||||
const metadata: any = JSON.parse(rawMetadata.content);
|
||||
setAccount(metadata);
|
||||
} catch (err) {
|
||||
console.error(err, rawMetadata);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
const insertDB = async () => {
|
||||
// save account to database
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
await db.execute(
|
||||
`INSERT INTO accounts (privkey, pubkey, npub, nsec, current, metadata) VALUES ("${privkey}", "${pubkey}", "${npub}", "${nsec}", "1", '${JSON.stringify(
|
||||
account
|
||||
)}')`
|
||||
);
|
||||
await db.close();
|
||||
};
|
||||
|
||||
if (account !== null) {
|
||||
insertDB()
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
router.push({
|
||||
pathname: '/onboarding/fetch-follows',
|
||||
query: { pubkey: pubkey },
|
||||
});
|
||||
}, 1500);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [account, npub, nsec, privkey, pubkey, router]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-between px-8">
|
||||
<div>{/* spacer */}</div>
|
||||
<motion.div layoutId="form">
|
||||
<div className="mb-8 flex flex-col gap-3">
|
||||
<motion.h1
|
||||
layoutId="title"
|
||||
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Fetching your profile...
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
As long as you have private key, you alway can sync your profile on every nostr client,
|
||||
so please keep your key safely
|
||||
</motion.h2>
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div layoutId="action" className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{loading === true ? (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
<OnboardingLayout>{page}</OnboardingLayout>
|
||||
</BaseLayout>
|
||||
);
|
||||
};
|
@ -10,9 +10,7 @@ export default function Page() {
|
||||
<div className="flex h-full flex-col justify-between px-8">
|
||||
<div>{/* spacer */}</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<motion.h1
|
||||
layoutId="title"
|
||||
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Other social network require email/password
|
||||
<br />
|
||||
nostr use{' '}
|
||||
@ -21,8 +19,8 @@ export default function Page() {
|
||||
</span>
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
If you have used nostr before, you can import your own private key. Otherwise, you can
|
||||
create a new key or use auto-generated account created by system.
|
||||
If you have used nostr before, you can import your own private key. Otherwise, you can create a new key or use auto-generated account
|
||||
created by system.
|
||||
</motion.h2>
|
||||
<motion.div layoutId="form"></motion.div>
|
||||
<motion.div layoutId="action" className="mt-4 flex gap-2">
|
||||
@ -32,7 +30,7 @@ export default function Page() {
|
||||
Create new key
|
||||
</Link>
|
||||
<Link
|
||||
href="/onboarding/import"
|
||||
href="/onboarding/login"
|
||||
className="hover:bg-zinc-900/2.5 transform rounded-lg border border-black/5 bg-zinc-800 px-3.5 py-2 font-medium ring-1 ring-inset ring-zinc-900/10 hover:text-zinc-900 active:translate-y-1 dark:text-zinc-300 dark:ring-white/10 dark:hover:bg-zinc-700 dark:hover:text-white">
|
||||
Login with private key
|
||||
</Link>
|
||||
@ -44,13 +42,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
|
122
src/pages/onboarding/login/fetch.tsx
Normal file
122
src/pages/onboarding/login/fetch.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||
|
||||
import { DatabaseContext } from '@components/contexts/database';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { motion } from 'framer-motion';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useMemo, useState } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const { db }: any = useContext(DatabaseContext);
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [relays] = useLocalStorage('relays');
|
||||
|
||||
const router = useRouter();
|
||||
const { privkey }: any = router.query;
|
||||
|
||||
const pubkey = useMemo(() => (privkey ? getPublicKey(privkey) : null), [privkey]);
|
||||
|
||||
// save account to database
|
||||
const insertAccount = useCallback(
|
||||
async (metadata) => {
|
||||
if (loading === false) {
|
||||
const npub = privkey ? nip19.npubEncode(pubkey) : null;
|
||||
const nsec = privkey ? nip19.nsecEncode(privkey) : null;
|
||||
await db.execute(
|
||||
`INSERT OR IGNORE INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubkey}", "${privkey}", "${npub}", "${nsec}", '${metadata}')`
|
||||
);
|
||||
setLoading(true);
|
||||
}
|
||||
},
|
||||
[db, privkey, pubkey, loading]
|
||||
);
|
||||
|
||||
// save follows to database
|
||||
const insertFollows = useCallback(
|
||||
async (follows) => {
|
||||
follows.forEach(async (item) => {
|
||||
if (item) {
|
||||
await db.execute(`INSERT OR IGNORE INTO follows (pubkey, account, kind) VALUES ("${item[1]}", "${pubkey}", "0")`);
|
||||
}
|
||||
});
|
||||
},
|
||||
[db, pubkey]
|
||||
);
|
||||
|
||||
relayPool.subscribe(
|
||||
[
|
||||
{
|
||||
authors: [pubkey],
|
||||
kinds: [0, 3],
|
||||
since: 0,
|
||||
},
|
||||
],
|
||||
relays,
|
||||
(event: any) => {
|
||||
if (event.kind === 0) {
|
||||
insertAccount(event.content);
|
||||
} else {
|
||||
if (event.tags.length > 0) {
|
||||
insertFollows(event.tags);
|
||||
}
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
(events: any, relayURL: any) => {
|
||||
console.log(events, relayURL);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-between px-8">
|
||||
<div>{/* spacer */}</div>
|
||||
<motion.div layoutId="form">
|
||||
<div className="mb-8 flex flex-col gap-3">
|
||||
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Fetching your profile...
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
As long as you have private key, you alway can sync your profile and follows list on every nostr client, so please keep your key safely
|
||||
</motion.h2>
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div layoutId="action" className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{loading === true ? (
|
||||
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
) : (
|
||||
<Link
|
||||
href="/"
|
||||
className="transform rounded-lg bg-[radial-gradient(ellipse_at_bottom_right,_var(--tw-gradient-stops))] from-gray-300 via-fuchsia-600 to-orange-600 px-3.5 py-2 font-medium active:translate-y-1 disabled:cursor-not-allowed disabled:opacity-30">
|
||||
<span className="drop-shadow-lg">Finish</span>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
<OnboardingLayout>{page}</OnboardingLayout>
|
||||
</BaseLayout>
|
||||
);
|
||||
};
|
@ -44,7 +44,7 @@ export default function Page() {
|
||||
|
||||
try {
|
||||
router.push({
|
||||
pathname: '/onboarding/fetch-profile',
|
||||
pathname: '/onboarding/login/fetch',
|
||||
query: { privkey: privkey },
|
||||
});
|
||||
} catch (error) {
|
||||
@ -60,14 +60,12 @@ export default function Page() {
|
||||
<div>{/* spacer */}</div>
|
||||
<motion.div layoutId="form">
|
||||
<div className="mb-8 flex flex-col gap-3">
|
||||
<motion.h1
|
||||
layoutId="title"
|
||||
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Import your private key
|
||||
</motion.h1>
|
||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
||||
You can import private key format as hex string or nsec. If you have installed Nostr
|
||||
Connect compality wallet in your mobile, you can connect by scan QR Code below
|
||||
You can import private key format as hex string or nsec. If you have installed Nostr Connect compality wallet in your mobile, you can
|
||||
connect by scan QR Code below
|
||||
</motion.h2>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
@ -85,18 +83,8 @@ export default function Page() {
|
||||
<motion.div layoutId="action" className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{isSubmitting ? (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
@ -117,13 +105,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
@ -2,13 +2,14 @@
|
||||
import BaseLayout from '@layouts/baseLayout';
|
||||
import UserLayout from '@layouts/userLayout';
|
||||
|
||||
import { currentUser } from '@stores/currentUser';
|
||||
import { RelayContext } from '@components/contexts/relay';
|
||||
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { dateToUnix } from '@utils/getDate';
|
||||
|
||||
import { useLocalStorage } from '@rehooks/local-storage';
|
||||
import { useRouter } from 'next/router';
|
||||
import { dateToUnix, useNostr } from 'nostr-react';
|
||||
import { getEventHash, signEvent } from 'nostr-tools';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useState } from 'react';
|
||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
|
||||
@ -24,15 +25,14 @@ type FormValues = {
|
||||
|
||||
// TODO: update the design
|
||||
export default function Page() {
|
||||
const relayPool: any = useContext(RelayContext);
|
||||
const [relays]: any = useLocalStorage('relays');
|
||||
|
||||
const router = useRouter();
|
||||
const { publish } = useNostr();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const $currentUser: any = useStore(currentUser);
|
||||
const profile =
|
||||
$currentUser.metadata !== undefined
|
||||
? JSON.parse($currentUser.metadata)
|
||||
: { display_name: null, username: null };
|
||||
const [currentUser]: any = useLocalStorage('current-user');
|
||||
const profile = currentUser.metadata !== undefined ? JSON.parse(currentUser.metadata) : { display_name: null, username: null };
|
||||
|
||||
const {
|
||||
register,
|
||||
@ -48,28 +48,25 @@ export default function Page() {
|
||||
content: JSON.stringify(data),
|
||||
created_at: dateToUnix(),
|
||||
kind: 0,
|
||||
pubkey: $currentUser.pubkey,
|
||||
pubkey: currentUser.pubkey,
|
||||
tags: [],
|
||||
};
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = signEvent(event, $currentUser.privkey);
|
||||
publish(event);
|
||||
event.sig = signEvent(event, currentUser.privkey);
|
||||
|
||||
relayPool.publish(event, relays);
|
||||
|
||||
// save account to database
|
||||
const db = await Database.load('sqlite:lume.db');
|
||||
await db.execute(
|
||||
`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${
|
||||
$currentUser.pubkey
|
||||
}"`
|
||||
);
|
||||
await db.close();
|
||||
await db.execute(`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${currentUser.pubkey}"`);
|
||||
|
||||
// set currentUser in global state
|
||||
currentUser.set({
|
||||
metadata: JSON.stringify(data),
|
||||
npub: $currentUser.npub,
|
||||
privkey: $currentUser.privkey,
|
||||
pubkey: $currentUser.pubkey,
|
||||
npub: currentUser.npub,
|
||||
privkey: currentUser.privkey,
|
||||
pubkey: currentUser.pubkey,
|
||||
});
|
||||
|
||||
// redirect to newsfeed
|
||||
@ -80,16 +77,11 @@ export default function Page() {
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="flex h-full w-full flex-col justify-between px-6">
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="flex h-full w-full flex-col justify-between px-6">
|
||||
<div className="mb-8 flex flex-col gap-3 pt-8">
|
||||
<h1 className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||
Update profile
|
||||
</h1>
|
||||
<h1 className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">Update profile</h1>
|
||||
<h2 className="w-3/4 text-zinc-400">
|
||||
Your profile will be published to all relays, as long as you have the private key, you
|
||||
always can recover your profile in any client
|
||||
Your profile will be published to all relays, as long as you have the private key, you always can recover your profile in any client
|
||||
</h2>
|
||||
</div>
|
||||
<fieldset className="flex flex-col gap-2">
|
||||
@ -105,9 +97,7 @@ export default function Page() {
|
||||
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.display_name && <p>{errors.display_name.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.display_name && <p>{errors.display_name.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -122,9 +112,7 @@ export default function Page() {
|
||||
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.name && <p>{errors.name.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.name && <p>{errors.name.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -139,9 +127,7 @@ export default function Page() {
|
||||
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.username && <p>{errors.username.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.username && <p>{errors.username.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -156,9 +142,7 @@ export default function Page() {
|
||||
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.picture && <p>{errors.picture.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.picture && <p>{errors.picture.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -173,9 +157,7 @@ export default function Page() {
|
||||
className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.banner && <p>{errors.banner.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.banner && <p>{errors.banner.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4">
|
||||
@ -190,27 +172,15 @@ export default function Page() {
|
||||
className="relative h-24 w-full resize-none rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-red-400">
|
||||
{errors.about && <p>{errors.about.message}</p>}
|
||||
</span>
|
||||
<span className="text-sm text-red-400">{errors.about && <p>{errors.about.message}</p>}</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div className="pb-5">
|
||||
<div className="flex h-10 items-center">
|
||||
{loading === true ? (
|
||||
<svg
|
||||
className="h-5 w-5 animate-spin text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
@ -231,13 +201,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(
|
||||
page:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
||||
| ReactFragment
|
||||
| ReactPortal
|
||||
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||
) {
|
||||
return (
|
||||
<BaseLayout>
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
export const currentUser = persistentAtom(
|
||||
'currentUser',
|
||||
{},
|
||||
{
|
||||
encode: JSON.stringify,
|
||||
decode: JSON.parse,
|
||||
}
|
||||
);
|
@ -1,10 +0,0 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
export const follows = persistentAtom('follows', [], {
|
||||
encode(value) {
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
decode(value) {
|
||||
return JSON.parse(value);
|
||||
},
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import { persistentAtom } from '@nanostores/persistent';
|
||||
|
||||
export const relays = persistentAtom(
|
||||
'relays',
|
||||
[
|
||||
'wss://relay.uselume.xyz',
|
||||
'wss://nostr-pub.wellorder.net',
|
||||
'wss://nostr.bongbong.com',
|
||||
'wss://nostr.zebedee.cloud',
|
||||
'wss://nostr.fmt.wiz.biz',
|
||||
'wss://nostr.walletofsatoshi.com',
|
||||
'wss://relay.snort.social',
|
||||
'wss://offchain.pub',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.damus.io',
|
||||
],
|
||||
{
|
||||
encode(value) {
|
||||
return JSON.stringify(value);
|
||||
},
|
||||
decode(value) {
|
||||
return JSON.parse(value);
|
||||
},
|
||||
}
|
||||
);
|
@ -11,3 +11,9 @@ export const hoursAgo = (numOfHours, date = new Date()) => {
|
||||
|
||||
return hoursAgo;
|
||||
};
|
||||
|
||||
export const dateToUnix = (_date?: Date) => {
|
||||
const date = _date || new Date();
|
||||
|
||||
return Math.floor(date.getTime() / 1000);
|
||||
};
|
||||
|
@ -6,7 +6,6 @@
|
||||
"@layouts/*": ["src/layouts/*"],
|
||||
"@components/*": ["src/components/*"],
|
||||
"@utils/*": ["src/utils/*"],
|
||||
"@stores/*": ["src/stores/*"],
|
||||
"@assets/*": ["src/assets/*"]
|
||||
},
|
||||
"target": "es2017",
|
||||
|
Loading…
Reference in New Issue
Block a user