mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 19:46:34 +00:00
completed migrate to prisma-rust-client
This commit is contained in:
parent
3f87d510ab
commit
fc8dc8fd0d
@ -27,7 +27,7 @@ CREATE TABLE "Note" (
|
|||||||
"content" TEXT NOT NULL,
|
"content" TEXT NOT NULL,
|
||||||
"parent_id" TEXT NOT NULL,
|
"parent_id" TEXT NOT NULL,
|
||||||
"parent_comment_id" TEXT NOT NULL,
|
"parent_comment_id" TEXT NOT NULL,
|
||||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" INTEGER NOT NULL,
|
||||||
"accountId" INTEGER NOT NULL,
|
"accountId" INTEGER NOT NULL,
|
||||||
CONSTRAINT "Note_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT "Note_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
);
|
);
|
||||||
@ -38,7 +38,7 @@ CREATE TABLE "Message" (
|
|||||||
"pubkey" TEXT NOT NULL,
|
"pubkey" TEXT NOT NULL,
|
||||||
"content" TEXT NOT NULL,
|
"content" TEXT NOT NULL,
|
||||||
"tags" TEXT NOT NULL,
|
"tags" TEXT NOT NULL,
|
||||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" INTEGER NOT NULL,
|
||||||
"accountId" INTEGER NOT NULL,
|
"accountId" INTEGER NOT NULL,
|
||||||
CONSTRAINT "Message_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT "Message_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
);
|
);
|
@ -0,0 +1,11 @@
|
|||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "Message_pubkey_idx";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "Note_eventId_idx";
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Message_pubkey_createdAt_idx" ON "Message"("pubkey", "createdAt");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "Note_eventId_createdAt_idx" ON "Note"("eventId", "createdAt");
|
@ -44,12 +44,12 @@ model Note {
|
|||||||
content String
|
content String
|
||||||
parent_id String
|
parent_id String
|
||||||
parent_comment_id String
|
parent_comment_id String
|
||||||
createdAt DateTime @default(now())
|
createdAt Int
|
||||||
|
|
||||||
Account Account @relation(fields: [accountId], references: [id])
|
Account Account @relation(fields: [accountId], references: [id])
|
||||||
accountId Int
|
accountId Int
|
||||||
|
|
||||||
@@index([eventId])
|
@@index([eventId, createdAt])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Message {
|
model Message {
|
||||||
@ -57,12 +57,12 @@ model Message {
|
|||||||
pubkey String
|
pubkey String
|
||||||
content String
|
content String
|
||||||
tags String
|
tags String
|
||||||
createdAt DateTime @default(now())
|
createdAt Int
|
||||||
|
|
||||||
Account Account @relation(fields: [accountId], references: [id])
|
Account Account @relation(fields: [accountId], references: [id])
|
||||||
accountId Int
|
accountId Int
|
||||||
|
|
||||||
@@index([pubkey])
|
@@index([pubkey, createdAt])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Relay {
|
model Relay {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate objc;
|
extern crate objc;
|
||||||
|
|
||||||
use prisma_client_rust::raw;
|
use prisma_client_rust::Direction;
|
||||||
use tauri::{Manager, WindowEvent};
|
use tauri::{Manager, WindowEvent};
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use window_ext::WindowExt;
|
use window_ext::WindowExt;
|
||||||
@ -54,12 +54,30 @@ struct CreateNoteData {
|
|||||||
content: String,
|
content: String,
|
||||||
parent_id: String,
|
parent_id: String,
|
||||||
parent_comment_id: String,
|
parent_comment_id: String,
|
||||||
|
created_at: i32,
|
||||||
account_id: i32,
|
account_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Type)]
|
||||||
|
struct GetNoteByIdData {
|
||||||
|
event_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Type)]
|
||||||
|
struct GetNoteData {
|
||||||
|
date: i32,
|
||||||
|
limit: i32,
|
||||||
|
offset: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Type)]
|
||||||
|
struct GetLatestNoteData {
|
||||||
|
date: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
async fn get_account(db: DbState<'_>) -> Result<Vec<account::Data>, ()> {
|
async fn get_accounts(db: DbState<'_>) -> Result<Vec<account::Data>, ()> {
|
||||||
db.account()
|
db.account()
|
||||||
.find_many(vec![account::active::equals(false)])
|
.find_many(vec![account::active::equals(false)])
|
||||||
.exec()
|
.exec()
|
||||||
@ -120,6 +138,7 @@ async fn create_note(db: DbState<'_>, data: CreateNoteData) -> Result<note::Data
|
|||||||
data.content,
|
data.content,
|
||||||
data.parent_id,
|
data.parent_id,
|
||||||
data.parent_comment_id,
|
data.parent_comment_id,
|
||||||
|
data.created_at,
|
||||||
account::id::equals(data.account_id),
|
account::id::equals(data.account_id),
|
||||||
vec![],
|
vec![],
|
||||||
),
|
),
|
||||||
@ -132,8 +151,12 @@ async fn create_note(db: DbState<'_>, data: CreateNoteData) -> Result<note::Data
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
async fn get_notes(db: DbState<'_>) -> Result<Vec<note::Data>, ()> {
|
async fn get_notes(db: DbState<'_>, data: GetNoteData) -> Result<Vec<note::Data>, ()> {
|
||||||
db._query_raw(raw!("SELECT * FROM Note"))
|
db.note()
|
||||||
|
.find_many(vec![note::created_at::lte(data.date)])
|
||||||
|
.order_by(note::created_at::order(Direction::Desc))
|
||||||
|
.take(data.limit.into())
|
||||||
|
.skip(data.offset.into())
|
||||||
.exec()
|
.exec()
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
@ -141,15 +164,30 @@ async fn get_notes(db: DbState<'_>) -> Result<Vec<note::Data>, ()> {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
async fn check_note(db: DbState<'_>) -> Result<Vec<note::Data>, ()> {
|
async fn get_latest_notes(db: DbState<'_>, data: GetLatestNoteData) -> Result<Vec<note::Data>, ()> {
|
||||||
db.note()
|
db.note()
|
||||||
.find_many(vec![])
|
.find_many(vec![note::created_at::gt(data.date)])
|
||||||
.take(5)
|
.order_by(note::created_at::order(Direction::Desc))
|
||||||
.exec()
|
.exec()
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
|
async fn get_note_by_id(db: DbState<'_>, data: GetNoteByIdData) -> Result<Option<note::Data>, ()> {
|
||||||
|
db.note()
|
||||||
|
.find_unique(note::event_id::equals(data.event_id))
|
||||||
|
.exec()
|
||||||
|
.await
|
||||||
|
.map_err(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn count_total_notes(db: DbState<'_>) -> Result<i64, ()> {
|
||||||
|
db.note().count(vec![]).exec().await.map_err(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let db = PrismaClient::_builder().build().await.unwrap();
|
let db = PrismaClient::_builder().build().await.unwrap();
|
||||||
@ -157,13 +195,14 @@ async fn main() {
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
ts::export(
|
ts::export(
|
||||||
collect_types![
|
collect_types![
|
||||||
get_account,
|
get_accounts,
|
||||||
create_account,
|
create_account,
|
||||||
get_follows,
|
get_follows,
|
||||||
create_follow,
|
create_follow,
|
||||||
create_note,
|
create_note,
|
||||||
get_notes,
|
get_notes,
|
||||||
check_note
|
get_latest_notes,
|
||||||
|
get_note_by_id
|
||||||
],
|
],
|
||||||
"../src/utils/bindings.ts",
|
"../src/utils/bindings.ts",
|
||||||
)
|
)
|
||||||
@ -196,13 +235,15 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
get_account,
|
get_accounts,
|
||||||
create_account,
|
create_account,
|
||||||
get_follows,
|
get_follows,
|
||||||
create_follow,
|
create_follow,
|
||||||
create_note,
|
create_note,
|
||||||
get_notes,
|
get_notes,
|
||||||
check_note
|
get_latest_notes,
|
||||||
|
get_note_by_id,
|
||||||
|
count_total_notes
|
||||||
])
|
])
|
||||||
.manage(Arc::new(db))
|
.manage(Arc::new(db))
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
|
@ -2,8 +2,7 @@ import { RelayContext } from '@components/relaysProvider';
|
|||||||
|
|
||||||
import { DEFAULT_AVATAR } from '@stores/constants';
|
import { DEFAULT_AVATAR } from '@stores/constants';
|
||||||
|
|
||||||
import { createFollows } from '@utils/storage';
|
import { fetchMetadata } from '@utils/metadata';
|
||||||
import { tagsToArray } from '@utils/transform';
|
|
||||||
|
|
||||||
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';
|
||||||
@ -11,7 +10,7 @@ import { writeText } from '@tauri-apps/api/clipboard';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
import { memo, useContext, useEffect } from 'react';
|
import { memo, useCallback, useContext, useEffect } from 'react';
|
||||||
|
|
||||||
export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }) {
|
export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }) {
|
||||||
const [pool, relays]: any = useContext(RelayContext);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
@ -27,6 +26,21 @@ export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }
|
|||||||
await writeText(nip19.npubEncode(user.id));
|
await writeText(nip19.npubEncode(user.id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const insertFollowsToStorage = useCallback(
|
||||||
|
async (tags) => {
|
||||||
|
const { createFollow } = await import('@utils/bindings');
|
||||||
|
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
|
||||||
|
|
||||||
|
for (const tag of tags) {
|
||||||
|
const metadata: any = await fetchMetadata(tag[1], pool, relays);
|
||||||
|
createFollow({ pubkey: tag[1], kind: 0, metadata: metadata.content, account_id: activeAccount.id }).catch(
|
||||||
|
console.error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[pool, relays]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = pool.subscribe(
|
const unsubscribe = pool.subscribe(
|
||||||
[
|
[
|
||||||
@ -38,7 +52,7 @@ export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }
|
|||||||
relays,
|
relays,
|
||||||
(event: any) => {
|
(event: any) => {
|
||||||
if (event.tags.length > 0) {
|
if (event.tags.length > 0) {
|
||||||
createFollows(tagsToArray(event.tags), user.id, 0);
|
insertFollowsToStorage(event.tags);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
@ -51,7 +65,7 @@ export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }
|
|||||||
return () => {
|
return () => {
|
||||||
unsubscribe;
|
unsubscribe;
|
||||||
};
|
};
|
||||||
}, [pool, relays, user.id]);
|
}, [insertFollowsToStorage, pool, relays, user.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
|
@ -1,41 +1,37 @@
|
|||||||
import { ActiveAccount } from '@components/multiAccounts/activeAccount';
|
import { ActiveAccount } from '@components/multiAccounts/activeAccount';
|
||||||
import { InactiveAccount } from '@components/multiAccounts/inactiveAccount';
|
import { InactiveAccount } from '@components/multiAccounts/inactiveAccount';
|
||||||
|
|
||||||
import { activeAccountAtom } from '@stores/account';
|
|
||||||
import { APP_VERSION } from '@stores/constants';
|
import { APP_VERSION } from '@stores/constants';
|
||||||
|
|
||||||
import { getAccounts } from '@utils/storage';
|
|
||||||
|
|
||||||
import LumeSymbol from '@assets/icons/Lume';
|
import LumeSymbol from '@assets/icons/Lume';
|
||||||
|
|
||||||
import { PlusIcon } from '@radix-ui/react-icons';
|
import { PlusIcon } from '@radix-ui/react-icons';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export default function MultiAccounts() {
|
export default function MultiAccounts() {
|
||||||
const activeAccount: any = useAtomValue(activeAccountAtom);
|
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
const renderAccount = useCallback(
|
const renderAccount = useCallback((user: { id: string }) => {
|
||||||
(user: { id: string }) => {
|
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
|
||||||
|
|
||||||
if (user.id === activeAccount.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} />;
|
||||||
}
|
}
|
||||||
},
|
}, []);
|
||||||
[activeAccount.id]
|
|
||||||
);
|
const fetchAccounts = useCallback(async () => {
|
||||||
|
const { getAccounts } = await import('@utils/bindings');
|
||||||
|
const accounts = await getAccounts();
|
||||||
|
// update state
|
||||||
|
setUsers(accounts);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAccount = async () => {
|
fetchAccounts().catch(console.error);
|
||||||
const result: any = await getAccounts();
|
}, [fetchAccounts]);
|
||||||
setUsers(result);
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchAccount().catch(console.error);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-between px-2 pb-4 pt-3">
|
<div className="flex h-full flex-col items-center justify-between px-2 pb-4 pt-3">
|
||||||
|
@ -63,13 +63,13 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
|
|||||||
|
|
||||||
const getParent = useMemo(() => {
|
const getParent = useMemo(() => {
|
||||||
if (event.parent_id) {
|
if (event.parent_id) {
|
||||||
if (event.parent_id !== event.id && !event.content.includes('#[0]')) {
|
if (event.parent_id !== event.eventId && !event.content.includes('#[0]')) {
|
||||||
return <NoteParent id={event.parent_id} />;
|
return <NoteParent id={event.parent_id} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}, [event.content, event.id, event.parent_id]);
|
}, [event.content, event.eventId, event.parent_id]);
|
||||||
|
|
||||||
const openThread = (e) => {
|
const openThread = (e) => {
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
@ -87,7 +87,7 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
|
|||||||
>
|
>
|
||||||
<>{getParent}</>
|
<>{getParent}</>
|
||||||
<div className="relative z-10 flex flex-col">
|
<div className="relative z-10 flex flex-col">
|
||||||
<UserExtend pubkey={event.pubkey} time={event.created_at} />
|
<UserExtend pubkey={event.pubkey} time={event.createdAt || event.created_at} />
|
||||||
<div className="-mt-5 pl-[52px]">
|
<div className="-mt-5 pl-[52px]">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
||||||
@ -97,10 +97,10 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
|
|||||||
</div>
|
</div>
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
||||||
<NoteMetadata
|
<NoteMetadata
|
||||||
eventID={event.id}
|
eventID={event.eventId}
|
||||||
eventPubkey={event.pubkey}
|
eventPubkey={event.pubkey}
|
||||||
eventContent={event.content}
|
eventContent={event.content}
|
||||||
eventTime={event.created_at}
|
eventTime={event.createdAt || event.created_at}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,7 +60,7 @@ export const NoteComment = memo(function NoteComment({ event }: { event: any })
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20">
|
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 px-3 py-5 hover:bg-black/20">
|
||||||
<div className="relative z-10 flex flex-col">
|
<div className="relative z-10 flex flex-col">
|
||||||
<UserExtend pubkey={event.pubkey} time={event.created_at} />
|
<UserExtend pubkey={event.pubkey} time={event.createdAt || event.created_at} />
|
||||||
<div className="-mt-5 pl-[52px]">
|
<div className="-mt-5 pl-[52px]">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
||||||
@ -70,10 +70,10 @@ export const NoteComment = memo(function NoteComment({ event }: { event: any })
|
|||||||
</div>
|
</div>
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
||||||
<NoteMetadata
|
<NoteMetadata
|
||||||
eventID={event.id}
|
eventID={event.eventId}
|
||||||
eventPubkey={event.pubkey}
|
eventPubkey={event.pubkey}
|
||||||
eventContent={event.content}
|
eventContent={event.content}
|
||||||
eventTime={event.created_at}
|
eventTime={event.createdAt || event.created_at}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { RelayContext } from '@components/relaysProvider';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
import { activeAccountAtom, lastLoginAtom } from '@stores/account';
|
import { lastLoginAtom } from '@stores/account';
|
||||||
import { hasNewerNoteAtom } from '@stores/note';
|
import { hasNewerNoteAtom } from '@stores/note';
|
||||||
|
|
||||||
import { dateToUnix } from '@utils/getDate';
|
import { dateToUnix } from '@utils/getDate';
|
||||||
import { createCacheNote, getAllFollowsByID } from '@utils/storage';
|
import { getParentID, pubkeyArray } from '@utils/transform';
|
||||||
import { pubkeyArray } from '@utils/transform';
|
|
||||||
|
|
||||||
import { TauriEvent } from '@tauri-apps/api/event';
|
import { TauriEvent } from '@tauri-apps/api/event';
|
||||||
import { appWindow, getCurrent } from '@tauri-apps/api/window';
|
import { appWindow, getCurrent } from '@tauri-apps/api/window';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export default function NoteConnector() {
|
export default function NoteConnector() {
|
||||||
@ -17,14 +16,18 @@ export default function NoteConnector() {
|
|||||||
|
|
||||||
const setLastLoginAtom = useSetAtom(lastLoginAtom);
|
const setLastLoginAtom = useSetAtom(lastLoginAtom);
|
||||||
const setHasNewerNote = useSetAtom(hasNewerNoteAtom);
|
const setHasNewerNote = useSetAtom(hasNewerNoteAtom);
|
||||||
const activeAccount: any = useAtomValue(activeAccountAtom);
|
|
||||||
|
|
||||||
const [isOnline] = useState(true);
|
const [isOnline] = useState(true);
|
||||||
const now = useRef(new Date());
|
|
||||||
|
|
||||||
const subscribe = useCallback(() => {
|
const now = useRef(new Date());
|
||||||
getAllFollowsByID(activeAccount.id).then((follows) => {
|
const unsubscribe = useRef(null);
|
||||||
pool.subscribe(
|
|
||||||
|
const subscribe = useCallback(async () => {
|
||||||
|
const { createNote } = await import('@utils/bindings');
|
||||||
|
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
|
||||||
|
const follows = JSON.parse(localStorage.getItem('activeAccountFollows'));
|
||||||
|
|
||||||
|
unsubscribe.current = pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
kinds: [1],
|
kinds: [1],
|
||||||
@ -33,14 +36,25 @@ export default function NoteConnector() {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
relays,
|
relays,
|
||||||
(event: any) => {
|
(event) => {
|
||||||
|
const parentID = getParentID(event.tags, event.id);
|
||||||
// insert event to local database
|
// insert event to local database
|
||||||
createCacheNote(event);
|
createNote({
|
||||||
|
event_id: event.id,
|
||||||
|
pubkey: event.pubkey,
|
||||||
|
kind: event.kind,
|
||||||
|
tags: JSON.stringify(event.tags),
|
||||||
|
content: event.content,
|
||||||
|
parent_id: parentID,
|
||||||
|
parent_comment_id: '',
|
||||||
|
created_at: event.created_at,
|
||||||
|
account_id: activeAccount.id,
|
||||||
|
}).catch(console.error);
|
||||||
|
// notify user reload to get newer note
|
||||||
setHasNewerNote(true);
|
setHasNewerNote(true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
}, [pool, relays, setHasNewerNote]);
|
||||||
}, [activeAccount.id, pool, relays, setHasNewerNote]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
subscribe();
|
subscribe();
|
||||||
@ -48,10 +62,13 @@ export default function NoteConnector() {
|
|||||||
setLastLoginAtom(now.current);
|
setLastLoginAtom(now.current);
|
||||||
appWindow.close();
|
appWindow.close();
|
||||||
});
|
});
|
||||||
}, [activeAccount.id, pool, relays, setHasNewerNote, subscribe]);
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe.current;
|
||||||
|
};
|
||||||
|
}, [setHasNewerNote, setLastLoginAtom, subscribe]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<div className="inline-flex items-center gap-1 rounded-md px-1.5 py-1 hover:bg-zinc-900">
|
<div className="inline-flex items-center gap-1 rounded-md px-1.5 py-1 hover:bg-zinc-900">
|
||||||
<span className="relative flex h-1.5 w-1.5">
|
<span className="relative flex h-1.5 w-1.5">
|
||||||
<span
|
<span
|
||||||
@ -65,6 +82,5 @@ export default function NoteConnector() {
|
|||||||
</span>
|
</span>
|
||||||
<p className="text-xs font-medium text-zinc-500">{isOnline ? 'Online' : 'Offline'}</p>
|
<p className="text-xs font-medium text-zinc-500">{isOnline ? 'Online' : 'Offline'}</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ export const NoteExtend = memo(function NoteExtend({ event }: { event: any }) {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col">
|
<div className="relative z-10 flex h-min min-h-min w-full select-text flex-col">
|
||||||
<div className="relative z-10 flex flex-col">
|
<div className="relative z-10 flex flex-col">
|
||||||
<UserLarge pubkey={event.pubkey} time={event.created_at} />
|
<UserLarge pubkey={event.pubkey} time={event.createdAt || event.created_at} />
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
||||||
@ -70,10 +70,10 @@ export const NoteExtend = memo(function NoteExtend({ event }: { event: any }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-5 flex items-center border-b border-t border-zinc-800 py-2">
|
<div className="mt-5 flex items-center border-b border-t border-zinc-800 py-2">
|
||||||
<NoteMetadata
|
<NoteMetadata
|
||||||
eventID={event.id}
|
eventID={event.eventId}
|
||||||
eventPubkey={event.pubkey}
|
eventPubkey={event.pubkey}
|
||||||
eventContent={event.content}
|
eventContent={event.content}
|
||||||
eventTime={event.created_at}
|
eventTime={event.createdAt || event.created_at}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ import { RelayContext } from '@components/relaysProvider';
|
|||||||
import { UserExtend } from '@components/user/extend';
|
import { UserExtend } from '@components/user/extend';
|
||||||
import { UserMention } from '@components/user/mention';
|
import { UserMention } from '@components/user/mention';
|
||||||
|
|
||||||
import { createCacheNote, getNoteByID } from '@utils/storage';
|
import { getParentID } from '@utils/transform';
|
||||||
|
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
@ -18,7 +18,10 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
const [event, setEvent] = useState(null);
|
const [event, setEvent] = useState(null);
|
||||||
const unsubscribe = useRef(null);
|
const unsubscribe = useRef(null);
|
||||||
|
|
||||||
const fetchEvent = useCallback(() => {
|
const fetchEvent = useCallback(async () => {
|
||||||
|
const { createNote } = await import('@utils/bindings');
|
||||||
|
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
|
||||||
|
|
||||||
unsubscribe.current = pool.subscribe(
|
unsubscribe.current = pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -31,7 +34,19 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
// update state
|
// update state
|
||||||
setEvent(event);
|
setEvent(event);
|
||||||
// insert to database
|
// insert to database
|
||||||
createCacheNote(event);
|
const parentID = getParentID(event.tags, event.id);
|
||||||
|
// insert event to local database
|
||||||
|
createNote({
|
||||||
|
event_id: event.id,
|
||||||
|
pubkey: event.pubkey,
|
||||||
|
kind: event.kind,
|
||||||
|
tags: JSON.stringify(event.tags),
|
||||||
|
content: event.content,
|
||||||
|
parent_id: parentID,
|
||||||
|
parent_comment_id: '',
|
||||||
|
created_at: event.created_at,
|
||||||
|
account_id: activeAccount.id,
|
||||||
|
}).catch(console.error);
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
@ -41,19 +56,26 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
);
|
);
|
||||||
}, [id, pool, relays]);
|
}, [id, pool, relays]);
|
||||||
|
|
||||||
useEffect(() => {
|
const checkNoteExist = useCallback(async () => {
|
||||||
getNoteByID(id).then((res) => {
|
const { getNoteById } = await import('@utils/bindings');
|
||||||
|
getNoteById({ event_id: id })
|
||||||
|
.then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
setEvent(res);
|
setEvent(res);
|
||||||
} else {
|
} else {
|
||||||
fetchEvent();
|
fetchEvent();
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}, [fetchEvent, id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkNoteExist();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe.current;
|
unsubscribe.current;
|
||||||
};
|
};
|
||||||
}, [fetchEvent, id]);
|
}, [checkNoteExist]);
|
||||||
|
|
||||||
const content = useMemo(() => {
|
const content = useMemo(() => {
|
||||||
let parsedContent = event ? event.content : null;
|
let parsedContent = event ? event.content : null;
|
||||||
@ -110,7 +132,7 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
<div className="relative pb-5">
|
<div className="relative pb-5">
|
||||||
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
|
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
|
||||||
<div className="relative z-10 flex flex-col">
|
<div className="relative z-10 flex flex-col">
|
||||||
<UserExtend pubkey={event.pubkey} time={event.created_at} />
|
<UserExtend pubkey={event.pubkey} time={event.createdAt || event.created_at} />
|
||||||
<div className="-mt-5 pl-[52px]">
|
<div className="-mt-5 pl-[52px]">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
||||||
@ -120,10 +142,10 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
</div>
|
</div>
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
||||||
<NoteMetadata
|
<NoteMetadata
|
||||||
eventID={event.id}
|
eventID={event.eventId}
|
||||||
eventPubkey={event.pubkey}
|
eventPubkey={event.pubkey}
|
||||||
eventContent={event.content}
|
eventContent={event.content}
|
||||||
eventTime={event.created_at}
|
eventTime={event.createdAt || event.created_at}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@ import { RelayContext } from '@components/relaysProvider';
|
|||||||
import { UserExtend } from '@components/user/extend';
|
import { UserExtend } from '@components/user/extend';
|
||||||
import { UserMention } from '@components/user/mention';
|
import { UserMention } from '@components/user/mention';
|
||||||
|
|
||||||
import { createCacheNote, getNoteByID } from '@utils/storage';
|
import { getParentID } from '@utils/transform';
|
||||||
|
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
@ -14,7 +14,10 @@ export const NoteRepost = memo(function NoteRepost({ id }: { id: string }) {
|
|||||||
const [event, setEvent] = useState(null);
|
const [event, setEvent] = useState(null);
|
||||||
const unsubscribe = useRef(null);
|
const unsubscribe = useRef(null);
|
||||||
|
|
||||||
const fetchEvent = useCallback(() => {
|
const fetchEvent = useCallback(async () => {
|
||||||
|
const { createNote } = await import('@utils/bindings');
|
||||||
|
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
|
||||||
|
|
||||||
unsubscribe.current = pool.subscribe(
|
unsubscribe.current = pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -27,7 +30,19 @@ export const NoteRepost = memo(function NoteRepost({ id }: { id: string }) {
|
|||||||
// update state
|
// update state
|
||||||
setEvent(event);
|
setEvent(event);
|
||||||
// insert to database
|
// insert to database
|
||||||
createCacheNote(event);
|
const parentID = getParentID(event.tags, event.id);
|
||||||
|
// insert event to local database
|
||||||
|
createNote({
|
||||||
|
event_id: event.id,
|
||||||
|
pubkey: event.pubkey,
|
||||||
|
kind: event.kind,
|
||||||
|
tags: JSON.stringify(event.tags),
|
||||||
|
content: event.content,
|
||||||
|
parent_id: parentID,
|
||||||
|
parent_comment_id: '',
|
||||||
|
created_at: event.created_at,
|
||||||
|
account_id: activeAccount.id,
|
||||||
|
}).catch(console.error);
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
@ -37,19 +52,26 @@ export const NoteRepost = memo(function NoteRepost({ id }: { id: string }) {
|
|||||||
);
|
);
|
||||||
}, [id, pool, relays]);
|
}, [id, pool, relays]);
|
||||||
|
|
||||||
useEffect(() => {
|
const checkNoteExist = useCallback(async () => {
|
||||||
getNoteByID(id).then((res) => {
|
const { getNoteById } = await import('@utils/bindings');
|
||||||
|
getNoteById({ event_id: id })
|
||||||
|
.then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
setEvent(res);
|
setEvent(res);
|
||||||
} else {
|
} else {
|
||||||
fetchEvent();
|
fetchEvent();
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}, [fetchEvent, id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkNoteExist();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe.current;
|
unsubscribe.current;
|
||||||
};
|
};
|
||||||
}, [fetchEvent, id]);
|
}, [checkNoteExist]);
|
||||||
|
|
||||||
const content = useMemo(() => {
|
const content = useMemo(() => {
|
||||||
let parsedContent = event ? event.content : null;
|
let parsedContent = event ? event.content : null;
|
||||||
@ -89,7 +111,7 @@ export const NoteRepost = memo(function NoteRepost({ id }: { id: string }) {
|
|||||||
return (
|
return (
|
||||||
<div className="relative mb-2 mt-3 rounded-lg border border-zinc-700 bg-zinc-800 p-2 py-3">
|
<div className="relative mb-2 mt-3 rounded-lg border border-zinc-700 bg-zinc-800 p-2 py-3">
|
||||||
<div className="relative z-10 flex flex-col">
|
<div className="relative z-10 flex flex-col">
|
||||||
<UserExtend pubkey={event.pubkey} time={event.created_at} />
|
<UserExtend pubkey={event.pubkey} time={event.createdAt || event.created_at} />
|
||||||
<div className="-mt-5 pl-[52px]">
|
<div className="-mt-5 pl-[52px]">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
<div className="prose prose-zinc max-w-none break-words text-[15px] leading-tight dark:prose-invert prose-p:m-0 prose-p:text-[15px] prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-img:m-0 prose-video:m-0">
|
||||||
|
@ -6,17 +6,17 @@ import { DEFAULT_AVATAR } from '@stores/constants';
|
|||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { Author } from 'nostr-relaypool';
|
import { Author } from 'nostr-relaypool';
|
||||||
import { memo, useContext, useEffect, useMemo, useState } from 'react';
|
import { memo, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const UserBase = memo(function UserBase({ pubkey }: { pubkey: string }) {
|
export const UserBase = memo(function UserBase({ pubkey }: { pubkey: string }) {
|
||||||
const [pool, relays]: any = useContext(RelayContext);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
const user = useMemo(() => new Author(pool, relays, pubkey), [pubkey, pool, relays]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const user = new Author(pool, relays, pubkey);
|
||||||
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
||||||
}, [user]);
|
}, [pool, relays, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
@ -10,16 +10,15 @@ 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 { Author } from 'nostr-relaypool';
|
import { Author } from 'nostr-relaypool';
|
||||||
import { memo, useContext, useEffect, useMemo, useState } from 'react';
|
import { memo, useContext, 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 [pool, relays]: any = useContext(RelayContext);
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
const user = useMemo(() => new Author(pool, relays, pubkey), [pubkey, pool, relays]);
|
|
||||||
|
|
||||||
const openUserPage = (e) => {
|
const openUserPage = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -27,8 +26,9 @@ export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: s
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const user = new Author(pool, relays, pubkey);
|
||||||
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
||||||
}, [user]);
|
}, [pool, relays, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group flex items-start gap-2">
|
<div className="group flex items-start gap-2">
|
||||||
|
@ -1,33 +1,22 @@
|
|||||||
import { ImageWithFallback } from '@components/imageWithFallback';
|
import { ImageWithFallback } from '@components/imageWithFallback';
|
||||||
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
import { DEFAULT_AVATAR } from '@stores/constants';
|
import { DEFAULT_AVATAR } from '@stores/constants';
|
||||||
|
|
||||||
import { createCacheProfile } from '@utils/storage';
|
|
||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { fetch } from '@tauri-apps/api/http';
|
import { Author } from 'nostr-relaypool';
|
||||||
import destr from 'destr';
|
import { memo, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { memo, useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
export const UserFollow = memo(function UserFollow({ pubkey }: { pubkey: string }) {
|
export const UserFollow = memo(function UserFollow({ pubkey }: { pubkey: string }) {
|
||||||
const [profile, setProfile] = useState(null);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const fetchProfile = useCallback(async (id: string) => {
|
const [profile, setProfile] = useState(null);
|
||||||
const res = await fetch(`https://rbr.bio/${id}/metadata.json`, {
|
const user = useMemo(() => new Author(pool, relays, pubkey), [pubkey, pool, relays]);
|
||||||
method: 'GET',
|
|
||||||
timeout: 30,
|
|
||||||
});
|
|
||||||
return res.data;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchProfile(pubkey)
|
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
||||||
.then((res: any) => {
|
}, [user]);
|
||||||
setProfile(destr(res.content));
|
|
||||||
createCacheProfile(res.pubkey, res.content);
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}, [fetchProfile, pubkey]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
@ -9,19 +9,18 @@ import { DotsHorizontalIcon } from '@radix-ui/react-icons';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import { Author } from 'nostr-relaypool';
|
import { Author } from 'nostr-relaypool';
|
||||||
import { memo, useContext, useEffect, useMemo, useState } from 'react';
|
import { memo, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
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 [pool, relays]: any = useContext(RelayContext);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
const user = useMemo(() => new Author(pool, relays, pubkey), [pubkey, pool, relays]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const user = new Author(pool, relays, pubkey);
|
||||||
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
||||||
}, [user]);
|
}, [pool, relays, pubkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
@ -3,17 +3,16 @@ import { RelayContext } from '@components/relaysProvider';
|
|||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { Author } from 'nostr-relaypool';
|
import { Author } from 'nostr-relaypool';
|
||||||
import { memo, useContext, useEffect, useMemo, useState } from 'react';
|
import { memo, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const UserMention = memo(function UserMention({ pubkey }: { pubkey: string }) {
|
export const UserMention = memo(function UserMention({ pubkey }: { pubkey: string }) {
|
||||||
const [pool, relays]: any = useContext(RelayContext);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
const user = useMemo(() => new Author(pool, relays, pubkey), [pubkey, pool, relays]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const user = new Author(pool, relays, pubkey);
|
||||||
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
||||||
}, [user]);
|
}, [pool, relays, 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>;
|
||||||
});
|
});
|
||||||
|
@ -6,17 +6,16 @@ import { DEFAULT_AVATAR } from '@stores/constants';
|
|||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { Author } from 'nostr-relaypool';
|
import { Author } from 'nostr-relaypool';
|
||||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const UserMini = ({ pubkey }: { pubkey: string }) => {
|
export const UserMini = ({ pubkey }: { pubkey: string }) => {
|
||||||
const [pool, relays]: any = useContext(RelayContext);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const [profile, setProfile] = useState(null);
|
const [profile, setProfile] = useState(null);
|
||||||
const user = useMemo(() => new Author(pool, relays, pubkey), [pubkey, pool, relays]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const user = new Author(pool, relays, pubkey);
|
||||||
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
user.metaData((res) => setProfile(JSON.parse(res.content)), 0);
|
||||||
}, [user]);
|
}, [pool, relays, pubkey]);
|
||||||
|
|
||||||
if (profile) {
|
if (profile) {
|
||||||
return (
|
return (
|
||||||
|
@ -14,8 +14,8 @@ export default function Page() {
|
|||||||
const setActiveAccountFollows = useSetAtom(activeAccountFollowsAtom);
|
const setActiveAccountFollows = useSetAtom(activeAccountFollowsAtom);
|
||||||
|
|
||||||
const fetchActiveAccount = useCallback(async () => {
|
const fetchActiveAccount = useCallback(async () => {
|
||||||
const { getAccount } = await import('@utils/bindings');
|
const { getAccounts } = await import('@utils/bindings');
|
||||||
return await getAccount();
|
return await getAccounts();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const fetchFollowsByAccount = useCallback(async (id) => {
|
const fetchFollowsByAccount = useCallback(async (id) => {
|
||||||
|
@ -2,14 +2,12 @@ import BaseLayout from '@layouts/base';
|
|||||||
|
|
||||||
import { RelayContext } from '@components/relaysProvider';
|
import { RelayContext } from '@components/relaysProvider';
|
||||||
|
|
||||||
import { activeAccountAtom, activeAccountFollowsAtom, lastLoginAtom } from '@stores/account';
|
|
||||||
|
|
||||||
import { dateToUnix, hoursAgo } from '@utils/getDate';
|
import { dateToUnix, hoursAgo } from '@utils/getDate';
|
||||||
import { getParentID, pubkeyArray } from '@utils/transform';
|
import { getParentID, pubkeyArray } from '@utils/transform';
|
||||||
|
|
||||||
import LumeSymbol from '@assets/icons/Lume';
|
import LumeSymbol from '@assets/icons/Lume';
|
||||||
|
|
||||||
import { useAtomValue } from 'jotai';
|
import { invoke } from '@tauri-apps/api/tauri';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import {
|
import {
|
||||||
JSXElementConstructor,
|
JSXElementConstructor,
|
||||||
@ -27,23 +25,22 @@ export default function Page() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [pool, relays]: any = useContext(RelayContext);
|
const [pool, relays]: any = useContext(RelayContext);
|
||||||
|
|
||||||
const activeAccount: any = useAtomValue(activeAccountAtom);
|
|
||||||
const activeAccountFollows: any = useAtomValue(activeAccountFollowsAtom);
|
|
||||||
const lastLogin: any = useAtomValue(lastLoginAtom);
|
|
||||||
|
|
||||||
const now = useRef(new Date());
|
const now = useRef(new Date());
|
||||||
const unsubscribe = useRef(null);
|
const unsubscribe = useRef(null);
|
||||||
|
|
||||||
const [eose, setEose] = useState(false);
|
const [eose, setEose] = useState(false);
|
||||||
|
|
||||||
const fetchData = useCallback(
|
const fetchData = useCallback(
|
||||||
async (since) => {
|
async (since: Date) => {
|
||||||
const { createNote } = await import('@utils/bindings');
|
const { createNote } = await import('@utils/bindings');
|
||||||
|
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
|
||||||
|
const follows = JSON.parse(localStorage.getItem('activeAccountFollows'));
|
||||||
|
|
||||||
unsubscribe.current = pool.subscribe(
|
unsubscribe.current = pool.subscribe(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
kinds: [1],
|
kinds: [1],
|
||||||
authors: pubkeyArray(activeAccountFollows),
|
authors: pubkeyArray(follows),
|
||||||
since: dateToUnix(since),
|
since: dateToUnix(since),
|
||||||
until: dateToUnix(now.current),
|
until: dateToUnix(now.current),
|
||||||
},
|
},
|
||||||
@ -59,7 +56,8 @@ export default function Page() {
|
|||||||
tags: JSON.stringify(event.tags),
|
tags: JSON.stringify(event.tags),
|
||||||
content: event.content,
|
content: event.content,
|
||||||
parent_id: parentID,
|
parent_id: parentID,
|
||||||
parent_comment_id: 'aaa',
|
parent_comment_id: '',
|
||||||
|
created_at: event.created_at,
|
||||||
account_id: activeAccount.id,
|
account_id: activeAccount.id,
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
},
|
},
|
||||||
@ -69,22 +67,20 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[activeAccount.id, activeAccountFollows, pool, relays]
|
[pool, relays]
|
||||||
);
|
);
|
||||||
|
|
||||||
const isNoteExist = useCallback(async () => {
|
const isNoteExist = useCallback(async () => {
|
||||||
const { checkNote } = await import('@utils/bindings');
|
invoke('count_total_notes').then((res: number) => {
|
||||||
checkNote()
|
if (res > 0) {
|
||||||
.then((res) => {
|
const lastLogin = JSON.parse(localStorage.getItem('lastLogin'));
|
||||||
if (res.length === 5) {
|
|
||||||
const parseDate = new Date(lastLogin);
|
const parseDate = new Date(lastLogin);
|
||||||
fetchData(parseDate);
|
fetchData(parseDate);
|
||||||
} else {
|
} else {
|
||||||
fetchData(hoursAgo(24, now.current));
|
fetchData(hoursAgo(24, now.current));
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.catch(console.error);
|
}, [fetchData]);
|
||||||
}, [fetchData, lastLogin]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (eose === false) {
|
if (eose === false) {
|
||||||
|
@ -8,7 +8,7 @@ import { Placeholder } from '@components/note/placeholder';
|
|||||||
import { hasNewerNoteAtom } from '@stores/note';
|
import { hasNewerNoteAtom } from '@stores/note';
|
||||||
|
|
||||||
import { dateToUnix } from '@utils/getDate';
|
import { dateToUnix } from '@utils/getDate';
|
||||||
import { getLatestNotes, getNotes } from '@utils/storage';
|
import { filteredData } from '@utils/transform';
|
||||||
|
|
||||||
import { ArrowUpIcon } from '@radix-ui/react-icons';
|
import { ArrowUpIcon } from '@radix-ui/react-icons';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
@ -48,23 +48,36 @@ export default function Page() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const initialData = useCallback(async () => {
|
const initialData = useCallback(async () => {
|
||||||
const result: any = await getNotes(dateToUnix(now.current), limit.current, offset.current);
|
const { getNotes } = await import('@utils/bindings');
|
||||||
setData((data) => [...data, ...result]);
|
const result: any = await getNotes({
|
||||||
|
date: dateToUnix(now.current),
|
||||||
|
limit: limit.current,
|
||||||
|
offset: offset.current,
|
||||||
|
});
|
||||||
|
const filteredResult = filteredData(result);
|
||||||
|
setData((data) => [...data, ...filteredResult]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadMore = useCallback(async () => {
|
const loadMore = useCallback(async () => {
|
||||||
|
const { getNotes } = await import('@utils/bindings');
|
||||||
offset.current += limit.current;
|
offset.current += limit.current;
|
||||||
// next query
|
// next query
|
||||||
const result: any = await getNotes(dateToUnix(now.current), limit.current, offset.current);
|
const result: any = await getNotes({
|
||||||
setData((data) => [...data, ...result]);
|
date: dateToUnix(now.current),
|
||||||
|
limit: limit.current,
|
||||||
|
offset: offset.current,
|
||||||
|
});
|
||||||
|
const filteredResult = filteredData(result);
|
||||||
|
setData((data) => [...data, ...filteredResult]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadLatest = useCallback(async () => {
|
const loadLatest = useCallback(async () => {
|
||||||
offset.current += limit.current;
|
const { getLatestNotes } = await import('@utils/bindings');
|
||||||
// next query
|
// next query
|
||||||
const result: any = await getLatestNotes(dateToUnix(now.current));
|
const result: any = await getLatestNotes({ date: dateToUnix(now.current) });
|
||||||
// update data
|
// update data
|
||||||
setData((data) => [...result, ...data]);
|
const filteredResult = filteredData(result);
|
||||||
|
setData((data) => [...data, ...filteredResult]);
|
||||||
// hide newer trigger
|
// hide newer trigger
|
||||||
setHasNewerNote(false);
|
setHasNewerNote(false);
|
||||||
// scroll to top
|
// scroll to top
|
||||||
|
@ -11,4 +11,4 @@ const createMyJsonStorage = () => {
|
|||||||
|
|
||||||
export const activeAccountAtom = atomWithStorage('activeAccount', {}, createMyJsonStorage());
|
export const activeAccountAtom = atomWithStorage('activeAccount', {}, createMyJsonStorage());
|
||||||
export const activeAccountFollowsAtom = atomWithStorage('activeAccountFollows', [], createMyJsonStorage());
|
export const activeAccountFollowsAtom = atomWithStorage('activeAccountFollows', [], createMyJsonStorage());
|
||||||
export const lastLoginAtom = atomWithStorage('lastLoginAtom', [], createMyJsonStorage());
|
export const lastLoginAtom = atomWithStorage('lastLogin', [], createMyJsonStorage());
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import { isSSR } from '@utils/ssr';
|
|
||||||
import { getAllRelays } from '@utils/storage';
|
|
||||||
|
|
||||||
import { atomWithCache } from 'jotai-cache';
|
|
||||||
|
|
||||||
export const relaysAtom = atomWithCache(async () => {
|
|
||||||
const response = isSSR ? [] : await getAllRelays();
|
|
||||||
return response;
|
|
||||||
});
|
|
@ -8,8 +8,8 @@ declare global {
|
|||||||
|
|
||||||
const invoke = window.__TAURI_INVOKE__;
|
const invoke = window.__TAURI_INVOKE__;
|
||||||
|
|
||||||
export function getAccount() {
|
export function getAccounts() {
|
||||||
return invoke<Account[]>('get_account');
|
return invoke<Account[]>('get_accounts');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAccount(data: CreateAccountData) {
|
export function createAccount(data: CreateAccountData) {
|
||||||
@ -28,15 +28,20 @@ export function createNote(data: CreateNoteData) {
|
|||||||
return invoke<Note>('create_note', { data });
|
return invoke<Note>('create_note', { data });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNotes() {
|
export function getNotes(data: GetNoteData) {
|
||||||
return invoke<Note[]>('get_notes');
|
return invoke<Note[]>('get_notes', { data });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkNote() {
|
export function getLatestNotes(data: GetLatestNoteData) {
|
||||||
return invoke<Note[]>('check_note');
|
return invoke<Note[]>('get_latest_notes', { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNoteById(data: GetNoteByIdData) {
|
||||||
|
return invoke<Note | null>('get_note_by_id', { data });
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetFollowData = { account_id: number };
|
export type GetFollowData = { account_id: number };
|
||||||
|
export type GetNoteByIdData = { event_id: string };
|
||||||
export type Note = {
|
export type Note = {
|
||||||
id: number;
|
id: number;
|
||||||
eventId: string;
|
eventId: string;
|
||||||
@ -46,9 +51,10 @@ export type Note = {
|
|||||||
content: string;
|
content: string;
|
||||||
parent_id: string;
|
parent_id: string;
|
||||||
parent_comment_id: string;
|
parent_comment_id: string;
|
||||||
createdAt: string;
|
createdAt: number;
|
||||||
accountId: number;
|
accountId: number;
|
||||||
};
|
};
|
||||||
|
export type GetNoteData = { date: number; limit: number; offset: number };
|
||||||
export type CreateFollowData = { pubkey: string; kind: number; metadata: string; account_id: number };
|
export type CreateFollowData = { pubkey: string; kind: number; metadata: string; account_id: number };
|
||||||
export type Account = { id: number; pubkey: string; privkey: string; active: boolean; metadata: string };
|
export type Account = { id: number; pubkey: string; privkey: string; active: boolean; metadata: string };
|
||||||
export type CreateNoteData = {
|
export type CreateNoteData = {
|
||||||
@ -59,7 +65,9 @@ export type CreateNoteData = {
|
|||||||
content: string;
|
content: string;
|
||||||
parent_id: string;
|
parent_id: string;
|
||||||
parent_comment_id: string;
|
parent_comment_id: string;
|
||||||
|
created_at: number;
|
||||||
account_id: number;
|
account_id: number;
|
||||||
};
|
};
|
||||||
|
export type GetLatestNoteData = { date: number };
|
||||||
export type CreateAccountData = { pubkey: string; privkey: string; metadata: string };
|
export type CreateAccountData = { pubkey: string; privkey: string; metadata: string };
|
||||||
export type Follow = { id: number; pubkey: string; kind: number; metadata: string; accountId: number };
|
export type Follow = { id: number; pubkey: string; kind: number; metadata: string; accountId: number };
|
||||||
|
@ -45,3 +45,16 @@ export const getParentID = (arr, fallback) => {
|
|||||||
|
|
||||||
return parentID;
|
return parentID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const filteredData = (obj) => {
|
||||||
|
const filteredArr = obj.reduce((item, current) => {
|
||||||
|
const x = item.find((item) => item.parent_id === current.parent_id);
|
||||||
|
if (!x) {
|
||||||
|
return item.concat([current]);
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return filteredArr;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user