LFG !!!
This commit is contained in:
@@ -18,7 +18,6 @@
|
|||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-popover": "^1.0.5",
|
"@radix-ui/react-popover": "^1.0.5",
|
||||||
"@radix-ui/react-tabs": "^1.0.3",
|
"@radix-ui/react-tabs": "^1.0.3",
|
||||||
"@rehooks/local-storage": "^2.4.4",
|
|
||||||
"@supabase/supabase-js": "^2.12.1",
|
"@supabase/supabase-js": "^2.12.1",
|
||||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||||
"@tauri-apps/api": "^1.2.0",
|
"@tauri-apps/api": "^1.2.0",
|
||||||
@@ -29,6 +28,7 @@
|
|||||||
"destr": "^1.2.2",
|
"destr": "^1.2.2",
|
||||||
"framer-motion": "^9.1.7",
|
"framer-motion": "^9.1.7",
|
||||||
"jotai": "^2.0.3",
|
"jotai": "^2.0.3",
|
||||||
|
"jotai-cache": "^0.3.0",
|
||||||
"next": "^13.2.4",
|
"next": "^13.2.4",
|
||||||
"next-remove-imports": "^1.0.10",
|
"next-remove-imports": "^1.0.10",
|
||||||
"nostr-relaypool": "^0.5.18",
|
"nostr-relaypool": "^0.5.18",
|
||||||
|
|||||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -7,7 +7,6 @@ specifiers:
|
|||||||
'@radix-ui/react-icons': ^1.3.0
|
'@radix-ui/react-icons': ^1.3.0
|
||||||
'@radix-ui/react-popover': ^1.0.5
|
'@radix-ui/react-popover': ^1.0.5
|
||||||
'@radix-ui/react-tabs': ^1.0.3
|
'@radix-ui/react-tabs': ^1.0.3
|
||||||
'@rehooks/local-storage': ^2.4.4
|
|
||||||
'@supabase/supabase-js': ^2.12.1
|
'@supabase/supabase-js': ^2.12.1
|
||||||
'@tailwindcss/typography': ^0.5.9
|
'@tailwindcss/typography': ^0.5.9
|
||||||
'@tanstack/react-virtual': 3.0.0-beta.54
|
'@tanstack/react-virtual': 3.0.0-beta.54
|
||||||
@@ -34,6 +33,7 @@ specifiers:
|
|||||||
framer-motion: ^9.1.7
|
framer-motion: ^9.1.7
|
||||||
husky: ^8.0.3
|
husky: ^8.0.3
|
||||||
jotai: ^2.0.3
|
jotai: ^2.0.3
|
||||||
|
jotai-cache: ^0.3.0
|
||||||
lint-staged: ^13.2.0
|
lint-staged: ^13.2.0
|
||||||
next: ^13.2.4
|
next: ^13.2.4
|
||||||
next-remove-imports: ^1.0.10
|
next-remove-imports: ^1.0.10
|
||||||
@@ -62,7 +62,6 @@ dependencies:
|
|||||||
'@radix-ui/react-icons': 1.3.0_react@18.2.0
|
'@radix-ui/react-icons': 1.3.0_react@18.2.0
|
||||||
'@radix-ui/react-popover': 1.0.5_zula6vjvt3wdocc4mwcxqa6nzi
|
'@radix-ui/react-popover': 1.0.5_zula6vjvt3wdocc4mwcxqa6nzi
|
||||||
'@radix-ui/react-tabs': 1.0.3_biqbaboplfbrettd7655fr4n2y
|
'@radix-ui/react-tabs': 1.0.3_biqbaboplfbrettd7655fr4n2y
|
||||||
'@rehooks/local-storage': 2.4.4_react@18.2.0
|
|
||||||
'@supabase/supabase-js': 2.12.1
|
'@supabase/supabase-js': 2.12.1
|
||||||
'@tanstack/react-virtual': 3.0.0-beta.54_react@18.2.0
|
'@tanstack/react-virtual': 3.0.0-beta.54_react@18.2.0
|
||||||
'@tauri-apps/api': 1.2.0
|
'@tauri-apps/api': 1.2.0
|
||||||
@@ -73,6 +72,7 @@ dependencies:
|
|||||||
destr: 1.2.2
|
destr: 1.2.2
|
||||||
framer-motion: 9.1.7_biqbaboplfbrettd7655fr4n2y
|
framer-motion: 9.1.7_biqbaboplfbrettd7655fr4n2y
|
||||||
jotai: 2.0.3_react@18.2.0
|
jotai: 2.0.3_react@18.2.0
|
||||||
|
jotai-cache: 0.3.0_jotai@2.0.3
|
||||||
next: 13.2.4_biqbaboplfbrettd7655fr4n2y
|
next: 13.2.4_biqbaboplfbrettd7655fr4n2y
|
||||||
next-remove-imports: 1.0.10
|
next-remove-imports: 1.0.10
|
||||||
nostr-relaypool: 0.5.18_ws@8.13.0
|
nostr-relaypool: 0.5.18_ws@8.13.0
|
||||||
@@ -1177,15 +1177,6 @@ packages:
|
|||||||
'@babel/runtime': 7.21.0
|
'@babel/runtime': 7.21.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@rehooks/local-storage/2.4.4_react@18.2.0:
|
|
||||||
resolution:
|
|
||||||
{ integrity: sha512-zE+kfOkG59n/1UTxdmbwktIosclr67Nlbf2MzUJ9mNtCSypVscNHeD1qT6JCSo5Pjj8DO893IKWNLJqKKzDL/Q== }
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
dependencies:
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@rushstack/eslint-patch/1.2.0:
|
/@rushstack/eslint-patch/1.2.0:
|
||||||
resolution:
|
resolution:
|
||||||
{ integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== }
|
{ integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== }
|
||||||
@@ -3829,6 +3820,15 @@ packages:
|
|||||||
{ integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== }
|
{ integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== }
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/jotai-cache/0.3.0_jotai@2.0.3:
|
||||||
|
resolution:
|
||||||
|
{ integrity: sha512-hV6DUD1frRpW0EN8Ss7n4KNaMZRBokQw6KPT3seA4P35QRXctMNUn4pHRlg3hzO+KBPpEZw+fyKlWAqagmuIwQ== }
|
||||||
|
peerDependencies:
|
||||||
|
jotai: '>=1.11.0'
|
||||||
|
dependencies:
|
||||||
|
jotai: 2.0.3_react@18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jotai/2.0.3_react@18.2.0:
|
/jotai/2.0.3_react@18.2.0:
|
||||||
resolution:
|
resolution:
|
||||||
{ integrity: sha512-MMjhSPAL3RoeZD9WbObufRT2quThEAEknHHridf2ma8Ml7ZVQmUiHk0ssdbR3F0h3kcwhYqSGJ59OjhPge7RRg== }
|
{ integrity: sha512-MMjhSPAL3RoeZD9WbObufRT2quThEAEknHHridf2ma8Ml7ZVQmUiHk0ssdbR3F0h3kcwhYqSGJ59OjhPge7RRg== }
|
||||||
|
|||||||
@@ -1,53 +1,41 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { createFollows } from '@utils/storage';
|
||||||
|
import { tagsToArray } from '@utils/tags';
|
||||||
|
|
||||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||||
import { AvatarIcon, ExitIcon, GearIcon } from '@radix-ui/react-icons';
|
import { AvatarIcon, ExitIcon, GearIcon } from '@radix-ui/react-icons';
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import { useAtom } from 'jotai';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { memo, useCallback, useContext, useMemo } from 'react';
|
import { memo, useContext, useEffect } from 'react';
|
||||||
|
|
||||||
export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }) {
|
export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }) {
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
const [relays] = useAtom(relaysAtom);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userData = JSON.parse(user.metadata);
|
const userData = JSON.parse(user.metadata);
|
||||||
|
|
||||||
const { db }: any = useContext(DatabaseContext);
|
|
||||||
const relayPool: any = useContext(RelayContext);
|
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
|
||||||
const [currentUser]: any = useLocalStorage('current-user');
|
|
||||||
|
|
||||||
const openProfile = () => {
|
const openProfile = () => {
|
||||||
router.push(`/users/${currentUser.id}`);
|
router.push(`/users/${user.pubkey}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// save follows to database
|
useEffect(() => {
|
||||||
const insertFollows = useCallback(
|
pool.subscribe(
|
||||||
async (follows) => {
|
|
||||||
follows.forEach(async (item) => {
|
|
||||||
if (item) {
|
|
||||||
// insert to database
|
|
||||||
await db.execute(
|
|
||||||
`INSERT OR IGNORE INTO follows (pubkey, account, kind) VALUES ("${item[1]}", "${currentUser.id}", "0")`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[db, currentUser.id]
|
|
||||||
);
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
relayPool.subscribe(
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
kinds: [3],
|
kinds: [3],
|
||||||
authors: [currentUser.id],
|
authors: [user.pubkey],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
relays,
|
relays,
|
||||||
(event: any) => {
|
(event: any) => {
|
||||||
insertFollows(event.tags);
|
if (event.tags.length > 0) {
|
||||||
|
createFollows(tagsToArray(event.tags), user.pubkey, 0);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
@@ -55,7 +43,7 @@ export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }
|
|||||||
unsubscribeOnEose: true,
|
unsubscribeOnEose: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [currentUser.id, insertFollows, relayPool, relays]);
|
}, [pool, relays, user.pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
|
|||||||
@@ -1,35 +1,36 @@
|
|||||||
import { ActiveAccount } from '@components/columns/account/active';
|
import { ActiveAccount } from '@components/columns/account/active';
|
||||||
import { InactiveAccount } from '@components/columns/account/inactive';
|
import { InactiveAccount } from '@components/columns/account/inactive';
|
||||||
|
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import { activeAccountAtom } from '@stores/account';
|
||||||
|
|
||||||
|
import { getAccounts } from '@utils/storage';
|
||||||
|
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
|
||||||
|
|
||||||
export default function AccountList() {
|
export default function AccountList() {
|
||||||
const [currentUser]: any = useLocalStorage('current-user');
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
const renderAccount = useCallback(
|
const renderAccount = useCallback(
|
||||||
(user: { id: string }) => {
|
(user: { id: string }) => {
|
||||||
if (user.id === currentUser.id) {
|
if (user.id === activeAccount.id) {
|
||||||
return <ActiveAccount key={user.id} user={user} />;
|
return <ActiveAccount key={user.id} user={user} />;
|
||||||
} else {
|
} else {
|
||||||
return <InactiveAccount key={user.id} user={user} />;
|
return <InactiveAccount key={user.id} user={user} />;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentUser.id]
|
[activeAccount.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getAccounts = useCallback(async () => {
|
|
||||||
const db = await Database.load('sqlite:lume.db');
|
|
||||||
const result: any = await db.select('SELECT * FROM accounts');
|
|
||||||
|
|
||||||
setUsers(result);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAccounts().catch(console.error);
|
const fetchAccount = async () => {
|
||||||
}, [getAccounts]);
|
const result: any = await getAccounts();
|
||||||
|
setUsers(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchAccount().catch(console.error);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return <>{users.map((user) => renderAccount(user))}</>;
|
return <>{users.map((user) => renderAccount(user))}</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
import { MessageList } from '@components/columns/navigator/messages/list';
|
import { MessageList } from '@components/columns/navigator/messages/list';
|
||||||
|
|
||||||
|
import { activeAccountAtom } from '@stores/account';
|
||||||
|
|
||||||
|
import { getAllFollowsByID } from '@utils/storage';
|
||||||
|
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { TriangleUpIcon } from '@radix-ui/react-icons';
|
import { TriangleUpIcon } from '@radix-ui/react-icons';
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import { useAtom } from 'jotai';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export default function Messages() {
|
export default function Messages() {
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
const [follows] = useLocalStorage('follows');
|
const [follows, setFollows] = useState([]);
|
||||||
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getAllFollowsByID(activeAccount.id)
|
||||||
|
.then((res: any) => setFollows(res))
|
||||||
|
.catch(console.error);
|
||||||
|
}, [activeAccount.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { deleteFromStorage, writeStorage } from '@rehooks/local-storage';
|
|
||||||
import { createContext, useCallback, useEffect, useState } from 'react';
|
|
||||||
import Database from 'tauri-plugin-sql-api';
|
|
||||||
|
|
||||||
export const DatabaseContext = createContext({});
|
|
||||||
|
|
||||||
const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null;
|
|
||||||
|
|
||||||
export default function DatabaseProvider({ children }: { children: React.ReactNode }) {
|
|
||||||
const [done, setDone] = useState(false);
|
|
||||||
|
|
||||||
const getRelays = useCallback(async () => {
|
|
||||||
const result: any[] = await db.select('SELECT relay_url FROM relays WHERE relay_status = "1"');
|
|
||||||
const arr = [];
|
|
||||||
result.forEach((item: { relay_url: string }) => {
|
|
||||||
arr.push(item.relay_url);
|
|
||||||
});
|
|
||||||
// delete old item then save new item to local storage
|
|
||||||
deleteFromStorage('relays');
|
|
||||||
writeStorage('relays', arr);
|
|
||||||
// return
|
|
||||||
return;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getAccount = useCallback(async () => {
|
|
||||||
const result = await db.select(`SELECT * FROM accounts LIMIT 1`);
|
|
||||||
// delete old item then save new item to local storage
|
|
||||||
deleteFromStorage('current-user');
|
|
||||||
if (result[0]) {
|
|
||||||
writeStorage('current-user', result[0]);
|
|
||||||
} else {
|
|
||||||
writeStorage('current-user', null);
|
|
||||||
}
|
|
||||||
// return first record
|
|
||||||
return result[0];
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getFollows = useCallback(async (id: string) => {
|
|
||||||
const result: any[] = await db.select(`SELECT pubkey FROM follows WHERE account = "${id}"`);
|
|
||||||
const arr = [];
|
|
||||||
result.forEach((item: { pubkey: string }) => {
|
|
||||||
arr.push(item.pubkey);
|
|
||||||
});
|
|
||||||
// delete old item then save new item to local storage
|
|
||||||
deleteFromStorage('follows');
|
|
||||||
writeStorage('follows', arr);
|
|
||||||
// return
|
|
||||||
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, clearCacheNote, getRelays]);
|
|
||||||
|
|
||||||
if (!done) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <DatabaseContext.Provider value={{ db }}>{children}</DatabaseContext.Provider>;
|
|
||||||
}
|
|
||||||
@@ -1,70 +1,53 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
|
|
||||||
|
import { activeAccountAtom } from '@stores/account';
|
||||||
import { hasNewerNoteAtom } from '@stores/note';
|
import { hasNewerNoteAtom } from '@stores/note';
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
import { dateToUnix, hoursAgo } from '@utils/getDate';
|
import { dateToUnix, hoursAgo } from '@utils/getDate';
|
||||||
|
import { createCacheNote, getAllFollowsByID } from '@utils/storage';
|
||||||
|
|
||||||
import { useLocalStorage } from '@rehooks/local-storage';
|
import { useAtom, useSetAtom } from 'jotai';
|
||||||
import { useSetAtom } from 'jotai';
|
import { memo, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
export const NoteConnector = memo(function NoteConnector() {
|
export const NoteConnector = memo(function NoteConnector() {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const relayPool: any = useContext(RelayContext);
|
|
||||||
|
|
||||||
const [follows]: any = useLocalStorage('follows');
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
|
||||||
|
|
||||||
const setHasNewerNote = useSetAtom(hasNewerNoteAtom);
|
const setHasNewerNote = useSetAtom(hasNewerNoteAtom);
|
||||||
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
const [relays] = useAtom(relaysAtom);
|
||||||
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
|
|
||||||
|
const [isOnline] = useState(navigator.onLine);
|
||||||
const now = useRef(new Date());
|
const now = useRef(new Date());
|
||||||
|
|
||||||
const insertDB = useCallback(
|
|
||||||
async (event: any) => {
|
|
||||||
// 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)]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[db]
|
|
||||||
);
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
relayPool.subscribe(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
kinds: [1],
|
|
||||||
authors: follows,
|
|
||||||
since: dateToUnix(hoursAgo(12, now.current)),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
relays,
|
|
||||||
(event: any) => {
|
|
||||||
// insert event to local database
|
|
||||||
insertDB(event).catch(console.error);
|
|
||||||
// ask user load newer note
|
|
||||||
if (event.created_at > dateToUnix(now.current)) {
|
|
||||||
setHasNewerNote(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}, [relayPool, follows, relays, insertDB, setHasNewerNote]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleStatusChange = () => {
|
let unsubscribe;
|
||||||
setIsOnline(navigator.onLine);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('online', handleStatusChange);
|
getAllFollowsByID(activeAccount.id).then((follows) => {
|
||||||
window.addEventListener('offline', handleStatusChange);
|
unsubscribe = pool.subscribe(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
kinds: [1],
|
||||||
|
authors: follows,
|
||||||
|
since: dateToUnix(hoursAgo(12, now.current)),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relays,
|
||||||
|
(event: any) => {
|
||||||
|
// insert event to local database
|
||||||
|
createCacheNote(event);
|
||||||
|
// ask user load newer note
|
||||||
|
if (event.created_at > dateToUnix(now.current)) {
|
||||||
|
setHasNewerNote(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('online', handleStatusChange);
|
unsubscribe();
|
||||||
window.removeEventListener('offline', handleStatusChange);
|
|
||||||
};
|
};
|
||||||
}, [isOnline]);
|
}, [activeAccount.id, pool, relays, setHasNewerNote]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
import { CommentsCounter } from '@components/note/counter/comments';
|
import { CommentsCounter } from '@components/note/counter/comments';
|
||||||
import { LikesCounter } from '@components/note/counter/likes';
|
import { LikesCounter } from '@components/note/counter/likes';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
import { useLocalStorage } from '@rehooks/local-storage';
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import { useContext, useEffect, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export default function NoteMetadata({
|
export default function NoteMetadata({
|
||||||
@@ -16,14 +18,14 @@ export default function NoteMetadata({
|
|||||||
eventTime: string;
|
eventTime: string;
|
||||||
eventContent: any;
|
eventContent: any;
|
||||||
}) {
|
}) {
|
||||||
const relayPool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
|
|
||||||
const [likes, setLikes] = useState(0);
|
const [likes, setLikes] = useState(0);
|
||||||
const [comments, setComments] = useState(0);
|
const [comments, setComments] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = relayPool.subscribe(
|
const unsubscribe = pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
'#e': [eventID],
|
'#e': [eventID],
|
||||||
@@ -56,7 +58,7 @@ export default function NoteMetadata({
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [eventID, relayPool, relays]);
|
}, [eventID, pool, relays]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative z-10 -ml-1 flex items-center gap-8">
|
<div className="relative z-10 -ml-1 flex items-center gap-8">
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
import { UserExtend } from '@components/user/extend';
|
import { UserExtend } from '@components/user/extend';
|
||||||
|
|
||||||
|
import { activeAccountAtom } from '@stores/account';
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
import { dateToUnix } from '@utils/getDate';
|
import { dateToUnix } from '@utils/getDate';
|
||||||
|
|
||||||
import CommentIcon from '@assets/icons/comment';
|
import CommentIcon from '@assets/icons/comment';
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
import { SizeIcon } from '@radix-ui/react-icons';
|
import { SizeIcon } from '@radix-ui/react-icons';
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import { useAtom } from 'jotai';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
import { memo, useContext, useState } from 'react';
|
import { memo, useContext, useState } from 'react';
|
||||||
@@ -27,15 +30,15 @@ export const CommentsCounter = memo(function CommentsCounter({
|
|||||||
eventContent: any;
|
eventContent: any;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const relayPool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [currentUser]: any = useLocalStorage('current-user');
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
const profile = JSON.parse(currentUser.metadata);
|
const profile = JSON.parse(activeAccount.metadata);
|
||||||
|
|
||||||
const openThread = () => {
|
const openThread = () => {
|
||||||
router.push(`/newsfeed/${eventID}`);
|
router.push(`/newsfeed/${eventID}`);
|
||||||
@@ -46,13 +49,13 @@ export const CommentsCounter = memo(function CommentsCounter({
|
|||||||
content: value,
|
content: value,
|
||||||
created_at: dateToUnix(),
|
created_at: dateToUnix(),
|
||||||
kind: 1,
|
kind: 1,
|
||||||
pubkey: currentUser.id,
|
pubkey: activeAccount.id,
|
||||||
tags: [['e', eventID]],
|
tags: [['e', eventID]],
|
||||||
};
|
};
|
||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, currentUser.privkey);
|
event.sig = signEvent(event, activeAccount.privkey);
|
||||||
|
|
||||||
relayPool.publish(event, relays);
|
pool.publish(event, relays);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { RelayContext } from '@components/contexts/relay';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
|
import { activeAccountAtom } from '@stores/account';
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
import { dateToUnix } from '@utils/getDate';
|
import { dateToUnix } from '@utils/getDate';
|
||||||
|
|
||||||
import LikeIcon from '@assets/icons/like';
|
import LikeIcon from '@assets/icons/like';
|
||||||
import LikedIcon from '@assets/icons/liked';
|
import LikedIcon from '@assets/icons/liked';
|
||||||
|
|
||||||
import { useLocalStorage } from '@rehooks/local-storage';
|
import { useAtom } from 'jotai';
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
import { memo, useContext, useEffect, useState } from 'react';
|
import { memo, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
@@ -18,10 +21,10 @@ export const LikesCounter = memo(function LikesCounter({
|
|||||||
eventID: string;
|
eventID: string;
|
||||||
eventPubkey: string;
|
eventPubkey: string;
|
||||||
}) {
|
}) {
|
||||||
const relayPool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [currentUser]: any = useLocalStorage('current-user');
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
|
|
||||||
const [isReact, setIsReact] = useState(false);
|
const [isReact, setIsReact] = useState(false);
|
||||||
const [like, setLike] = useState(0);
|
const [like, setLike] = useState(0);
|
||||||
@@ -37,12 +40,12 @@ export const LikesCounter = memo(function LikesCounter({
|
|||||||
['p', eventPubkey],
|
['p', eventPubkey],
|
||||||
],
|
],
|
||||||
created_at: dateToUnix(),
|
created_at: dateToUnix(),
|
||||||
pubkey: currentUser.id,
|
pubkey: activeAccount.id,
|
||||||
};
|
};
|
||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, currentUser.privkey);
|
event.sig = signEvent(event, activeAccount.privkey);
|
||||||
// publish event to all relays
|
// publish event to all relays
|
||||||
relayPool.publish(event, relays);
|
pool.publish(event, relays);
|
||||||
// update state to change icon to filled heart
|
// update state to change icon to filled heart
|
||||||
setIsReact(true);
|
setIsReact(true);
|
||||||
// update counter
|
// update counter
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { RelayContext } from '@components/contexts/relay';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
|
import { activeAccountAtom } from '@stores/account';
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
import { dateToUnix } from '@utils/getDate';
|
import { dateToUnix } from '@utils/getDate';
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
import { SizeIcon } from '@radix-ui/react-icons';
|
import { SizeIcon } from '@radix-ui/react-icons';
|
||||||
import { useLocalStorage } from '@rehooks/local-storage';
|
|
||||||
import * as commands from '@uiw/react-md-editor/lib/commands';
|
import * as commands from '@uiw/react-md-editor/lib/commands';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
@@ -15,16 +18,16 @@ const MDEditor = dynamic(() => import('@uiw/react-md-editor').then((mod) => mod.
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default function FormBasic() {
|
export default function FormBasic() {
|
||||||
const relayPool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [currentUser]: any = useLocalStorage('current-user');
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
const pubkey = currentUser.id;
|
const pubkey = activeAccount.id;
|
||||||
const privkey = currentUser.privkey;
|
const privkey = activeAccount.privkey;
|
||||||
|
|
||||||
const submitEvent = () => {
|
const submitEvent = () => {
|
||||||
const event: any = {
|
const event: any = {
|
||||||
@@ -37,7 +40,7 @@ export default function FormBasic() {
|
|||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, privkey);
|
event.sig = signEvent(event, privkey);
|
||||||
|
|
||||||
relayPool.publish(event, relays);
|
pool.publish(event, relays);
|
||||||
setValue('');
|
setValue('');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,36 @@
|
|||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
|
import { activeAccountAtom } from '@stores/account';
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
import { dateToUnix } from '@utils/getDate';
|
import { dateToUnix } from '@utils/getDate';
|
||||||
|
|
||||||
import { useLocalStorage } from '@rehooks/local-storage';
|
import { useAtom } from 'jotai';
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
import { useContext, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
|
|
||||||
export default function FormComment({ eventID }: { eventID: any }) {
|
export default function FormComment({ eventID }: { eventID: any }) {
|
||||||
const relayPool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [currentUser]: any = useLocalStorage('current-user');
|
const [activeAccount] = useAtom(activeAccountAtom);
|
||||||
|
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
const profile = JSON.parse(currentUser.metadata);
|
const profile = JSON.parse(activeAccount.metadata);
|
||||||
|
|
||||||
const submitEvent = () => {
|
const submitEvent = () => {
|
||||||
const event: any = {
|
const event: any = {
|
||||||
content: value,
|
content: value,
|
||||||
created_at: dateToUnix(),
|
created_at: dateToUnix(),
|
||||||
kind: 1,
|
kind: 1,
|
||||||
pubkey: currentUser.id,
|
pubkey: activeAccount.id,
|
||||||
tags: [['e', eventID]],
|
tags: [['e', eventID]],
|
||||||
};
|
};
|
||||||
event.id = getEventHash(event);
|
event.id = getEventHash(event);
|
||||||
event.sig = signEvent(event, currentUser.privkey);
|
event.sig = signEvent(event, activeAccount.privkey);
|
||||||
|
|
||||||
relayPool.publish(event, relays);
|
pool.publish(event, relays);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -37,7 +40,7 @@ export default function FormComment({ eventID }: { eventID: any }) {
|
|||||||
<div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-md border border-white/10">
|
<div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-md border border-white/10">
|
||||||
<ImageWithFallback
|
<ImageWithFallback
|
||||||
src={profile.picture}
|
src={profile.picture}
|
||||||
alt={currentUser.id}
|
alt={activeAccount.id}
|
||||||
fill={true}
|
fill={true}
|
||||||
className="rounded-md object-cover"
|
className="rounded-md object-cover"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,35 +1,21 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
|
||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
import { Content } from '@components/note/content';
|
import { Content } from '@components/note/content';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { createCacheNote, getNoteByID } from '@utils/storage';
|
||||||
|
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const relayPool: any = useContext(RelayContext);
|
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [event, setEvent] = useState(null);
|
const [event, setEvent] = useState(null);
|
||||||
|
|
||||||
const insertDB = useCallback(
|
|
||||||
async (event: any) => {
|
|
||||||
// insert to local database
|
|
||||||
await db.execute(
|
|
||||||
'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);',
|
|
||||||
[event.id, event.pubkey, event.created_at, event.kind, event.content, JSON.stringify(event.tags), 0]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[db]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getData = useCallback(async () => {
|
|
||||||
const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`);
|
|
||||||
return result[0];
|
|
||||||
}, [db, id]);
|
|
||||||
|
|
||||||
const fetchEvent = useCallback(() => {
|
const fetchEvent = useCallback(() => {
|
||||||
relayPool.subscribe(
|
pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
ids: [id],
|
ids: [id],
|
||||||
@@ -41,7 +27,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
|||||||
// update state
|
// update state
|
||||||
setEvent(event);
|
setEvent(event);
|
||||||
// insert to database
|
// insert to database
|
||||||
insertDB(event);
|
createCacheNote(event);
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
@@ -49,19 +35,17 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
|||||||
unsubscribeOnEose: true,
|
unsubscribeOnEose: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [id, insertDB, relayPool, relays]);
|
}, [id, pool, relays]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData()
|
getNoteByID(id).then((res) => {
|
||||||
.then((res) => {
|
if (res) {
|
||||||
if (res) {
|
setEvent(res);
|
||||||
setEvent(res);
|
} else {
|
||||||
} else {
|
fetchEvent();
|
||||||
fetchEvent();
|
}
|
||||||
}
|
});
|
||||||
})
|
}, [fetchEvent, id]);
|
||||||
.catch(console.error);
|
|
||||||
}, [fetchEvent, getData]);
|
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,35 +1,21 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
|
||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
import { Content } from '@components/note/content';
|
import { Content } from '@components/note/content';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { createCacheNote, getNoteByID } from '@utils/storage';
|
||||||
|
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const RootNote = memo(function RootNote({ id }: { id: string }) {
|
export const RootNote = memo(function RootNote({ id }: { id: string }) {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const relayPool: any = useContext(RelayContext);
|
|
||||||
|
|
||||||
const [relays]: any = useLocalStorage('relays');
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [event, setEvent] = useState(null);
|
const [event, setEvent] = useState(null);
|
||||||
|
|
||||||
const insertDB = useCallback(
|
|
||||||
async (event: any) => {
|
|
||||||
// insert to local database
|
|
||||||
await db.execute(
|
|
||||||
'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);',
|
|
||||||
[event.id, event.pubkey, event.created_at, event.kind, event.content, JSON.stringify(event.tags), 1]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[db]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getData = useCallback(async () => {
|
|
||||||
const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`);
|
|
||||||
return result[0];
|
|
||||||
}, [db, id]);
|
|
||||||
|
|
||||||
const fetchEvent = useCallback(() => {
|
const fetchEvent = useCallback(() => {
|
||||||
relayPool.subscribe(
|
pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
ids: [id],
|
ids: [id],
|
||||||
@@ -41,7 +27,7 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) {
|
|||||||
// update state
|
// update state
|
||||||
setEvent(event);
|
setEvent(event);
|
||||||
// insert to database
|
// insert to database
|
||||||
insertDB(event);
|
createCacheNote(event);
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
@@ -49,19 +35,17 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) {
|
|||||||
unsubscribeOnEose: true,
|
unsubscribeOnEose: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [id, insertDB, relayPool, relays]);
|
}, [id, pool, relays]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData()
|
getNoteByID(id).then((res) => {
|
||||||
.then((res) => {
|
if (res) {
|
||||||
if (res) {
|
setEvent(res);
|
||||||
setEvent(res);
|
} else {
|
||||||
} else {
|
fetchEvent();
|
||||||
fetchEvent();
|
}
|
||||||
}
|
});
|
||||||
})
|
}, [fetchEvent, id]);
|
||||||
.catch(console.error);
|
|
||||||
}, [fetchEvent, getData]);
|
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createContext, useMemo } from 'react';
|
|||||||
|
|
||||||
export const RelayContext = createContext({});
|
export const RelayContext = createContext({});
|
||||||
|
|
||||||
export default function RelayProvider({ relays, children }: { relays: any; children: React.ReactNode }) {
|
export default function RelayProvider({ relays, children }: { relays: Array<string>; children: React.ReactNode }) {
|
||||||
const value = useMemo(() => new RelayPool(relays, { useEventCache: false, logSubscriptions: false }), [relays]);
|
const value = useMemo(() => new RelayPool(relays, { useEventCache: false, logSubscriptions: false }), [relays]);
|
||||||
return <RelayContext.Provider value={value}>{children}</RelayContext.Provider>;
|
return <RelayContext.Provider value={value}>{children}</RelayContext.Provider>;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
|
||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
|
||||||
|
import { createCacheProfile, getCacheProfile } from '@utils/storage';
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
||||||
@@ -9,13 +9,12 @@ import Avatar from 'boring-avatars';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: string; time: any }) {
|
export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: string; time: any }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { db }: any = useContext(DatabaseContext);
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
|
|
||||||
const openUserPage = (e) => {
|
const openUserPage = (e) => {
|
||||||
@@ -24,7 +23,6 @@ export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: s
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchProfile = useCallback(async (id: string) => {
|
const fetchProfile = useCallback(async (id: string) => {
|
||||||
console.log('fetch');
|
|
||||||
const res = await fetch(`https://rbr.bio/${id}/metadata.json`, {
|
const res = await fetch(`https://rbr.bio/${id}/metadata.json`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
timeout: 30,
|
timeout: 30,
|
||||||
@@ -32,34 +30,20 @@ export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: s
|
|||||||
return res.data;
|
return res.data;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCacheProfile = useCallback(async () => {
|
|
||||||
const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`);
|
|
||||||
return result[0];
|
|
||||||
}, [db, pubkey]);
|
|
||||||
|
|
||||||
const insertCacheProfile = useCallback(
|
|
||||||
async (event) => {
|
|
||||||
// update state
|
|
||||||
setProfile(JSON.parse(event.content));
|
|
||||||
// insert to database
|
|
||||||
await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]);
|
|
||||||
},
|
|
||||||
[db, pubkey]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCacheProfile()
|
getCacheProfile(pubkey).then((res) => {
|
||||||
.then((res) => {
|
if (res) {
|
||||||
if (res !== undefined) {
|
setProfile(JSON.parse(res.metadata));
|
||||||
setProfile(JSON.parse(res.metadata));
|
} else {
|
||||||
} else {
|
fetchProfile(pubkey)
|
||||||
fetchProfile(pubkey)
|
.then((res: any) => {
|
||||||
.then((res) => insertCacheProfile(res))
|
setProfile(JSON.parse(res.content));
|
||||||
.catch(console.error);
|
createCacheProfile(pubkey, res.content);
|
||||||
}
|
})
|
||||||
})
|
.catch(console.error);
|
||||||
.catch(console.error);
|
}
|
||||||
}, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]);
|
});
|
||||||
|
}, [fetchProfile, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={(e) => openUserPage(e)} className="group flex items-start gap-2">
|
<div onClick={(e) => openUserPage(e)} className="group flex items-start gap-2">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
|
||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
|
||||||
|
import { createCacheProfile, getCacheProfile } from '@utils/storage';
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
||||||
@@ -8,14 +8,11 @@ import { fetch } from '@tauri-apps/api/http';
|
|||||||
import Avatar from 'boring-avatars';
|
import Avatar from 'boring-avatars';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
truncate;
|
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
export const UserLarge = memo(function UserLarge({ pubkey, time }: { pubkey: string; time: any }) {
|
export const UserLarge = memo(function UserLarge({ pubkey, time }: { pubkey: string; time: any }) {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
|
|
||||||
const fetchProfile = useCallback(async (id: string) => {
|
const fetchProfile = useCallback(async (id: string) => {
|
||||||
@@ -26,34 +23,20 @@ export const UserLarge = memo(function UserLarge({ pubkey, time }: { pubkey: str
|
|||||||
return res.data;
|
return res.data;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCacheProfile = useCallback(async () => {
|
|
||||||
const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`);
|
|
||||||
return result[0];
|
|
||||||
}, [db, pubkey]);
|
|
||||||
|
|
||||||
const insertCacheProfile = useCallback(
|
|
||||||
async (event) => {
|
|
||||||
// update state
|
|
||||||
setProfile(JSON.parse(event.content));
|
|
||||||
// insert to database
|
|
||||||
await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]);
|
|
||||||
},
|
|
||||||
[db, pubkey]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCacheProfile()
|
getCacheProfile(pubkey).then((res) => {
|
||||||
.then((res) => {
|
if (res) {
|
||||||
if (res !== undefined) {
|
setProfile(JSON.parse(res.metadata));
|
||||||
setProfile(JSON.parse(res.metadata));
|
} else {
|
||||||
} else {
|
fetchProfile(pubkey)
|
||||||
fetchProfile(pubkey)
|
.then((res: any) => {
|
||||||
.then((res) => insertCacheProfile(res))
|
setProfile(JSON.parse(res.content));
|
||||||
.catch(console.error);
|
createCacheProfile(pubkey, res.content);
|
||||||
}
|
})
|
||||||
})
|
.catch(console.error);
|
||||||
.catch(console.error);
|
}
|
||||||
}, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]);
|
});
|
||||||
|
}, [fetchProfile, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
import { createCacheProfile, getCacheProfile } from '@utils/storage';
|
||||||
|
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { fetch } from '@tauri-apps/api/http';
|
import { fetch } from '@tauri-apps/api/http';
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const UserMention = memo(function UserMention({ pubkey }: { pubkey: string }) {
|
export const UserMention = memo(function UserMention({ pubkey }: { pubkey: string }) {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
|
|
||||||
const fetchProfile = useCallback(async (id: string) => {
|
const fetchProfile = useCallback(async (id: string) => {
|
||||||
@@ -17,34 +15,20 @@ export const UserMention = memo(function UserMention({ pubkey }: { pubkey: strin
|
|||||||
return res.data;
|
return res.data;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCacheProfile = useCallback(async () => {
|
|
||||||
const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`);
|
|
||||||
return result[0];
|
|
||||||
}, [db, pubkey]);
|
|
||||||
|
|
||||||
const insertCacheProfile = useCallback(
|
|
||||||
async (event) => {
|
|
||||||
// insert to database
|
|
||||||
await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]);
|
|
||||||
// update state
|
|
||||||
setProfile(JSON.parse(event.content));
|
|
||||||
},
|
|
||||||
[db, pubkey]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCacheProfile()
|
getCacheProfile(pubkey).then((res) => {
|
||||||
.then((res) => {
|
if (res) {
|
||||||
if (res !== undefined) {
|
setProfile(JSON.parse(res.metadata));
|
||||||
setProfile(JSON.parse(res.metadata));
|
} else {
|
||||||
} else {
|
fetchProfile(pubkey)
|
||||||
fetchProfile(pubkey)
|
.then((res: any) => {
|
||||||
.then((res) => insertCacheProfile(res))
|
setProfile(JSON.parse(res.content));
|
||||||
.catch(console.error);
|
createCacheProfile(pubkey, res.content);
|
||||||
}
|
})
|
||||||
})
|
.catch(console.error);
|
||||||
.catch(console.error);
|
}
|
||||||
}, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]);
|
});
|
||||||
|
}, [fetchProfile, pubkey]);
|
||||||
|
|
||||||
return <span className="cursor-pointer text-fuchsia-500">@{profile?.name || truncate(pubkey, 16, ' .... ')}</span>;
|
return <span className="cursor-pointer text-fuchsia-500">@{profile?.name || truncate(pubkey, 16, ' .... ')}</span>;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { DatabaseContext } from '@components/contexts/database';
|
|
||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
|
||||||
|
import { createCacheProfile, getCacheProfile } from '@utils/storage';
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { fetch } from '@tauri-apps/api/http';
|
import { fetch } from '@tauri-apps/api/http';
|
||||||
import Avatar from 'boring-avatars';
|
import Avatar from 'boring-avatars';
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react';
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const UserMini = memo(function UserMini({ pubkey }: { pubkey: string }) {
|
export const UserMini = memo(function UserMini({ pubkey }: { pubkey: string }) {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
|
|
||||||
const fetchProfile = useCallback(async (id: string) => {
|
const fetchProfile = useCallback(async (id: string) => {
|
||||||
@@ -19,34 +18,20 @@ export const UserMini = memo(function UserMini({ pubkey }: { pubkey: string }) {
|
|||||||
return res.data;
|
return res.data;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCacheProfile = useCallback(async () => {
|
|
||||||
const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`);
|
|
||||||
return result[0];
|
|
||||||
}, [db, pubkey]);
|
|
||||||
|
|
||||||
const insertCacheProfile = useCallback(
|
|
||||||
async (event) => {
|
|
||||||
// update state
|
|
||||||
setProfile(JSON.parse(event.content));
|
|
||||||
// insert to database
|
|
||||||
await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]);
|
|
||||||
},
|
|
||||||
[db, pubkey]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCacheProfile()
|
getCacheProfile(pubkey).then((res) => {
|
||||||
.then((res) => {
|
if (res) {
|
||||||
if (res !== undefined) {
|
setProfile(JSON.parse(res.metadata));
|
||||||
setProfile(JSON.parse(res.metadata));
|
} else {
|
||||||
} else {
|
fetchProfile(pubkey)
|
||||||
fetchProfile(pubkey)
|
.then((res: any) => {
|
||||||
.then((res) => insertCacheProfile(res))
|
setProfile(JSON.parse(res.content));
|
||||||
.catch(console.error);
|
createCacheProfile(pubkey, res.content);
|
||||||
}
|
})
|
||||||
})
|
.catch(console.error);
|
||||||
.catch(console.error);
|
}
|
||||||
}, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]);
|
});
|
||||||
|
}, [fetchProfile, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex cursor-pointer items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm font-medium hover:bg-zinc-900">
|
<div className="flex cursor-pointer items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm font-medium hover:bg-zinc-900">
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { Provider } from 'jotai';
|
import RelayProvider from '@components/relaysProvider';
|
||||||
|
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { Provider, useAtom } from 'jotai';
|
||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
import { ReactElement, ReactNode } from 'react';
|
import { ReactElement, ReactNode } from 'react';
|
||||||
@@ -17,5 +21,11 @@ type AppPropsWithLayout = AppProps & {
|
|||||||
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||||
// Use the layout defined at the page level, if available
|
// Use the layout defined at the page level, if available
|
||||||
const getLayout = Component.getLayout ?? ((page) => page);
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
return <Provider>{getLayout(<Component {...pageProps} />)}</Provider>;
|
const [relays] = useAtom(relaysAtom);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Provider>
|
||||||
|
<RelayProvider relays={relays}>{getLayout(<Component {...pageProps} />)}</RelayProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import BaseLayout from '@layouts/base';
|
import BaseLayout from '@layouts/base';
|
||||||
|
|
||||||
import { getActiveAccount } from '@utils/storage';
|
import { getAccounts } from '@utils/storage';
|
||||||
|
|
||||||
import LumeSymbol from '@assets/icons/Lume';
|
import LumeSymbol from '@assets/icons/Lume';
|
||||||
|
|
||||||
@@ -11,10 +11,10 @@ export default function Page() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getActiveAccount()
|
getAccounts()
|
||||||
.then((res: any[]) => {
|
.then((res: any) => {
|
||||||
if (res.length > 0) {
|
if (res.length > 0) {
|
||||||
router.push('/newsfeed/following');
|
router.push('/newsfeed/circle');
|
||||||
} else {
|
} else {
|
||||||
router.push('/onboarding');
|
router.push('/onboarding');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import BaseLayout from '@layouts/base';
|
import BaseLayout from '@layouts/base';
|
||||||
import WithSidebarLayout from '@layouts/withSidebar';
|
import WithSidebarLayout from '@layouts/withSidebar';
|
||||||
|
|
||||||
import { DatabaseContext } from '@components/contexts/database';
|
|
||||||
import { RelayContext } from '@components/contexts/relay';
|
|
||||||
import { Content } from '@components/note/content';
|
import { Content } from '@components/note/content';
|
||||||
import { ContentExtend } from '@components/note/content/extend';
|
import { ContentExtend } from '@components/note/content/extend';
|
||||||
import FormComment from '@components/note/form/comment';
|
import FormComment from '@components/note/form/comment';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
|
import { getNoteByID } from '@utils/storage';
|
||||||
|
|
||||||
import useLocalStorage from '@rehooks/local-storage';
|
import useLocalStorage from '@rehooks/local-storage';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@@ -14,15 +15,13 @@ import {
|
|||||||
ReactElement,
|
ReactElement,
|
||||||
ReactFragment,
|
ReactFragment,
|
||||||
ReactPortal,
|
ReactPortal,
|
||||||
useCallback,
|
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { db }: any = useContext(DatabaseContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const relayPool: any = useContext(RelayContext);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const id = router.query.id;
|
const id = router.query.id;
|
||||||
@@ -32,17 +31,15 @@ export default function Page() {
|
|||||||
const [rootEvent, setRootEvent] = useState(null);
|
const [rootEvent, setRootEvent] = useState(null);
|
||||||
const [comments, setComments] = useState([]);
|
const [comments, setComments] = useState([]);
|
||||||
|
|
||||||
const fetchRoot = useCallback(async () => {
|
|
||||||
const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`);
|
|
||||||
setRootEvent(result[0]);
|
|
||||||
}, [db, id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let unsubscribe: () => void;
|
let unsubscribe: () => void;
|
||||||
|
|
||||||
fetchRoot()
|
getNoteByID(id)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
unsubscribe = relayPool.subscribe(
|
// update state
|
||||||
|
setRootEvent(res);
|
||||||
|
// get all comments
|
||||||
|
unsubscribe = pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
'#e': [id],
|
'#e': [id],
|
||||||
@@ -60,7 +57,7 @@ export default function Page() {
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [fetchRoot, id, relayPool, relays]);
|
}, [id, pool, relays]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="scrollbar-hide flex h-full flex-col gap-2 overflow-y-auto py-5">
|
<div className="scrollbar-hide flex h-full flex-col gap-2 overflow-y-auto py-5">
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import BaseLayout from '@layouts/base';
|
import BaseLayout from '@layouts/base';
|
||||||
|
|
||||||
import { pool } from '@utils/pool';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
import { createAccount, getAllRelays } from '@utils/storage';
|
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { createAccount } from '@utils/storage';
|
||||||
|
|
||||||
import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
|
import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
|
import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools';
|
||||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useMemo, useState } from 'react';
|
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useMemo, useState } from 'react';
|
||||||
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
|
import { Config, names, uniqueNamesGenerator } from 'unique-names-generator';
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
@@ -16,7 +20,9 @@ const config: Config = {
|
|||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [type, setType] = useState('password');
|
const [type, setType] = useState('password');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@@ -78,16 +84,11 @@ export default function Page() {
|
|||||||
// insert to database then broadcast
|
// insert to database then broadcast
|
||||||
createAccount(data)
|
createAccount(data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
getAllRelays()
|
pool.publish(event, relays);
|
||||||
.then((res) => {
|
router.push({
|
||||||
// publish to relays
|
pathname: '/onboarding/create/step-2',
|
||||||
pool(res).publish(event, res);
|
query: { id: pubKey, privkey: privKey },
|
||||||
router.push({
|
});
|
||||||
pathname: '/onboarding/create/step-2',
|
|
||||||
query: { id: pubKey, privkey: privKey },
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import BaseLayout from '@layouts/base';
|
import BaseLayout from '@layouts/base';
|
||||||
|
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
import { UserBase } from '@components/user/base';
|
import { UserBase } from '@components/user/base';
|
||||||
|
|
||||||
import { pool } from '@utils/pool';
|
import { relaysAtom } from '@stores/relays';
|
||||||
import { createFollows, getAllRelays } from '@utils/storage';
|
|
||||||
|
import { createFollows } from '@utils/storage';
|
||||||
|
|
||||||
import { CheckCircledIcon } from '@radix-ui/react-icons';
|
import { CheckCircledIcon } from '@radix-ui/react-icons';
|
||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
import { JSXElementConstructor, Key, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react';
|
import {
|
||||||
|
JSXElementConstructor,
|
||||||
|
Key,
|
||||||
|
ReactElement,
|
||||||
|
ReactFragment,
|
||||||
|
ReactPortal,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
const supabase = createClient(
|
const supabase = createClient(
|
||||||
'https://niwaazauwnrwiwmnocnn.supabase.co',
|
'https://niwaazauwnrwiwmnocnn.supabase.co',
|
||||||
@@ -52,9 +64,12 @@ const initialList = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { id, privkey }: any = router.query;
|
const { id, privkey }: any = router.query;
|
||||||
|
|
||||||
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [list, setList]: any = useState(initialList);
|
const [list, setList]: any = useState(initialList);
|
||||||
const [follows, setFollows] = useState([]);
|
const [follows, setFollows] = useState([]);
|
||||||
@@ -93,13 +108,9 @@ export default function Page() {
|
|||||||
createFollows(follows, id, 0)
|
createFollows(follows, id, 0)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res === 'ok') {
|
if (res === 'ok') {
|
||||||
getAllRelays()
|
// publish to relays
|
||||||
.then((res) => {
|
pool.publish(event, relays);
|
||||||
// publish to relays
|
router.push('/');
|
||||||
pool(res).publish(event, res);
|
|
||||||
router.push('/');
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|||||||
@@ -1,69 +1,72 @@
|
|||||||
import BaseLayout from '@layouts/base';
|
import BaseLayout from '@layouts/base';
|
||||||
|
|
||||||
import { pool } from '@utils/pool';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
import { createAccount, createFollows, getAllRelays } from '@utils/storage';
|
|
||||||
|
import { relaysAtom } from '@stores/relays';
|
||||||
|
|
||||||
|
import { createAccount, createFollows } from '@utils/storage';
|
||||||
|
import { tagsToArray } from '@utils/tags';
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react';
|
import {
|
||||||
|
JSXElementConstructor,
|
||||||
const tags = (arr) => {
|
ReactElement,
|
||||||
const newarr = [];
|
ReactFragment,
|
||||||
// push item to newarr
|
ReactPortal,
|
||||||
arr.forEach((item) => {
|
useContext,
|
||||||
newarr.push(['p', item]);
|
useEffect,
|
||||||
});
|
useState,
|
||||||
return newarr;
|
} from 'react';
|
||||||
};
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const privkey: any = router.query.privkey;
|
const privkey: any = router.query.privkey;
|
||||||
const pubkey = getPublicKey(privkey);
|
const pubkey = getPublicKey(privkey);
|
||||||
|
|
||||||
|
const [relays] = useAtom(relaysAtom);
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAllRelays()
|
pool.subscribe(
|
||||||
.then((res) => {
|
[
|
||||||
pool(res).subscribe(
|
{
|
||||||
[
|
authors: [pubkey],
|
||||||
{
|
kinds: [0, 3],
|
||||||
authors: [pubkey],
|
since: 0,
|
||||||
kinds: [0, 3],
|
},
|
||||||
since: 0,
|
],
|
||||||
},
|
relays,
|
||||||
],
|
(event: any) => {
|
||||||
res,
|
if (event.kind === 0) {
|
||||||
(event: any) => {
|
const data = {
|
||||||
if (event.kind === 0) {
|
pubkey: pubkey,
|
||||||
const data = {
|
privkey: privkey,
|
||||||
pubkey: pubkey,
|
npub: nip19.npubEncode(pubkey),
|
||||||
privkey: privkey,
|
nsec: nip19.nsecEncode(privkey),
|
||||||
npub: nip19.npubEncode(pubkey),
|
metadata: event.content,
|
||||||
nsec: nip19.nsecEncode(privkey),
|
};
|
||||||
metadata: event.content,
|
setProfile(destr(event.content));
|
||||||
};
|
createAccount(data);
|
||||||
setProfile(destr(event.content));
|
} else {
|
||||||
createAccount(data);
|
if (event.tags.length > 0) {
|
||||||
} else {
|
createFollows(tagsToArray(event.tags), pubkey, 0);
|
||||||
if (event.tags.length > 0) {
|
|
||||||
createFollows(tags(event.tags), pubkey, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
unsubscribeOnEose: true,
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
})
|
},
|
||||||
.catch(console.error);
|
undefined,
|
||||||
}, [privkey, pubkey]);
|
undefined,
|
||||||
|
{
|
||||||
|
unsubscribeOnEose: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, [pool, privkey, pubkey, relays]);
|
||||||
|
|
||||||
// submit then redirect to home
|
// submit then redirect to home
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
|
|||||||
9
src/stores/account.tsx
Normal file
9
src/stores/account.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { isSSR } from '@utils/ssr';
|
||||||
|
import { getActiveAccount } from '@utils/storage';
|
||||||
|
|
||||||
|
import { atomWithCache } from 'jotai-cache';
|
||||||
|
|
||||||
|
export const activeAccountAtom = atomWithCache(async () => {
|
||||||
|
const response = isSSR ? null : await getActiveAccount();
|
||||||
|
return response;
|
||||||
|
});
|
||||||
9
src/stores/relays.tsx
Normal file
9
src/stores/relays.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { isSSR } from '@utils/ssr';
|
||||||
|
import { getAllRelays } from '@utils/storage';
|
||||||
|
|
||||||
|
import { atomWithCache } from 'jotai-cache';
|
||||||
|
|
||||||
|
export const relaysAtom = atomWithCache(async () => {
|
||||||
|
const response = isSSR ? null : await getAllRelays();
|
||||||
|
return response;
|
||||||
|
});
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { atom } from 'jotai';
|
|
||||||
|
|
||||||
export const currentUser = atom({});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { RelayPool } from 'nostr-relaypool';
|
|
||||||
|
|
||||||
export function pool({ relays }: { relays: any }) {
|
|
||||||
const createPool = new RelayPool(relays, { useEventCache: false, logSubscriptions: false });
|
|
||||||
return createPool;
|
|
||||||
}
|
|
||||||
1
src/utils/ssr.tsx
Normal file
1
src/utils/ssr.tsx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const isSSR = typeof window === 'undefined';
|
||||||
@@ -25,7 +25,14 @@ export async function getAllRelays() {
|
|||||||
// get active account
|
// get active account
|
||||||
export async function getActiveAccount() {
|
export async function getActiveAccount() {
|
||||||
const db = await connect();
|
const db = await connect();
|
||||||
return await db.select(`SELECT * FROM accounts LIMIT 1;`);
|
const result = await db.select(`SELECT * FROM accounts LIMIT 1;`);
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all accounts
|
||||||
|
export async function getAccounts() {
|
||||||
|
const db = await connect();
|
||||||
|
return await db.select(`SELECT * FROM accounts`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all follows by account id
|
// get all follows by account id
|
||||||
@@ -71,3 +78,26 @@ export async function createCacheProfile(id, metadata) {
|
|||||||
const db = await connect();
|
const db = await connect();
|
||||||
return await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [id, metadata]);
|
return await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [id, metadata]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get cache profile
|
||||||
|
export async function getCacheProfile(id) {
|
||||||
|
const db = await connect();
|
||||||
|
const result = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${id}"`);
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// get note by id
|
||||||
|
export async function getNoteByID(id) {
|
||||||
|
const db = await connect();
|
||||||
|
const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`);
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// create cache note
|
||||||
|
export async function createCacheNote(data) {
|
||||||
|
const db = await connect();
|
||||||
|
return await db.execute(
|
||||||
|
'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);',
|
||||||
|
[data.id, data.pubkey, data.created_at, data.kind, data.content, JSON.stringify(data.tags), 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
8
src/utils/tags.tsx
Normal file
8
src/utils/tags.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export const tagsToArray = (arr) => {
|
||||||
|
const newarr = [];
|
||||||
|
// push item to newarr
|
||||||
|
arr.forEach((item) => {
|
||||||
|
newarr.push(item[1]);
|
||||||
|
});
|
||||||
|
return newarr;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user