From 332dbf608d2b174f836deb1f3468ead6e2792c08 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:36:03 +0700 Subject: [PATCH] fully support nip05 --- package.json | 4 +- pnpm-lock.yaml | 112 +++++++------- .../20230418013219_initial_data.sql | 18 --- src/app/chat/components/messages/form.tsx | 2 +- src/app/chat/components/modal.tsx | 40 ++--- src/app/chat/components/sidebar.tsx | 16 +- src/app/root.tsx | 2 + src/app/space/components/blocks/feed.tsx | 2 - src/app/space/components/blocks/following.tsx | 5 +- src/libs/ndk.tsx | 29 +--- src/libs/storage.tsx | 58 +------ src/shared/composer/types/post.tsx | 4 +- src/shared/editProfileModal.tsx | 142 +++++++++++++++--- src/shared/icons/index.tsx | 1 + src/shared/icons/unverified.tsx | 23 +++ src/utils/hooks/useProfile.tsx | 34 ++--- src/utils/hooks/useSocial.tsx | 3 + 17 files changed, 250 insertions(+), 245 deletions(-) create mode 100644 src/shared/icons/unverified.tsx diff --git a/package.json b/package.json index 62792b2c..5c58783f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@floating-ui/react": "^0.23.1", "@headlessui/react": "^1.7.15", - "@nostr-dev-kit/ndk": "^0.6.3", + "@nostr-dev-kit/ndk": "0.6.0", "@radix-ui/react-tooltip": "^1.0.6", "@tanstack/react-query": "^4.29.19", "@tanstack/react-virtual": "3.0.0-beta.54", @@ -27,7 +27,7 @@ "get-urls": "^11.0.0", "immer": "^10.0.2", "light-bolt11-decoder": "^3.0.0", - "nostr-tools": "^1.12.0", + "nostr-tools": "^1.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.45.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f389c3c..992e48bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,8 +8,8 @@ dependencies: specifier: ^1.7.15 version: 1.7.15(react-dom@18.2.0)(react@18.2.0) '@nostr-dev-kit/ndk': - specifier: ^0.6.3 - version: 0.6.3(typescript@4.9.5) + specifier: 0.6.0 + version: 0.6.0(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) @@ -44,8 +44,8 @@ dependencies: specifier: ^3.0.0 version: 3.0.0 nostr-tools: - specifier: ^1.12.0 - version: 1.12.0 + specifier: ^1.12.1 + version: 1.12.1 react: specifier: ^18.2.0 version: 18.2.0 @@ -424,8 +424,8 @@ packages: resolution: {integrity: sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==} dev: false - /@floating-ui/dom@1.4.2: - resolution: {integrity: sha512-VKmvHVatWnewmGGy+7Mdy4cTJX71Pli6v/Wjb5RQBuq5wjUYx+Ef+kRThi8qggZqDgD8CogCpqhRoVp3+yQk+g==} + /@floating-ui/dom@1.4.3: + resolution: {integrity: sha512-nB/68NyaQlcdY22L+Fgd1HERQ7UGv7XFN+tPxwrEfQL4nKtAP/jIZnZtpUlXbtV+VEGHh6W/63Gy2C5biWI3sA==} dependencies: '@floating-ui/core': 1.3.1 dev: false @@ -436,7 +436,7 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - '@floating-ui/dom': 1.4.2 + '@floating-ui/dom': 1.4.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -447,7 +447,7 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - '@floating-ui/dom': 1.4.2 + '@floating-ui/dom': 1.4.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -591,8 +591,8 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - /@nostr-dev-kit/ndk@0.6.3(typescript@4.9.5): - resolution: {integrity: sha512-lXMahPRepqNmt7rIM/I7pJz/VB5PSCakZScV3+L0AmUpsN4UXHX+fFtkFjyfNpM/zW+YlxFMFbo/WM+jB55j0A==} + /@nostr-dev-kit/ndk@0.6.0(typescript@4.9.5): + resolution: {integrity: sha512-0ptE6OIZhFW+aRRIXAI8PvUIoVU6iQLpiwFtJj48XAUO2EC3WiSuqKLshjg6wj1bbo9qGs1PyFS9AUhUlWWJtg==} dependencies: '@noble/hashes': 1.3.1 '@noble/secp256k1': 2.0.0 @@ -605,11 +605,11 @@ packages: eslint: 8.43.0 eslint-config-prettier: 8.8.0(eslint@8.43.0) eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.60.1)(eslint@8.43.0) - esm-loader-typescript: 1.0.4 + esm-loader-typescript: 1.0.5 eventemitter3: 5.0.1 light-bolt11-decoder: 3.0.0 node-fetch: 3.3.1 - nostr-tools: 1.12.0 + nostr-tools: 1.12.1 tsd: 0.28.1 utf8-buffer: 1.0.0 websocket-polyfill: 0.0.3 @@ -1071,8 +1071,8 @@ packages: resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} dev: false - /@swc/core-darwin-arm64@1.3.66: - resolution: {integrity: sha512-UijJsvuLy73vxeVYEy7urIHksXS+3BdvJ9s9AY+bRMSQW483NO7RLp8g4FdTyJbRaN0BH15SQnY0dcjQBkVuHw==} + /@swc/core-darwin-arm64@1.3.67: + resolution: {integrity: sha512-zCT2mCkOBVNf5uJDcQ3A9KDoO1OEaGdfjsRTZTo7sejDd9AXLfJg+xgyCBBrK2jNS/uWcT21IvSv3LqKp4K8pA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -1080,8 +1080,8 @@ packages: dev: true optional: true - /@swc/core-darwin-x64@1.3.66: - resolution: {integrity: sha512-xGsHKvViQnwTNLF30Y/5OqWdnN6RsiyUI8awZXfz1sHcXCEaLe+v+WLQ+/E8sgw0YUkYVHzzfV/sAN2CezJK5Q==} + /@swc/core-darwin-x64@1.3.67: + resolution: {integrity: sha512-hXTVsfTatPEec5gFVyjGj3NccKZsYj/OXyHn6XA+l3Q76lZzGm2ISHdku//XNwXu8OmJ0HhS7LPsC4XXwxXQhg==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -1089,8 +1089,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm-gnueabihf@1.3.66: - resolution: {integrity: sha512-gNbLcSIV2pq90BkMSpzvK4xPXOl8GEF3YR4NaqF0CYSzQsVXXTTqMuX/r26xNYudBKzH0345S1MpoRk2qricnA==} + /@swc/core-linux-arm-gnueabihf@1.3.67: + resolution: {integrity: sha512-l8AKL0RkDL5FRTeWMmjoz9zvAc37amxC+0rheaNwE+gZya7ObyNjnIYz5FwN+3y+z6JFU7LS2x/5f6iwruv6pg==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -1098,8 +1098,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-gnu@1.3.66: - resolution: {integrity: sha512-cJSQ0oplyWbJqy4rzVcnBYLAi6z1QT3QCcR7iAey0aAmCvfRBZJfXlyjggMjn4iosuadkauwCZR1xYNhBDRn7w==} + /@swc/core-linux-arm64-gnu@1.3.67: + resolution: {integrity: sha512-S8zOB1AXEpb7kmtgMaFNeLAj01VOky4B0RNZ+uJWigdrDiFT67FeZzNHUNmNSOU0QM79G+Lie/xD/beqEw0vDg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -1107,8 +1107,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-musl@1.3.66: - resolution: {integrity: sha512-GDQZpcB9aGxG9PTA2shdIkoMZlGK5omJ8NR49uoBTtLBVYiGeXAwV0U1Uaw8kXEZj9i7wZDkvjzjSaNH3evRsg==} + /@swc/core-linux-arm64-musl@1.3.67: + resolution: {integrity: sha512-Fex8J8ASrt13pmOr2xWh41tEeKWwXYGk3sV8L/aGHiYtIJEUi2f+RtMx3jp7LIdOD8pQptor7i5WBlfR9jhp8A==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -1116,8 +1116,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-gnu@1.3.66: - resolution: {integrity: sha512-lg8E4O/Pd9KfK0lajdinVMuGME8dSv7V9arhEpmlfGE2eXSDCWqDn5Htk5QVBstt9lt1lsRhWHJ/YYc2eQY30Q==} + /@swc/core-linux-x64-gnu@1.3.67: + resolution: {integrity: sha512-9bz9/bMphrv5vDg0os/d8ve0QgFpDzJgZgHUaHiGwcmfnlgdOSAaYJLIvWdcGTjZuQeV4L0m+iru357D9TXEzA==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -1125,8 +1125,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-musl@1.3.66: - resolution: {integrity: sha512-lo8ZcAO/zL2pZWH+LZIyge8u2MklaeuT6+FpVVpBFktMVdYXbaVtzpvWbgRFBZHvL3SRDF+u8jxjtkXhvGUpTw==} + /@swc/core-linux-x64-musl@1.3.67: + resolution: {integrity: sha512-ED0H6oLvQmhgo9zs8usmEA/lcZPGTu7K9og9K871b7HhHX0h/R+Xg2pb5KD7S/GyUHpfuopxjVROm+h6X1jMUA==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -1134,8 +1134,8 @@ packages: dev: true optional: true - /@swc/core-win32-arm64-msvc@1.3.66: - resolution: {integrity: sha512-cQoVwBuJY5WkHbfpCOlndNwYr1ZThatRjQQvKy540NUIeAEk9Fa6ozlDBtU75UdaWKtUG6YQ/bWz+KTemheVxw==} + /@swc/core-win32-arm64-msvc@1.3.67: + resolution: {integrity: sha512-J1yFDLgPFeRtA8t5E159OXX+ww1gbkFg70yr4OP7EsOkOD1uMkuTf9yK/woHfsaVJlUYjJHzw7MkUIEgQBucqQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -1143,8 +1143,8 @@ packages: dev: true optional: true - /@swc/core-win32-ia32-msvc@1.3.66: - resolution: {integrity: sha512-y/FrAIINK4UBeUQQknGlWXEyjo+MBvjF7WkUf2KP7sNr9EHHy8+dXohAGd5Anz0eJrqOM1ZXR/GEjxRp7bGQ1Q==} + /@swc/core-win32-ia32-msvc@1.3.67: + resolution: {integrity: sha512-bK11/KtasewqHxzkjKUBXRE9MSAidbZCxrgJUd49bItG2N/DHxkwMYu8Xkh5VDHdTYWv/2idYtf/VM9Yi+53qw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -1152,8 +1152,8 @@ packages: dev: true optional: true - /@swc/core-win32-x64-msvc@1.3.66: - resolution: {integrity: sha512-yI64ACzS14qFLrfyO12qW+f/UROTotzDeEbuyJAaPD2IZexoT1cICznI3sBmIfrSt33mVuW8eF5m3AG/NUImzw==} + /@swc/core-win32-x64-msvc@1.3.67: + resolution: {integrity: sha512-GxzUU3+NA3cPcYxCxtfSQIS2ySD7Z8IZmKTVaWA9GOUQbKLyCE8H5js31u39+0op/1gNgxOgYFDoj2lUyvLCqw==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -1161,8 +1161,8 @@ packages: dev: true optional: true - /@swc/core@1.3.66: - resolution: {integrity: sha512-Hpf91kH5ly7fHkWnApwryTQryT+TO4kMMPH3WyciUSQOWLE3UuQz1PtETHQQk7PZ/b1QF0qQurJrgfBr5bSKUA==} + /@swc/core@1.3.67: + resolution: {integrity: sha512-9DROjzfAEt0xt0CDkOYsWpkUPyne8fl5ggWGon049678BOM7p0R0dmaalZGAsKatG5vYP1IWSKWsKhJIubDCsQ==} engines: {node: '>=10'} requiresBuild: true peerDependencies: @@ -1171,16 +1171,16 @@ packages: '@swc/helpers': optional: true optionalDependencies: - '@swc/core-darwin-arm64': 1.3.66 - '@swc/core-darwin-x64': 1.3.66 - '@swc/core-linux-arm-gnueabihf': 1.3.66 - '@swc/core-linux-arm64-gnu': 1.3.66 - '@swc/core-linux-arm64-musl': 1.3.66 - '@swc/core-linux-x64-gnu': 1.3.66 - '@swc/core-linux-x64-musl': 1.3.66 - '@swc/core-win32-arm64-msvc': 1.3.66 - '@swc/core-win32-ia32-msvc': 1.3.66 - '@swc/core-win32-x64-msvc': 1.3.66 + '@swc/core-darwin-arm64': 1.3.67 + '@swc/core-darwin-x64': 1.3.67 + '@swc/core-linux-arm-gnueabihf': 1.3.67 + '@swc/core-linux-arm64-gnu': 1.3.67 + '@swc/core-linux-arm64-musl': 1.3.67 + '@swc/core-linux-x64-gnu': 1.3.67 + '@swc/core-linux-x64-musl': 1.3.67 + '@swc/core-win32-arm64-msvc': 1.3.67 + '@swc/core-win32-ia32-msvc': 1.3.67 + '@swc/core-win32-x64-msvc': 1.3.67 dev: true /@tailwindcss/typography@0.5.9(tailwindcss@3.3.2): @@ -1551,7 +1551,7 @@ packages: peerDependencies: vite: ^4 dependencies: - '@swc/core': 1.3.66 + '@swc/core': 1.3.67 vite: 4.3.9(@types/node@18.16.18) transitivePeerDependencies: - '@swc/helpers' @@ -1796,7 +1796,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001509 - electron-to-chromium: 1.4.444 + electron-to-chromium: 1.4.446 node-releases: 2.0.12 update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: true @@ -2227,8 +2227,8 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - /electron-to-chromium@1.4.444: - resolution: {integrity: sha512-/AjkL4syRvOpowXWy3N4OHmVbFdWQpfSKHh0sCVYipDeEAtMce3rLjMJi/27Ia9jNIbw6P1JuPN32pSWybXXEQ==} + /electron-to-chromium@1.4.446: + resolution: {integrity: sha512-4Gnw7ztEQ/E0eOt5JWfPn9jjeupfUlKoeW5ETKP9nLdWj+4spFoS3Stj19fqlKIaX28UQs0fNX+uKEyoLCBnkw==} dev: true /emoji-regex@8.0.0: @@ -2567,8 +2567,8 @@ packages: - supports-color dev: false - /esm-loader-typescript@1.0.4: - resolution: {integrity: sha512-ejo2f+NKIt23jaJKVpjcRQC0xTa6cTqt6VA+z8Ef/A/TCjQ5u7opPO1J4fJFeTiiHEo8JqU92EG4I1KwuXYZMg==} + /esm-loader-typescript@1.0.5: + resolution: {integrity: sha512-BeHp2TrYbRL9fUttlyzPQJPTvLDBXXUli09UNoAr87WKi8jedcULlMteNZgl7DtFZ3ZE1Mmv74SwRgwJDWyc0A==} dependencies: create-esm-loader: 0.2.3 npm-run-all: 4.1.5 @@ -3832,8 +3832,8 @@ packages: engines: {node: '>=12.20'} dev: false - /nostr-tools@1.12.0: - resolution: {integrity: sha512-fsIXaNJPKaSrO9MxsCEWbhI4tG4pToQK4D4sgLRD0fRDfZ6ocCg8CLlh9lcNx0o8pVErCMLVASxbJ+w4WNK0MA==} + /nostr-tools@1.12.1: + resolution: {integrity: sha512-ZeoV7g3jBUAlb4mKa3C+6hrc84htPkbebMShfGNgV4vAiz18e/sQukUBFL6vb/+sxZy+dBQFkRwsJIaVFs8Gfw==} dependencies: '@noble/curves': 1.0.0 '@noble/hashes': 1.3.0 @@ -4455,8 +4455,8 @@ packages: glob: 7.2.3 dev: false - /rollup@3.25.3: - resolution: {integrity: sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==} + /rollup@3.26.0: + resolution: {integrity: sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -5159,7 +5159,7 @@ packages: vite: '>=2.8' dependencies: '@rollup/plugin-virtual': 3.0.1 - '@swc/core': 1.3.66 + '@swc/core': 1.3.67 uuid: 9.0.0 vite: 4.3.9(@types/node@18.16.18) transitivePeerDependencies: @@ -5212,7 +5212,7 @@ packages: '@types/node': 18.16.18 esbuild: 0.17.19 postcss: 8.4.24 - rollup: 3.25.3 + rollup: 3.26.0 optionalDependencies: fsevents: 2.3.2 dev: true diff --git a/src-tauri/migrations/20230418013219_initial_data.sql b/src-tauri/migrations/20230418013219_initial_data.sql index 0ab7a60a..c53aa131 100644 --- a/src-tauri/migrations/20230418013219_initial_data.sql +++ b/src-tauri/migrations/20230418013219_initial_data.sql @@ -14,24 +14,6 @@ CREATE TABLE created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); --- create plebs table -CREATE TABLE - plebs ( - id INTEGER NOT NULL PRIMARY KEY, - npub TEXT NOT NULL UNIQUE, - name TEXT, - displayName TEXT, - image TEXT, - banner TEXT, - bio TEXT, - nip05 TEXT, - lud06 TEXT, - lud16 TEXT, - about TEXT, - zapService TEXT, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - -- create notes table CREATE TABLE notes ( diff --git a/src/app/chat/components/messages/form.tsx b/src/app/chat/components/messages/form.tsx index 5d148b13..2549e5a9 100644 --- a/src/app/chat/components/messages/form.tsx +++ b/src/app/chat/components/messages/form.tsx @@ -20,7 +20,7 @@ export function ChatMessageForm({ const tags = [["p", receiverPubkey]]; // publish message - publish({ content: message, kind: 4, tags }); + await publish({ content: message, kind: 4, tags }); // reset state setValue(""); diff --git a/src/app/chat/components/modal.tsx b/src/app/chat/components/modal.tsx index 96349acd..7ee350ee 100644 --- a/src/app/chat/components/modal.tsx +++ b/src/app/chat/components/modal.tsx @@ -1,21 +1,17 @@ +import { User } from "@app/auth/components/user"; import { Dialog, Transition } from "@headlessui/react"; -import { getPlebs } from "@libs/storage"; import { CancelIcon, PlusIcon } from "@shared/icons"; -import { DEFAULT_AVATAR } from "@stores/constants"; -import { useQuery } from "@tanstack/react-query"; -import { nip19 } from "nostr-tools"; +import { useAccount } from "@utils/hooks/useAccount"; import { Fragment, useState } from "react"; import { useNavigate } from "react-router-dom"; export function NewMessageModal() { const navigate = useNavigate(); - - const { status, data }: any = useQuery(["plebs"], async () => { - return await getPlebs(); - }); - const [isOpen, setIsOpen] = useState(false); + const { status, account } = useAccount(); + const follows = account ? JSON.parse(account.follows) : []; + const closeModal = () => { setIsOpen(false); }; @@ -24,8 +20,7 @@ export function NewMessageModal() { setIsOpen(true); }; - const openChat = (npub: string) => { - const pubkey = nip19.decode(npub).data; + const openChat = (pubkey: string) => { closeModal(); navigate(`/app/chat/${pubkey}`); }; @@ -99,31 +94,16 @@ export function NewMessageModal() { {status === "loading" ? (

Loading...

) : ( - data.map((pleb) => ( + follows.map((follow) => (
-
- {pleb.npub} -
-

- {pleb.displayName || pleb.name} -

- - {pleb.nip05 || - pleb.npub.substring(0, 16).concat("...")} - -
-
+
+
diff --git a/src/app/root.tsx b/src/app/root.tsx index 1cfa6b32..53de7eb1 100644 --- a/src/app/root.tsx +++ b/src/app/root.tsx @@ -6,6 +6,7 @@ import { createNote, getChannels, getLastLogin, + updateLastLogin, } from "@libs/storage"; import { NDKFilter } from "@nostr-dev-kit/ndk"; import { LoaderIcon, LumeIcon } from "@shared/icons"; @@ -142,6 +143,7 @@ export function Root() { const chats = await fetchChats(); // const channels = await fetchChannelMessages(); if (chats) { + await updateLastLogin(dateToUnix()); navigate("/app/space", { replace: true }); } } diff --git a/src/app/space/components/blocks/feed.tsx b/src/app/space/components/blocks/feed.tsx index 0a623f76..2d835c0f 100644 --- a/src/app/space/components/blocks/feed.tsx +++ b/src/app/space/components/blocks/feed.tsx @@ -11,7 +11,6 @@ import { useVirtualizer } from "@tanstack/react-virtual"; import { useEffect, useRef } from "react"; const ITEM_PER_PAGE = 10; -const TIME = Math.floor(Date.now() / 1000); export function FeedBlock({ params }: { params: any }) { const queryClient = useQueryClient(); @@ -21,7 +20,6 @@ export function FeedBlock({ params }: { params: any }) { queryFn: async ({ pageParam = 0 }) => { return await getNotesByAuthors( params.content, - TIME, ITEM_PER_PAGE, pageParam, ); diff --git a/src/app/space/components/blocks/following.tsx b/src/app/space/components/blocks/following.tsx index a5e572ee..1e81d4c4 100644 --- a/src/app/space/components/blocks/following.tsx +++ b/src/app/space/components/blocks/following.tsx @@ -9,7 +9,6 @@ import { useVirtualizer } from "@tanstack/react-virtual"; import { useEffect, useRef } from "react"; const ITEM_PER_PAGE = 10; -const TIME = Math.floor(Date.now() / 1000); export function FollowingBlock({ block }: { block: number }) { // subscribe for live update @@ -29,7 +28,7 @@ export function FollowingBlock({ block }: { block: number }) { }: any = useInfiniteQuery({ queryKey: ["newsfeed-circle"], queryFn: async ({ pageParam = 0 }) => { - return await getNotes(TIME, ITEM_PER_PAGE, pageParam); + return await getNotes(ITEM_PER_PAGE, pageParam); }, getNextPageParam: (lastPage) => lastPage.nextCursor, }); @@ -64,7 +63,7 @@ export function FollowingBlock({ block }: { block: number }) { const refreshFirstPage = () => { // refetch - refetch({ refetchPage: (page, index) => index === 0 }); + refetch({ refetchPage: (_, index: number) => index === 0 }); // scroll to top rowVirtualizer.scrollToIndex(1); // stop notify diff --git a/src/libs/ndk.tsx b/src/libs/ndk.tsx index 5bd0b90a..f8862ce1 100644 --- a/src/libs/ndk.tsx +++ b/src/libs/ndk.tsx @@ -1,4 +1,3 @@ -import { createReplyNote } from "./storage"; import NDK, { NDKConstructorParams, NDKEvent, @@ -15,21 +14,10 @@ export async function initNDK(relays?: string[]): Promise { const opts: NDKConstructorParams = {}; const defaultRelays = new Set(relays || FULL_RELAYS); - /* - for (const relay of defaultRelays) { - const url = new URL(relay); - url.protocol = url.protocol = url.protocol.replace("wss", "https"); - const res = await fetch(url.href, { method: "HEAD", timeout: 5 }); - if (!res.ok) { - defaultRelays.delete(relay); - } - } - */ - opts.explicitRelayUrls = [...defaultRelays]; const ndk = new NDK(opts); - await ndk.connect(); + await ndk.connect(500); return ndk; } @@ -57,15 +45,10 @@ export async function prefetchEvents( } export function usePublish() { - const { account } = useAccount(); const ndk = useContext(RelayContext); + const { account } = useAccount(); - if (!ndk.signer) { - const signer = new NDKPrivateKeySigner(account?.privkey); - ndk.signer = signer; - } - - const publish = ({ + const publish = async ({ content, kind, tags, @@ -73,8 +56,9 @@ export function usePublish() { content: string; kind: NDKKind; tags: string[][]; - }): NDKEvent => { + }): Promise => { const event = new NDKEvent(ndk); + const signer = new NDKPrivateKeySigner(account.privkey); event.content = content; event.kind = kind; @@ -82,7 +66,8 @@ export function usePublish() { event.pubkey = account.pubkey; event.tags = tags; - event.publish(); + await event.sign(signer); + await event.publish(); return event; }; diff --git a/src/libs/storage.tsx b/src/libs/storage.tsx index 2d18fd25..2bab3ebc 100644 --- a/src/libs/storage.tsx +++ b/src/libs/storage.tsx @@ -1,6 +1,4 @@ -import { NDKTag, NDKUserProfile } from "@nostr-dev-kit/ndk"; import { getParentID } from "@utils/transform"; -import { nip19 } from "nostr-tools"; import Database from "tauri-plugin-sql-api"; let db: null | Database = null; @@ -73,55 +71,6 @@ export async function updateAccount( ); } -// get all plebs -export async function getPlebs() { - const db = await connect(); - return await db.select("SELECT * FROM plebs ORDER BY created_at DESC;"); -} - -// get pleb by pubkey -export async function getPleb(npub: string) { - const db = await connect(); - const result = await db.select(`SELECT * FROM plebs WHERE npub = "${npub}";`); - - if (result) { - return result[0]; - } else { - return null; - } -} - -// create pleb -export async function createPleb(key: string, data: NDKUserProfile) { - const db = await connect(); - const now = Math.floor(Date.now() / 1000); - let npub: string; - - if (key.substring(0, 4) === "npub") { - npub = key; - } else { - npub = nip19.npubEncode(key); - } - - return await db.execute( - "INSERT OR REPLACE INTO plebs (npub, name, displayName, image, banner, bio, nip05, lud06, lud16, about, zapService, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", - [ - npub, - data.name, - data.displayName, - data.image, - data.banner, - data.bio, - data.nip05, - data.lud06, - data.lud16, - data.about, - data.zapService, - now, - ], - ); -} - // count total notes export async function countTotalChannels() { const db = await connect(); @@ -139,14 +88,14 @@ export async function countTotalNotes() { } // get all notes -export async function getNotes(time: number, limit: number, offset: number) { +export async function getNotes(limit: number, offset: number) { const db = await connect(); const totalNotes = await countTotalNotes(); const nextCursor = offset + limit; const notes: any = { data: null, nextCursor: 0 }; const query: any = await db.select( - `SELECT * FROM notes WHERE created_at <= "${time}" AND kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, + `SELECT * FROM notes WHERE kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, ); notes["data"] = query; @@ -169,7 +118,6 @@ export async function getNotesByPubkey(pubkey: string) { // get all notes by authors export async function getNotesByAuthors( authors: string, - time: number, limit: number, offset: number, ) { @@ -181,7 +129,7 @@ export async function getNotesByAuthors( const notes: any = { data: null, nextCursor: 0 }; const query: any = await db.select( - `SELECT * FROM notes WHERE created_at <= "${time}" AND pubkey IN (${finalArray}) AND kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, + `SELECT * FROM notes WHERE pubkey IN (${finalArray}) AND kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, ); notes["data"] = query; diff --git a/src/shared/composer/types/post.tsx b/src/shared/composer/types/post.tsx index 039ef952..53af1c96 100644 --- a/src/shared/composer/types/post.tsx +++ b/src/shared/composer/types/post.tsx @@ -97,7 +97,7 @@ export function Post() { const refID = getRef(); - const submit = () => { + const submit = async () => { let tags: string[][] = []; let kind: number; @@ -130,7 +130,7 @@ export function Post() { const serializedContent = serialize(content); // publish message - publish({ content: serializedContent, kind, tags }); + await publish({ content: serializedContent, kind, tags }); // close modal toggle(false); diff --git a/src/shared/editProfileModal.tsx b/src/shared/editProfileModal.tsx index e48e98a3..11b4dc06 100644 --- a/src/shared/editProfileModal.tsx +++ b/src/shared/editProfileModal.tsx @@ -1,38 +1,51 @@ import { Dialog, Transition } from "@headlessui/react"; import { usePublish } from "@libs/ndk"; -import { getPleb } from "@libs/storage"; +import { NDKEvent } from "@nostr-dev-kit/ndk"; import { AvatarUploader } from "@shared/avatarUploader"; import { BannerUploader } from "@shared/bannerUploader"; -import { CancelIcon, LoaderIcon } from "@shared/icons"; +import { + CancelIcon, + CheckCircleIcon, + LoaderIcon, + UnverifiedIcon, +} from "@shared/icons"; import { Image } from "@shared/image"; import { DEFAULT_AVATAR } from "@stores/constants"; +import { useQueryClient } from "@tanstack/react-query"; +import { fetch } from "@tauri-apps/api/http"; import { useAccount } from "@utils/hooks/useAccount"; -import { Fragment, useState } from "react"; +import { Fragment, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; export function EditProfileModal() { + const queryClient = useQueryClient(); 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 [banner, setBanner] = useState(""); + const [nip05, setNIP05] = useState({ verified: false, text: "" }); const { account } = useAccount(); const { register, handleSubmit, reset, - formState: { isValid }, + setError, + formState: { isValid, errors }, } = useForm({ defaultValues: async () => { - const res = await getPleb(account.npub); - if (res.picture) { + const res: any = queryClient.getQueryData(["user", account.pubkey]); + if (res.image) { setPicture(res.image); } if (res.banner) { setBanner(res.banner); } + if (res.nip05) { + setNIP05((prev) => ({ ...prev, text: res.nip05 })); + } return res; }, }); @@ -45,24 +58,72 @@ export function EditProfileModal() { setIsOpen(true); }; - const onSubmit = (data: any) => { + const verifyNIP05 = async (data: string) => { + if (data) { + const url = data.split("@"); + const username = url[0]; + const service = url[1]; + const verifyURL = `https://${service}/.well-known/nostr.json?name=${username}`; + + const res: any = await fetch(verifyURL, { + method: "GET", + timeout: 30, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }); + + if (!res.ok) return false; + if (res.data.names[username] === account.pubkey) { + setNIP05((prev) => ({ ...prev, verified: true })); + return true; + } else { + return false; + } + } + }; + + const onSubmit = async (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: [], - }); + let event: NDKEvent; - if (event) { + const content = { + ...data, + username: data.name, + display_name: data.name, + bio: data.about, + image: data.picture, + }; + + if (data.nip05) { + const verify = await verifyNIP05(data.nip05); + if (verify) { + event = await publish({ + content: JSON.stringify({ ...content, nip05: data.nip05 }), + kind: 0, + tags: [], + }); + } else { + setNIP05((prev) => ({ ...prev, verified: false })); + setError("nip05", { + type: "manual", + message: "Can't verify your Lume ID / NIP-05, please check again", + }); + } + } else { + event = await publish({ + content: JSON.stringify(content), + kind: 0, + tags: [], + }); + } + + if (event.id) { setTimeout(() => { + // invalid cache + queryClient.invalidateQueries(["user", account.pubkey]); // reset form reset(); // reset state @@ -71,9 +132,17 @@ export function EditProfileModal() { setPicture(DEFAULT_AVATAR); setBanner(null); }, 1200); + } else { + setLoading(false); } }; + useEffect(() => { + if (!nip05.verified && /\S+@\S+\.\S+/.test(nip05.text)) { + verifyNIP05(nip05.text); + } + }, [nip05.text]); + return ( <>