diff --git a/src/app/space/components/button.tsx b/src/app/space/components/button.tsx deleted file mode 100644 index 96bbac9e..00000000 --- a/src/app/space/components/button.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; - -import { useStorage } from '@libs/storage/provider'; - -import { - AddWidgetIcon, - FeedIcon, - FileIcon, - HashtagIcon, - ThreadsIcon, - TrendingIcon, -} from '@shared/icons'; - -import { WidgetKinds, useWidgets } from '@stores/widgets'; - -export function AddWidgetButton() { - const { db } = useStorage(); - const setWidget = useWidgets((state) => state.setWidget); - - const setTrendingProfilesWidget = () => { - setWidget(db, { - kind: WidgetKinds.trendingProfiles, - title: 'Trending Profiles', - content: 'https://api.nostr.band/v0/trending/profiles', - }); - }; - - const setTrendingNotesWidget = () => { - setWidget(db, { - kind: WidgetKinds.trendingNotes, - title: 'Trending Notes', - content: 'https://api.nostr.band/v0/trending/notes', - }); - }; - - const setArticleWidget = () => { - setWidget(db, { - kind: WidgetKinds.article, - title: 'Articles', - content: '', - }); - }; - - const setFileWidget = () => { - setWidget(db, { - kind: WidgetKinds.file, - title: 'Files', - content: '', - }); - }; - - const setHashtagWidget = () => { - setWidget(db, { - kind: WidgetKinds.xhashtag, - title: 'New hashtag', - content: '', - }); - }; - - const setGroupFeedWidget = () => { - setWidget(db, { - kind: WidgetKinds.xfeed, - title: 'New user group feed', - content: '', - }); - }; - - return ( - -
-
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - -
- ); -} diff --git a/src/app/space/components/toggle.tsx b/src/app/space/components/toggle.tsx new file mode 100644 index 00000000..07e32c49 --- /dev/null +++ b/src/app/space/components/toggle.tsx @@ -0,0 +1,25 @@ +import { useStorage } from '@libs/storage/provider'; + +import { PlusIcon } from '@shared/icons'; + +import { WidgetKinds, useWidgets } from '@stores/widgets'; + +export function ToggleWidgetList() { + const { db } = useStorage(); + const setWidget = useWidgets((state) => state.setWidget); + + return ( +
+ +
+ ); +} diff --git a/src/app/space/components/widgetList.tsx b/src/app/space/components/widgetList.tsx new file mode 100644 index 00000000..477e3d46 --- /dev/null +++ b/src/app/space/components/widgetList.tsx @@ -0,0 +1,51 @@ +import { useCallback } from 'react'; + +import { useStorage } from '@libs/storage/provider'; + +import { TitleBar } from '@shared/titleBar'; + +import { DefaultWidgets, useWidgets } from '@stores/widgets'; + +import { Widget, WidgetGroup, WidgetGroupItem } from '@utils/types'; + +export function WidgetList({ params }: { params: Widget }) { + const { db } = useStorage(); + const [setWidget, removeWidget] = useWidgets((state) => [ + state.setWidget, + state.removeWidget, + ]); + + const openWidget = (widget: WidgetGroupItem) => { + setWidget(db, { kind: widget.kind, title: widget.title, content: '' }); + removeWidget(db, params.id); + }; + + const renderItem = useCallback( + (row: WidgetGroup) => { + return ( +
+

{row.title}

+
+ {row.data.map((item, index) => ( + + ))} +
+
+ ); + }, + [DefaultWidgets] + ); + + return ( +
+ +
+ {DefaultWidgets.map((row: WidgetGroup) => renderItem(row))} +
+
+ ); +} diff --git a/src/app/space/index.tsx b/src/app/space/index.tsx index c2be9fe4..b3de3e62 100644 --- a/src/app/space/index.tsx +++ b/src/app/space/index.tsx @@ -1,21 +1,26 @@ import { useCallback, useEffect } from 'react'; -import { AddWidgetButton } from '@app/space/components/button'; -import { FeedWidgetForm } from '@app/space/components/forms/feed'; -import { HashTagWidgetForm } from '@app/space/components/forms/hashtag'; -import { ArticleWidget } from '@app/space/components/widgets/article'; -import { FeedWidget } from '@app/space/components/widgets/feed'; -import { FileWidget } from '@app/space/components/widgets/file'; -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 { TrendingNotesWidget } from '@app/space/components/widgets/trendingNotes'; -import { TrendingProfilesWidget } from '@app/space/components/widgets/trendingProfile'; -import { UserWidget } from '@app/space/components/widgets/user'; +import { ToggleWidgetList } from '@app/space/components/toggle'; +import { WidgetList } from '@app/space/components/widgetList'; import { useStorage } from '@libs/storage/provider'; import { LoaderIcon } from '@shared/icons'; +import { + GlobalArticlesWidget, + GlobalFilesWidget, + GlobalHashtagWidget, + LocalArticlesWidget, + LocalFeedsWidget, + LocalFilesWidget, + LocalNetworkWidget, + LocalThreadWidget, + LocalUserWidget, + TrendingAccountsWidget, + TrendingNotesWidget, + XfeedsWidget, + XhashtagWidget, +} from '@shared/widgets'; import { WidgetKinds, useWidgets } from '@stores/widgets'; @@ -33,28 +38,34 @@ export function SpaceScreen() { (widget: Widget) => { if (!widget) return; switch (widget.kind) { - case WidgetKinds.feed: - return ; - case WidgetKinds.thread: - return ; - case WidgetKinds.hashtag: - return ; - case WidgetKinds.user: - return ; - case WidgetKinds.trendingProfiles: - return ; - case WidgetKinds.trendingNotes: + case WidgetKinds.local.network: + return ; + case WidgetKinds.local.feeds: + return ; + case WidgetKinds.local.files: + return ; + case WidgetKinds.local.articles: + return ; + case WidgetKinds.local.user: + return ; + case WidgetKinds.local.thread: + return ; + case WidgetKinds.global.hashtag: + return ; + case WidgetKinds.global.articles: + return ; + case WidgetKinds.global.files: + return ; + case WidgetKinds.nostrBand.trendingAccounts: + return ; + case WidgetKinds.nostrBand.trendingNotes: return ; - case WidgetKinds.network: - return ; - case WidgetKinds.article: - return ; - case WidgetKinds.file: - return ; - case WidgetKinds.xhashtag: - return ; - case WidgetKinds.xfeed: - return ; + case WidgetKinds.tmp.xfeed: + return ; + case WidgetKinds.tmp.xhashtag: + return ; + case WidgetKinds.tmp.list: + return ; default: break; } @@ -77,7 +88,7 @@ export function SpaceScreen() { ) : ( widgets.map((widget) => renderItem(widget)) )} - + ); } diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts index 9a248696..f364c539 100644 --- a/src/libs/storage/instance.ts +++ b/src/libs/storage/instance.ts @@ -223,6 +223,35 @@ export class LumeStorage { }; } + public async getAllEventsByKinds(kinds: number[], limit: number, offset: number) { + const totalEvents = await this.countTotalEvents(); + const nextCursor = offset + limit; + const authorsArr = `'${kinds.join("','")}'`; + + const events: { data: DBEvent[] | null; nextCursor: number } = { + data: null, + nextCursor: 0, + }; + + const query: DBEvent[] = await this.db.select( + `SELECT * FROM events WHERE kinds IN (${authorsArr}) ORDER BY created_at DESC LIMIT $1 OFFSET $2;`, + [limit, offset] + ); + + if (query && query.length > 0) { + events['data'] = query; + events['nextCursor'] = + Math.round(totalEvents / nextCursor) > 1 ? nextCursor : undefined; + + return events; + } + + return { + data: [], + nextCursor: 0, + }; + } + public async isEventsEmpty() { const results: DBEvent[] = await this.db.select( 'SELECT * FROM events ORDER BY id DESC LIMIT 1;' diff --git a/src/shared/notes/kinds/repost.tsx b/src/shared/notes/kinds/repost.tsx index 6d5e809c..3f5f5918 100644 --- a/src/shared/notes/kinds/repost.tsx +++ b/src/shared/notes/kinds/repost.tsx @@ -20,7 +20,7 @@ export function Repost({ event }: { event: NDKEvent }) { if (status === 'loading') { return ( -
+
@@ -30,7 +30,7 @@ export function Repost({ event }: { event: NDKEvent }) { if (status === 'error') { return ( -
+

Failed to get repost with ID @@ -57,7 +57,7 @@ export function Repost({ event }: { event: NDKEvent }) { }; return ( -

+
diff --git a/src/shared/notes/mentions/hashtag.tsx b/src/shared/notes/mentions/hashtag.tsx index e6781512..5b7b98ae 100644 --- a/src/shared/notes/mentions/hashtag.tsx +++ b/src/shared/notes/mentions/hashtag.tsx @@ -11,7 +11,7 @@ export function Hashtag({ tag }: { tag: string }) { type="button" onClick={() => setWidget(db, { - kind: WidgetKinds.hashtag, + kind: WidgetKinds.global.hashtag, title: tag, content: tag.replace('#', ''), }) diff --git a/src/shared/notes/mentions/note.tsx b/src/shared/notes/mentions/note.tsx index e40aafdc..a1604912 100644 --- a/src/shared/notes/mentions/note.tsx +++ b/src/shared/notes/mentions/note.tsx @@ -25,7 +25,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) { const openThread = (event, thread: string) => { const selection = window.getSelection(); if (selection.toString().length === 0) { - setWidget(db, { kind: WidgetKinds.thread, title: 'Thread', content: thread }); + setWidget(db, { kind: WidgetKinds.local.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 95384432..24876e04 100644 --- a/src/shared/notes/mentions/user.tsx +++ b/src/shared/notes/mentions/user.tsx @@ -16,7 +16,7 @@ export function MentionUser({ pubkey }: { pubkey: string }) { type="button" onClick={() => setWidget(db, { - kind: WidgetKinds.user, + kind: WidgetKinds.local.user, title: user?.nip05 || user?.name || user?.display_name, content: pubkey, }) diff --git a/src/shared/titleBar.tsx b/src/shared/titleBar.tsx index 78e9c8b3..1871b59f 100644 --- a/src/shared/titleBar.tsx +++ b/src/shared/titleBar.tsx @@ -19,7 +19,7 @@ export function TitleBar({ id, title }: { id?: string; title: string }) { diff --git a/src/app/space/components/widgets/article.tsx b/src/shared/widgets/global/articles.tsx similarity index 96% rename from src/app/space/components/widgets/article.tsx rename to src/shared/widgets/global/articles.tsx index f4cfc367..23f65129 100644 --- a/src/app/space/components/widgets/article.tsx +++ b/src/shared/widgets/global/articles.tsx @@ -10,10 +10,10 @@ import { TitleBar } from '@shared/titleBar'; import { Widget } from '@utils/types'; -export function ArticleWidget({ params }: { params: Widget }) { +export function GlobalArticlesWidget({ params }: { params: Widget }) { const { ndk } = useNDK(); const { status, data } = useQuery( - ['article-widget', params.content], + ['global-articles-widget'], async () => { const events = await ndk.fetchEvents({ kinds: [NDKKind.Article], diff --git a/src/app/space/components/widgets/file.tsx b/src/shared/widgets/global/files.tsx similarity index 94% rename from src/app/space/components/widgets/file.tsx rename to src/shared/widgets/global/files.tsx index 1bde7a81..95781208 100644 --- a/src/app/space/components/widgets/file.tsx +++ b/src/shared/widgets/global/files.tsx @@ -10,12 +10,13 @@ import { TitleBar } from '@shared/titleBar'; import { Widget } from '@utils/types'; -export function FileWidget({ params }: { params: Widget }) { +export function GlobalFilesWidget({ params }: { params: Widget }) { const { ndk } = useNDK(); const { status, data } = useQuery( - ['file-widget', params.content], + ['global-files-widget'], async () => { const events = await ndk.fetchEvents({ + // @ts-expect-error, NDK not support file metadata yet kinds: [1063], limit: 100, }); diff --git a/src/app/space/components/widgets/hashtag.tsx b/src/shared/widgets/global/hashtag.tsx similarity index 97% rename from src/app/space/components/widgets/hashtag.tsx rename to src/shared/widgets/global/hashtag.tsx index eff1b09f..df9d6ab2 100644 --- a/src/app/space/components/widgets/hashtag.tsx +++ b/src/shared/widgets/global/hashtag.tsx @@ -19,10 +19,10 @@ import { TitleBar } from '@shared/titleBar'; import { nHoursAgo } from '@utils/date'; import { Widget } from '@utils/types'; -export function HashtagWidget({ params }: { params: Widget }) { +export function GlobalHashtagWidget({ params }: { params: Widget }) { const { ndk } = useNDK(); const { status, data } = useQuery( - ['hashtag-widget', params.content], + ['global-hashtag-widget', params.content], async () => { const events = await ndk.fetchEvents({ kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Article], diff --git a/src/shared/widgets/index.ts b/src/shared/widgets/index.ts new file mode 100644 index 00000000..bda8fbf9 --- /dev/null +++ b/src/shared/widgets/index.ts @@ -0,0 +1,13 @@ +export * from './local/feeds'; +export * from './local/network'; +export * from './local/user'; +export * from './local/thread'; +export * from './local/files'; +export * from './local/articles'; +export * from './global/articles'; +export * from './global/files'; +export * from './global/hashtag'; +export * from './nostrBand/trendingNotes'; +export * from './nostrBand/trendingAccounts'; +export * from './tmp/feeds'; +export * from './tmp/hashtag'; diff --git a/src/shared/widgets/local/articles.tsx b/src/shared/widgets/local/articles.tsx new file mode 100644 index 00000000..5b94ab9c --- /dev/null +++ b/src/shared/widgets/local/articles.tsx @@ -0,0 +1,130 @@ +import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { useVirtualizer } from '@tanstack/react-virtual'; +import { useCallback, useMemo, useRef } from 'react'; + +import { useStorage } from '@libs/storage/provider'; + +import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; +import { FileNote, NoteSkeleton, NoteWrapper } from '@shared/notes'; +import { TitleBar } from '@shared/titleBar'; + +import { DBEvent, Widget } from '@utils/types'; + +export function LocalArticlesWidget({ params }: { params: Widget }) { + const { db } = useStorage(); + const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: ['local-articles-widget'], + queryFn: async ({ pageParam = 0 }) => { + return await db.getAllEventsByKinds([NDKKind.Article], 20, pageParam); + }, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }); + + const dbEvents = useMemo( + () => (data ? data.pages.flatMap((d: { data: DBEvent[] }) => d.data) : []), + [data] + ); + const parentRef = useRef(); + const virtualizer = useVirtualizer({ + count: hasNextPage ? dbEvents.length : dbEvents.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 650, + overscan: 4, + }); + const items = virtualizer.getVirtualItems(); + + // render event match event kind + const renderItem = useCallback( + (index: string | number) => { + const event: NDKEvent = data[index]; + if (!event) return; + + return ( +
+ + + +
+ ); + }, + [data] + ); + + return ( +
+ +
+ {status === 'loading' ? ( +
+
+ +
+
+ ) : items.length === 0 ? ( +
+
+
+

+ There have been no new posts. +

+
+
+
+ ) : ( +
+
+ {items.map((item) => renderItem(item.index))} +
+
+ )} + {isFetchingNextPage && ( +
+
+ +
+
+ )} +
+ +
+
+
+ ); +} diff --git a/src/app/space/components/widgets/feed.tsx b/src/shared/widgets/local/feeds.tsx similarity index 98% rename from src/app/space/components/widgets/feed.tsx rename to src/shared/widgets/local/feeds.tsx index be7b6279..ae11a61a 100644 --- a/src/app/space/components/widgets/feed.tsx +++ b/src/shared/widgets/local/feeds.tsx @@ -19,11 +19,11 @@ import { TitleBar } from '@shared/titleBar'; import { DBEvent, Widget } from '@utils/types'; -export function FeedWidget({ params }: { params: Widget }) { +export function LocalFeedsWidget({ params }: { params: Widget }) { const { db } = useStorage(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ - queryKey: ['groupfeed-widget', params.content], + queryKey: ['local-feeds-widget', params.content], queryFn: async ({ pageParam = 0 }) => { const authors = JSON.parse(params.content); return await db.getAllEventsByAuthors(authors, 20, pageParam); diff --git a/src/shared/widgets/local/files.tsx b/src/shared/widgets/local/files.tsx new file mode 100644 index 00000000..a3fe4fd7 --- /dev/null +++ b/src/shared/widgets/local/files.tsx @@ -0,0 +1,130 @@ +import { NDKEvent } from '@nostr-dev-kit/ndk'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { useVirtualizer } from '@tanstack/react-virtual'; +import { useCallback, useMemo, useRef } from 'react'; + +import { useStorage } from '@libs/storage/provider'; + +import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons'; +import { FileNote, NoteSkeleton, NoteWrapper } from '@shared/notes'; +import { TitleBar } from '@shared/titleBar'; + +import { DBEvent, Widget } from '@utils/types'; + +export function LocalFilesWidget({ params }: { params: Widget }) { + const { db } = useStorage(); + const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: ['local-files-widget'], + queryFn: async ({ pageParam = 0 }) => { + return await db.getAllEventsByKinds([1063], 20, pageParam); + }, + getNextPageParam: (lastPage) => lastPage.nextCursor, + }); + + const dbEvents = useMemo( + () => (data ? data.pages.flatMap((d: { data: DBEvent[] }) => d.data) : []), + [data] + ); + const parentRef = useRef(); + const virtualizer = useVirtualizer({ + count: hasNextPage ? dbEvents.length : dbEvents.length, + getScrollElement: () => parentRef.current, + estimateSize: () => 650, + overscan: 4, + }); + const items = virtualizer.getVirtualItems(); + + // render event match event kind + const renderItem = useCallback( + (index: string | number) => { + const event: NDKEvent = data[index]; + if (!event) return; + + return ( +
+ + + +
+ ); + }, + [data] + ); + + return ( +
+ +
+ {status === 'loading' ? ( +
+
+ +
+
+ ) : items.length === 0 ? ( +
+
+
+

+ There have been no new posts. +

+
+
+
+ ) : ( +
+
+ {items.map((item) => renderItem(item.index))} +
+
+ )} + {isFetchingNextPage && ( +
+
+ +
+
+ )} +
+ +
+
+
+ ); +} diff --git a/src/app/space/components/widgets/network.tsx b/src/shared/widgets/local/network.tsx similarity index 98% rename from src/app/space/components/widgets/network.tsx rename to src/shared/widgets/local/network.tsx index f478eff3..b54643b4 100644 --- a/src/app/space/components/widgets/network.tsx +++ b/src/shared/widgets/local/network.tsx @@ -21,12 +21,12 @@ import { useNostr } from '@utils/hooks/useNostr'; import { toRawEvent } from '@utils/rawEvent'; import { DBEvent } from '@utils/types'; -export function NetworkWidget() { +export function LocalNetworkWidget() { const { sub } = useNostr(); const { db } = useStorage(); const { status, data, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ - queryKey: ['network-widget'], + queryKey: ['local-network-widget'], queryFn: async ({ pageParam = 0 }) => { return await db.getAllEvents(30, pageParam); }, diff --git a/src/app/space/components/widgets/thread.tsx b/src/shared/widgets/local/thread.tsx similarity index 96% rename from src/app/space/components/widgets/thread.tsx rename to src/shared/widgets/local/thread.tsx index c0e81ba5..4fda8166 100644 --- a/src/app/space/components/widgets/thread.tsx +++ b/src/shared/widgets/local/thread.tsx @@ -20,7 +20,7 @@ import { TitleBar } from '@shared/titleBar'; import { useEvent } from '@utils/hooks/useEvent'; import { Widget } from '@utils/types'; -export function ThreadBlock({ params }: { params: Widget }) { +export function LocalThreadWidget({ params }: { params: Widget }) { const { db } = useStorage(); const { status, data } = useEvent(params.content); diff --git a/src/app/space/components/widgets/user.tsx b/src/shared/widgets/local/user.tsx similarity index 97% rename from src/app/space/components/widgets/user.tsx rename to src/shared/widgets/local/user.tsx index 100fc476..703adf5a 100644 --- a/src/app/space/components/widgets/user.tsx +++ b/src/shared/widgets/local/user.tsx @@ -20,10 +20,10 @@ import { UserProfile } from '@shared/userProfile'; import { nHoursAgo } from '@utils/date'; import { Widget } from '@utils/types'; -export function UserWidget({ params }: { params: Widget }) { +export function LocalUserWidget({ params }: { params: Widget }) { const { ndk } = useNDK(); const { status, data } = useQuery( - ['user-widget', params.content], + ['local-user-widget', params.content], async () => { const events = await ndk.fetchEvents({ kinds: [1, 6], diff --git a/src/app/space/components/widgets/trendingProfile.tsx b/src/shared/widgets/nostrBand/trendingAccounts.tsx similarity index 73% rename from src/app/space/components/widgets/trendingProfile.tsx rename to src/shared/widgets/nostrBand/trendingAccounts.tsx index c914f9f0..c8e3286d 100644 --- a/src/app/space/components/widgets/trendingProfile.tsx +++ b/src/shared/widgets/nostrBand/trendingAccounts.tsx @@ -1,9 +1,8 @@ import { useQuery } from '@tanstack/react-query'; -import { type Profile, UserProfile } from '@app/space/components/userProfile'; - import { NoteSkeleton } from '@shared/notes/skeleton'; import { TitleBar } from '@shared/titleBar'; +import { NostrBandUserProfile, type Profile } from '@shared/widgets/nostrBandUserProfile'; import { Widget } from '@utils/types'; @@ -11,11 +10,11 @@ interface Response { profiles: Array<{ pubkey: string }>; } -export function TrendingProfilesWidget({ params }: { params: Widget }) { +export function TrendingAccountsWidget({ params }: { params: Widget }) { const { status, data } = useQuery( ['trending-profiles-widget'], async () => { - const res = await fetch(params.content); + const res = await fetch('https://api.nostr.band/v0/trending/profiles'); if (!res.ok) { throw new Error('Error'); } @@ -32,9 +31,9 @@ export function TrendingProfilesWidget({ params }: { params: Widget }) { ); return ( -
- -
+
+ +
{status === 'loading' ? (
@@ -52,7 +51,7 @@ export function TrendingProfilesWidget({ params }: { params: Widget }) { ) : (
{data.map((item: Profile) => ( - + ))}
)} diff --git a/src/app/space/components/widgets/trendingNotes.tsx b/src/shared/widgets/nostrBand/trendingNotes.tsx similarity index 85% rename from src/app/space/components/widgets/trendingNotes.tsx rename to src/shared/widgets/nostrBand/trendingNotes.tsx index f1fd0fe4..84caf6bc 100644 --- a/src/app/space/components/widgets/trendingNotes.tsx +++ b/src/shared/widgets/nostrBand/trendingNotes.tsx @@ -14,7 +14,7 @@ export function TrendingNotesWidget({ params }: { params: Widget }) { const { status, data } = useQuery( ['trending-notes-widget'], async () => { - const res = await fetch(params.content); + const res = await fetch('https://api.nostr.band/v0/trending/notes'); if (!res.ok) { throw new Error('failed to fecht trending notes'); } @@ -31,9 +31,9 @@ export function TrendingNotesWidget({ params }: { params: Widget }) { ); return ( -
- -
+
+ +
{status === 'loading' ? (
diff --git a/src/app/space/components/userProfile.tsx b/src/shared/widgets/nostrBandUserProfile.tsx similarity index 98% rename from src/app/space/components/userProfile.tsx rename to src/shared/widgets/nostrBandUserProfile.tsx index 739403dd..2c36a4f5 100644 --- a/src/app/space/components/userProfile.tsx +++ b/src/shared/widgets/nostrBandUserProfile.tsx @@ -15,7 +15,7 @@ export interface Profile { profile: { content: string }; } -export function UserProfile({ data }: { data: Profile }) { +export function NostrBandUserProfile({ data }: { data: Profile }) { const { db } = useStorage(); const { addContact, removeContact } = useNostr(); const { status, data: userStats } = useQuery( diff --git a/src/app/space/components/forms/feed.tsx b/src/shared/widgets/tmp/feeds.tsx similarity index 97% rename from src/app/space/components/forms/feed.tsx rename to src/shared/widgets/tmp/feeds.tsx index d89d473b..c3b756a0 100644 --- a/src/app/space/components/forms/feed.tsx +++ b/src/shared/widgets/tmp/feeds.tsx @@ -10,7 +10,7 @@ import { WidgetKinds, useWidgets } from '@stores/widgets'; import { Widget } from '@utils/types'; -export function FeedWidgetForm({ params }: { params: Widget }) { +export function XfeedsWidget({ params }: { params: Widget }) { const { db } = useStorage(); const [setWidget, removeWidget] = useWidgets((state) => [ @@ -34,7 +34,7 @@ export function FeedWidgetForm({ params }: { params: Widget }) { const submit = async () => { setWidget(db, { - kind: WidgetKinds.feed, + kind: WidgetKinds.local.feeds, title: title || 'Group', content: JSON.stringify(groups), }); diff --git a/src/app/space/components/forms/hashtag.tsx b/src/shared/widgets/tmp/hashtag.tsx similarity index 96% rename from src/app/space/components/forms/hashtag.tsx rename to src/shared/widgets/tmp/hashtag.tsx index 1c4cd271..76393c4c 100644 --- a/src/app/space/components/forms/hashtag.tsx +++ b/src/shared/widgets/tmp/hashtag.tsx @@ -26,7 +26,7 @@ const resolver: Resolver = async (values) => { }; }; -export function HashTagWidgetForm({ params }: { params: Widget }) { +export function XhashtagWidget({ params }: { params: Widget }) { const [setWidget, removeWidget] = useWidgets((state) => [ state.setWidget, state.removeWidget, @@ -47,7 +47,7 @@ export function HashTagWidgetForm({ params }: { params: Widget }) { const onSubmit = async (data: FormValues) => { try { setWidget(db, { - kind: WidgetKinds.hashtag, + kind: WidgetKinds.global.hashtag, title: data.hashtag + ' in 24 hours ago', content: data.hashtag.replace('#', ''), }); diff --git a/src/stores/widgets.ts b/src/stores/widgets.ts index 74b29b38..214e245c 100644 --- a/src/stores/widgets.ts +++ b/src/stores/widgets.ts @@ -13,19 +13,81 @@ interface WidgetState { } export const WidgetKinds = { - feed: 1, // NIP-01 - thread: 2, // NIP-01 - hashtag: 3, // NIP-01 - article: 4, // NIP-23 - user: 5, // NIP-01 - trendingProfiles: 6, - trendingNotes: 7, - file: 8, // NIP-94 - network: 9999, - xfeed: 10000, // x is temporary state for new feed widget form - xhashtag: 10001, // x is temporary state for new hashtag widget form + local: { + network: 100, + feeds: 101, + files: 102, + articles: 103, + user: 104, + thread: 105, + }, + global: { + feeds: 1000, + files: 1001, + articles: 1002, + hashtag: 1003, + }, + nostrBand: { + trendingAccounts: 1, + trendingNotes: 2, + }, + tmp: { + list: 10000, + xfeed: 10001, + xhashtag: 10002, + }, }; +export const DefaultWidgets = [ + { + title: 'Network / Follows', + data: [ + { + kind: WidgetKinds.tmp.xfeed, + title: 'Group feeds', + }, + { + kind: WidgetKinds.local.files, + title: 'Files', + }, + { + kind: WidgetKinds.local.articles, + title: 'Articles', + }, + ], + }, + { + title: 'Global', + data: [ + { + kind: WidgetKinds.tmp.xhashtag, + title: 'Hashtag', + }, + { + kind: WidgetKinds.global.files, + title: 'Files', + }, + { + kind: WidgetKinds.global.articles, + title: 'Articles', + }, + ], + }, + { + title: 'Trending (nostr.band)', + data: [ + { + kind: WidgetKinds.nostrBand.trendingAccounts, + title: 'Accounts', + }, + { + kind: WidgetKinds.nostrBand.trendingNotes, + title: 'Notes', + }, + ], + }, +]; + export const useWidgets = create()( persist( (set) => ({ @@ -39,7 +101,7 @@ export const useWidgets = create()( id: '9999', title: 'Network', content: '', - kind: WidgetKinds.network, + kind: WidgetKinds.local.network, }); set({ widgets: dbWidgets }); diff --git a/src/utils/types.d.ts b/src/utils/types.d.ts index 91d9f7ff..b9e1b66b 100644 --- a/src/utils/types.d.ts +++ b/src/utils/types.d.ts @@ -36,6 +36,16 @@ export interface Profile extends NDKUserProfile { pubkey?: string; } +export interface WidgetGroup { + title: string; + data: WidgetGroupItem[]; +} + +export interface WidgetGroupItem { + title: string; + kind: number; +} + export interface Widget { id?: string; account_id?: number;