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)} />
+
+
+
+
+
+
+ {user?.displayName || user?.name || 'No name'}
+
+
+ {user?.nip05 || shortenKey(params.content)}
+
+
+
+
+ {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 (