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",
|
"endOfLine": "lf",
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"bracketSameLine": true,
|
"bracketSameLine": true,
|
||||||
"importOrder": [
|
"importOrder": ["^@layouts/(.*)$", "^@pages/(.*)$", "^@components/(.*)$", "^@utils/(.*)$", "^@stores/(.*)$", "<THIRD_PARTY_MODULES>", "^[./]"],
|
||||||
"^@layouts/(.*)$",
|
|
||||||
"^@pages/(.*)$",
|
|
||||||
"^@components/(.*)$",
|
|
||||||
"^@utils/(.*)$",
|
|
||||||
"^@stores/(.*)$",
|
|
||||||
"^@assets/(.*)$",
|
|
||||||
"<THIRD_PARTY_MODULES>",
|
|
||||||
"^[./]"
|
|
||||||
],
|
|
||||||
"importOrderSeparation": true,
|
"importOrderSeparation": true,
|
||||||
"importOrderSortSpecifiers": true,
|
"importOrderSortSpecifiers": true,
|
||||||
"plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
|
"plugins": ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
|
||||||
|
@ -9,6 +9,9 @@ module.exports = removeImports({
|
|||||||
typescript: {
|
typescript: {
|
||||||
ignoreBuildErrors: true,
|
ignoreBuildErrors: true,
|
||||||
},
|
},
|
||||||
|
experimental: {
|
||||||
|
scrollRestoration: true,
|
||||||
|
},
|
||||||
webpack: (config) => {
|
webpack: (config) => {
|
||||||
config.experiments = { ...config.experiments, topLevelAwait: true };
|
config.experiments = { ...config.experiments, topLevelAwait: true };
|
||||||
return config;
|
return config;
|
||||||
|
22
package.json
22
package.json
@ -12,11 +12,10 @@
|
|||||||
"**/*": "prettier --write --ignore-unknown"
|
"**/*": "prettier --write --ignore-unknown"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nanostores/persistent": "^0.7.0",
|
|
||||||
"@nanostores/react": "^0.4.1",
|
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.3",
|
"@radix-ui/react-dropdown-menu": "^2.0.3",
|
||||||
"@radix-ui/react-icons": "^1.2.0",
|
"@radix-ui/react-icons": "^1.2.0",
|
||||||
|
"@rehooks/local-storage": "^2.4.4",
|
||||||
"@tauri-apps/api": "^1.2.0",
|
"@tauri-apps/api": "^1.2.0",
|
||||||
"@uiw/react-markdown-preview": "^4.1.9",
|
"@uiw/react-markdown-preview": "^4.1.9",
|
||||||
"@uiw/react-md-editor": "^3.20.5",
|
"@uiw/react-md-editor": "^3.20.5",
|
||||||
@ -27,8 +26,8 @@
|
|||||||
"nanostores": "^0.7.4",
|
"nanostores": "^0.7.4",
|
||||||
"next": "^13.2.1",
|
"next": "^13.2.1",
|
||||||
"next-remove-imports": "^1.0.10",
|
"next-remove-imports": "^1.0.10",
|
||||||
"nostr-react": "^0.6.4",
|
"nostr-relaypool": "^0.5.3",
|
||||||
"nostr-tools": "^1.6.0",
|
"nostr-tools": "^1.7.1",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@ -37,25 +36,26 @@
|
|||||||
"react-player": "^2.11.2",
|
"react-player": "^2.11.2",
|
||||||
"react-virtuoso": "^4.1.0",
|
"react-virtuoso": "^4.1.0",
|
||||||
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql",
|
"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": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@tauri-apps/cli": "^1.2.3",
|
"@tauri-apps/cli": "^1.2.3",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.1.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
||||||
"@types/node": "^18.14.1",
|
"@types/node": "^18.14.2",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
"@typescript-eslint/parser": "^5.53.0",
|
"@typescript-eslint/parser": "^5.54.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"csstype": "^3.1.1",
|
"csstype": "^3.1.1",
|
||||||
"eslint": "^8.34.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-config-next": "^13.2.1",
|
"eslint-config-next": "^13.2.1",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.1.2",
|
"lint-staged": "^13.1.2",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
|
397
pnpm-lock.yaml
397
pnpm-lock.yaml
@ -1,40 +1,39 @@
|
|||||||
lockfileVersion: 5.4
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@nanostores/persistent': ^0.7.0
|
|
||||||
'@nanostores/react': ^0.4.1
|
|
||||||
'@radix-ui/react-dialog': ^1.0.2
|
'@radix-ui/react-dialog': ^1.0.2
|
||||||
'@radix-ui/react-dropdown-menu': ^2.0.3
|
'@radix-ui/react-dropdown-menu': ^2.0.3
|
||||||
'@radix-ui/react-icons': ^1.2.0
|
'@radix-ui/react-icons': ^1.2.0
|
||||||
|
'@rehooks/local-storage': ^2.4.4
|
||||||
'@tailwindcss/typography': ^0.5.9
|
'@tailwindcss/typography': ^0.5.9
|
||||||
'@tauri-apps/api': ^1.2.0
|
'@tauri-apps/api': ^1.2.0
|
||||||
'@tauri-apps/cli': ^1.2.3
|
'@tauri-apps/cli': ^1.2.3
|
||||||
'@trivago/prettier-plugin-sort-imports': ^4.1.0
|
'@trivago/prettier-plugin-sort-imports': ^4.1.1
|
||||||
'@types/node': ^18.14.1
|
'@types/node': ^18.14.2
|
||||||
'@types/react': ^18.0.28
|
'@types/react': ^18.0.28
|
||||||
'@types/react-dom': ^18.0.11
|
'@types/react-dom': ^18.0.11
|
||||||
'@typescript-eslint/eslint-plugin': ^5.53.0
|
'@typescript-eslint/eslint-plugin': ^5.54.0
|
||||||
'@typescript-eslint/parser': ^5.53.0
|
'@typescript-eslint/parser': ^5.54.0
|
||||||
'@uiw/react-markdown-preview': ^4.1.9
|
'@uiw/react-markdown-preview': ^4.1.9
|
||||||
'@uiw/react-md-editor': ^3.20.5
|
'@uiw/react-md-editor': ^3.20.5
|
||||||
autoprefixer: ^10.4.13
|
autoprefixer: ^10.4.13
|
||||||
bitcoin-address-validation: ^2.2.1
|
bitcoin-address-validation: ^2.2.1
|
||||||
boring-avatars: ^1.7.0
|
boring-avatars: ^1.7.0
|
||||||
csstype: ^3.1.1
|
csstype: ^3.1.1
|
||||||
eslint: ^8.34.0
|
eslint: ^8.35.0
|
||||||
eslint-config-next: ^13.2.1
|
eslint-config-next: ^13.2.1
|
||||||
eslint-config-prettier: ^8.6.0
|
eslint-config-prettier: ^8.6.0
|
||||||
eslint-plugin-react: ^7.32.2
|
eslint-plugin-react: ^7.32.2
|
||||||
eslint-plugin-react-hooks: ^4.6.0
|
eslint-plugin-react-hooks: ^4.6.0
|
||||||
framer-motion: ^9.1.7
|
framer-motion: ^9.1.7
|
||||||
husky: ^8.0.0
|
husky: ^8.0.3
|
||||||
lint-staged: ^13.1.2
|
lint-staged: ^13.1.2
|
||||||
moment: ^2.29.4
|
moment: ^2.29.4
|
||||||
nanostores: ^0.7.4
|
nanostores: ^0.7.4
|
||||||
next: ^13.2.1
|
next: ^13.2.1
|
||||||
next-remove-imports: ^1.0.10
|
next-remove-imports: ^1.0.10
|
||||||
nostr-react: ^0.6.4
|
nostr-relaypool: ^0.5.3
|
||||||
nostr-tools: ^1.6.0
|
nostr-tools: ^1.7.1
|
||||||
postcss: ^8.4.21
|
postcss: ^8.4.21
|
||||||
prettier: ^2.8.4
|
prettier: ^2.8.4
|
||||||
prettier-plugin-tailwindcss: ^0.2.3
|
prettier-plugin-tailwindcss: ^0.2.3
|
||||||
@ -50,13 +49,13 @@ specifiers:
|
|||||||
tauri-plugin-sql-api: github:tauri-apps/tauri-plugin-sql
|
tauri-plugin-sql-api: github:tauri-apps/tauri-plugin-sql
|
||||||
typescript: ^4.9.5
|
typescript: ^4.9.5
|
||||||
unique-names-generator: ^4.7.1
|
unique-names-generator: ^4.7.1
|
||||||
|
ws: ^8.12.1
|
||||||
|
|
||||||
dependencies:
|
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-dialog': 1.0.2_zula6vjvt3wdocc4mwcxqa6nzi
|
||||||
'@radix-ui/react-dropdown-menu': 2.0.3_zula6vjvt3wdocc4mwcxqa6nzi
|
'@radix-ui/react-dropdown-menu': 2.0.3_zula6vjvt3wdocc4mwcxqa6nzi
|
||||||
'@radix-ui/react-icons': 1.2.0_react@18.2.0
|
'@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
|
'@tauri-apps/api': 1.2.0
|
||||||
'@uiw/react-markdown-preview': 4.1.9_zula6vjvt3wdocc4mwcxqa6nzi
|
'@uiw/react-markdown-preview': 4.1.9_zula6vjvt3wdocc4mwcxqa6nzi
|
||||||
'@uiw/react-md-editor': 3.20.5_zula6vjvt3wdocc4mwcxqa6nzi
|
'@uiw/react-md-editor': 3.20.5_zula6vjvt3wdocc4mwcxqa6nzi
|
||||||
@ -67,8 +66,8 @@ dependencies:
|
|||||||
nanostores: 0.7.4
|
nanostores: 0.7.4
|
||||||
next: 13.2.1_biqbaboplfbrettd7655fr4n2y
|
next: 13.2.1_biqbaboplfbrettd7655fr4n2y
|
||||||
next-remove-imports: 1.0.10
|
next-remove-imports: 1.0.10
|
||||||
nostr-react: 0.6.4_react@18.2.0
|
nostr-relaypool: 0.5.3_ws@8.12.1
|
||||||
nostr-tools: 1.6.0
|
nostr-tools: 1.7.1
|
||||||
qrcode.react: 3.1.0_react@18.2.0
|
qrcode.react: 3.1.0_react@18.2.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
@ -78,28 +77,29 @@ dependencies:
|
|||||||
react-virtuoso: 4.1.0_biqbaboplfbrettd7655fr4n2y
|
react-virtuoso: 4.1.0_biqbaboplfbrettd7655fr4n2y
|
||||||
tauri-plugin-sql-api: github.com/tauri-apps/tauri-plugin-sql/abd8759ef49e1ba441540a2260b453d43d86c7ee
|
tauri-plugin-sql-api: github.com/tauri-apps/tauri-plugin-sql/abd8759ef49e1ba441540a2260b453d43d86c7ee
|
||||||
unique-names-generator: 4.7.1
|
unique-names-generator: 4.7.1
|
||||||
|
ws: 8.12.1
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.7
|
'@tailwindcss/typography': 0.5.9_tailwindcss@3.2.7
|
||||||
'@tauri-apps/cli': 1.2.3
|
'@tauri-apps/cli': 1.2.3
|
||||||
'@trivago/prettier-plugin-sort-imports': 4.1.0_prettier@2.8.4
|
'@trivago/prettier-plugin-sort-imports': 4.1.1_prettier@2.8.4
|
||||||
'@types/node': 18.14.1
|
'@types/node': 18.14.2
|
||||||
'@types/react': 18.0.28
|
'@types/react': 18.0.28
|
||||||
'@types/react-dom': 18.0.11
|
'@types/react-dom': 18.0.11
|
||||||
'@typescript-eslint/eslint-plugin': 5.53.0_ny4s7qc6yg74faf3d6xty2ofzy
|
'@typescript-eslint/eslint-plugin': 5.54.0_6mj2wypvdnknez7kws2nfdgupi
|
||||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
autoprefixer: 10.4.13_postcss@8.4.21
|
autoprefixer: 10.4.13_postcss@8.4.21
|
||||||
csstype: 3.1.1
|
csstype: 3.1.1
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-config-next: 13.2.1_7kw3g6rralp5ps6mg3uyzz6azm
|
eslint-config-next: 13.2.1_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
eslint-config-prettier: 8.6.0_eslint@8.34.0
|
eslint-config-prettier: 8.6.0_eslint@8.35.0
|
||||||
eslint-plugin-react: 7.32.2_eslint@8.34.0
|
eslint-plugin-react: 7.32.2_eslint@8.35.0
|
||||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.34.0
|
eslint-plugin-react-hooks: 4.6.0_eslint@8.35.0
|
||||||
husky: 8.0.3
|
husky: 8.0.3
|
||||||
lint-staged: 13.1.2
|
lint-staged: 13.1.2
|
||||||
postcss: 8.4.21
|
postcss: 8.4.21
|
||||||
prettier: 2.8.4
|
prettier: 2.8.4
|
||||||
prettier-plugin-tailwindcss: 0.2.3_3p4xqifn6m4d44r76wgcnqfi3i
|
prettier-plugin-tailwindcss: 0.2.3_zmkqdpv3ldc45e6wei6qtrbrca
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
tailwindcss: 3.2.7_postcss@8.4.21
|
tailwindcss: 3.2.7_postcss@8.4.21
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
@ -350,8 +350,8 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@eslint/eslintrc/1.4.1:
|
/@eslint/eslintrc/2.0.0:
|
||||||
resolution: { integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== }
|
resolution: { integrity: sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
@ -367,6 +367,11 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
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:
|
/@floating-ui/core/0.7.3:
|
||||||
resolution: { integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg== }
|
resolution: { integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg== }
|
||||||
dev: false
|
dev: false
|
||||||
@ -411,6 +416,15 @@ packages:
|
|||||||
resolution: { integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== }
|
resolution: { integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== }
|
||||||
dev: true
|
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:
|
/@jridgewell/gen-mapping/0.1.1:
|
||||||
resolution: { integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== }
|
resolution: { integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== }
|
||||||
engines: { node: '>=6.0.0' }
|
engines: { node: '>=6.0.0' }
|
||||||
@ -449,27 +463,6 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec': 1.4.14
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
dev: false
|
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:
|
/@next/env/13.2.1:
|
||||||
resolution: { integrity: sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ== }
|
resolution: { integrity: sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ== }
|
||||||
dev: false
|
dev: false
|
||||||
@ -991,6 +984,14 @@ packages:
|
|||||||
'@babel/runtime': 7.21.0
|
'@babel/runtime': 7.21.0
|
||||||
dev: false
|
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:
|
/@rushstack/eslint-patch/1.2.0:
|
||||||
resolution: { integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== }
|
resolution: { integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== }
|
||||||
dev: true
|
dev: true
|
||||||
@ -1134,8 +1135,8 @@ packages:
|
|||||||
'@tauri-apps/cli-win32-x64-msvc': 1.2.3
|
'@tauri-apps/cli-win32-x64-msvc': 1.2.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@trivago/prettier-plugin-sort-imports/4.1.0_prettier@2.8.4:
|
/@trivago/prettier-plugin-sort-imports/4.1.1_prettier@2.8.4:
|
||||||
resolution: { integrity: sha512-aTr6QPFaPAAzPRFn9yWB/9yKi3ZAFqfGpxIGLPWuQfYJFGUed+W3KKwxntsoCiNvNE2iuKOg6haMo5KG8WXltg== }
|
resolution: { integrity: sha512-dQ2r2uzNr1x6pJsuh/8x0IRA3CBUB+pWEW3J/7N98axqt7SQSm+2fy0FLNXvXGg77xEDC7KHxJlHfLYyi7PDcw== }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@vue/compiler-sfc': 3.x
|
'@vue/compiler-sfc': 3.x
|
||||||
prettier: 2.x
|
prettier: 2.x
|
||||||
@ -1183,8 +1184,8 @@ packages:
|
|||||||
resolution: { integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== }
|
resolution: { integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/node/18.14.1:
|
/@types/node/18.14.2:
|
||||||
resolution: { integrity: sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== }
|
resolution: { integrity: sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA== }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/parse5/6.0.3:
|
/@types/parse5/6.0.3:
|
||||||
@ -1222,8 +1223,8 @@ packages:
|
|||||||
resolution: { integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== }
|
resolution: { integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin/5.53.0_ny4s7qc6yg74faf3d6xty2ofzy:
|
/@typescript-eslint/eslint-plugin/5.54.0_6mj2wypvdnknez7kws2nfdgupi:
|
||||||
resolution: { integrity: sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw== }
|
resolution: { integrity: sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@typescript-eslint/parser': ^5.0.0
|
'@typescript-eslint/parser': ^5.0.0
|
||||||
@ -1233,12 +1234,12 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
'@typescript-eslint/scope-manager': 5.53.0
|
'@typescript-eslint/scope-manager': 5.54.0
|
||||||
'@typescript-eslint/type-utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/type-utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
'@typescript-eslint/utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
grapheme-splitter: 1.0.4
|
grapheme-splitter: 1.0.4
|
||||||
ignore: 5.2.4
|
ignore: 5.2.4
|
||||||
natural-compare-lite: 1.4.0
|
natural-compare-lite: 1.4.0
|
||||||
@ -1250,8 +1251,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/parser/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm:
|
/@typescript-eslint/parser/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||||
resolution: { integrity: sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ== }
|
resolution: { integrity: sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
@ -1260,26 +1261,26 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 5.53.0
|
'@typescript-eslint/scope-manager': 5.54.0
|
||||||
'@typescript-eslint/types': 5.53.0
|
'@typescript-eslint/types': 5.54.0
|
||||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/scope-manager/5.53.0:
|
/@typescript-eslint/scope-manager/5.54.0:
|
||||||
resolution: { integrity: sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w== }
|
resolution: { integrity: sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 5.53.0
|
'@typescript-eslint/types': 5.54.0
|
||||||
'@typescript-eslint/visitor-keys': 5.53.0
|
'@typescript-eslint/visitor-keys': 5.54.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/type-utils/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm:
|
/@typescript-eslint/type-utils/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||||
resolution: { integrity: sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw== }
|
resolution: { integrity: sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '*'
|
eslint: '*'
|
||||||
@ -1288,23 +1289,23 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||||
'@typescript-eslint/utils': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
tsutils: 3.21.0_typescript@4.9.5
|
tsutils: 3.21.0_typescript@4.9.5
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/types/5.53.0:
|
/@typescript-eslint/types/5.54.0:
|
||||||
resolution: { integrity: sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A== }
|
resolution: { integrity: sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/typescript-estree/5.53.0_typescript@4.9.5:
|
/@typescript-eslint/typescript-estree/5.54.0_typescript@4.9.5:
|
||||||
resolution: { integrity: sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w== }
|
resolution: { integrity: sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '*'
|
typescript: '*'
|
||||||
@ -1312,8 +1313,8 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 5.53.0
|
'@typescript-eslint/types': 5.54.0
|
||||||
'@typescript-eslint/visitor-keys': 5.53.0
|
'@typescript-eslint/visitor-keys': 5.54.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@ -1324,31 +1325,31 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/utils/5.53.0_7kw3g6rralp5ps6mg3uyzz6azm:
|
/@typescript-eslint/utils/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||||
resolution: { integrity: sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g== }
|
resolution: { integrity: sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/json-schema': 7.0.11
|
'@types/json-schema': 7.0.11
|
||||||
'@types/semver': 7.3.13
|
'@types/semver': 7.3.13
|
||||||
'@typescript-eslint/scope-manager': 5.53.0
|
'@typescript-eslint/scope-manager': 5.54.0
|
||||||
'@typescript-eslint/types': 5.53.0
|
'@typescript-eslint/types': 5.54.0
|
||||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-scope: 5.1.1
|
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
|
semver: 7.3.8
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/visitor-keys/5.53.0:
|
/@typescript-eslint/visitor-keys/5.54.0:
|
||||||
resolution: { integrity: sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w== }
|
resolution: { integrity: sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 5.53.0
|
'@typescript-eslint/types': 5.54.0
|
||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@ -1609,7 +1610,7 @@ packages:
|
|||||||
postcss: ^8.1.0
|
postcss: ^8.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.21.5
|
browserslist: 4.21.5
|
||||||
caniuse-lite: 1.0.30001457
|
caniuse-lite: 1.0.30001458
|
||||||
fraction.js: 4.2.0
|
fraction.js: 4.2.0
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
@ -1714,8 +1715,8 @@ packages:
|
|||||||
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001457
|
caniuse-lite: 1.0.30001458
|
||||||
electron-to-chromium: 1.4.310
|
electron-to-chromium: 1.4.313
|
||||||
node-releases: 2.0.10
|
node-releases: 2.0.10
|
||||||
update-browserslist-db: 1.0.10_browserslist@4.21.5
|
update-browserslist-db: 1.0.10_browserslist@4.21.5
|
||||||
|
|
||||||
@ -1729,15 +1730,14 @@ packages:
|
|||||||
/callsites/3.1.0:
|
/callsites/3.1.0:
|
||||||
resolution: { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== }
|
resolution: { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== }
|
||||||
engines: { node: '>=6' }
|
engines: { node: '>=6' }
|
||||||
dev: true
|
|
||||||
|
|
||||||
/camelcase-css/2.0.1:
|
/camelcase-css/2.0.1:
|
||||||
resolution: { integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== }
|
resolution: { integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== }
|
||||||
engines: { node: '>= 6' }
|
engines: { node: '>= 6' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/caniuse-lite/1.0.30001457:
|
/caniuse-lite/1.0.30001458:
|
||||||
resolution: { integrity: sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA== }
|
resolution: { integrity: sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w== }
|
||||||
|
|
||||||
/ccount/2.0.1:
|
/ccount/2.0.1:
|
||||||
resolution: { integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== }
|
resolution: { integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== }
|
||||||
@ -2030,8 +2030,8 @@ packages:
|
|||||||
resolution: { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== }
|
resolution: { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/electron-to-chromium/1.4.310:
|
/electron-to-chromium/1.4.313:
|
||||||
resolution: { integrity: sha512-/xlATgfwkm5uDDwLw5nt/MNEf7c1oazLURMZLy39vOioGYyYzLWIDT8fZMJak6qTiAJ7udFTy7JG7ziyjNutiA== }
|
resolution: { integrity: sha512-QckB9OVqr2oybjIrbMI99uF+b9+iTja5weFe0ePbqLb5BHqXOJUO1SG6kDj/1WtWPRIBr51N153AEq8m7HuIaA== }
|
||||||
|
|
||||||
/emoji-regex/8.0.0:
|
/emoji-regex/8.0.0:
|
||||||
resolution: { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== }
|
resolution: { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== }
|
||||||
@ -2144,7 +2144,7 @@ packages:
|
|||||||
engines: { node: '>=12' }
|
engines: { node: '>=12' }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/eslint-config-next/13.2.1_7kw3g6rralp5ps6mg3uyzz6azm:
|
/eslint-config-next/13.2.1_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||||
resolution: { integrity: sha512-2GAx7EjSiCzJN6H2L/v1kbYrNiwQxzkyjy6eWSjuhAKt+P6d3nVNHGy9mON8ZcYd72w/M8kyMjm4UB9cvijgrw== }
|
resolution: { integrity: sha512-2GAx7EjSiCzJN6H2L/v1kbYrNiwQxzkyjy6eWSjuhAKt+P6d3nVNHGy9mON8ZcYd72w/M8kyMjm4UB9cvijgrw== }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7.23.0 || ^8.0.0
|
eslint: ^7.23.0 || ^8.0.0
|
||||||
@ -2155,27 +2155,27 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@next/eslint-plugin-next': 13.2.1
|
'@next/eslint-plugin-next': 13.2.1
|
||||||
'@rushstack/eslint-patch': 1.2.0
|
'@rushstack/eslint-patch': 1.2.0
|
||||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-import-resolver-node: 0.3.7
|
eslint-import-resolver-node: 0.3.7
|
||||||
eslint-import-resolver-typescript: 3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm
|
eslint-import-resolver-typescript: 3.5.3_yckic57kx266ph64dhq6ozvb54
|
||||||
eslint-plugin-import: 2.27.5_2hqppaeqs2axgzqg6vttejknky
|
eslint-plugin-import: 2.27.5_tqrcrxlenpngfto46ddarus52y
|
||||||
eslint-plugin-jsx-a11y: 6.7.1_eslint@8.34.0
|
eslint-plugin-jsx-a11y: 6.7.1_eslint@8.35.0
|
||||||
eslint-plugin-react: 7.32.2_eslint@8.34.0
|
eslint-plugin-react: 7.32.2_eslint@8.35.0
|
||||||
eslint-plugin-react-hooks: 4.6.0_eslint@8.34.0
|
eslint-plugin-react-hooks: 4.6.0_eslint@8.35.0
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
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== }
|
resolution: { integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=7.0.0'
|
eslint: '>=7.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-import-resolver-node/0.3.7:
|
/eslint-import-resolver-node/0.3.7:
|
||||||
@ -2188,7 +2188,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-import-resolver-typescript/3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm:
|
/eslint-import-resolver-typescript/3.5.3_yckic57kx266ph64dhq6ozvb54:
|
||||||
resolution: { integrity: sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== }
|
resolution: { integrity: sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== }
|
||||||
engines: { node: ^14.18.0 || >=16.0.0 }
|
engines: { node: ^14.18.0 || >=16.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2197,8 +2197,8 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
enhanced-resolve: 5.12.0
|
enhanced-resolve: 5.12.0
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-plugin-import: 2.27.5_2hqppaeqs2axgzqg6vttejknky
|
eslint-plugin-import: 2.27.5_tqrcrxlenpngfto46ddarus52y
|
||||||
get-tsconfig: 4.4.0
|
get-tsconfig: 4.4.0
|
||||||
globby: 13.1.3
|
globby: 13.1.3
|
||||||
is-core-module: 2.11.0
|
is-core-module: 2.11.0
|
||||||
@ -2208,7 +2208,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-module-utils/2.7.4_yfzt44nswbaazp63chcrlz6vvq:
|
/eslint-module-utils/2.7.4_igrub7c6rucg6hjc3uqgumd66y:
|
||||||
resolution: { integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== }
|
resolution: { integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== }
|
||||||
engines: { node: '>=4' }
|
engines: { node: '>=4' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2229,16 +2229,16 @@ packages:
|
|||||||
eslint-import-resolver-webpack:
|
eslint-import-resolver-webpack:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-import-resolver-node: 0.3.7
|
eslint-import-resolver-node: 0.3.7
|
||||||
eslint-import-resolver-typescript: 3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm
|
eslint-import-resolver-typescript: 3.5.3_yckic57kx266ph64dhq6ozvb54
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-import/2.27.5_2hqppaeqs2axgzqg6vttejknky:
|
/eslint-plugin-import/2.27.5_tqrcrxlenpngfto46ddarus52y:
|
||||||
resolution: { integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== }
|
resolution: { integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== }
|
||||||
engines: { node: '>=4' }
|
engines: { node: '>=4' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2248,15 +2248,15 @@ packages:
|
|||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.53.0_7kw3g6rralp5ps6mg3uyzz6azm
|
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
array-includes: 3.1.6
|
array-includes: 3.1.6
|
||||||
array.prototype.flat: 1.3.1
|
array.prototype.flat: 1.3.1
|
||||||
array.prototype.flatmap: 1.3.1
|
array.prototype.flatmap: 1.3.1
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-import-resolver-node: 0.3.7
|
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
|
has: 1.0.3
|
||||||
is-core-module: 2.11.0
|
is-core-module: 2.11.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@ -2264,14 +2264,14 @@ packages:
|
|||||||
object.values: 1.1.6
|
object.values: 1.1.6
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
semver: 6.3.0
|
semver: 6.3.0
|
||||||
tsconfig-paths: 3.14.1
|
tsconfig-paths: 3.14.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- eslint-import-resolver-typescript
|
- eslint-import-resolver-typescript
|
||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
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== }
|
resolution: { integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== }
|
||||||
engines: { node: '>=4.0' }
|
engines: { node: '>=4.0' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2286,7 +2286,7 @@ packages:
|
|||||||
axobject-query: 3.1.1
|
axobject-query: 3.1.1
|
||||||
damerau-levenshtein: 1.0.8
|
damerau-levenshtein: 1.0.8
|
||||||
emoji-regex: 9.2.2
|
emoji-regex: 9.2.2
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
jsx-ast-utils: 3.3.3
|
jsx-ast-utils: 3.3.3
|
||||||
language-tags: 1.0.5
|
language-tags: 1.0.5
|
||||||
@ -2296,16 +2296,16 @@ packages:
|
|||||||
semver: 6.3.0
|
semver: 6.3.0
|
||||||
dev: true
|
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== }
|
resolution: { integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== }
|
||||||
engines: { node: '>=10' }
|
engines: { node: '>=10' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
|
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
dev: true
|
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== }
|
resolution: { integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== }
|
||||||
engines: { node: '>=4' }
|
engines: { node: '>=4' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2315,7 +2315,7 @@ packages:
|
|||||||
array.prototype.flatmap: 1.3.1
|
array.prototype.flatmap: 1.3.1
|
||||||
array.prototype.tosorted: 1.1.1
|
array.prototype.tosorted: 1.1.1
|
||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
jsx-ast-utils: 3.3.3
|
jsx-ast-utils: 3.3.3
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
@ -2345,13 +2345,13 @@ packages:
|
|||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
dev: true
|
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== }
|
resolution: { integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== }
|
||||||
engines: { node: ^10.0.0 || ^12.0.0 || >= 14.0.0 }
|
engines: { node: ^10.0.0 || ^12.0.0 || >= 14.0.0 }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=5'
|
eslint: '>=5'
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 8.34.0
|
eslint: 8.35.0
|
||||||
eslint-visitor-keys: 2.1.0
|
eslint-visitor-keys: 2.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@ -2365,12 +2365,13 @@ packages:
|
|||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint/8.34.0:
|
/eslint/8.35.0:
|
||||||
resolution: { integrity: sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg== }
|
resolution: { integrity: sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw== }
|
||||||
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint/eslintrc': 1.4.1
|
'@eslint/eslintrc': 2.0.0
|
||||||
|
'@eslint/js': 8.35.0
|
||||||
'@humanwhocodes/config-array': 0.11.8
|
'@humanwhocodes/config-array': 0.11.8
|
||||||
'@humanwhocodes/module-importer': 1.0.1
|
'@humanwhocodes/module-importer': 1.0.1
|
||||||
'@nodelib/fs.walk': 1.2.8
|
'@nodelib/fs.walk': 1.2.8
|
||||||
@ -2381,7 +2382,7 @@ packages:
|
|||||||
doctrine: 3.0.0
|
doctrine: 3.0.0
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
eslint-scope: 7.1.1
|
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
|
eslint-visitor-keys: 3.3.0
|
||||||
espree: 9.4.1
|
espree: 9.4.1
|
||||||
esquery: 1.4.2
|
esquery: 1.4.2
|
||||||
@ -2734,7 +2735,6 @@ packages:
|
|||||||
|
|
||||||
/graceful-fs/4.2.10:
|
/graceful-fs/4.2.10:
|
||||||
resolution: { integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== }
|
resolution: { integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== }
|
||||||
dev: true
|
|
||||||
|
|
||||||
/grapheme-splitter/1.0.4:
|
/grapheme-splitter/1.0.4:
|
||||||
resolution: { integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== }
|
resolution: { integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== }
|
||||||
@ -3187,53 +3187,18 @@ packages:
|
|||||||
resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== }
|
resolution: { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== }
|
||||||
dev: true
|
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:
|
/javascript-natural-sort/0.7.1:
|
||||||
resolution: { integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== }
|
resolution: { integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== }
|
||||||
dev: true
|
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:
|
/js-sdsl/4.3.0:
|
||||||
resolution: { integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== }
|
resolution: { integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== }
|
||||||
dev: true
|
dev: true
|
||||||
@ -3447,7 +3412,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 3.0.10
|
'@types/mdast': 3.0.10
|
||||||
escape-string-regexp: 5.0.0
|
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
|
unist-util-visit-parents: 5.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -3530,7 +3495,7 @@ packages:
|
|||||||
resolution: { integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg== }
|
resolution: { integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg== }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 3.0.10
|
'@types/mdast': 3.0.10
|
||||||
unist-util-is: 5.2.0
|
unist-util-is: 5.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/mdast-util-to-hast/12.3.0:
|
/mdast-util-to-hast/12.3.0:
|
||||||
@ -3923,7 +3888,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 13.2.1
|
'@next/env': 13.2.1
|
||||||
'@swc/helpers': 0.4.14
|
'@swc/helpers': 0.4.14
|
||||||
caniuse-lite: 1.0.30001457
|
caniuse-lite: 1.0.30001458
|
||||||
postcss: 8.4.14
|
postcss: 8.4.14
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
@ -3960,31 +3925,19 @@ packages:
|
|||||||
engines: { node: '>=0.10.0' }
|
engines: { node: '>=0.10.0' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/nostr-react/0.6.4_react@18.2.0:
|
/nostr-relaypool/0.5.3_ws@8.12.1:
|
||||||
resolution: { integrity: sha512-esRgmhTP5kPQ8ufs8cFAQxxJtMmzuba/k2QfXevG/ejHP3IMa41pb82qi8V0aPzY3KJ0Nr54x0OSa39d2InKzA== }
|
resolution: { integrity: sha512-1INGKleOTuUTFUs3RnnZrew4+G/idLUewh44WBtmTTJ9g+kRiQtMMaBGTVUpf9621nBNleEVOB8p3XSNcaX3FQ== }
|
||||||
engines: { node: '>=12' }
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16'
|
|
||||||
dependencies:
|
dependencies:
|
||||||
jotai: 1.13.1_react@18.2.0
|
'@jest/source-map': 29.4.3
|
||||||
nostr-tools: 1.6.0
|
isomorphic-ws: 5.0.0_ws@8.12.1
|
||||||
react: 18.2.0
|
nostr-tools: 1.7.1
|
||||||
|
safe-stable-stringify: 2.4.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- ws
|
||||||
- '@babel/template'
|
|
||||||
- jotai-devtools
|
|
||||||
- jotai-immer
|
|
||||||
- jotai-optics
|
|
||||||
- jotai-redux
|
|
||||||
- jotai-tanstack-query
|
|
||||||
- jotai-urql
|
|
||||||
- jotai-valtio
|
|
||||||
- jotai-xstate
|
|
||||||
- jotai-zustand
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/nostr-tools/1.6.0:
|
/nostr-tools/1.7.1:
|
||||||
resolution: { integrity: sha512-qjjJQ7YxJUMzgS24eVlxkZ87PKJtU6dlH04OzVuK6w+GSPL+VdUZkMe2lfSpnb7OkCrDIzmbFbtx+Q4LXdU2xw== }
|
resolution: { integrity: sha512-r72KpbLVz6Gaqei6LIj6m+cyp24eF3myiIMlmv93WCgDFCI5w72u+OrZzjSrJaeE94vYoEJfOF16/2Rl5o5z5w== }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.0.0
|
'@noble/hashes': 1.0.0
|
||||||
'@noble/secp256k1': 1.7.1
|
'@noble/secp256k1': 1.7.1
|
||||||
@ -4336,7 +4289,7 @@ packages:
|
|||||||
engines: { node: '>= 0.8.0' }
|
engines: { node: '>= 0.8.0' }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/prettier-plugin-tailwindcss/0.2.3_3p4xqifn6m4d44r76wgcnqfi3i:
|
/prettier-plugin-tailwindcss/0.2.3_zmkqdpv3ldc45e6wei6qtrbrca:
|
||||||
resolution: { integrity: sha512-s2N5Dh7Ao5KTV1mao5ZBnn8EKtUcDPJEkGViZIjI0Ij9TTI5zgTz4IHOxW33jOdjHKa8CSjM88scelUiC5TNRQ== }
|
resolution: { integrity: sha512-s2N5Dh7Ao5KTV1mao5ZBnn8EKtUcDPJEkGViZIjI0Ij9TTI5zgTz4IHOxW33jOdjHKa8CSjM88scelUiC5TNRQ== }
|
||||||
engines: { node: '>=12.17.0' }
|
engines: { node: '>=12.17.0' }
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -4388,7 +4341,7 @@ packages:
|
|||||||
prettier-plugin-twig-melody:
|
prettier-plugin-twig-melody:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
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
|
prettier: 2.8.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@ -4824,6 +4777,11 @@ packages:
|
|||||||
is-regex: 1.1.4
|
is-regex: 1.1.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/safe-stable-stringify/2.4.2:
|
||||||
|
resolution: { integrity: sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA== }
|
||||||
|
engines: { node: '>=10' }
|
||||||
|
dev: false
|
||||||
|
|
||||||
/scheduler/0.23.0:
|
/scheduler/0.23.0:
|
||||||
resolution: { integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== }
|
resolution: { integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== }
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5146,8 +5104,8 @@ packages:
|
|||||||
resolution: { integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== }
|
resolution: { integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/tsconfig-paths/3.14.1:
|
/tsconfig-paths/3.14.2:
|
||||||
resolution: { integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== }
|
resolution: { integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/json5': 0.0.29
|
'@types/json5': 0.0.29
|
||||||
json5: 1.0.2
|
json5: 1.0.2
|
||||||
@ -5233,7 +5191,7 @@ packages:
|
|||||||
resolution: { integrity: sha512-RynicUM/vbOSTSiUK+BnaK9XMfmQUh6gyi7L6taNgc7FIf84GukXVV3ucGzEN/PhUUkdP5hb1MmXc+3cvPUm5Q== }
|
resolution: { integrity: sha512-RynicUM/vbOSTSiUK+BnaK9XMfmQUh6gyi7L6taNgc7FIf84GukXVV3ucGzEN/PhUUkdP5hb1MmXc+3cvPUm5Q== }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': 2.0.6
|
||||||
unist-util-is: 5.2.0
|
unist-util-is: 5.2.1
|
||||||
unist-util-visit-parents: 5.1.3
|
unist-util-visit-parents: 5.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -5241,8 +5199,10 @@ packages:
|
|||||||
resolution: { integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A== }
|
resolution: { integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A== }
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/unist-util-is/5.2.0:
|
/unist-util-is/5.2.1:
|
||||||
resolution: { integrity: sha512-Glt17jWwZeyqrFqOK0pF1Ded5U3yzJnFr8CG1GMjCWTp9zDo2p+cmD6pWbZU8AgM5WU3IzRv6+rBwhzsGh6hBQ== }
|
resolution: { integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== }
|
||||||
|
dependencies:
|
||||||
|
'@types/unist': 2.0.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/unist-util-position/4.0.4:
|
/unist-util-position/4.0.4:
|
||||||
@ -5261,14 +5221,14 @@ packages:
|
|||||||
resolution: { integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== }
|
resolution: { integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': 2.0.6
|
||||||
unist-util-is: 5.2.0
|
unist-util-is: 5.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/unist-util-visit/4.1.2:
|
/unist-util-visit/4.1.2:
|
||||||
resolution: { integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== }
|
resolution: { integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== }
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': 2.0.6
|
||||||
unist-util-is: 5.2.0
|
unist-util-is: 5.2.1
|
||||||
unist-util-visit-parents: 5.1.3
|
unist-util-visit-parents: 5.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -5331,14 +5291,6 @@ packages:
|
|||||||
tslib: 2.5.0
|
tslib: 2.5.0
|
||||||
dev: false
|
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:
|
/util-deprecate/1.0.2:
|
||||||
resolution: { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== }
|
resolution: { integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== }
|
||||||
dev: true
|
dev: true
|
||||||
@ -5447,6 +5399,19 @@ packages:
|
|||||||
resolution: { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== }
|
resolution: { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== }
|
||||||
dev: true
|
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:
|
/xtend/4.0.2:
|
||||||
resolution: { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== }
|
resolution: { integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== }
|
||||||
engines: { node: '>=0.4' }
|
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
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]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.13.4"
|
version = "0.13.4"
|
||||||
@ -1393,6 +1363,30 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
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]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.139"
|
version = "0.2.139"
|
||||||
@ -1408,6 +1402,16 @@ dependencies = [
|
|||||||
"pkg-config",
|
"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]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.24.2"
|
version = "0.24.2"
|
||||||
@ -1419,18 +1423,6 @@ dependencies = [
|
|||||||
"vcpkg",
|
"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]]
|
[[package]]
|
||||||
name = "line-wrap"
|
name = "line-wrap"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -1485,7 +1477,6 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-sql",
|
"tauri-plugin-sql",
|
||||||
"webpage",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1504,7 +1495,7 @@ dependencies = [
|
|||||||
"dirs-next",
|
"dirs-next",
|
||||||
"objc-foundation",
|
"objc-foundation",
|
||||||
"objc_id",
|
"objc_id",
|
||||||
"time 0.3.17",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1530,18 +1521,6 @@ dependencies = [
|
|||||||
"tendril",
|
"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]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1795,25 +1774,6 @@ dependencies = [
|
|||||||
"windows-sys 0.42.0",
|
"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]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -2068,7 +2028,7 @@ dependencies = [
|
|||||||
"line-wrap",
|
"line-wrap",
|
||||||
"quick-xml 0.26.0",
|
"quick-xml 0.26.0",
|
||||||
"serde",
|
"serde",
|
||||||
"time 0.3.17",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2421,15 +2381,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -2917,6 +2868,7 @@ dependencies = [
|
|||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
"dirs-next",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"gdk",
|
"gdk",
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf",
|
||||||
@ -2930,6 +2882,7 @@ dependencies = [
|
|||||||
"instant",
|
"instant",
|
||||||
"jni",
|
"jni",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libappindicator",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"ndk",
|
"ndk",
|
||||||
@ -3047,7 +3000,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.17",
|
"time",
|
||||||
"uuid 1.3.0",
|
"uuid 1.3.0",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
@ -3069,7 +3022,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-sql"
|
name = "tauri-plugin-sql"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
@ -3220,17 +3173,6 @@ dependencies = [
|
|||||||
"once_cell",
|
"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]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.17"
|
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
@ -3675,18 +3611,6 @@ dependencies = [
|
|||||||
"system-deps 6.0.3",
|
"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]]
|
[[package]]
|
||||||
name = "webpki"
|
name = "webpki"
|
||||||
version = "0.22.0"
|
version = "0.22.0"
|
||||||
@ -4051,15 +3975,3 @@ checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"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]
|
[dependencies]
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.2", features = ["clipboard-all", "notification-all", "shell-open", "window-start-dragging"] }
|
tauri = { version = "1.2", features = ["clipboard-all", "notification-all", "shell-open", "system-tray", "window-start-dragging"] }
|
||||||
webpage = "1.5.0"
|
|
||||||
|
|
||||||
[dependencies.tauri-plugin-sql]
|
[dependencies.tauri-plugin-sql]
|
||||||
git = "https://github.com/tauri-apps/plugins-workspace"
|
git = "https://github.com/tauri-apps/plugins-workspace"
|
||||||
branch = "fix/sql-types"
|
branch = "dev"
|
||||||
features = ["sqlite"]
|
features = ["sqlite"]
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[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]
|
#[macro_use]
|
||||||
extern crate objc;
|
extern crate objc;
|
||||||
|
|
||||||
use std::time::Duration;
|
use tauri::{Manager, SystemTray, WindowEvent};
|
||||||
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||||
use tauri::{Manager, WindowEvent};
|
|
||||||
use webpage::{Webpage, WebpageOptions};
|
|
||||||
use window_ext::WindowExt;
|
use window_ext::WindowExt;
|
||||||
|
|
||||||
mod window_ext;
|
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() {
|
fn main() {
|
||||||
|
let tray = SystemTray::new();
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let main_window = app.get_window("main").unwrap();
|
let main_window = app.get_window("main").unwrap();
|
||||||
@ -57,8 +24,20 @@ fn main() {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![opengraph])
|
.system_tray(tray)
|
||||||
.plugin(tauri_plugin_sql::Builder::default().build())
|
.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| {
|
.on_window_event(|e| {
|
||||||
let apply_offset = || {
|
let apply_offset = || {
|
||||||
let win = e.window();
|
let win = e.window();
|
||||||
|
@ -37,13 +37,7 @@
|
|||||||
"depends": []
|
"depends": []
|
||||||
},
|
},
|
||||||
"externalBin": [],
|
"externalBin": [],
|
||||||
"icon": [
|
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
],
|
|
||||||
"identifier": "com.uselume.xyz",
|
"identifier": "com.uselume.xyz",
|
||||||
"longDescription": "",
|
"longDescription": "",
|
||||||
"macOS": {
|
"macOS": {
|
||||||
@ -68,6 +62,10 @@
|
|||||||
"updater": {
|
"updater": {
|
||||||
"active": false
|
"active": false
|
||||||
},
|
},
|
||||||
|
"systemTray": {
|
||||||
|
"iconPath": "icons/icon.png",
|
||||||
|
"iconAsTemplate": true
|
||||||
|
},
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"theme": "Dark",
|
"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' : ''
|
current === user.pubkey ? 'ring-1 ring-fuchsia-500 ring-offset-4 ring-offset-black' : ''
|
||||||
}`}>
|
}`}>
|
||||||
{userData?.picture !== undefined ? (
|
{userData?.picture !== undefined ? (
|
||||||
<Image
|
<Image src={userData.picture} alt="user's avatar" fill={true} className="rounded-full object-cover" />
|
||||||
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" />
|
<div className="h-11 w-11 animate-pulse rounded-full bg-zinc-700" />
|
||||||
)}
|
)}
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { Account } from '@components/accountBar/account';
|
import { Account } from '@components/accountBar/account';
|
||||||
|
|
||||||
import { currentUser } from '@stores/currentUser';
|
|
||||||
|
|
||||||
import LumeSymbol from '@assets/icons/Lume';
|
import LumeSymbol from '@assets/icons/Lume';
|
||||||
|
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
import { PlusIcon } from '@radix-ui/react-icons';
|
import { PlusIcon } from '@radix-ui/react-icons';
|
||||||
|
import { useLocalStorage } from '@rehooks/local-storage';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
import Database from 'tauri-plugin-sql-api';
|
||||||
|
|
||||||
export default function AccountBar() {
|
export default function AccountBar() {
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
|
|
||||||
const getAccounts = useCallback(async () => {
|
const getAccounts = useCallback(async () => {
|
||||||
const db = await Database.load('sqlite:lume.db');
|
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 h-full flex-col items-center justify-between px-2 pt-12 pb-4">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{users.map((user, index) => (
|
{users.map((user, index) => (
|
||||||
<Account key={index} user={user} current={$currentUser.pubkey} />
|
<Account key={index} user={user} current={currentUser.pubkey} />
|
||||||
))}
|
))}
|
||||||
<Link
|
<Link
|
||||||
href="/onboarding"
|
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 */
|
/* 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 * as Dialog from '@radix-ui/react-dialog';
|
||||||
|
import { useLocalStorage } from '@rehooks/local-storage';
|
||||||
import * as commands from '@uiw/react-md-editor/lib/commands';
|
import * as commands from '@uiw/react-md-editor/lib/commands';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { dateToUnix, useNostr } from 'nostr-react';
|
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
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), {
|
const MDEditor = dynamic(() => import('@uiw/react-md-editor').then((mod) => mod.default), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function CreatePost() {
|
export default function CreatePost() {
|
||||||
const { publish } = useNostr();
|
const relayPool: any = useContext(RelayContext);
|
||||||
|
const [relays]: any = useLocalStorage('relays');
|
||||||
|
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
const pubkey = $currentUser.pubkey;
|
const pubkey = currentUser.pubkey;
|
||||||
const privkey = $currentUser.privkey;
|
const privkey = currentUser.privkey;
|
||||||
|
|
||||||
const postButton = {
|
const postButton = {
|
||||||
name: 'post',
|
name: 'post',
|
||||||
@ -27,9 +30,7 @@ export default function CreatePost() {
|
|||||||
buttonProps: { className: 'cta-btn', 'aria-label': 'Post a message' },
|
buttonProps: { className: 'cta-btn', 'aria-label': 'Post a message' },
|
||||||
icon: (
|
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">
|
<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">
|
<span className="absolute inset-px z-10 inline-flex items-center justify-center rounded bg-zinc-900 text-zinc-200">Post</span>
|
||||||
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>
|
<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>
|
</div>
|
||||||
),
|
),
|
||||||
@ -44,11 +45,10 @@ export default function CreatePost() {
|
|||||||
pubkey: pubkey,
|
pubkey: pubkey,
|
||||||
tags: [],
|
tags: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, privkey);
|
event.sig = signEvent(event, privkey);
|
||||||
|
|
||||||
publish(event);
|
relayPool.publish(event, relays);
|
||||||
setValue('');
|
setValue('');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,30 +3,22 @@ import ActiveLink from '@components/activeLink';
|
|||||||
import CreatePost from '@components/navigatorBar/createPost';
|
import CreatePost from '@components/navigatorBar/createPost';
|
||||||
import { ProfileMenu } from '@components/navigatorBar/profileMenu';
|
import { ProfileMenu } from '@components/navigatorBar/profileMenu';
|
||||||
|
|
||||||
import { currentUser } from '@stores/currentUser';
|
|
||||||
|
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
import { PlusIcon } from '@radix-ui/react-icons';
|
import { PlusIcon } from '@radix-ui/react-icons';
|
||||||
|
import { useLocalStorage } from '@rehooks/local-storage';
|
||||||
|
|
||||||
export default function NavigatorBar() {
|
export default function NavigatorBar() {
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
const profile =
|
const profile = JSON.parse(currentUser.metadata);
|
||||||
$currentUser.metadata !== undefined
|
|
||||||
? JSON.parse($currentUser.metadata)
|
|
||||||
: { display_name: null, username: null };
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col flex-wrap justify-between overflow-hidden px-2 pt-3 pb-4">
|
<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">
|
<div className="flex flex-col gap-4">
|
||||||
{/* Create post */}
|
{/* Create post */}
|
||||||
<div className="flex flex-col rounded-lg bg-zinc-900 ring-1 ring-white/10">
|
<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 flex-col p-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h5 className="font-semibold leading-tight text-zinc-100">
|
<h5 className="font-semibold leading-tight text-zinc-100">{profile.display_name || ''}</h5>
|
||||||
{profile.display_name || ''}
|
<ProfileMenu pubkey={currentUser.pubkey} />
|
||||||
</h5>
|
|
||||||
<ProfileMenu pubkey={$currentUser.pubkey} />
|
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm leading-tight text-zinc-500">@{profile.username || ''}</span>
|
<span className="text-sm leading-tight text-zinc-500">@{profile.username || ''}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -38,9 +30,7 @@ export default function NavigatorBar() {
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center justify-between px-2">
|
<div className="flex items-center justify-between px-2">
|
||||||
<h3 className="text-sm font-bold text-zinc-400">Newsfeed</h3>
|
<h3 className="text-sm font-bold text-zinc-400">Newsfeed</h3>
|
||||||
<button
|
<button type="button" className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||||
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" />
|
<PlusIcon className="h-3 w-3 text-zinc-400 group-hover:text-zinc-100" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -65,9 +55,7 @@ export default function NavigatorBar() {
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center justify-between px-2">
|
<div className="flex items-center justify-between px-2">
|
||||||
<h3 className="text-sm font-bold text-zinc-400">Direct Messages</h3>
|
<h3 className="text-sm font-bold text-zinc-400">Direct Messages</h3>
|
||||||
<button
|
<button type="button" className="group flex h-6 w-6 items-center justify-center rounded-full hover:bg-zinc-900">
|
||||||
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" />
|
<PlusIcon className="h-3 w-3 text-zinc-400 group-hover:text-zinc-100" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -77,17 +65,3 @@ export default function NavigatorBar() {
|
|||||||
</div>
|
</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 */
|
/* 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 { 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 { 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 [reaction, setReaction] = useState(0);
|
||||||
const [isReact, setIsReact] = useState(false);
|
const [isReact, setIsReact] = useState(false);
|
||||||
|
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
const pubkey = $currentUser.pubkey;
|
const pubkey = currentUser.pubkey;
|
||||||
const privkey = $currentUser.privkey;
|
const privkey = currentUser.privkey;
|
||||||
|
|
||||||
const { onEvent } = useNostrEvents({
|
/*
|
||||||
filter: {
|
relayPool.subscribe(
|
||||||
'#e': [eventID],
|
[
|
||||||
since: 0,
|
{
|
||||||
kinds: [7],
|
'#e': [eventID],
|
||||||
limit: 20,
|
since: 0,
|
||||||
},
|
kinds: [7],
|
||||||
});
|
limit: 10,
|
||||||
|
},
|
||||||
onEvent((rawMetadata) => {
|
],
|
||||||
try {
|
relays,
|
||||||
const content = rawMetadata.content;
|
(event: any) => {
|
||||||
if (content === '🤙' || content === '+') {
|
if (event.content === '🤙' || event.content === '+') {
|
||||||
setReaction(reaction + 1);
|
setReaction(reaction + 1);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
},
|
||||||
console.error(err, rawMetadata);
|
undefined,
|
||||||
|
(events: any, relayURL: any) => {
|
||||||
|
console.log(events, relayURL);
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
const handleReaction = (e: any) => {
|
const handleReaction = (e: any) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -58,22 +58,16 @@ export default function Reaction({
|
|||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, privkey);
|
event.sig = signEvent(event, privkey);
|
||||||
|
|
||||||
publish(event);
|
relayPool.publish(event, relays);
|
||||||
|
|
||||||
setIsReact(true);
|
setIsReact(true);
|
||||||
setReaction(reaction + 1);
|
setReaction(reaction + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button onClick={(e) => handleReaction(e)} className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
||||||
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">
|
<div className="rounded-lg p-1 group-hover:bg-zinc-600">
|
||||||
{isReact ? (
|
{isReact ? <HeartFilledIcon className="h-4 w-4 group-hover:text-red-400" /> : <HeartIcon className="h-4 w-4 text-zinc-500" />}
|
||||||
<HeartFilledIcon className="h-4 w-4 group-hover:text-red-400" />
|
|
||||||
) : (
|
|
||||||
<HeartIcon className="h-4 w-4 text-zinc-500" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<span>{reaction}</span>
|
<span>{reaction}</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,22 +1,16 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { ChatBubbleIcon } from '@radix-ui/react-icons';
|
import { ChatBubbleIcon } from '@radix-ui/react-icons';
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export default function Reply({ eventID }: { eventID: string }) {
|
export default function Reply() {
|
||||||
const { events } = useNostrEvents({
|
const [count] = useState(0);
|
||||||
filter: {
|
|
||||||
'#e': [eventID],
|
|
||||||
since: 0,
|
|
||||||
kinds: [1],
|
|
||||||
limit: 10,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
|
<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">
|
<div className="rounded-lg p-1 group-hover:bg-zinc-600">
|
||||||
<ChatBubbleIcon className="h-4 w-4 group-hover:text-orange-400" />
|
<ChatBubbleIcon className="h-4 w-4 group-hover:text-orange-400" />
|
||||||
</div>
|
</div>
|
||||||
<span>{events.length || 0}</span>
|
<span>{count}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,88 +1,65 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { DatabaseContext } from '@components/contexts/database';
|
||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
||||||
import Avatar from 'boring-avatars';
|
import Avatar from 'boring-avatars';
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import { memo, useEffect, useState } from 'react';
|
|
||||||
import Moment from 'react-moment';
|
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 }) {
|
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 [profile, setProfile] = useState({ picture: null, name: null, username: null });
|
||||||
|
|
||||||
const { onEvent } = useNostrEvents({
|
const insertCacheProfile = useCallback(
|
||||||
filter: {
|
async (event) => {
|
||||||
authors: [pubkey],
|
const metadata: any = JSON.parse(event.content);
|
||||||
kinds: [0],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
onEvent(async (rawMetadata) => {
|
await db.execute(`INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES ("${pubkey}", '${JSON.stringify(metadata)}')`);
|
||||||
try {
|
setProfile(metadata);
|
||||||
const metadata: any = JSON.parse(rawMetadata.content);
|
},
|
||||||
if (profile.picture === null || profile.name === null) {
|
[db, pubkey]
|
||||||
setProfile(metadata);
|
);
|
||||||
await db.execute(
|
|
||||||
`INSERT OR IGNORE INTO cache_profiles (pubkey, metadata) VALUES ("${pubkey}", '${JSON.stringify(
|
const getCacheProfile = useCallback(async () => {
|
||||||
metadata
|
const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`);
|
||||||
)}')`
|
return result;
|
||||||
);
|
}, [db, pubkey]);
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err, rawMetadata);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialProfile = async () => {
|
getCacheProfile()
|
||||||
const result: any = await db.select(
|
|
||||||
`SELECT metadata FROM cache_profiles WHERE pubkey = "${pubkey}"`
|
|
||||||
);
|
|
||||||
db.close;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
initialProfile()
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res[0] !== undefined) {
|
if (res[0] !== undefined) {
|
||||||
setProfile(JSON.parse(res[0].metadata));
|
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);
|
.catch(console.error);
|
||||||
}, [pubkey]);
|
}, [getCacheProfile, insertCacheProfile, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-start gap-4">
|
<div className="relative flex items-start gap-4">
|
||||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-full border border-white/10">
|
<div className="relative h-11 w-11 shrink overflow-hidden rounded-full border border-white/10">
|
||||||
{profile.picture ? (
|
{profile.picture ? (
|
||||||
<ImageWithFallback
|
<ImageWithFallback src={profile.picture} alt={pubkey} fill={true} className="rounded-full object-cover" />
|
||||||
src={profile.picture}
|
|
||||||
alt={pubkey}
|
|
||||||
fill={true}
|
|
||||||
className="rounded-full object-cover"
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<Avatar
|
<Avatar size={44} name={pubkey} variant="beam" colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']} />
|
||||||
size={44}
|
|
||||||
name={pubkey}
|
|
||||||
variant="beam"
|
|
||||||
colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-1 items-start justify-between">
|
<div className="flex w-full flex-1 items-start justify-between">
|
||||||
<div className="flex w-full justify-between">
|
<div className="flex w-full justify-between">
|
||||||
<div className="flex items-baseline gap-2 text-sm">
|
<div className="flex items-baseline gap-2 text-sm">
|
||||||
<span className="font-bold leading-tight">
|
<span className="font-bold leading-tight">{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}</span>
|
||||||
{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}
|
|
||||||
</span>
|
|
||||||
<span className="leading-tight text-zinc-500">·</span>
|
<span className="leading-tight text-zinc-500">·</span>
|
||||||
<Moment fromNow unix className="text-zinc-500">
|
<Moment fromNow unix className="text-zinc-500">
|
||||||
{time}
|
{time}
|
||||||
|
@ -1,30 +1,19 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { memo, useState } from 'react';
|
|
||||||
|
|
||||||
export const UserRepost = memo(function UserRepost({ pubkey }: { pubkey: string }) {
|
export const UserRepost = memo(function UserRepost({ pubkey }: { pubkey: string }) {
|
||||||
const [profile, setProfile] = useState({ picture: null, name: null });
|
const [profile, setProfile] = useState({ picture: null, name: null });
|
||||||
|
|
||||||
const { onEvent } = useNostrEvents({
|
useEffect(() => {
|
||||||
filter: {
|
fetch(`https://rbr.bio/${pubkey}/metadata.json`).then((res) =>
|
||||||
authors: [pubkey],
|
res.json().then((res) => {
|
||||||
kinds: [0],
|
// update state
|
||||||
},
|
setProfile(JSON.parse(res.content));
|
||||||
});
|
})
|
||||||
|
);
|
||||||
// #TODO: save response to DB
|
}, [pubkey]);
|
||||||
onEvent((rawMetadata) => {
|
|
||||||
try {
|
|
||||||
const metadata: any = JSON.parse(rawMetadata.content);
|
|
||||||
if (metadata) {
|
|
||||||
setProfile(metadata);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err, rawMetadata);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-zinc-400">
|
<div className="text-zinc-400">
|
||||||
|
@ -5,86 +5,34 @@ import { truncate } from '@utils/truncate';
|
|||||||
|
|
||||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
||||||
import Avatar from 'boring-avatars';
|
import Avatar from 'boring-avatars';
|
||||||
import { useNostrEvents } from 'nostr-react';
|
|
||||||
import { memo, useEffect, useState } from '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 }) {
|
export const UserWithUsername = memo(function UserWithUsername({ pubkey }: { pubkey: string }) {
|
||||||
const [profile, setProfile] = useState({ picture: null, name: null, username: null });
|
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(() => {
|
useEffect(() => {
|
||||||
const initialProfile = async () => {
|
fetch(`https://rbr.bio/${pubkey}/metadata.json`).then((res) =>
|
||||||
const result: any = await db.select(
|
res.json().then((res) => {
|
||||||
`SELECT metadata FROM cache_profiles WHERE pubkey = "${pubkey}"`
|
// update state
|
||||||
);
|
setProfile(JSON.parse(res.content));
|
||||||
db.close;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
initialProfile()
|
|
||||||
.then((res) => {
|
|
||||||
if (res[0] !== undefined) {
|
|
||||||
setProfile(JSON.parse(res[0].metadata));
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
);
|
||||||
}, [pubkey]);
|
}, [pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-start gap-2">
|
<div className="relative flex items-start gap-2">
|
||||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-full border border-white/10">
|
<div className="relative h-11 w-11 shrink overflow-hidden rounded-full border border-white/10">
|
||||||
{profile.picture ? (
|
{profile.picture ? (
|
||||||
<ImageWithFallback
|
<ImageWithFallback src={profile.picture} alt={pubkey} fill={true} className="rounded-full object-cover" />
|
||||||
src={profile.picture}
|
|
||||||
alt={pubkey}
|
|
||||||
fill={true}
|
|
||||||
className="rounded-full object-cover"
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<Avatar
|
<Avatar size={44} name={pubkey} variant="beam" colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']} />
|
||||||
size={44}
|
|
||||||
name={pubkey}
|
|
||||||
variant="beam"
|
|
||||||
colors={['#FEE2E2', '#FEF3C7', '#F59E0B', '#EC4899', '#D946EF', '#8B5CF6']}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-1 items-start justify-between">
|
<div className="flex w-full flex-1 items-start justify-between">
|
||||||
<div className="flex w-full justify-between">
|
<div className="flex w-full justify-between">
|
||||||
<div className="flex flex-col gap-1 text-sm">
|
<div className="flex flex-col gap-1 text-sm">
|
||||||
<span className="font-bold leading-tight">
|
<span className="font-bold leading-tight">{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}</span>
|
||||||
{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')}
|
<span className="text-zinc-500">{profile.username ? profile.username : truncate(pubkey, 16, ' .... ')}</span>
|
||||||
</span>
|
|
||||||
<span className="text-zinc-500">
|
|
||||||
{profile.username ? profile.username : truncate(pubkey, 16, ' .... ')}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<DotsHorizontalIcon className="h-4 w-4 text-zinc-500" />
|
<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 { Content } from '@components/note/content';
|
||||||
import NoteReply from '@components/note/modal/noteReply';
|
import NoteReply from '@components/note/modal/noteReply';
|
||||||
|
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import useLocalStorage from '@rehooks/local-storage';
|
||||||
import { memo } from 'react';
|
import { memo, useContext, useState } from 'react';
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
const Modal = ({ event }: { event: any }) => {
|
const Modal = ({ event }: { event: any }) => {
|
||||||
const { events } = useNostrEvents({
|
const relayPool: any = useContext(RelayContext);
|
||||||
filter: {
|
const [relays]: any = useLocalStorage('relays');
|
||||||
'#e': [event.id],
|
const [events, setEvents] = useState([]);
|
||||||
since: event.created_at,
|
|
||||||
kinds: [1],
|
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 (
|
return (
|
||||||
<div className="flex min-h-full items-center justify-center p-4">
|
<div className="flex min-h-full items-center justify-center p-4">
|
||||||
|
@ -1,49 +1,47 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { RelayContext } from '@components/contexts/relay';
|
||||||
import { UserRepost } from '@components/note/atoms/userRepost';
|
import { UserRepost } from '@components/note/atoms/userRepost';
|
||||||
import { Content } from '@components/note/content';
|
import { Content } from '@components/note/content';
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
import { Placeholder } from '@components/note/placeholder';
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog';
|
|
||||||
import { LoopIcon } from '@radix-ui/react-icons';
|
import { LoopIcon } from '@radix-ui/react-icons';
|
||||||
import dynamic from 'next/dynamic';
|
import useLocalStorage from '@rehooks/local-storage';
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import { memo, useContext, useState } from 'react';
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
const Modal = dynamic(() => import('@components/note/modal'), {
|
|
||||||
ssr: false,
|
|
||||||
loading: () => <></>,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Repost = memo(function Repost({ root, user }: { root: any; user: string }) {
|
export const Repost = memo(function Repost({ root, user }: { root: any; user: string }) {
|
||||||
const { events } = useNostrEvents({
|
const relayPool: any = useContext(RelayContext);
|
||||||
filter: {
|
const [relays]: any = useLocalStorage('relays');
|
||||||
ids: [root[0][1]],
|
const [events, setEvents] = useState([]);
|
||||||
since: 0,
|
|
||||||
kinds: [1],
|
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) {
|
if (events !== null && Object.keys(events).length > 0) {
|
||||||
return (
|
return (
|
||||||
<Dialog.Root>
|
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
||||||
<Dialog.Trigger>
|
<div className="flex items-center gap-1 pl-8 text-sm">
|
||||||
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
<LoopIcon className="h-4 w-4 text-zinc-400" />
|
||||||
<div className="flex items-center gap-1 pl-8 text-sm">
|
<div className="ml-2">
|
||||||
<LoopIcon className="h-4 w-4 text-zinc-400" />
|
<UserRepost pubkey={user} />
|
||||||
<div className="ml-2">
|
|
||||||
<UserRepost pubkey={user} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{events[0].content && <Content data={events[0]} />}
|
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Portal>
|
</div>
|
||||||
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
|
{events[0].content && <Content data={events[0]} />}
|
||||||
<Dialog.Content className="fixed inset-0 overflow-y-auto">
|
</div>
|
||||||
{events[0].content && <Modal event={events[0]} />}
|
|
||||||
</Dialog.Content>
|
|
||||||
</Dialog.Portal>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
</Dialog.Root>
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
|
@ -1,30 +1,12 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { Content } from '@components/note/content';
|
import { Content } from '@components/note/content';
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog';
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import { memo } from 'react';
|
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 }) {
|
export const Single = memo(function Single({ event }: { event: any }) {
|
||||||
return (
|
return (
|
||||||
<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">
|
||||||
<Dialog.Trigger asChild>
|
<Content data={event} />
|
||||||
<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">
|
</div>
|
||||||
<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>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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 AccountBar from '@components/accountBar';
|
||||||
import ActiveLink from '@components/activeLink';
|
import ActiveLink from '@components/activeLink';
|
||||||
|
|
||||||
import { currentUser } from '@stores/currentUser';
|
import { useLocalStorage } from '@rehooks/local-storage';
|
||||||
|
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
|
|
||||||
export default function UserLayout({ children }: { children: React.ReactNode }) {
|
export default function UserLayout({ children }: { children: React.ReactNode }) {
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-row">
|
<div className="flex h-full w-full flex-row">
|
||||||
@ -27,13 +25,13 @@ export default function UserLayout({ children }: { children: React.ReactNode })
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 text-zinc-500">
|
<div className="flex flex-col gap-1 text-zinc-500">
|
||||||
<ActiveLink
|
<ActiveLink
|
||||||
href={`/profile/${$currentUser.pubkey}`}
|
href={`/profile/${currentUser.pubkey}`}
|
||||||
activeClassName="ring-1 ring-white/10 dark:bg-zinc-900 dark:text-white"
|
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">
|
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>
|
<span>Personal Page</span>
|
||||||
</ActiveLink>
|
</ActiveLink>
|
||||||
<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"
|
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">
|
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>
|
<span>Update Profile</span>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* 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 { NextPage } from 'next';
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
import { NostrProvider } from 'nostr-react';
|
import { ReactElement, ReactNode } from 'react';
|
||||||
import type { ReactElement, ReactNode } from 'react';
|
|
||||||
|
|
||||||
import '../App.css';
|
import '../App.css';
|
||||||
|
|
||||||
@ -21,12 +21,12 @@ type AppPropsWithLayout = AppProps & {
|
|||||||
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||||
// Use the layout defined at the page level, if available
|
// Use the layout defined at the page level, if available
|
||||||
const getLayout = Component.getLayout ?? ((page) => page);
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
// Get relays
|
// Get relays from localstorage
|
||||||
const $relays = useStore(relays);
|
const [relays] = useLocalStorage('relays');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NostrProvider relayUrls={$relays} debug={false}>
|
<DatabaseProvider>
|
||||||
{getLayout(<Component {...pageProps} />)}
|
<RelayProvider relays={relays}>{getLayout(<Component {...pageProps} />)}</RelayProvider>
|
||||||
</NostrProvider>
|
</DatabaseProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,113 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import BaseLayout from '@layouts/baseLayout';
|
import BaseLayout from '@layouts/baseLayout';
|
||||||
import NewsFeedLayout from '@layouts/newsfeedLayout';
|
import NewsFeedLayout from '@layouts/newsfeedLayout';
|
||||||
|
|
||||||
|
import { DatabaseContext } from '@components/contexts/database';
|
||||||
|
import { NoteConnector } from '@components/note/connector';
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
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 { writeStorage } from '@rehooks/local-storage';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
import { useStore } from '@nanostores/react';
|
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useRef } from 'react';
|
||||||
import { dateToUnix, useNostrEvents } from 'nostr-react';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
import {
|
|
||||||
JSXElementConstructor,
|
|
||||||
ReactElement,
|
|
||||||
ReactFragment,
|
|
||||||
ReactPortal,
|
|
||||||
Suspense,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const $follows = useStore(follows);
|
const { db }: any = useContext(DatabaseContext);
|
||||||
const now = useRef(new Date());
|
|
||||||
|
|
||||||
const { events } = useNostrEvents({
|
const [data, setData] = useState([]);
|
||||||
filter: {
|
const [parentReload, setParentReload] = useState(false);
|
||||||
authors: $follows,
|
const [hasNewNote, setHasNewNote] = useState(false);
|
||||||
since: dateToUnix(hoursAgo(6, now.current)),
|
|
||||||
kinds: [1],
|
const now = useRef(new Date());
|
||||||
limit: 100,
|
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 (
|
return (
|
||||||
<div className="h-full w-full">
|
<div className="relative h-full w-full">
|
||||||
<Suspense fallback={<Placeholder />}>
|
<NoteConnector setParentReload={setParentReload} setHasNewNote={setHasNewNote} currentDate={now.current} />
|
||||||
<Thread data={events} />
|
{hasNewNote && (
|
||||||
</Suspense>
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
|
@ -1,50 +1,18 @@
|
|||||||
import BaseLayout from '@layouts/baseLayout';
|
import BaseLayout from '@layouts/baseLayout';
|
||||||
import NewsFeedLayout from '@layouts/newsfeedLayout';
|
import NewsFeedLayout from '@layouts/newsfeedLayout';
|
||||||
|
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
|
||||||
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';
|
|
||||||
|
|
||||||
export default function Page() {
|
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 (
|
return (
|
||||||
<div className="h-full w-full">
|
<div className="h-full w-full">
|
||||||
<Suspense fallback={<Placeholder />}>
|
<p>Global</p>
|
||||||
<Thread data={events} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
|
@ -2,108 +2,31 @@
|
|||||||
import BaseLayout from '@layouts/baseLayout';
|
import BaseLayout from '@layouts/baseLayout';
|
||||||
import FullLayout from '@layouts/fullLayout';
|
import FullLayout from '@layouts/fullLayout';
|
||||||
|
|
||||||
import { currentUser } from '@stores/currentUser';
|
|
||||||
import { follows } from '@stores/follows';
|
|
||||||
|
|
||||||
import LumeSymbol from '@assets/icons/Lume';
|
import LumeSymbol from '@assets/icons/Lume';
|
||||||
|
import { useLocalStorage } from '@rehooks/local-storage';
|
||||||
import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/api/notification';
|
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useEffect, useState } from 'react';
|
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
|
||||||
|
|
||||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
const [loading, setLoading] = useState(true);
|
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(() => {
|
useEffect(() => {
|
||||||
initDB()
|
if (!currentUser) {
|
||||||
.then(() => notification())
|
setTimeout(() => {
|
||||||
.then(() => {
|
setLoading(false);
|
||||||
getAccount()
|
router.push('/onboarding');
|
||||||
.then((res: any) => {
|
}, 1500);
|
||||||
if (res.length === 0) {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
router.push('/onboarding');
|
router.push('/feed/following');
|
||||||
}, 1500);
|
}, 1500);
|
||||||
} else {
|
}
|
||||||
// store current user in localstorage
|
}, [currentUser, router]);
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full flex-col items-center justify-between">
|
<div className="relative flex h-full flex-col items-center justify-between">
|
||||||
|
@ -2,107 +2,41 @@
|
|||||||
import BaseLayout from '@layouts/baseLayout';
|
import BaseLayout from '@layouts/baseLayout';
|
||||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
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 { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
|
||||||
|
import { useLocalStorage, writeStorage } from '@rehooks/local-storage';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { dateToUnix, useNostr } from 'nostr-react';
|
|
||||||
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
|
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
|
||||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useState } from 'react';
|
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useMemo, useState } from 'react';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
|
||||||
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
|
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
dictionaries: [names],
|
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() {
|
export default function Page() {
|
||||||
const router = useRouter();
|
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 [type, setType] = useState('password');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const [privKey] = useState(() => generatePrivateKey());
|
const [privKey] = useState(() => generatePrivateKey());
|
||||||
const [name] = useState(() => uniqueNamesGenerator(config).toString());
|
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 pubKey = getPublicKey(privKey);
|
||||||
const npub = nip19.npubEncode(pubKey);
|
const npub = nip19.npubEncode(pubKey);
|
||||||
const nsec = nip19.nsecEncode(privKey);
|
const nsec = nip19.nsecEncode(privKey);
|
||||||
|
|
||||||
// auto-generated profile
|
const showPrivateKey = () => {
|
||||||
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 = () => {
|
|
||||||
if (type === 'password') {
|
if (type === 'password') {
|
||||||
setType('text');
|
setType('text');
|
||||||
} else {
|
} 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 (
|
return (
|
||||||
<div className="flex h-full flex-col justify-between px-8">
|
<div className="flex h-full flex-col justify-between px-8">
|
||||||
<div>{/* spacer */}</div>
|
<div>{/* spacer */}</div>
|
||||||
<motion.div layoutId="form">
|
<motion.div layoutId="form">
|
||||||
<div className="mb-8 flex flex-col gap-3">
|
<div className="mb-8 flex flex-col gap-3">
|
||||||
<motion.h1
|
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||||
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
|
Create new key
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
<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
|
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
|
||||||
store your key safely so you can restore your account or use other client
|
account or use other client
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<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"
|
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
|
<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">
|
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700">
|
||||||
{type === 'password' ? (
|
{type === 'password' ? (
|
||||||
<EyeClosedIcon className="h-5 w-5 text-zinc-500 group-hover:text-zinc-200" />
|
<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>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<label className="text-sm font-semibold text-zinc-400">
|
<label className="text-sm font-semibold text-zinc-400">Default Profile (you can change it later)</label>
|
||||||
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 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="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="flex space-x-4">
|
||||||
<div className="relative h-10 w-10 rounded-full">
|
<div className="relative h-10 w-10 rounded-full">
|
||||||
<Image
|
<Image className="inline-block rounded-full" src={data.picture} alt="" fill={true} />
|
||||||
className="inline-block rounded-full"
|
|
||||||
src={data.picture}
|
|
||||||
alt=""
|
|
||||||
fill={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 space-y-4 py-1">
|
<div className="flex-1 space-y-4 py-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@ -193,18 +170,8 @@ export default function Page() {
|
|||||||
<motion.div layoutId="action" className="pb-5">
|
<motion.div layoutId="action" className="pb-5">
|
||||||
<div className="flex h-10 items-center">
|
<div className="flex h-10 items-center">
|
||||||
{loading === true ? (
|
{loading === true ? (
|
||||||
<svg
|
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
className="h-5 w-5 animate-spin text-white"
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||||
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
|
<path
|
||||||
className="opacity-75"
|
className="opacity-75"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@ -226,13 +193,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<BaseLayout>
|
@ -2,29 +2,29 @@
|
|||||||
import BaseLayout from '@layouts/baseLayout';
|
import BaseLayout from '@layouts/baseLayout';
|
||||||
import OnboardingLayout from '@layouts/onboardingLayout';
|
import OnboardingLayout from '@layouts/onboardingLayout';
|
||||||
|
|
||||||
|
import { DatabaseContext } from '@components/contexts/database';
|
||||||
|
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { currentUser } from '@stores/currentUser';
|
|
||||||
|
|
||||||
import data from '@assets/directory.json';
|
import data from '@assets/directory.json';
|
||||||
|
|
||||||
import { useStore } from '@nanostores/react';
|
|
||||||
import { CheckCircledIcon } from '@radix-ui/react-icons';
|
import { CheckCircledIcon } from '@radix-ui/react-icons';
|
||||||
|
import { useLocalStorage } from '@rehooks/local-storage';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useState } from 'react';
|
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
|
||||||
|
const shuffle = (arr: { name: string; avatar: string; npub: string }[]) => [...arr].sort(() => Math.random() - 0.5);
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const db: any = useContext(DatabaseContext);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const shuffle = (arr) => [...arr].sort(() => Math.random() - 0.5);
|
|
||||||
|
|
||||||
const [follow, setFollow] = useState([]);
|
const [follow, setFollow] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [list] = useState(shuffle(data));
|
const [list] = useState(shuffle(data));
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
|
|
||||||
const followUser = (e) => {
|
const followUser = (e) => {
|
||||||
const npub = e.currentTarget.getAttribute('data-npub');
|
const npub = e.currentTarget.getAttribute('data-npub');
|
||||||
@ -32,15 +32,12 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const insertDB = async () => {
|
const insertDB = async () => {
|
||||||
const db = await Database.load('sqlite:lume.db');
|
// self follow
|
||||||
await db.execute(
|
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${currentUser.pubkey}", "${currentUser.pubkey}", "0")`);
|
||||||
`INSERT INTO follows (pubkey, account) VALUES ("${$currentUser.pubkey}", "${$currentUser.pubkey}")`
|
// follow selected
|
||||||
);
|
|
||||||
follow.forEach(async (npub) => {
|
follow.forEach(async (npub) => {
|
||||||
const { data } = nip19.decode(npub);
|
const { data } = nip19.decode(npub);
|
||||||
await db.execute(
|
await db.execute(`INSERT INTO follows (pubkey, account, kind) VALUES ("${data}", "${currentUser.pubkey}", "0")`);
|
||||||
`INSERT INTO follows (pubkey, account) VALUES ("${data}", "${$currentUser.pubkey}")`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,14 +57,11 @@ export default function Page() {
|
|||||||
<div>{/* spacer */}</div>
|
<div>{/* spacer */}</div>
|
||||||
<motion.div layoutId="form" className="flex flex-col">
|
<motion.div layoutId="form" className="flex flex-col">
|
||||||
<div className="mb-8 flex flex-col gap-3">
|
<div className="mb-8 flex flex-col gap-3">
|
||||||
<motion.h1
|
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||||
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
|
Choose 10 people you want to following
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
<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
|
For better experiences, you should follow the people you care about to personalize your newsfeed, otherwise you will be very bored
|
||||||
newsfeed, otherwise you will be very bored
|
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full w-full shrink">
|
<div className="h-full w-full shrink">
|
||||||
@ -81,27 +75,14 @@ export default function Page() {
|
|||||||
follow.includes(item.npub) ? 'bg-zinc-800' : ''
|
follow.includes(item.npub) ? 'bg-zinc-800' : ''
|
||||||
}`}>
|
}`}>
|
||||||
<div className="relative h-10 w-10 flex-shrink-0">
|
<div className="relative h-10 w-10 flex-shrink-0">
|
||||||
<Image
|
<Image className="rounded-full object-cover" src={item.avatar} alt={item.name} fill={true} />
|
||||||
className="rounded-full object-cover"
|
|
||||||
src={item.avatar}
|
|
||||||
alt={item.name}
|
|
||||||
fill={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="inline-flex flex-1 items-center justify-between">
|
<div className="inline-flex flex-1 items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="truncate text-sm font-medium text-zinc-200">{item.name}</p>
|
<p className="truncate text-sm font-medium text-zinc-200">{item.name}</p>
|
||||||
<p className="text-sm leading-tight text-zinc-500">
|
<p className="text-sm leading-tight text-zinc-500">{truncate(item.npub, 16, ' .... ')}</p>
|
||||||
{truncate(item.npub, 16, ' .... ')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{follow.includes(item.npub) ? (
|
|
||||||
<CheckCircledIcon className="h-4 w-4 text-green-500" />
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>{follow.includes(item.npub) ? <CheckCircledIcon className="h-4 w-4 text-green-500" /> : <></>}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -111,18 +92,8 @@ export default function Page() {
|
|||||||
<motion.div layoutId="action" className="pb-5">
|
<motion.div layoutId="action" className="pb-5">
|
||||||
<div className="flex h-10 items-center">
|
<div className="flex h-10 items-center">
|
||||||
{loading === true ? (
|
{loading === true ? (
|
||||||
<svg
|
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
className="h-5 w-5 animate-spin text-white"
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||||
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
|
<path
|
||||||
className="opacity-75"
|
className="opacity-75"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@ -145,13 +116,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<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 className="flex h-full flex-col justify-between px-8">
|
||||||
<div>{/* spacer */}</div>
|
<div>{/* spacer */}</div>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<motion.h1
|
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||||
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
|
Other social network require email/password
|
||||||
<br />
|
<br />
|
||||||
nostr use{' '}
|
nostr use{' '}
|
||||||
@ -21,8 +19,8 @@ export default function Page() {
|
|||||||
</span>
|
</span>
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
<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
|
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
|
||||||
create a new key or use auto-generated account created by system.
|
created by system.
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.div layoutId="form"></motion.div>
|
<motion.div layoutId="form"></motion.div>
|
||||||
<motion.div layoutId="action" className="mt-4 flex gap-2">
|
<motion.div layoutId="action" className="mt-4 flex gap-2">
|
||||||
@ -32,7 +30,7 @@ export default function Page() {
|
|||||||
Create new key
|
Create new key
|
||||||
</Link>
|
</Link>
|
||||||
<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">
|
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
|
Login with private key
|
||||||
</Link>
|
</Link>
|
||||||
@ -44,13 +42,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<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 {
|
try {
|
||||||
router.push({
|
router.push({
|
||||||
pathname: '/onboarding/fetch-profile',
|
pathname: '/onboarding/login/fetch',
|
||||||
query: { privkey: privkey },
|
query: { privkey: privkey },
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -60,14 +60,12 @@ export default function Page() {
|
|||||||
<div>{/* spacer */}</div>
|
<div>{/* spacer */}</div>
|
||||||
<motion.div layoutId="form">
|
<motion.div layoutId="form">
|
||||||
<div className="mb-8 flex flex-col gap-3">
|
<div className="mb-8 flex flex-col gap-3">
|
||||||
<motion.h1
|
<motion.h1 layoutId="title" className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">
|
||||||
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
|
Import your private key
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.h2 layoutId="subtitle" className="w-3/4 text-zinc-400">
|
<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
|
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 compality wallet in your mobile, you can connect by scan QR Code below
|
connect by scan QR Code below
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
@ -85,18 +83,8 @@ export default function Page() {
|
|||||||
<motion.div layoutId="action" className="pb-5">
|
<motion.div layoutId="action" className="pb-5">
|
||||||
<div className="flex h-10 items-center">
|
<div className="flex h-10 items-center">
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<svg
|
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
className="h-5 w-5 animate-spin text-white"
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||||
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
|
<path
|
||||||
className="opacity-75"
|
className="opacity-75"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@ -117,13 +105,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<BaseLayout>
|
@ -2,13 +2,14 @@
|
|||||||
import BaseLayout from '@layouts/baseLayout';
|
import BaseLayout from '@layouts/baseLayout';
|
||||||
import UserLayout from '@layouts/userLayout';
|
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 { useRouter } from 'next/router';
|
||||||
import { dateToUnix, useNostr } from 'nostr-react';
|
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
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 { useForm } from 'react-hook-form';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
import Database from 'tauri-plugin-sql-api';
|
||||||
|
|
||||||
@ -24,15 +25,14 @@ type FormValues = {
|
|||||||
|
|
||||||
// TODO: update the design
|
// TODO: update the design
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const relayPool: any = useContext(RelayContext);
|
||||||
|
const [relays]: any = useLocalStorage('relays');
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { publish } = useNostr();
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const $currentUser: any = useStore(currentUser);
|
const [currentUser]: any = useLocalStorage('current-user');
|
||||||
const profile =
|
const profile = currentUser.metadata !== undefined ? JSON.parse(currentUser.metadata) : { display_name: null, username: null };
|
||||||
$currentUser.metadata !== undefined
|
|
||||||
? JSON.parse($currentUser.metadata)
|
|
||||||
: { display_name: null, username: null };
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -48,28 +48,25 @@ export default function Page() {
|
|||||||
content: JSON.stringify(data),
|
content: JSON.stringify(data),
|
||||||
created_at: dateToUnix(),
|
created_at: dateToUnix(),
|
||||||
kind: 0,
|
kind: 0,
|
||||||
pubkey: $currentUser.pubkey,
|
pubkey: currentUser.pubkey,
|
||||||
tags: [],
|
tags: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, $currentUser.privkey);
|
event.sig = signEvent(event, currentUser.privkey);
|
||||||
publish(event);
|
|
||||||
|
relayPool.publish(event, relays);
|
||||||
|
|
||||||
// save account to database
|
// save account to database
|
||||||
const db = await Database.load('sqlite:lume.db');
|
const db = await Database.load('sqlite:lume.db');
|
||||||
await db.execute(
|
await db.execute(`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${currentUser.pubkey}"`);
|
||||||
`UPDATE accounts SET metadata = '${JSON.stringify(data)}' WHERE pubkey = "${
|
|
||||||
$currentUser.pubkey
|
|
||||||
}"`
|
|
||||||
);
|
|
||||||
await db.close();
|
|
||||||
|
|
||||||
// set currentUser in global state
|
// set currentUser in global state
|
||||||
currentUser.set({
|
currentUser.set({
|
||||||
metadata: JSON.stringify(data),
|
metadata: JSON.stringify(data),
|
||||||
npub: $currentUser.npub,
|
npub: currentUser.npub,
|
||||||
privkey: $currentUser.privkey,
|
privkey: currentUser.privkey,
|
||||||
pubkey: $currentUser.pubkey,
|
pubkey: currentUser.pubkey,
|
||||||
});
|
});
|
||||||
|
|
||||||
// redirect to newsfeed
|
// redirect to newsfeed
|
||||||
@ -80,16 +77,11 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form onSubmit={handleSubmit(onSubmit)} className="flex h-full w-full flex-col justify-between px-6">
|
||||||
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">
|
<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">
|
<h1 className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-3xl font-medium text-transparent">Update profile</h1>
|
||||||
Update profile
|
|
||||||
</h1>
|
|
||||||
<h2 className="w-3/4 text-zinc-400">
|
<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
|
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
|
||||||
always can recover your profile in any client
|
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<fieldset className="flex flex-col gap-2">
|
<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"
|
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>
|
</div>
|
||||||
<span className="text-sm text-red-400">
|
<span className="text-sm text-red-400">{errors.display_name && <p>{errors.display_name.message}</p>}</span>
|
||||||
{errors.display_name && <p>{errors.display_name.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4">
|
<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"
|
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>
|
</div>
|
||||||
<span className="text-sm text-red-400">
|
<span className="text-sm text-red-400">{errors.name && <p>{errors.name.message}</p>}</span>
|
||||||
{errors.name && <p>{errors.name.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4">
|
<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"
|
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>
|
</div>
|
||||||
<span className="text-sm text-red-400">
|
<span className="text-sm text-red-400">{errors.username && <p>{errors.username.message}</p>}</span>
|
||||||
{errors.username && <p>{errors.username.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4">
|
<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"
|
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>
|
</div>
|
||||||
<span className="text-sm text-red-400">
|
<span className="text-sm text-red-400">{errors.picture && <p>{errors.picture.message}</p>}</span>
|
||||||
{errors.picture && <p>{errors.picture.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4">
|
<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"
|
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>
|
</div>
|
||||||
<span className="text-sm text-red-400">
|
<span className="text-sm text-red-400">{errors.banner && <p>{errors.banner.message}</p>}</span>
|
||||||
{errors.banner && <p>{errors.banner.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4">
|
<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"
|
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>
|
</div>
|
||||||
<span className="text-sm text-red-400">
|
<span className="text-sm text-red-400">{errors.about && <p>{errors.about.message}</p>}</span>
|
||||||
{errors.about && <p>{errors.about.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div className="pb-5">
|
<div className="pb-5">
|
||||||
<div className="flex h-10 items-center">
|
<div className="flex h-10 items-center">
|
||||||
{loading === true ? (
|
{loading === true ? (
|
||||||
<svg
|
<svg className="h-5 w-5 animate-spin text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
className="h-5 w-5 animate-spin text-white"
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||||
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
|
<path
|
||||||
className="opacity-75"
|
className="opacity-75"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@ -231,13 +201,7 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
Page.getLayout = function getLayout(
|
||||||
page:
|
page: string | number | boolean | ReactElement<unknown, string | JSXElementConstructor<unknown>> | ReactFragment | ReactPortal
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<BaseLayout>
|
<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;
|
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/*"],
|
"@layouts/*": ["src/layouts/*"],
|
||||||
"@components/*": ["src/components/*"],
|
"@components/*": ["src/components/*"],
|
||||||
"@utils/*": ["src/utils/*"],
|
"@utils/*": ["src/utils/*"],
|
||||||
"@stores/*": ["src/stores/*"],
|
|
||||||
"@assets/*": ["src/assets/*"]
|
"@assets/*": ["src/assets/*"]
|
||||||
},
|
},
|
||||||
"target": "es2017",
|
"target": "es2017",
|
||||||
|
Loading…
Reference in New Issue
Block a user