wip: refactor

This commit is contained in:
Ren Amamiya
2023-08-17 15:11:40 +07:00
parent ab61bfb2cd
commit 414dd50a5c
33 changed files with 958 additions and 648 deletions

View File

@@ -1,92 +1,112 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useCallback, useEffect, useRef } from 'react';
import { useCallback, useMemo, useRef } from 'react';
import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
import { NoteKind_1, NoteKind_1063, NoteThread, Repost } from '@shared/notes';
import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { NoteSkeleton } from '@shared/notes/skeleton';
import { TitleBar } from '@shared/titleBar';
import { LumeEvent, Widget } from '@utils/types';
import { DBEvent, Widget } from '@utils/types';
export function FeedBlock({ params }: { params: Widget }) {
const { status, data, fetchNextPage, hasNextPage, isFetchingNextPage } =
export function FeedWidget({ params }: { params: Widget }) {
const { db } = useStorage();
const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ['newsfeed', params.content],
queryFn: async () => {
return { data: [], nextCursor: 0 };
queryFn: async ({ pageParam = 0 }) => {
const authors = JSON.parse(params.content);
return await db.getAllEventsByAuthors(authors, 20, pageParam);
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
const notes = data ? data.pages.flatMap((d: { data: LumeEvent[] }) => d.data) : [];
const parentRef = useRef();
const rowVirtualizer = useVirtualizer({
count: hasNextPage ? notes.length + 1 : notes.length,
const dbEvents = useMemo(
() => (data ? data.pages.flatMap((d: { data: DBEvent[] }) => d.data) : []),
[data]
);
const parentRef = useRef<HTMLDivElement>();
const virtualizer = useVirtualizer({
count: hasNextPage ? dbEvents.length : dbEvents.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 500,
overscan: 2,
estimateSize: () => 650,
overscan: 4,
});
const items = virtualizer.getVirtualItems();
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
const totalSize = rowVirtualizer.getTotalSize();
useEffect(() => {
const [lastItem] = [...rowVirtualizer.getVirtualItems()].reverse();
if (!lastItem) {
return;
}
if (lastItem.index >= notes.length - 1 && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}, [notes.length, fetchNextPage, rowVirtualizer.getVirtualItems()]);
// render event match event kind
const renderItem = useCallback(
(index: string | number) => {
const note: LumeEvent = notes[index];
if (!note) return;
switch (note.kind) {
const dbEvent: DBEvent = dbEvents[index];
if (!dbEvent) return;
const event: NDKEvent = JSON.parse(dbEvent.event as string);
switch (event.kind) {
case 1: {
const root = note.tags.find((el) => el[3] === 'root')?.[1];
const reply = note.tags.find((el) => el[3] === 'reply')?.[1];
if (root || reply) {
if (dbEvent.root_id || dbEvent.reply_id) {
return (
<div key={note.id} data-index={index} ref={rowVirtualizer.measureElement}>
<NoteThread event={note} root={root} reply={reply} />
<div
key={(dbEvent.root_id || dbEvent.reply_id) + dbEvent.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<NoteThread
event={event}
root={dbEvent.root_id}
reply={dbEvent.reply_id}
/>
</div>
);
} else {
return (
<div key={note.id} data-index={index} ref={rowVirtualizer.measureElement}>
<NoteKind_1 event={note} skipMetadata={false} />
<div
key={dbEvent.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<NoteKind_1 event={event} skipMetadata={false} />
</div>
);
}
}
case 6:
return (
<div key={note.id} data-index={index} ref={rowVirtualizer.measureElement}>
<Repost key={note.id} event={note} />
<div
key={dbEvent.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<Repost key={dbEvent.id} event={event} />
</div>
);
case 1063:
return (
<div key={note.id} data-index={index} ref={rowVirtualizer.measureElement}>
<NoteKind_1063 key={note.id} event={note} />
<div
key={dbEvent.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<NoteKind_1063 key={dbEvent.id} event={event} />
</div>
);
default:
return (
<div key={note.id} data-index={index} ref={rowVirtualizer.measureElement}>
<NoteKindUnsupport key={note.id} event={note} />
<div
key={dbEvent.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<NoteKindUnsupport key={dbEvent.id} event={event} />
</div>
);
}
},
[notes]
[dbEvents]
);
return (
@@ -99,32 +119,31 @@ export function FeedBlock({ params }: { params: Widget }) {
<NoteSkeleton />
</div>
</div>
) : itemsVirtualizer.length === 0 ? (
) : items.length === 0 ? (
<div className="px-3 py-1.5">
<div className="bbg-white/10 rounded-xl px-3 py-6">
<div className="flex flex-col items-center gap-4">
<p className="text-center text-sm text-white">
Not found any posts from last 48 hours
Not found any postrs from last 48 hours
</p>
</div>
</div>
</div>
) : (
<div
className="relative w-full"
style={{
height: `${totalSize}px`,
position: 'relative',
width: '100%',
height: `${virtualizer.getTotalSize()}px`,
}}
>
<div
className="absolute left-0 top-0 w-full"
style={{
transform: `translateY(${
itemsVirtualizer[0].start - rowVirtualizer.options.scrollMargin
}px)`,
transform: `translateY(${items[0].start}px)`,
}}
>
{itemsVirtualizer.map((virtualRow) => renderItem(virtualRow.index))}
{items.map((item) => renderItem(item.index))}
</div>
</div>
)}
@@ -135,6 +154,33 @@ export function FeedBlock({ params }: { params: Widget }) {
</div>
</div>
)}
<div className="px-3 py-1.5">
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
className="inline-flex h-11 w-full items-center justify-between gap-2 rounded-lg bg-fuchsia-500 px-6 font-medium leading-none text-white hover:bg-fuchsia-600 focus:outline-none"
>
{isFetchingNextPage ? (
<>
<span className="w-5" />
<span>Loading...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : hasNextPage ? (
<>
<span className="w-5" />
<span>Load more</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
) : (
<>
<span className="w-5" />
<span>Nothing more to load</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
)}
</button>
</div>
</div>
</div>
);

View File

@@ -1,35 +1,74 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useRef } from 'react';
import { useCallback, useRef } from 'react';
import { useNDK } from '@libs/ndk/provider';
import { NoteKind_1, NoteSkeleton } from '@shared/notes';
import { NoteKind_1, NoteSkeleton, Repost } from '@shared/notes';
import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { TitleBar } from '@shared/titleBar';
import { nHoursAgo } from '@utils/date';
import { LumeEvent, Widget } from '@utils/types';
import { Widget } from '@utils/types';
export function HashtagBlock({ params }: { params: Widget }) {
export function HashtagWidget({ params }: { params: Widget }) {
const { ndk } = useNDK();
const { status, data } = useQuery(['hashtag', params.content], async () => {
const { status, data } = useQuery(['hashtag-widget', params.content], async () => {
const events = await ndk.fetchEvents({
kinds: [1],
'#t': [params.content],
since: nHoursAgo(24),
});
return [...events] as unknown as LumeEvent[];
return [...events] as unknown as NDKEvent[];
});
const parentRef = useRef();
const rowVirtualizer = useVirtualizer({
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: data ? data.length : 0,
getScrollElement: () => parentRef.current,
estimateSize: () => 400,
estimateSize: () => 650,
overscan: 4,
});
const items = virtualizer.getVirtualItems();
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
const totalSize = rowVirtualizer.getTotalSize();
// render event match event kind
const renderItem = useCallback(
(index: string | number) => {
const event: NDKEvent = data[index];
if (!event) return;
switch (event.kind) {
case 1:
return (
<div key={event.id} data-index={index} ref={virtualizer.measureElement}>
<NoteKind_1 event={event} skipMetadata={false} />
</div>
);
case 6:
return (
<div
key={event.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<Repost key={event.id} event={event} />
</div>
);
default:
return (
<div
key={event.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<NoteKindUnsupport key={event.id} event={event} />
</div>
);
}
},
[data]
);
return (
<div className="relative w-[400px] shrink-0 bg-white/10">
@@ -41,40 +80,31 @@ export function HashtagBlock({ params }: { params: Widget }) {
<NoteSkeleton />
</div>
</div>
) : itemsVirtualizer.length === 0 ? (
) : items.length === 0 ? (
<div className="px-3 py-1.5">
<div className="rounded-xl bg-white/10 px-3 py-6">
<div className="flex flex-col items-center gap-4">
<p className="text-center text-sm font-medium text-white">
No new posts about this hashtag in 24 hours ago
No new postrs about this hashtag in 24 hours ago
</p>
</div>
</div>
</div>
) : (
<div
className="relative w-full"
style={{
height: `${totalSize}px`,
position: 'relative',
width: '100%',
height: `${virtualizer.getTotalSize()}px`,
}}
>
<div
className="absolute left-0 top-0 w-full"
style={{
transform: `translateY(${
itemsVirtualizer[0].start - rowVirtualizer.options.scrollMargin
}px)`,
transform: `translateY(${items[0].start}px)`,
}}
>
{itemsVirtualizer.map((virtualRow) => (
<div
key={virtualRow.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}
>
<NoteKind_1 event={data[virtualRow.index]} />
</div>
))}
{items.map((item) => renderItem(item.index))}
</div>
</div>
)}

View File

@@ -1,4 +1,4 @@
import { NDKFilter } from '@nostr-dev-kit/ndk';
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useCallback, useEffect, useMemo, useRef } from 'react';
@@ -6,85 +6,75 @@ import { Link } from 'react-router-dom';
import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
import { NoteKind_1, NoteKind_1063, NoteThread, Repost } from '@shared/notes';
import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { NoteSkeleton } from '@shared/notes/skeleton';
import { TitleBar } from '@shared/titleBar';
import { useNostr } from '@utils/hooks/useNostr';
import { LumeEvent } from '@utils/types';
import { DBEvent } from '@utils/types';
export function NetworkBlock() {
export function NetworkWidget() {
const { sub } = useNostr();
const { db } = useStorage();
const { sub, fetchNotes } = useNostr();
const { status, data, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
queryKey: ['network-widget'],
queryFn: async ({ pageParam = 24 }) => {
return { data: [], nextCursor: 0 };
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ['network-widget'],
queryFn: async ({ pageParam = 0 }) => {
return await db.getAllEvents(20, pageParam);
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
});
const parentRef = useRef();
const notes = useMemo(
() => (data ? data.pages.flatMap((d: { data: LumeEvent[] }) => d.data) : []),
const dbEvents = useMemo(
() => (data ? data.pages.flatMap((d: { data: DBEvent[] }) => d.data) : []),
[data]
);
const rowVirtualizer = useVirtualizer({
count: hasNextPage ? notes.length + 1 : notes.length,
const parentRef = useRef<HTMLDivElement>();
const virtualizer = useVirtualizer({
count: hasNextPage ? dbEvents.length : dbEvents.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 500,
overscan: 2,
estimateSize: () => 650,
overscan: 4,
});
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
const totalSize = rowVirtualizer.getTotalSize();
useEffect(() => {
const since = Math.floor(Date.now() / 1000);
const filter: NDKFilter = {
kinds: [1, 6],
authors: db.account.network,
since: since,
};
sub(filter, (event) => console.log('[network] event received: ', event));
}, []);
const items = virtualizer.getVirtualItems();
// render event match event kind
const renderItem = useCallback(
(index: string | number) => {
const note: LumeEvent = notes[index];
if (!note) return;
switch (note.kind) {
const dbEvent: DBEvent = dbEvents[index];
if (!dbEvent) return;
const event: NDKEvent = JSON.parse(dbEvent.event as string);
switch (event.kind) {
case 1: {
let root: string;
let reply: string;
if (note.tags?.[0]?.[0] === 'e' && !note.tags?.[0]?.[3]) {
root = note.tags[0][1];
} else {
root = note.tags.find((el) => el[3] === 'root')?.[1];
reply = note.tags.find((el) => el[3] === 'reply')?.[1];
}
if (root || reply) {
if (dbEvent.root_id || dbEvent.reply_id) {
return (
<div
key={(root || reply) + note.id + index}
key={(dbEvent.root_id || dbEvent.reply_id) + dbEvent.id + index}
data-index={index}
ref={rowVirtualizer.measureElement}
ref={virtualizer.measureElement}
>
<NoteThread event={note} root={root} reply={reply} />
<NoteThread
event={event}
root={dbEvent.root_id}
reply={dbEvent.reply_id}
/>
</div>
);
} else {
return (
<div
key={note.id + index}
key={dbEvent.id + index}
data-index={index}
ref={rowVirtualizer.measureElement}
ref={virtualizer.measureElement}
>
<NoteKind_1 event={note} skipMetadata={false} />
<NoteKind_1 event={event} skipMetadata={false} />
</div>
);
}
@@ -92,38 +82,52 @@ export function NetworkBlock() {
case 6:
return (
<div
key={note.id + index}
key={dbEvent.id + index}
data-index={index}
ref={rowVirtualizer.measureElement}
ref={virtualizer.measureElement}
>
<Repost key={note.id} event={note} />
<Repost key={dbEvent.id} event={event} />
</div>
);
case 1063:
return (
<div
key={note.id + index}
key={dbEvent.id + index}
data-index={index}
ref={rowVirtualizer.measureElement}
ref={virtualizer.measureElement}
>
<NoteKind_1063 key={note.id} event={note} />
<NoteKind_1063 key={dbEvent.id} event={event} />
</div>
);
default:
return (
<div
key={note.id + index}
key={dbEvent.id + index}
data-index={index}
ref={rowVirtualizer.measureElement}
ref={virtualizer.measureElement}
>
<NoteKindUnsupport key={note.id} event={note} />
<NoteKindUnsupport key={dbEvent.id} event={event} />
</div>
);
}
},
[notes]
[dbEvents]
);
// subscribe for new event
// sub will be managed by lru-cache
useEffect(() => {
if (db.account && db.account.network) {
const filter: NDKFilter = {
kinds: [1, 6],
authors: db.account.network,
since: Math.floor(Date.now() / 1000),
};
sub(filter, (event) => console.log('[network] event received: ', event.content));
}
}, []);
return (
<div className="relative w-[400px] shrink-0 bg-white/10">
<TitleBar title="Network" />
@@ -134,7 +138,7 @@ export function NetworkBlock() {
<NoteSkeleton />
</div>
</div>
) : itemsVirtualizer.length === 0 ? (
) : dbEvents.length === 0 ? (
<div className="px-3 py-1.5">
<div className="rounded-xl bg-white/10 px-3 py-6">
<div className="flex flex-col items-center gap-4">
@@ -154,30 +158,56 @@ export function NetworkBlock() {
</div>
) : (
<div
className="relative w-full"
style={{
height: `${totalSize}px`,
position: 'relative',
width: '100%',
height: `${virtualizer.getTotalSize()}px`,
}}
>
<div
className="absolute left-0 top-0 w-full"
style={{
transform: `translateY(${
itemsVirtualizer[0].start - rowVirtualizer.options.scrollMargin
}px)`,
transform: `translateY(${items[0].start}px)`,
}}
>
{itemsVirtualizer.map((virtualRow) => renderItem(virtualRow.index))}
{items.map((item) => renderItem(item.index))}
</div>
</div>
)}
{isFetchingNextPage && (
<div className="px-3 py-1.5">
<div className="mb-20 px-3 py-1.5">
<div className="rounded-xl bg-white/10 px-3 py-3">
<NoteSkeleton />
</div>
</div>
)}
<div className="px-3 py-1.5">
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
className="inline-flex h-11 w-full items-center justify-between gap-2 rounded-lg bg-fuchsia-500 px-6 font-medium leading-none text-white hover:bg-fuchsia-600 focus:outline-none"
>
{isFetchingNextPage ? (
<>
<span className="w-5" />
<span>Loading...</span>
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
</>
) : hasNextPage ? (
<>
<span className="w-5" />
<span>Load more</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
) : (
<>
<span className="w-5" />
<span>Nothing more to load</span>
<ArrowRightCircleIcon className="h-5 w-5" />
</>
)}
</button>
</div>
</div>
</div>
);

View File

@@ -1,37 +1,79 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useRef } from 'react';
import { useCallback, useRef } from 'react';
import { useNDK } from '@libs/ndk/provider';
import { NoteKind_1, NoteSkeleton } from '@shared/notes';
import { NoteKind_1, NoteSkeleton, Repost } from '@shared/notes';
import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { TitleBar } from '@shared/titleBar';
import { UserProfile } from '@shared/userProfile';
import { nHoursAgo } from '@utils/date';
import { LumeEvent, Widget } from '@utils/types';
export function UserBlock({ params }: { params: Widget }) {
const parentRef = useRef<HTMLDivElement>(null);
import { DBEvent, Widget } from '@utils/types';
export function UserWidget({ params }: { params: Widget }) {
const { ndk } = useNDK();
const { status, data } = useQuery(['user-feed', params.content], async () => {
const events = await ndk.fetchEvents({
kinds: [1],
authors: [params.content],
since: nHoursAgo(48),
});
return [...events] as unknown as LumeEvent[];
});
const { status, data } = useQuery(
['user-widget', params.content],
async () => {
const events = await ndk.fetchEvents({
kinds: [1],
authors: [params.content],
since: nHoursAgo(24),
});
return [...events] as unknown as DBEvent[];
},
{ refetchOnMount: false, refetchOnReconnect: false, refetchOnWindowFocus: false }
);
const rowVirtualizer = useVirtualizer({
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: data ? data.length : 0,
getScrollElement: () => parentRef.current,
estimateSize: () => 400,
overscan: 2,
estimateSize: () => 650,
overscan: 4,
});
const items = virtualizer.getVirtualItems();
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
// render event match event kind
const renderItem = useCallback(
(index: string | number) => {
const event: NDKEvent = data[index];
if (!event) return;
switch (event.kind) {
case 1:
return (
<div key={event.id} data-index={index} ref={virtualizer.measureElement}>
<NoteKind_1 event={event} skipMetadata={false} />
</div>
);
case 6:
return (
<div
key={event.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<Repost key={event.id} event={event} />
</div>
);
default:
return (
<div
key={event.id + index}
data-index={index}
ref={virtualizer.measureElement}
>
<NoteKindUnsupport key={event.id} event={event} />
</div>
);
}
},
[data]
);
return (
<div className="relative w-[400px] shrink-0 bg-white/10">
@@ -49,41 +91,31 @@ export function UserBlock({ params }: { params: Widget }) {
<NoteSkeleton />
</div>
</div>
) : itemsVirtualizer.length === 0 ? (
) : items.length === 0 ? (
<div className="px-3 py-1.5">
<div className="rounded-xl bg-white/10 px-3 py-6">
<div className="flex flex-col items-center gap-4">
<p className="text-center text-sm text-white">
No new posts from this user in 48 hours ago
No new postr from user in 24 hours ago
</p>
</div>
</div>
</div>
) : (
<div
className="relative w-full"
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
position: 'relative',
width: '100%',
height: `${virtualizer.getTotalSize()}px`,
}}
>
<div
className="absolute left-0 top-0 w-full"
style={{
transform: `translateY(${
itemsVirtualizer[0].start - rowVirtualizer.options.scrollMargin
}px)`,
transform: `translateY(${items[0].start}px)`,
}}
>
{itemsVirtualizer.map((virtualRow) => (
<div
key={virtualRow.key}
data-index={virtualRow.index}
ref={rowVirtualizer.measureElement}
>
<NoteKind_1 event={data[virtualRow.index]} />
</div>
))}
<div className="h-20" />
{items.map((item) => renderItem(item.index))}
</div>
</div>
)}

View File

@@ -3,11 +3,11 @@ import { useCallback, useEffect } from 'react';
import { FeedModal } from '@app/space/components/modals/feed';
import { HashtagModal } from '@app/space/components/modals/hashtag';
import { ImageModal } from '@app/space/components/modals/image';
import { FeedBlock } from '@app/space/components/widgets/feed';
import { HashtagBlock } from '@app/space/components/widgets/hashtag';
import { NetworkBlock } from '@app/space/components/widgets/network';
import { FeedWidget } from '@app/space/components/widgets/feed';
import { HashtagWidget } from '@app/space/components/widgets/hashtag';
import { NetworkWidget } from '@app/space/components/widgets/network';
import { ThreadBlock } from '@app/space/components/widgets/thread';
import { UserBlock } from '@app/space/components/widgets/user';
import { UserWidget } from '@app/space/components/widgets/user';
import { useStorage } from '@libs/storage/provider';
@@ -29,15 +29,15 @@ export function SpaceScreen() {
(widget: Widget) => {
switch (widget.kind) {
case 1:
return <FeedBlock key={widget.id} params={widget} />;
return <FeedWidget key={widget.id} params={widget} />;
case 2:
return <ThreadBlock key={widget.id} params={widget} />;
case 3:
return <HashtagBlock key={widget.id} params={widget} />;
return <HashtagWidget key={widget.id} params={widget} />;
case 5:
return <UserBlock key={widget.id} params={widget} />;
return <UserWidget key={widget.id} params={widget} />;
case 9999:
return <NetworkBlock key={widget.id} />;
return <NetworkWidget key={widget.id} />;
default:
break;
}
@@ -52,7 +52,7 @@ export function SpaceScreen() {
return (
<div className="scrollbar-hide flex h-full w-full flex-nowrap divide-x divide-white/5 overflow-x-auto overflow-y-hidden">
{!widgets ? (
<div className="flex w-[350px] shrink-0 flex-col">
<div className="flex w-[400px] shrink-0 flex-col">
<div className="flex w-full flex-1 items-center justify-center p-3">
<LoaderIcon className="h-5 w-5 animate-spin text-white/10" />
</div>
@@ -60,14 +60,14 @@ export function SpaceScreen() {
) : (
widgets.map((widget) => renderItem(widget))
)}
<div className="flex w-[350px] shrink-0 flex-col">
<div className="flex w-[250px] shrink-0 flex-col">
<div className="inline-flex h-full w-full flex-col items-center justify-center gap-1">
<FeedModal />
<ImageModal />
<HashtagModal />
</div>
</div>
<div className="w-[250px] shrink-0" />
<div className="w-[150px] shrink-0" />
</div>
);
}