diff --git a/package.json b/package.json index 49bb721b..3ac57ebd 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "@radix-ui/react-tabs": "^1.0.3", "@rehooks/local-storage": "^2.4.4", "@supabase/supabase-js": "^2.12.0", + "@tanstack/query-core": "^4.27.0", + "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.2.0", "@uiw/react-markdown-preview": "^4.1.10", "@uiw/react-md-editor": "^3.20.5", @@ -28,6 +30,7 @@ "dayjs": "^1.11.7", "framer-motion": "^9.1.7", "jotai": "^2.0.3", + "jotai-tanstack-query": "^0.6.0", "moment": "^2.29.4", "next": "^13.2.4", "next-remove-imports": "^1.0.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbcecb85..79d385a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,8 @@ specifiers: '@rehooks/local-storage': ^2.4.4 '@supabase/supabase-js': ^2.12.0 '@tailwindcss/typography': ^0.5.9 + '@tanstack/query-core': ^4.27.0 + '@tanstack/react-virtual': 3.0.0-beta.54 '@tauri-apps/api': ^1.2.0 '@tauri-apps/cli': ^1.2.3 '@trivago/prettier-plugin-sort-imports': ^4.1.1 @@ -33,6 +35,7 @@ specifiers: framer-motion: ^9.1.7 husky: ^8.0.3 jotai: ^2.0.3 + jotai-tanstack-query: ^0.6.0 lint-staged: ^13.2.0 moment: ^2.29.4 next: ^13.2.4 @@ -65,6 +68,8 @@ dependencies: '@radix-ui/react-tabs': 1.0.3_biqbaboplfbrettd7655fr4n2y '@rehooks/local-storage': 2.4.4_react@18.2.0 '@supabase/supabase-js': 2.12.0 + '@tanstack/query-core': 4.27.0 + '@tanstack/react-virtual': 3.0.0-beta.54_react@18.2.0 '@tauri-apps/api': 1.2.0 '@uiw/react-markdown-preview': 4.1.10_zula6vjvt3wdocc4mwcxqa6nzi '@uiw/react-md-editor': 3.20.5_zula6vjvt3wdocc4mwcxqa6nzi @@ -73,6 +78,7 @@ dependencies: dayjs: 1.11.7 framer-motion: 9.1.7_biqbaboplfbrettd7655fr4n2y jotai: 2.0.3_react@18.2.0 + jotai-tanstack-query: 0.6.0_jqgumvl52k2nlr5n23qdneaa6y moment: 2.29.4 next: 13.2.4_biqbaboplfbrettd7655fr4n2y next-remove-imports: 1.0.10 @@ -1302,6 +1308,26 @@ packages: tailwindcss: 3.2.7_postcss@8.4.21 dev: true + /@tanstack/query-core/4.27.0: + resolution: + { integrity: sha512-sm+QncWaPmM73IPwFlmWSKPqjdTXZeFf/7aEmWh00z7yl2FjqophPt0dE1EHW9P1giMC5rMviv7OUbSDmWzXXA== } + dev: false + + /@tanstack/react-virtual/3.0.0-beta.54_react@18.2.0: + resolution: + { integrity: sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ== } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/virtual-core': 3.0.0-beta.54 + react: 18.2.0 + dev: false + + /@tanstack/virtual-core/3.0.0-beta.54: + resolution: + { integrity: sha512-jtkwqdP2rY2iCCDVAFuaNBH3fiEi29aTn2RhtIoky8DTTiCdc48plpHHreLwmv1PICJ4AJUUESaq3xa8fZH8+g== } + dev: false + /@tauri-apps/api/1.2.0: resolution: { integrity: sha512-lsI54KI6HGf7VImuf/T9pnoejfgkNoXveP14pVV7XarrQ46rOejIVJLFqHI9sRReJMGdh2YuCoI3cc/yCWCsrw== } @@ -3836,6 +3862,17 @@ packages: { integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== } dev: true + /jotai-tanstack-query/0.6.0_jqgumvl52k2nlr5n23qdneaa6y: + resolution: + { integrity: sha512-87oD6MnjrgfLWeCJXB/dQt4xyCmyFYZeG9jw4Y2lIprtwLKS5s/vCEjNP5fnYG2nuPBkopiRECTEqtIysvHSxg== } + peerDependencies: + '@tanstack/query-core': '*' + jotai: '>=1.11.0' + dependencies: + '@tanstack/query-core': 4.27.0 + jotai: 2.0.3_react@18.2.0 + dev: false + /jotai/2.0.3_react@18.2.0: resolution: { integrity: sha512-MMjhSPAL3RoeZD9WbObufRT2quThEAEknHHridf2ma8Ml7ZVQmUiHk0ssdbR3F0h3kcwhYqSGJ59OjhPge7RRg== } diff --git a/src-tauri/migrations/20230226004139_create_tables.sql b/src-tauri/migrations/20230226004139_create_tables.sql index 3ae80b73..c040da30 100644 --- a/src-tauri/migrations/20230226004139_create_tables.sql +++ b/src-tauri/migrations/20230226004139_create_tables.sql @@ -48,7 +48,7 @@ CREATE TABLE npub TEXT NOT NULL, nsec TEXT NOT NULL, is_active INTEGER NOT NULL DEFAULT 0, - metadata JSON + metadata TEXT ); -- create follows @@ -61,7 +61,7 @@ CREATE TABLE pubkey TEXT NOT NULL, account TEXT NOT NULL, kind INTEGER NOT NULL DEFAULT 0, - metadata JSON + metadata TEXT ); -- create index for pubkey in follows @@ -71,7 +71,7 @@ CREATE UNIQUE INDEX index_pubkey_on_follows ON follows (pubkey); CREATE TABLE cache_profiles ( id TEXT PRIMARY KEY, - metadata JSON, + metadata TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -84,5 +84,8 @@ CREATE TABLE created_at TEXT, kind INTEGER NOT NULL DEFAULT 1, tags TEXT NOT NULL, - content TEXT NOT NULL + content TEXT NOT NULL, + is_circle INTEGER NOT NULL DEFAULT 0, + is_root INTEGER NOT NULL DEFAULT 0, + is_reply INTEGER NOT NULL DEFAULT 0 ); \ No newline at end of file diff --git a/src/components/contexts/database.tsx b/src/components/contexts/database.tsx index 31b7aaee..83e55d87 100644 --- a/src/components/contexts/database.tsx +++ b/src/components/contexts/database.tsx @@ -48,17 +48,25 @@ export default function DatabaseProvider({ children }: { children: React.ReactNo return; }, []); + const clearCacheNote = useCallback(async () => { + const result: any = await db.select('SELECT COUNT(*) AS "total" FROM cache_notes'); + if (result[0].total >= 1000) { + await db.execute('DELETE FROM cache_notes'); + } + }, []); + useEffect(() => { getRelays().catch(console.error); getAccount() .then((res) => { if (res) { getFollows(res.id).catch(console.error); + clearCacheNote().catch(console.error); } setDone(true); }) .catch(console.error); - }, [getAccount, getFollows, getRelays]); + }, [getAccount, getFollows, clearCacheNote, getRelays]); if (!done) { return <>; diff --git a/src/components/note/connector.tsx b/src/components/note/connector.tsx index 618bb9f6..16735a17 100644 --- a/src/components/note/connector.tsx +++ b/src/components/note/connector.tsx @@ -1,7 +1,7 @@ import { DatabaseContext } from '@components/contexts/database'; import { RelayContext } from '@components/contexts/relay'; -import { atomHasNewerNote } from '@stores/note'; +import { hasNewerNoteAtom } from '@stores/note'; import { dateToUnix, hoursAgo } from '@utils/getDate'; @@ -16,7 +16,7 @@ export const NoteConnector = memo(function NoteConnector() { const [follows]: any = useLocalStorage('follows'); const [relays]: any = useLocalStorage('relays'); - const setHasNewerNote = useSetAtom(atomHasNewerNote); + const setHasNewerNote = useSetAtom(hasNewerNoteAtom); const [isOnline, setIsOnline] = useState(navigator.onLine); const now = useRef(new Date()); @@ -25,7 +25,7 @@ export const NoteConnector = memo(function NoteConnector() { // insert to local database await db.execute( 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags) VALUES (?, ?, ?, ?, ?, ?);', - [event.id, event.pubkey, event.created_at, event.kind, event.content, JSON.stringify(event.tags)] + [event.id, event.pubkey, event.created_at, event.kind, event.content, String(event.tags)] ); }, [db] diff --git a/src/components/note/content/index.tsx b/src/components/note/content/index.tsx index 8742ffd9..2dec501c 100644 --- a/src/components/note/content/index.tsx +++ b/src/components/note/content/index.tsx @@ -9,11 +9,9 @@ import reactStringReplace from 'react-string-replace'; export const Content = memo(function Content({ data }: { data: any }) { const content = useMemo(() => { let parsedContent; - let tags; // get data tags - if (data.tags.length > 1) { - tags = JSON.parse(data.tags); - } + const tags = String(data.tags).replaceAll("'", '"'); + const parseTags = JSON.parse(tags); // remove all image urls parsedContent = data.content.replace(/(https?:\/\/.*\.(jpg|jpeg|gif|png|webp|mp4|webm)((\?.*)$|$))/gim, ''); // handle urls @@ -29,10 +27,10 @@ export const Content = memo(function Content({ data }: { data: any }) { )); // handle mentions - if (tags.length > 0) { + if (parseTags.length > 0) { parsedContent = reactStringReplace(parsedContent, /\#\[(\d+)\]/gm, (match, i) => { - if (tags[match][0] === 'p') { - return ; + if (parseTags[match][0] === 'p') { + return ; } else { // #TODO: handle mention other note // console.log(tags[match]); @@ -48,7 +46,7 @@ export const Content = memo(function Content({ data }: { data: any }) {
-
+
{content}
diff --git a/src/components/note/counter/likes.tsx b/src/components/note/counter/likes.tsx index 9a59c3e5..07e35470 100644 --- a/src/components/note/counter/likes.tsx +++ b/src/components/note/counter/likes.tsx @@ -7,7 +7,7 @@ import LikedIcon from '@assets/icons/liked'; import { useLocalStorage } from '@rehooks/local-storage'; import { getEventHash, signEvent } from 'nostr-tools'; -import { memo, useContext, useState } from 'react'; +import { memo, useContext, useEffect, useState } from 'react'; export const LikesCounter = memo(function LikesCounter({ count, @@ -24,7 +24,7 @@ export const LikesCounter = memo(function LikesCounter({ const [currentUser]: any = useLocalStorage('current-user'); const [isReact, setIsReact] = useState(false); - const [like, setLike] = useState(count); + const [like, setLike] = useState(0); const handleLike = (e: any) => { e.stopPropagation(); @@ -49,6 +49,10 @@ export const LikesCounter = memo(function LikesCounter({ setLike(like + 1); }; + useEffect(() => { + setLike(count); + }, [count]); + return (