diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index bf759896..635927ae 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,7 +16,7 @@ tauri-build = { version = "1.2", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.2", features = [ "path-all", "fs-read-dir", "fs-read-file", "clipboard-read-text", "clipboard-write-text", "dialog-open", "http-all", "http-multipart", "notification-all", "os-all", "process-relaunch", "shell-open", "system-tray", "updater", "window-close", "window-start-dragging"] } +tauri = { version = "1.2", features = [ "window-create", "path-all", "fs-read-dir", "fs-read-file", "clipboard-read-text", "clipboard-write-text", "dialog-open", "http-all", "http-multipart", "notification-all", "os-all", "process-relaunch", "shell-open", "system-tray", "updater", "window-close", "window-start-dragging"] } tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-stronghold = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 048d7fd5..355ea0d6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -62,7 +62,8 @@ }, "window": { "startDragging": true, - "close": true + "close": true, + "create": true }, "process": { "all": false, diff --git a/src/app/space/components/blocks/thread.tsx b/src/app/space/components/blocks/thread.tsx index f1e37821..fcd41fc7 100644 --- a/src/app/space/components/blocks/thread.tsx +++ b/src/app/space/components/blocks/thread.tsx @@ -3,7 +3,13 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; // import { useLiveThread } from '@app/space/hooks/useLiveThread'; import { removeBlock } from '@libs/storage'; -import { NoteContent, NoteStats, ThreadUser } from '@shared/notes'; +import { + NoteActions, + NoteContent, + NoteReplyForm, + NoteStats, + ThreadUser, +} from '@shared/notes'; import { RepliesList } from '@shared/notes/replies/list'; import { NoteSkeleton } from '@shared/notes/skeleton'; import { TitleBar } from '@shared/titleBar'; @@ -46,12 +52,14 @@ export function ThreadBlock({ params }: { params: Block }) {
+
)}
+
diff --git a/src/app/space/components/blocks/user.tsx b/src/app/space/components/blocks/user.tsx new file mode 100644 index 00000000..66a0e227 --- /dev/null +++ b/src/app/space/components/blocks/user.tsx @@ -0,0 +1,141 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; + +import { UserFeed } from '@app/user/components/feed'; +import { UserMetadata } from '@app/user/components/metadata'; + +import { removeBlock } from '@libs/storage'; + +import { ZapIcon } from '@shared/icons'; +import { Image } from '@shared/image'; +import { TitleBar } from '@shared/titleBar'; + +import { DEFAULT_AVATAR } from '@stores/constants'; + +import { useProfile } from '@utils/hooks/useProfile'; +import { useSocial } from '@utils/hooks/useSocial'; +import { shortenKey } from '@utils/shortenKey'; +import { Block } from '@utils/types'; + +export function UserBlock({ params }: { params: Block }) { + const queryClient = useQueryClient(); + + const { user } = useProfile(params.content); + const { status, userFollows, follow, unfollow } = useSocial(); + + const [followed, setFollowed] = useState(false); + + const block = useMutation({ + mutationFn: (id: string) => { + return removeBlock(id); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['blocks'] }); + }, + }); + + const followUser = (pubkey: string) => { + try { + follow(pubkey); + + // update state + setFollowed(true); + } catch (error) { + console.log(error); + } + }; + + const unfollowUser = (pubkey: string) => { + try { + unfollow(pubkey); + + // update state + setFollowed(false); + } catch (error) { + console.log(error); + } + }; + + useEffect(() => { + if (status === 'success' && userFollows) { + if (userFollows.includes(params.content)) { + setFollowed(true); + } + } + }, [status]); + + return ( +
+ block.mutate(params.id)} /> +
+
+ {params.content} +
+
+
+ {user?.displayName || user?.name || 'No name'} +
+ + {user?.nip05 || shortenKey(params.content)} + +
+
+

+ {user?.about} +

+ +
+
+ {status === 'loading' ? ( + + ) : followed ? ( + + ) : ( + + )} + + Message + + +
+
+
+
+

Timeline

+ +
+
+
+ ); +} diff --git a/src/app/space/index.tsx b/src/app/space/index.tsx index a8643542..607ea55f 100644 --- a/src/app/space/index.tsx +++ b/src/app/space/index.tsx @@ -1,6 +1,7 @@ import { useQuery } from '@tanstack/react-query'; import { useCallback } from 'react'; +import { UserBlock } from '@app/space//components/blocks/user'; import { AddBlock } from '@app/space/components/add'; import { FeedBlock } from '@app/space/components/blocks/feed'; import { FollowingBlock } from '@app/space/components/blocks/following'; @@ -43,6 +44,8 @@ export function SpaceScreen() { return ; case 3: return ; + case 5: + return ; default: break; } diff --git a/src/app/user/components/feed.tsx b/src/app/user/components/feed.tsx index f0d1f81d..fb9da7cf 100644 --- a/src/app/user/components/feed.tsx +++ b/src/app/user/components/feed.tsx @@ -1,11 +1,17 @@ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useVirtualizer } from '@tanstack/react-virtual'; +import { useRef } from 'react'; import { useNDK } from '@libs/ndk/provider'; +import { NoteKind_1, NoteSkeleton } from '@shared/notes'; + import { nHoursAgo } from '@utils/date'; import { LumeEvent } from '@utils/types'; export function UserFeed({ pubkey }: { pubkey: string }) { + const parentRef = useRef(); + const { fetcher, relayUrls } = useNDK(); const { status, data } = useQuery(['user-feed', pubkey], async () => { const events = await fetcher.fetchAllEvents( @@ -17,5 +23,63 @@ export function UserFeed({ pubkey }: { pubkey: string }) { return events as unknown as LumeEvent[]; }); - return
; + const rowVirtualizer = useVirtualizer({ + count: data ? data.length : 0, + getScrollElement: () => parentRef.current, + estimateSize: () => 400, + }); + + const itemsVirtualizer = rowVirtualizer.getVirtualItems(); + + return ( +
+ {status === 'loading' ? ( +
+
+ +
+
+ ) : itemsVirtualizer.length === 0 ? ( +
+
+
+

+ No new posts about this hashtag in 48 hours ago +

+
+
+
+ ) : ( +
+
+ {itemsVirtualizer.map((virtualRow) => ( +
+ +
+ ))} +
+
+ )} +
+ ); } diff --git a/src/app/user/components/metadata.tsx b/src/app/user/components/metadata.tsx index 25e0a435..a471e7e9 100644 --- a/src/app/user/components/metadata.tsx +++ b/src/app/user/components/metadata.tsx @@ -19,13 +19,13 @@ export function UserMetadata({ pubkey }: { pubkey: string }) {
- {data.stats[pubkey].followers_pubkey_count ?? 0} + {compactNumber.format(data.stats[pubkey].followers_pubkey_count) ?? 0} Followers
- {data.stats[pubkey].pub_following_pubkey_count ?? 0} + {compactNumber.format(data.stats[pubkey].pub_following_pubkey_count) ?? 0} Following
diff --git a/src/shared/notes/actions.tsx b/src/shared/notes/actions.tsx index 9255850d..b7953fbd 100644 --- a/src/shared/notes/actions.tsx +++ b/src/shared/notes/actions.tsx @@ -11,7 +11,15 @@ import { NoteZap } from '@shared/notes/actions/zap'; import { BLOCK_KINDS } from '@stores/constants'; -export function NoteActions({ id, pubkey }: { id: string; pubkey: string }) { +export function NoteActions({ + id, + pubkey, + noOpenThread, +}: { + id: string; + pubkey: string; + noOpenThread?: boolean; +}) { const queryClient = useQueryClient(); const block = useMutation({ @@ -36,24 +44,28 @@ export function NoteActions({ id, pubkey }: { id: string; pubkey: string }) {
-
- - - - - - - Open thread - - - - + {!noOpenThread && ( + <> +
+ + + + + + + Open thread + + + + + + )}
); diff --git a/src/shared/notes/mentions/note.tsx b/src/shared/notes/mentions/note.tsx index 17f7ab1e..e9f8e60b 100644 --- a/src/shared/notes/mentions/note.tsx +++ b/src/shared/notes/mentions/note.tsx @@ -8,6 +8,8 @@ import { createBlock } from '@libs/storage'; import { MentionUser, NoteSkeleton } from '@shared/notes'; import { User } from '@shared/user'; +import { BLOCK_KINDS } from '@stores/constants'; + import { useEvent } from '@utils/hooks/useEvent'; export const MentionNote = memo(function MentionNote({ id }: { id: string }) { @@ -15,7 +17,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) { const queryClient = useQueryClient(); const block = useMutation({ - mutationFn: (data: any) => { + mutationFn: (data: { kind: number; title: string; content: string }) => { return createBlock(data.kind, data.title, data.content); }, onSuccess: () => { @@ -26,7 +28,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) { const openThread = (event: any, thread: string) => { const selection = window.getSelection(); if (selection.toString().length === 0) { - block.mutate({ kind: 2, title: 'Thread', content: thread }); + block.mutate({ kind: BLOCK_KINDS.thread, title: 'Thread', content: thread }); } else { event.stopPropagation(); } diff --git a/src/shared/notes/mentions/user.tsx b/src/shared/notes/mentions/user.tsx index c0ddf550..540c01e0 100644 --- a/src/shared/notes/mentions/user.tsx +++ b/src/shared/notes/mentions/user.tsx @@ -1,12 +1,37 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { createBlock } from '@libs/storage'; + +import { BLOCK_KINDS } from '@stores/constants'; + import { useProfile } from '@utils/hooks/useProfile'; import { shortenKey } from '@utils/shortenKey'; export function MentionUser({ pubkey }: { pubkey: string }) { const { user } = useProfile(pubkey); + const queryClient = useQueryClient(); + const block = useMutation({ + mutationFn: (data: { kind: number; title: string; content: string }) => { + return createBlock(data.kind, data.title, data.content); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['blocks'] }); + }, + }); + + const openBlock = () => { + block.mutate({ + kind: BLOCK_KINDS.user, + title: user?.name || user?.displayNam, + content: pubkey, + }); + }; + return (