add edit profile

This commit is contained in:
Ren Amamiya 2023-06-29 10:37:35 +07:00
parent ec1ff9ab87
commit 1ba7f823cb
20 changed files with 965 additions and 184 deletions

View File

@ -15,7 +15,8 @@
"dependencies": { "dependencies": {
"@floating-ui/react": "^0.23.1", "@floating-ui/react": "^0.23.1",
"@headlessui/react": "^1.7.15", "@headlessui/react": "^1.7.15",
"@nostr-dev-kit/ndk": "^0.5.13", "@nostr-dev-kit/ndk": "^0.6.3",
"@radix-ui/react-tooltip": "^1.0.6",
"@tanstack/react-query": "^4.29.19", "@tanstack/react-query": "^4.29.19",
"@tanstack/react-virtual": "3.0.0-beta.54", "@tanstack/react-virtual": "3.0.0-beta.54",
"@tauri-apps/api": "^1.4.0", "@tauri-apps/api": "^1.4.0",
@ -29,7 +30,7 @@
"nostr-tools": "^1.12.0", "nostr-tools": "^1.12.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.45.0", "react-hook-form": "^7.45.1",
"react-hotkeys-hook": "^4.4.0", "react-hotkeys-hook": "^4.4.0",
"react-player": "^2.12.0", "react-player": "^2.12.0",
"react-router-dom": "^6.14.0", "react-router-dom": "^6.14.0",
@ -55,7 +56,7 @@
"csstype": "^3.1.2", "csstype": "^3.1.2",
"encoding": "^0.1.13", "encoding": "^0.1.13",
"husky": "^8.0.3", "husky": "^8.0.3",
"lint-staged": "^13.2.2", "lint-staged": "^13.2.3",
"postcss": "^8.4.24", "postcss": "^8.4.24",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"rome": "12.1.0", "rome": "12.1.0",

View File

@ -8,8 +8,11 @@ dependencies:
specifier: ^1.7.15 specifier: ^1.7.15
version: 1.7.15(react-dom@18.2.0)(react@18.2.0) version: 1.7.15(react-dom@18.2.0)(react@18.2.0)
'@nostr-dev-kit/ndk': '@nostr-dev-kit/ndk':
specifier: ^0.5.13 specifier: ^0.6.3
version: 0.5.13(typescript@4.9.5) version: 0.6.3(typescript@4.9.5)
'@radix-ui/react-tooltip':
specifier: ^1.0.6
version: 1.0.6(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^4.29.19 specifier: ^4.29.19
version: 4.29.19(react-dom@18.2.0)(react@18.2.0) version: 4.29.19(react-dom@18.2.0)(react@18.2.0)
@ -50,8 +53,8 @@ dependencies:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-hook-form: react-hook-form:
specifier: ^7.45.0 specifier: ^7.45.1
version: 7.45.0(react@18.2.0) version: 7.45.1(react@18.2.0)
react-hotkeys-hook: react-hotkeys-hook:
specifier: ^4.4.0 specifier: ^4.4.0
version: 4.4.0(react-dom@18.2.0)(react@18.2.0) version: 4.4.0(react-dom@18.2.0)(react@18.2.0)
@ -124,8 +127,8 @@ devDependencies:
specifier: ^8.0.3 specifier: ^8.0.3
version: 8.0.3 version: 8.0.3
lint-staged: lint-staged:
specifier: ^13.2.2 specifier: ^13.2.3
version: 13.2.2 version: 13.2.3
postcss: postcss:
specifier: ^8.4.24 specifier: ^8.4.24
version: 8.4.24 version: 8.4.24
@ -153,8 +156,8 @@ devDependencies:
packages: packages:
/@aashutoshrathi/word-wrap@1.2.5: /@aashutoshrathi/word-wrap@1.2.6:
resolution: {integrity: sha512-plhoNEfSVdHMKXQyAxvH0Zyv3/4NL8r6pwgMQdmHR2vBUXn2t74PN2pBRppqKUa6RMT0yldyvOHG5Dbjwy2mBQ==} resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: false dev: false
@ -184,6 +187,13 @@ packages:
js-tokens: 4.0.0 js-tokens: 4.0.0
dev: false dev: false
/@babel/runtime@7.22.5:
resolution: {integrity: sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
dev: false
/@emotion/is-prop-valid@0.8.8: /@emotion/is-prop-valid@0.8.8:
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
requiresBuild: true requiresBuild: true
@ -431,6 +441,17 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@floating-ui/react-dom@2.0.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/dom': 1.4.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@floating-ui/react@0.23.1(react-dom@18.2.0)(react@18.2.0): /@floating-ui/react@0.23.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OCc2ViQOBUKOGcE9NLAbpyqB+8Zz92IKIhxgz7XAkynKkVzcVSKtkWOcgyvO4SAzB2OybgRwk3WdzdzDRdh2QQ==} resolution: {integrity: sha512-OCc2ViQOBUKOGcE9NLAbpyqB+8Zz92IKIhxgz7XAkynKkVzcVSKtkWOcgyvO4SAzB2OybgRwk3WdzdzDRdh2QQ==}
peerDependencies: peerDependencies:
@ -570,8 +591,8 @@ packages:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.15.0 fastq: 1.15.0
/@nostr-dev-kit/ndk@0.5.13(typescript@4.9.5): /@nostr-dev-kit/ndk@0.6.3(typescript@4.9.5):
resolution: {integrity: sha512-Lu5vHzKmV3NrQyIxbneft7QlY2KpxOhsNOo1sN71z1b4plBSDrUT8xBgPTzm1itfL1Xw0kxy6pa/LSdVlsA3oQ==} resolution: {integrity: sha512-lXMahPRepqNmt7rIM/I7pJz/VB5PSCakZScV3+L0AmUpsN4UXHX+fFtkFjyfNpM/zW+YlxFMFbo/WM+jB55j0A==}
dependencies: dependencies:
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.1
'@noble/secp256k1': 2.0.0 '@noble/secp256k1': 2.0.0
@ -613,6 +634,357 @@ packages:
dev: false dev: false
optional: true optional: true
/@radix-ui/primitive@1.0.1:
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
dependencies:
'@babel/runtime': 7.22.5
dev: false
/@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-context@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-id@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@floating-ui/react-dom': 2.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-slot@1.0.2(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-tooltip@1.0.6(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-use-rect@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-use-size@1.0.1(@types/react@18.2.14)(react@18.2.0):
resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.14)(react@18.2.0)
'@types/react': 18.2.14
react: 18.2.0
dev: false
/@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.22.5
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.14
'@types/react-dom': 18.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies:
'@babel/runtime': 7.22.5
dev: false
/@remix-run/router@1.7.0: /@remix-run/router@1.7.0:
resolution: {integrity: sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw==} resolution: {integrity: sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -1020,13 +1392,11 @@ packages:
/@types/prop-types@15.7.5: /@types/prop-types@15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
dev: true
/@types/react-dom@18.2.6: /@types/react-dom@18.2.6:
resolution: {integrity: sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==} resolution: {integrity: sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==}
dependencies: dependencies:
'@types/react': 18.2.14 '@types/react': 18.2.14
dev: true
/@types/react@18.2.14: /@types/react@18.2.14:
resolution: {integrity: sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==} resolution: {integrity: sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==}
@ -1034,11 +1404,9 @@ packages:
'@types/prop-types': 15.7.5 '@types/prop-types': 15.7.5
'@types/scheduler': 0.16.3 '@types/scheduler': 0.16.3
csstype: 3.1.2 csstype: 3.1.2
dev: true
/@types/scheduler@0.16.3: /@types/scheduler@0.16.3:
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
dev: true
/@types/semver@7.5.0: /@types/semver@7.5.0:
resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==}
@ -1379,7 +1747,7 @@ packages:
postcss: ^8.1.0 postcss: ^8.1.0
dependencies: dependencies:
browserslist: 4.21.9 browserslist: 4.21.9
caniuse-lite: 1.0.30001508 caniuse-lite: 1.0.30001509
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
@ -1427,8 +1795,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.30001508 caniuse-lite: 1.0.30001509
electron-to-chromium: 1.4.442 electron-to-chromium: 1.4.444
node-releases: 2.0.12 node-releases: 2.0.12
update-browserslist-db: 1.0.11(browserslist@4.21.9) update-browserslist-db: 1.0.11(browserslist@4.21.9)
dev: true dev: true
@ -1490,8 +1858,8 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: false dev: false
/caniuse-lite@1.0.30001508: /caniuse-lite@1.0.30001509:
resolution: {integrity: sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==} resolution: {integrity: sha512-2uDDk+TRiTX5hMcUYT/7CSyzMZxjfGu0vAUjS2g0LSD8UoXOv0LtpH4LxGMemsiPq6LCVIUjNwVM0erkOkGCDA==}
dev: true dev: true
/chalk@2.4.2: /chalk@2.4.2:
@ -1697,7 +2065,6 @@ packages:
/csstype@3.1.2: /csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
dev: true
/d@1.0.1: /d@1.0.1:
resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
@ -1860,8 +2227,8 @@ packages:
/eastasianwidth@0.2.0: /eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
/electron-to-chromium@1.4.442: /electron-to-chromium@1.4.444:
resolution: {integrity: sha512-RkrZF//Ya+0aJq2NM3OdisNh5ZodZq1rdXOS96G8DdDgpDKqKE81yTbbQ3F/4CKm1JBPsGu1Lp/akkna2xO06Q==} resolution: {integrity: sha512-/AjkL4syRvOpowXWy3N4OHmVbFdWQpfSKHh0sCVYipDeEAtMce3rLjMJi/27Ia9jNIbw6P1JuPN32pSWybXXEQ==}
dev: true dev: true
/emoji-regex@8.0.0: /emoji-regex@8.0.0:
@ -2192,7 +2559,7 @@ packages:
lodash.merge: 4.6.2 lodash.merge: 4.6.2
minimatch: 3.1.2 minimatch: 3.1.2
natural-compare: 1.4.0 natural-compare: 1.4.0
optionator: 0.9.2 optionator: 0.9.3
strip-ansi: 6.0.1 strip-ansi: 6.0.1
strip-json-comments: 3.1.1 strip-json-comments: 3.1.1
text-table: 0.2.0 text-table: 0.2.0
@ -2206,7 +2573,7 @@ packages:
create-esm-loader: 0.2.3 create-esm-loader: 0.2.3
npm-run-all: 4.1.5 npm-run-all: 4.1.5
semver: 7.5.3 semver: 7.5.3
typescript: 5.1.5 typescript: 5.1.6
dev: false dev: false
/espree@9.5.2: /espree@9.5.2:
@ -3024,8 +3391,8 @@ packages:
/lines-and-columns@1.2.4: /lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
/lint-staged@13.2.2: /lint-staged@13.2.3:
resolution: {integrity: sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==} resolution: {integrity: sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==}
engines: {node: ^14.13.1 || >=16.0.0} engines: {node: ^14.13.1 || >=16.0.0}
hasBin: true hasBin: true
dependencies: dependencies:
@ -3569,11 +3936,11 @@ packages:
mimic-fn: 4.0.0 mimic-fn: 4.0.0
dev: true dev: true
/optionator@0.9.2: /optionator@0.9.3:
resolution: {integrity: sha512-W/Q5m5jpuspnghjVKWcs+5sm9Xl9BIdDmNLWaQbwneNQEZFosaiVPC9vtwN0Dbxx4x7TLY7bxjvttW26AmcKrQ==} resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
dependencies: dependencies:
'@aashutoshrathi/word-wrap': 1.2.5 '@aashutoshrathi/word-wrap': 1.2.6
deep-is: 0.1.4 deep-is: 0.1.4
fast-levenshtein: 2.0.6 fast-levenshtein: 2.0.6
levn: 0.4.1 levn: 0.4.1
@ -3891,8 +4258,8 @@ packages:
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
dev: false dev: false
/react-hook-form@7.45.0(react@18.2.0): /react-hook-form@7.45.1(react@18.2.0):
resolution: {integrity: sha512-AbHeZ4ad+0dEIknSW9dBgIwcvRDfZ1O97sgj75WaMdOX0eg8TBiUf9wxzVkIjZbk76BBIE9lmFOzyD4PN80ZQg==} resolution: {integrity: sha512-6dWoFJwycbuFfw/iKMcl+RdAOAOHDiF11KWYhNDRN/OkUt+Di5qsZHwA0OwsVnu9y135gkHpTw9DJA+WzCeR9w==}
engines: {node: '>=12.22.0'} engines: {node: '>=12.22.0'}
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17 || ^18 react: ^16.8.0 || ^17 || ^18
@ -4034,6 +4401,10 @@ packages:
strip-indent: 3.0.0 strip-indent: 3.0.0
dev: false dev: false
/regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
dev: false
/regexp.prototype.flags@1.5.0: /regexp.prototype.flags@1.5.0:
resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -4686,8 +5057,8 @@ packages:
engines: {node: '>=4.2.0'} engines: {node: '>=4.2.0'}
hasBin: true hasBin: true
/typescript@5.1.5: /typescript@5.1.6:
resolution: {integrity: sha512-FOH+WN/DQjUvN6WgW+c4Ml3yi0PH+a/8q+kNIfRehv1wLhWONedw85iu+vQ39Wp49IzTJEsZ2lyLXpBF7mkF1g==} resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
dev: false dev: false

View File

@ -2,7 +2,7 @@ import { Dialog, Transition } from "@headlessui/react";
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { CancelIcon, HideIcon } from "@shared/icons"; import { CancelIcon, HideIcon } from "@shared/icons";
import { RelayContext } from "@shared/relayProvider"; import { RelayContext } from "@shared/relayProvider";
import { Tooltip } from "@shared/tooltip"; import { Tooltip } from "@shared/tooltip_dep";
import { useChannelMessages } from "@stores/channels"; import { useChannelMessages } from "@stores/channels";
import { dateToUnix } from "@utils/date"; import { dateToUnix } from "@utils/date";
import { useAccount } from "@utils/hooks/useAccount"; import { useAccount } from "@utils/hooks/useAccount";

View File

@ -2,7 +2,7 @@ import { Dialog, Transition } from "@headlessui/react";
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { CancelIcon, MuteIcon } from "@shared/icons"; import { CancelIcon, MuteIcon } from "@shared/icons";
import { RelayContext } from "@shared/relayProvider"; import { RelayContext } from "@shared/relayProvider";
import { Tooltip } from "@shared/tooltip"; import { Tooltip } from "@shared/tooltip_dep";
import { useChannelMessages } from "@stores/channels"; import { useChannelMessages } from "@stores/channels";
import { dateToUnix } from "@utils/date"; import { dateToUnix } from "@utils/date";
import { useAccount } from "@utils/hooks/useAccount"; import { useAccount } from "@utils/hooks/useAccount";

View File

@ -1,5 +1,5 @@
import { ReplyMessageIcon } from "@shared/icons"; import { ReplyMessageIcon } from "@shared/icons";
import { Tooltip } from "@shared/tooltip"; import { Tooltip } from "@shared/tooltip_dep";
import { useChannelMessages } from "@stores/channels"; import { useChannelMessages } from "@stores/channels";
export function MessageReplyButton({ export function MessageReplyButton({

View File

@ -1,9 +1,11 @@
import { UserFeed } from "@app/user/components/feed"; import { UserFeed } from "@app/user/components/feed";
import { UserMetadata } from "@app/user/components/metadata"; import { UserMetadata } from "@app/user/components/metadata";
import { Tab } from "@headlessui/react"; import { Tab } from "@headlessui/react";
import { EditProfileModal } from "@shared/editProfileModal";
import { ThreadsIcon, ZapIcon } from "@shared/icons"; import { ThreadsIcon, ZapIcon } from "@shared/icons";
import { Image } from "@shared/image"; import { Image } from "@shared/image";
import { DEFAULT_AVATAR } from "@stores/constants"; import { DEFAULT_AVATAR } from "@stores/constants";
import { useAccount } from "@utils/hooks/useAccount";
import { useProfile } from "@utils/hooks/useProfile"; import { useProfile } from "@utils/hooks/useProfile";
import { useSocial } from "@utils/hooks/useSocial"; import { useSocial } from "@utils/hooks/useSocial";
import { shortenKey } from "@utils/shortenKey"; import { shortenKey } from "@utils/shortenKey";
@ -13,6 +15,7 @@ import { Link, useParams } from "react-router-dom";
export function UserScreen() { export function UserScreen() {
const { pubkey } = useParams(); const { pubkey } = useParams();
const { user } = useProfile(pubkey); const { user } = useProfile(pubkey);
const { account } = useAccount();
const { status, userFollows, follow, unfollow } = useSocial(); const { status, userFollows, follow, unfollow } = useSocial();
const [followed, setFollowed] = useState(false); const [followed, setFollowed] = useState(false);
@ -116,6 +119,8 @@ export function UserScreen() {
> >
<ZapIcon className="w-5 h-5" /> <ZapIcon className="w-5 h-5" />
</button> </button>
<span className="inline-flex mx-2 w-px h-4 bg-zinc-900" />
{account && account.pubkey === pubkey && <EditProfileModal />}
</div> </div>
</div> </div>
<div className="flex flex-col gap-8"> <div className="flex flex-col gap-8">

View File

@ -8,6 +8,7 @@ import { useProfile } from "@utils/hooks/useProfile";
import { sendNativeNotification } from "@utils/notification"; import { sendNativeNotification } from "@utils/notification";
import { produce } from "immer"; import { produce } from "immer";
import { useContext, useEffect } from "react"; import { useContext, useEffect } from "react";
import { Link } from "react-router-dom";
const lastLogin = await getLastLogin(); const lastLogin = await getLastLogin();
@ -92,7 +93,10 @@ export function ActiveAccount({ data }: { data: any }) {
} }
return ( return (
<div className="relative inline-block h-9 w-9"> <Link
to={`/app/user/${data.pubkey}`}
className="relative inline-block h-9 w-9"
>
<Image <Image
src={user.image} src={user.image}
fallback={DEFAULT_AVATAR} fallback={DEFAULT_AVATAR}
@ -100,6 +104,6 @@ export function ActiveAccount({ data }: { data: any }) {
className="h-9 w-9 rounded-md object-cover" className="h-9 w-9 rounded-md object-cover"
/> />
<NetworkStatusIndicator /> <NetworkStatusIndicator />
</div> </Link>
); );
} }

View File

@ -1,11 +1,10 @@
import { createBlobFromFile } from "@utils/createBlobFromFile"; import { LoaderIcon, PlusIcon } from "@shared/icons";
import { LoaderIcon } from "./icons";
import { open } from "@tauri-apps/api/dialog"; import { open } from "@tauri-apps/api/dialog";
import { Body, fetch } from "@tauri-apps/api/http"; import { Body, fetch } from "@tauri-apps/api/http";
import { createBlobFromFile } from "@utils/createBlobFromFile";
import { useState } from "react"; import { useState } from "react";
export function AvatarUploader({ valueState }: { valueState: any }) { export function AvatarUploader({ setPicture }: { setPicture: any }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const openFileDialog = async () => { const openFileDialog = async () => {
@ -44,23 +43,26 @@ export function AvatarUploader({ valueState }: { valueState: any }) {
body: Body.bytes(buf), body: Body.bytes(buf),
}, },
); );
const webpImage = `https://void.cat/d/${res.data.file.id}.webp`; const image = `https://void.cat/d/${res.data.file.id}.jpg`;
valueState(webpImage); // update parent state
setPicture(image);
// disable loader
setLoading(false); setLoading(false);
} }
}; };
return ( return (
<button <button
onClick={() => openFileDialog()}
type="button" type="button"
className="inline-flex h-7 items-center justify-center rounded bg-zinc-900 px-3 text-sm font-medium text-zinc-200 hover:bg-zinc-800" onClick={() => openFileDialog()}
className="w-full h-full inline-flex items-center justify-center bg-zinc-900/40"
> >
{loading ? ( {loading ? (
<LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-zinc-100" /> <LoaderIcon className="h-6 w-6 animate-spintext-zinc-100" />
) : ( ) : (
<span className="leading-none">Upload</span> <PlusIcon className="h-6 w-6 text-zinc-100" />
)} )}
</button> </button>
); );

View File

@ -0,0 +1,69 @@
import { LoaderIcon, PlusIcon } from "@shared/icons";
import { open } from "@tauri-apps/api/dialog";
import { Body, fetch } from "@tauri-apps/api/http";
import { createBlobFromFile } from "@utils/createBlobFromFile";
import { useState } from "react";
export function BannerUploader({ setBanner }: { setBanner: any }) {
const [loading, setLoading] = useState(false);
const openFileDialog = async () => {
const selected: any = await open({
multiple: false,
filters: [
{
name: "Image",
extensions: ["png", "jpeg", "jpg", "gif"],
},
],
});
if (Array.isArray(selected)) {
// user selected multiple files
} else if (selected === null) {
// user cancelled the selection
} else {
setLoading(true);
const filename = selected.split("/").pop();
const file = await createBlobFromFile(selected);
const buf = await file.arrayBuffer();
const res: { data: { file: { id: string } } } = await fetch(
"https://void.cat/upload?cli=false",
{
method: "POST",
timeout: 5,
headers: {
accept: "*/*",
"Content-Type": "application/octet-stream",
"V-Filename": filename,
"V-Description": "Upload from https://lume.nu",
"V-Strip-Metadata": "true",
},
body: Body.bytes(buf),
},
);
const image = `https://void.cat/d/${res.data.file.id}.jpg`;
// update parent state
setBanner(image);
// disable loader
setLoading(false);
}
};
return (
<button
type="button"
onClick={() => openFileDialog()}
className="w-full h-full inline-flex items-center justify-center bg-zinc-900/40"
>
{loading ? (
<LoaderIcon className="h-8 w-8 animate-spintext-zinc-100" />
) : (
<PlusIcon className="h-8 w-8 text-zinc-100" />
)}
</button>
);
}

View File

@ -0,0 +1,226 @@
import { Dialog, Transition } from "@headlessui/react";
import { usePublish } from "@libs/ndk";
import { getPleb } from "@libs/storage";
import { AvatarUploader } from "@shared/avatarUploader";
import { BannerUploader } from "@shared/bannerUploader";
import { CancelIcon, LoaderIcon } from "@shared/icons";
import { Image } from "@shared/image";
import { DEFAULT_AVATAR } from "@stores/constants";
import { useAccount } from "@utils/hooks/useAccount";
import { Fragment, useState } from "react";
import { useForm } from "react-hook-form";
export function EditProfileModal() {
const publish = usePublish();
const [isOpen, setIsOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [picture, setPicture] = useState(DEFAULT_AVATAR);
const [banner, setBanner] = useState(null);
const { account } = useAccount();
const {
register,
handleSubmit,
reset,
formState: { isValid },
} = useForm({
defaultValues: async () => {
const res = await getPleb(account.npub);
if (res.picture) {
setPicture(res.image);
}
if (res.banner) {
setBanner(res.banner);
}
return res;
},
});
const closeModal = () => {
setIsOpen(false);
};
const openModal = () => {
setIsOpen(true);
};
const onSubmit = (data: any) => {
// start loading
setLoading(true);
// publish
const event = publish({
content: JSON.stringify({
...data,
display_name: data.name,
bio: data.about,
image: data.picture,
}),
kind: 0,
tags: [],
});
if (event) {
setTimeout(() => {
// reset form
reset();
// reset state
setLoading(false);
setIsOpen(false);
setPicture(DEFAULT_AVATAR);
setBanner(null);
}, 1200);
}
};
return (
<>
<button
type="button"
onClick={() => openModal()}
className="inline-flex w-36 h-10 items-center justify-center rounded-md bg-zinc-900 hover:bg-fuchsia-500 text-sm font-medium"
>
Edit profile
</button>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
</Transition.Child>
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col rounded-lg border-t border-zinc-800/50 bg-zinc-900">
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-5">
<div className="flex items-center justify-between">
<Dialog.Title
as="h3"
className="text-lg font-semibold leading-none text-zinc-100"
>
Edit profile
</Dialog.Title>
<button
type="button"
onClick={closeModal}
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
>
<CancelIcon className="w-5 h-5 text-zinc-300" />
</button>
</div>
</div>
<div className="flex h-full w-full flex-col overflow-y-auto">
<form onSubmit={handleSubmit(onSubmit)} className="mb-0">
<input
type={"hidden"}
{...register("picture")}
value={picture}
className="relative h-10 w-full rounded-lg border border-black/5 px-3 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-100 dark:shadow-black/10 dark:placeholder:text-zinc-500"
/>
<input
type={"hidden"}
{...register("banner")}
value={banner}
className="relative h-10 w-full rounded-lg border border-black/5 px-3 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-100 dark:shadow-black/10 dark:placeholder:text-zinc-500"
/>
<div className="relative">
<div className="relative w-full h-44 bg-zinc-800">
<Image
src={banner}
fallback="https://void.cat/d/QY1myro5tkHVs2nY7dy74b.jpg"
alt="user's banner"
className="h-full w-full object-cover"
/>
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-10 w-full h-full">
<BannerUploader setBanner={setBanner} />
</div>
</div>
<div className="px-4 mb-5">
<div className="z-10 relative h-14 w-14 -mt-7">
<Image
src={picture}
fallback={DEFAULT_AVATAR}
alt="user's avatar"
className="h-14 w-14 object-cover ring-2 ring-zinc-900 rounded-lg"
/>
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-10 w-full h-full">
<AvatarUploader setPicture={setPicture} />
</div>
</div>
</div>
</div>
<div className="flex flex-col gap-4 px-4 pb-4">
<div className="flex flex-col gap-1">
<label className="text-sm font-semibold uppercase tracking-wider text-zinc-400">
Name
</label>
<input
type={"text"}
{...register("name", {
required: true,
minLength: 4,
})}
spellCheck={false}
className="relative h-10 w-full rounded-lg px-3 py-2 !outline-none bg-zinc-800 text-zinc-100 placeholder:text-zinc-500"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-semibold uppercase tracking-wider text-zinc-400">
Bio
</label>
<textarea
{...register("about")}
spellCheck={false}
className="relative resize-none h-20 w-full rounded-lg px-3 py-2 !outline-none bg-zinc-800 text-zinc-100 placeholder:text-zinc-500"
/>
</div>
<div className="flex flex-col gap-1">
<label className="text-sm font-semibold uppercase tracking-wider text-zinc-400">
Website
</label>
<input
type={"text"}
{...register("website", { required: false })}
spellCheck={false}
className="relative h-10 w-full rounded-lg px-3 py-2 !outline-none bg-zinc-800 text-zinc-100 placeholder:text-zinc-500"
/>
</div>
<div>
<button
type="submit"
disabled={!isValid}
className="inline-flex items-center justify-center gap-1 transform active:translate-y-1 disabled:pointer-events-none disabled:opacity-50 focus:outline-none h-11 w-full bg-fuchsia-500 rounded-md font-medium text-zinc-100 hover:bg-fuchsia-600"
>
{loading ? (
<LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-zinc-100" />
) : (
"Update"
)}
</button>
</div>
</div>
</form>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</Dialog>
</Transition>
</>
);
}

View File

@ -13,8 +13,11 @@ export function ThreadIcon(
{...props} {...props}
> >
<path <path
fill="currentColor" stroke="currentColor"
d="M7.5 5.75a1.75 1.75 0 113.5 0 1.75 1.75 0 01-3.5 0zM13 5.75a1.75 1.75 0 113.5 0 1.75 1.75 0 01-3.5 0zM7.5 18.25a1.75 1.75 0 113.5 0 1.75 1.75 0 01-3.5 0zM13 18.25a1.75 1.75 0 113.5 0 1.75 1.75 0 01-3.5 0zM7.5 11.9a1.75 1.75 0 113.5 0v.1a1.75 1.75 0 11-3.5 0v-.1zM13 11.9a1.75 1.75 0 113.5 0v.1a1.75 1.75 0 11-3.5 0v-.1z" strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M2.75 12h18.5M2.75 5.75h18.5m-18.5 12.5h8.75"
/> />
</svg> </svg>
); );

View File

@ -1,5 +1,5 @@
import { MediaIcon } from "@shared/icons"; import * as Tooltip from "@radix-ui/react-tooltip";
import { Tooltip } from "@shared/tooltip"; import { LoaderIcon, MediaIcon } from "@shared/icons";
import { open } from "@tauri-apps/api/dialog"; import { open } from "@tauri-apps/api/dialog";
import { Body, fetch } from "@tauri-apps/api/http"; import { Body, fetch } from "@tauri-apps/api/http";
import { createBlobFromFile } from "@utils/createBlobFromFile"; import { createBlobFromFile } from "@utils/createBlobFromFile";
@ -56,34 +56,16 @@ export function MediaUploader({ setState }: { setState: any }) {
}; };
return ( return (
<Tooltip message="Upload media"> <Tooltip.Provider>
<Tooltip.Root delayDuration={150}>
<Tooltip.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => openFileDialog()} onClick={() => openFileDialog()}
className="group inline-flex h-6 w-6 items-center justify-center rounded bg-zinc-700 hover:bg-zinc-600" className="group inline-flex h-6 w-6 items-center justify-center rounded bg-zinc-700 hover:bg-zinc-600"
> >
{loading ? ( {loading ? (
<svg <LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-zinc-100" />
className="h-4 w-4 animate-spin text-black dark:text-zinc-100"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<title id="loading">Loading</title>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<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"
/>
</svg>
) : ( ) : (
<MediaIcon <MediaIcon
width={14} width={14}
@ -92,6 +74,17 @@ export function MediaUploader({ setState }: { setState: any }) {
/> />
)} )}
</button> </button>
</Tooltip> </Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content
className="-left-10 data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade select-none text-sm rounded-md text-zinc-100 bg-zinc-800/80 backdrop-blur-lg px-3.5 py-1.5 leading-none will-change-[transform,opacity]"
sideOffset={5}
>
Upload media
<Tooltip.Arrow className="fill-zinc-800/80 backdrop-blur-lg" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
); );
} }

View File

@ -1,11 +1,13 @@
import { createReplyNote } from "@libs/storage"; import { createBlock, createReplyNote } from "@libs/storage";
import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk"; import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk";
import * as Tooltip from "@radix-ui/react-tooltip";
import { LoaderIcon, ReplyIcon, RepostIcon, ZapIcon } from "@shared/icons"; import { LoaderIcon, ReplyIcon, RepostIcon, ZapIcon } from "@shared/icons";
import { ThreadIcon } from "@shared/icons/thread";
import { NoteReply } from "@shared/notes/metadata/reply"; import { NoteReply } from "@shared/notes/metadata/reply";
import { NoteRepost } from "@shared/notes/metadata/repost"; import { NoteRepost } from "@shared/notes/metadata/repost";
import { NoteZap } from "@shared/notes/metadata/zap"; import { NoteZap } from "@shared/notes/metadata/zap";
import { RelayContext } from "@shared/relayProvider"; import { RelayContext } from "@shared/relayProvider";
import { useQuery } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { decode } from "light-bolt11-decoder"; import { decode } from "light-bolt11-decoder";
import { useContext } from "react"; import { useContext } from "react";
@ -19,6 +21,8 @@ export function NoteMetadata({
eventPubkey: string; eventPubkey: string;
}) { }) {
const ndk = useContext(RelayContext); const ndk = useContext(RelayContext);
const queryClient = useQueryClient();
const { status, data } = useQuery(["note-metadata", id], async () => { const { status, data } = useQuery(["note-metadata", id], async () => {
let replies = 0; let replies = 0;
let reposts = 0; let reposts = 0;
@ -67,10 +71,27 @@ export function NoteMetadata({
return { replies, reposts, zap }; return { replies, reposts, zap };
}); });
const block = useMutation({
mutationFn: (data: any) => {
return createBlock(data.kind, data.title, data.content);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["blocks"] });
},
});
const openThread = (thread: string) => {
const selection = window.getSelection();
if (selection.toString().length === 0) {
block.mutate({ kind: 2, title: "Thread", content: thread });
} else {
event.stopPropagation();
}
};
if (status === "loading") {
return ( return (
<div className="inline-flex items-center w-full h-12 mt-2"> <div className="inline-flex items-center w-full h-12 mt-2">
{status === "loading" ? (
<>
<div className="w-20 group inline-flex items-center gap-1.5"> <div className="w-20 group inline-flex items-center gap-1.5">
<ReplyIcon <ReplyIcon
width={16} width={16}
@ -107,9 +128,14 @@ export function NoteMetadata({
className="animate-spin text-black dark:text-zinc-100" className="animate-spin text-black dark:text-zinc-100"
/> />
</div> </div>
</> </div>
) : ( );
<> }
return (
<Tooltip.Provider>
<div className="inline-flex items-center justify-between w-full h-12 mt-2">
<div className="inline-flex justify-between items-center">
<NoteReply <NoteReply
id={id} id={id}
rootID={rootID} rootID={rootID}
@ -118,8 +144,28 @@ export function NoteMetadata({
/> />
<NoteRepost id={id} pubkey={eventPubkey} reposts={data.reposts} /> <NoteRepost id={id} pubkey={eventPubkey} reposts={data.reposts} />
<NoteZap zaps={data.zap} /> <NoteZap zaps={data.zap} />
</>
)}
</div> </div>
<Tooltip.Root delayDuration={150}>
<Tooltip.Trigger asChild>
<button
type="button"
onClick={() => openThread(id)}
className="w-6 h-6 inline-flex items-center justify-center rounded border-t border-zinc-700/50 bg-zinc-800 hover:bg-zinc-700"
>
<ThreadIcon className="w-4 h-4 text-zinc-400" />
</button>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content
className="-left-10 data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade select-none text-sm rounded-md text-zinc-100 bg-zinc-800/80 backdrop-blur-lg px-3.5 py-1.5 leading-none will-change-[transform,opacity]"
sideOffset={5}
>
Open thread
<Tooltip.Arrow className="fill-zinc-800/80 backdrop-blur-lg" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</div>
</Tooltip.Provider>
); );
} }

View File

@ -1,3 +1,4 @@
import * as Tooltip from "@radix-ui/react-tooltip";
import { ReplyIcon } from "@shared/icons"; import { ReplyIcon } from "@shared/icons";
import { useComposer } from "@stores/composer"; import { useComposer } from "@stores/composer";
import { compactNumber } from "@utils/number"; import { compactNumber } from "@utils/number";
@ -11,19 +12,30 @@ export function NoteReply({
const setReply = useComposer((state) => state.setReply); const setReply = useComposer((state) => state.setReply);
return ( return (
<Tooltip.Root delayDuration={150}>
<button <button
type="button" type="button"
onClick={() => setReply(id, rootID, pubkey)} onClick={() => setReply(id, rootID, pubkey)}
className="w-20 group inline-flex items-center gap-1.5" className="group w-20 h-6 group inline-flex items-center gap-1.5"
> >
<ReplyIcon <Tooltip.Trigger asChild>
width={16} <span className="inline-flex items-center justify-center w-6 h-6 rounded group-hover:bg-zinc-800">
height={16} <ReplyIcon className="w-4 h-4 text-zinc-400 group-hover:text-green-500" />
className="text-zinc-400 group-hover:text-green-400" </span>
/> </Tooltip.Trigger>
<span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100"> <span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100">
{compactNumber.format(replies)} {compactNumber.format(replies)}
</span> </span>
</button> </button>
<Tooltip.Portal>
<Tooltip.Content
className="-left-10 data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade select-none text-sm rounded-md text-zinc-100 bg-zinc-800/80 backdrop-blur-lg px-3.5 py-1.5 leading-none will-change-[transform,opacity]"
sideOffset={5}
>
Quick reply
<Tooltip.Arrow className="fill-zinc-800/80 backdrop-blur-lg" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
); );
} }

View File

@ -1,3 +1,4 @@
import * as Tooltip from "@radix-ui/react-tooltip";
import { RepostIcon } from "@shared/icons"; import { RepostIcon } from "@shared/icons";
import { useComposer } from "@stores/composer"; import { useComposer } from "@stores/composer";
import { compactNumber } from "@utils/number"; import { compactNumber } from "@utils/number";
@ -10,19 +11,30 @@ export function NoteRepost({
const setRepost = useComposer((state) => state.setRepost); const setRepost = useComposer((state) => state.setRepost);
return ( return (
<Tooltip.Root delayDuration={150}>
<button <button
type="button" type="button"
onClick={() => setRepost(id, pubkey)} onClick={() => setRepost(id, pubkey)}
className="w-20 group inline-flex items-center gap-1.5" className="group w-20 h-6 group inline-flex items-center gap-1.5"
> >
<RepostIcon <Tooltip.Trigger asChild>
width={16} <span className="inline-flex items-center justify-center w-6 h-6 rounded group-hover:bg-zinc-800">
height={16} <RepostIcon className="w-4 h-4 text-zinc-400 group-hover:text-blue-400" />
className="text-zinc-400 group-hover:text-blue-400" </span>
/> </Tooltip.Trigger>
<span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100"> <span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100">
{compactNumber.format(reposts)} {compactNumber.format(reposts)}
</span> </span>
</button> </button>
<Tooltip.Portal>
<Tooltip.Content
className="-left-10 data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade select-none text-sm rounded-md text-zinc-100 bg-zinc-800/80 backdrop-blur-lg px-3.5 py-1.5 leading-none will-change-[transform,opacity]"
sideOffset={5}
>
Repost
<Tooltip.Arrow className="fill-zinc-800/80 backdrop-blur-lg" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
); );
} }

View File

@ -1,20 +1,32 @@
import * as Tooltip from "@radix-ui/react-tooltip";
import { ZapIcon } from "@shared/icons"; import { ZapIcon } from "@shared/icons";
import { compactNumber } from "@utils/number"; import { compactNumber } from "@utils/number";
export function NoteZap({ zaps }: { zaps: number }) { export function NoteZap({ zaps }: { zaps: number }) {
return ( return (
<Tooltip.Root delayDuration={150}>
<button <button
type="button" type="button"
className="w-20 group inline-flex items-center gap-1.5" className="group w-20 h-6 group inline-flex items-center gap-1.5"
> >
<ZapIcon <Tooltip.Trigger asChild>
width={16} <span className="inline-flex items-center justify-center w-6 h-6 rounded group-hover:bg-zinc-800">
height={16} <ZapIcon className="w-4 h-4 text-zinc-400 group-hover:text-orange-400" />
className="text-zinc-400 group-hover:text-blue-400" </span>
/> </Tooltip.Trigger>
<span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100"> <span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100">
{compactNumber.format(zaps)} {compactNumber.format(zaps)}
</span> </span>
</button> </button>
<Tooltip.Portal>
<Tooltip.Content
className="-left-10 data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade select-none text-sm rounded-md text-zinc-100 bg-zinc-800/80 backdrop-blur-lg px-3.5 py-1.5 leading-none will-change-[transform,opacity]"
sideOffset={5}
>
Coming Soon
<Tooltip.Arrow className="fill-zinc-800/80 backdrop-blur-lg" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
); );
} }

View File

@ -24,12 +24,10 @@ export function useProfile(id: string) {
const result = await getPleb(npub); const result = await getPleb(npub);
if (result && parseInt(result.created_at) + 86400 >= current) { if (result && parseInt(result.created_at) + 86400 >= current) {
console.log("cache", result);
return result; return result;
} else { } else {
const user = ndk.getUser({ npub }); const user = ndk.getUser({ npub });
await user.fetchProfile(); await user.fetchProfile();
console.log("new", user);
await createPleb(id, user.profile); await createPleb(id, user.profile);
return user.profile; return user.profile;

View File

@ -4,7 +4,35 @@ module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx}", "index.html"], content: ["./src/**/*.{js,ts,jsx,tsx}", "index.html"],
darkMode: "class", darkMode: "class",
theme: { theme: {
extend: {}, extend: {
keyframes: {
slideDownAndFade: {
from: { opacity: 0, transform: "translateY(-2px)" },
to: { opacity: 1, transform: "translateY(0)" },
},
slideLeftAndFade: {
from: { opacity: 0, transform: "translateX(2px)" },
to: { opacity: 1, transform: "translateX(0)" },
},
slideUpAndFade: {
from: { opacity: 0, transform: "translateY(2px)" },
to: { opacity: 1, transform: "translateY(0)" },
},
slideRightAndFade: {
from: { opacity: 0, transform: "translateX(-2px)" },
to: { opacity: 1, transform: "translateX(0)" },
},
},
animation: {
slideDownAndFade:
"slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
slideLeftAndFade:
"slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
slideUpAndFade: "slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
slideRightAndFade:
"slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
},
},
}, },
plugins: [require("@tailwindcss/typography")], plugins: [require("@tailwindcss/typography")],
}; };

View File

@ -8,7 +8,6 @@
"@stores/*": ["src/stores/*"], "@stores/*": ["src/stores/*"],
"@utils/*": ["src/utils/*"] "@utils/*": ["src/utils/*"]
}, },
"types": ["vidstack/globals"],
"target": "es2017", "target": "es2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
@ -25,6 +24,6 @@
"jsx": "preserve", "jsx": "preserve",
"strictNullChecks": false "strictNullChecks": false
}, },
"include": ["global.d.ts", "**/*.ts", "**/*.tsx", "src/main.tsx"], "include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "dist", "src-tauri"] "exclude": ["node_modules", "dist", "src-tauri"]
} }