wip: update widget list
BIN
public/anime.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
public/art.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/gaming.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
public/movie.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
public/music.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
public/nsfw.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
public/photography.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
public/technology.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
@ -6,9 +6,18 @@ import { useStorage } from '@libs/storage/provider';
|
|||||||
|
|
||||||
import { LoaderIcon } from '@shared/icons';
|
import { LoaderIcon } from '@shared/icons';
|
||||||
import {
|
import {
|
||||||
|
ArticleWidget,
|
||||||
|
FileWidget,
|
||||||
|
GroupWidget,
|
||||||
|
HashtagWidget,
|
||||||
NewsfeedWidget,
|
NewsfeedWidget,
|
||||||
NotificationWidget,
|
NotificationWidget,
|
||||||
|
ThreadWidget,
|
||||||
ToggleWidgetList,
|
ToggleWidgetList,
|
||||||
|
TopicWidget,
|
||||||
|
TrendingAccountsWidget,
|
||||||
|
TrendingNotesWidget,
|
||||||
|
UserWidget,
|
||||||
WidgetList,
|
WidgetList,
|
||||||
} from '@shared/widgets';
|
} from '@shared/widgets';
|
||||||
|
|
||||||
@ -54,6 +63,24 @@ export function HomeScreen() {
|
|||||||
return <NotificationWidget key={widget.id} />;
|
return <NotificationWidget key={widget.id} />;
|
||||||
case WIDGET_KIND.newsfeed:
|
case WIDGET_KIND.newsfeed:
|
||||||
return <NewsfeedWidget key={widget.id} />;
|
return <NewsfeedWidget key={widget.id} />;
|
||||||
|
case WIDGET_KIND.topic:
|
||||||
|
return <TopicWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.user:
|
||||||
|
return <UserWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.thread:
|
||||||
|
return <ThreadWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.article:
|
||||||
|
return <ArticleWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.file:
|
||||||
|
return <FileWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.hashtag:
|
||||||
|
return <HashtagWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.group:
|
||||||
|
return <GroupWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.trendingNotes:
|
||||||
|
return <TrendingNotesWidget key={widget.id} widget={widget} />;
|
||||||
|
case WIDGET_KIND.trendingAccounts:
|
||||||
|
return <TrendingAccountsWidget key={widget.id} widget={widget} />;
|
||||||
case WIDGET_KIND.list:
|
case WIDGET_KIND.list:
|
||||||
return <WidgetList key={widget.id} widget={widget} />;
|
return <WidgetList key={widget.id} widget={widget} />;
|
||||||
default:
|
default:
|
||||||
|
@ -15,7 +15,7 @@ interface Response {
|
|||||||
profiles: Array<{ pubkey: string }>;
|
profiles: Array<{ pubkey: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TrendingAccountsWidget({ params }: { params: Widget }) {
|
export function TrendingAccountsWidget({ widget }: { widget: Widget }) {
|
||||||
const { status, data } = useQuery({
|
const { status, data } = useQuery({
|
||||||
queryKey: ['trending-profiles-widget'],
|
queryKey: ['trending-profiles-widget'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@ -35,7 +35,7 @@ export function TrendingAccountsWidget({ params }: { params: Widget }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<WidgetWrapper>
|
<WidgetWrapper>
|
||||||
<TitleBar id={params.id} title="Trending Accounts" />
|
<TitleBar id={widget.id} title="Trending Accounts" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
{status === 'pending' ? (
|
{status === 'pending' ? (
|
||||||
<div className="flex h-full w-full items-center justify-center ">
|
<div className="flex h-full w-full items-center justify-center ">
|
||||||
|
@ -16,7 +16,7 @@ export function ToggleWidgetList() {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
addWidget.mutate({ kind: WIDGET_KIND.list, title: '', content: '' })
|
addWidget.mutate({ kind: WIDGET_KIND.list, title: '', content: '' })
|
||||||
}
|
}
|
||||||
className="inline-flex h-9 items-center gap-2 rounded-full bg-neutral-200 px-3 text-neutral-900 hover:bg-neutral-300 dark:bg-neutral-800 dark:text-neutral-100 dark:hover:bg-neutral-700"
|
className="inline-flex h-9 items-center gap-2 rounded-full bg-neutral-100 px-3 text-neutral-900 hover:bg-neutral-200 dark:bg-neutral-900 dark:text-neutral-100 dark:hover:bg-neutral-800"
|
||||||
>
|
>
|
||||||
<PlusIcon className="h-4 w-4 text-neutral-900 dark:text-zinc-100" />
|
<PlusIcon className="h-4 w-4 text-neutral-900 dark:text-zinc-100" />
|
||||||
<p className="text-sm font-semibold leading-none">Add widget</p>
|
<p className="text-sm font-semibold leading-none">Add widget</p>
|
||||||
|
@ -1,27 +1,196 @@
|
|||||||
|
import {
|
||||||
|
ArticleIcon,
|
||||||
|
BellIcon,
|
||||||
|
GroupFeedsIcon,
|
||||||
|
HashtagIcon,
|
||||||
|
MediaIcon,
|
||||||
|
PlusIcon,
|
||||||
|
TrendingIcon,
|
||||||
|
} from '@shared/icons';
|
||||||
import { TitleBar } from '@shared/titleBar';
|
import { TitleBar } from '@shared/titleBar';
|
||||||
import { WidgetWrapper } from '@shared/widgets';
|
import { WidgetWrapper } from '@shared/widgets';
|
||||||
|
|
||||||
|
import { TOPICS, WIDGET_KIND } from '@stores/constants';
|
||||||
|
|
||||||
|
import { useWidget } from '@utils/hooks/useWidget';
|
||||||
import { Widget } from '@utils/types';
|
import { Widget } from '@utils/types';
|
||||||
|
|
||||||
export function WidgetList({ widget }: { widget: Widget }) {
|
export function WidgetList({ widget }: { widget: Widget }) {
|
||||||
|
const { replaceWidget } = useWidget();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WidgetWrapper>
|
<WidgetWrapper>
|
||||||
<TitleBar id={widget.id} title="Add widgets" />
|
<TitleBar id={widget.id} title="Add widgets" />
|
||||||
<div className="flex-1 overflow-y-auto pb-10 scrollbar-none">
|
<div className="flex-1 overflow-y-auto pb-10 scrollbar-none">
|
||||||
<div className="flex flex-col gap-6 px-3">
|
<div className="flex flex-col gap-6 px-3">
|
||||||
<div className="border-t border-neutral-200 pt-6 dark:border-neutral-800">
|
<div className="rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||||
<button
|
<h3 className="mb-2.5 text-sm font-semibold uppercase text-neutral-700 dark:text-neutral-300">
|
||||||
type="button"
|
Topics
|
||||||
disabled
|
</h3>
|
||||||
className="inline-flex h-14 w-full items-center justify-center gap-2.5 rounded-xl bg-neutral-50 text-sm font-medium text-neutral-900 dark:bg-neutral-950 dark:text-neutral-100"
|
<div className="flex flex-col gap-3">
|
||||||
>
|
{TOPICS.sort((a, b) => a.title.localeCompare(b.title)).map(
|
||||||
Build your own widget{' '}
|
(topic, index) => (
|
||||||
<div className="-rotate-3 transform-gpu rounded-md border border-neutral-200 bg-neutral-100 px-1.5 py-1 dark:border-neutral-800 dark:bg-neutral-900">
|
<div
|
||||||
<span className="bg-gradient-to-r from-blue-400 via-red-400 to-orange-500 bg-clip-text text-xs text-transparent dark:from-blue-200 dark:via-red-200 dark:to-orange-300">
|
key={index}
|
||||||
Coming soon
|
className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50"
|
||||||
</span>
|
>
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="h-9 w-9 shrink-0 rounded-md">
|
||||||
|
<img
|
||||||
|
src={`/${topic.title.toLowerCase()}.jpg`}
|
||||||
|
alt={topic.title}
|
||||||
|
className="h-9 w-9 rounded-md"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">{topic.title}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
replaceWidget.mutate({
|
||||||
|
currentId: widget.id,
|
||||||
|
widget: {
|
||||||
|
kind: WIDGET_KIND.topic,
|
||||||
|
title: topic.title,
|
||||||
|
content: JSON.stringify(topic.content),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||||
|
<h3 className="mb-2.5 text-sm font-semibold uppercase text-neutral-700 dark:text-neutral-300">
|
||||||
|
Newsfeed
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<ArticleIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Article</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<MediaIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Media</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<GroupFeedsIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Group feeds</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<HashtagIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Hashtag</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||||
|
<h3 className="mb-2.5 text-sm font-semibold uppercase text-neutral-700 dark:text-neutral-300">
|
||||||
|
Nostr Band
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<TrendingIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Trending posts</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<TrendingIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Trending users</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||||
|
<h3 className="mb-2.5 text-sm font-semibold uppercase text-neutral-700 dark:text-neutral-300">
|
||||||
|
Other
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="inline-flex h-14 w-full items-center justify-between rounded-lg bg-white px-3 hover:shadow-md hover:shadow-neutral-200/50 dark:hover:shadow-neutral-800/50">
|
||||||
|
<div className="inline-flex items-center gap-2.5">
|
||||||
|
<div className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-neutral-100">
|
||||||
|
<BellIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">Notification</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex h-6 items-center gap-1 rounded-md bg-neutral-100 pl-1.5 pr-2.5 text-sm font-medium hover:bg-blue-500 hover:text-white dark:bg-neutral-900"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-3 w-3" />
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||||
|
import { FetchFilter } from 'nostr-fetch';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { VList } from 'virtua';
|
import { VList } from 'virtua';
|
||||||
|
|
||||||
@ -33,18 +34,18 @@ export function TopicWidget({ widget }: { widget: Widget }) {
|
|||||||
pageParam: number;
|
pageParam: number;
|
||||||
}) => {
|
}) => {
|
||||||
const hashtags: string[] = JSON.parse(widget.content as string);
|
const hashtags: string[] = JSON.parse(widget.content as string);
|
||||||
|
const filter: FetchFilter = {
|
||||||
|
kinds: [NDKKind.Text, NDKKind.Repost],
|
||||||
|
'#t': hashtags.map((tag) => tag.replace('#', '')),
|
||||||
|
};
|
||||||
|
|
||||||
const rootIds = new Set();
|
const rootIds = new Set();
|
||||||
const dedupQueue = new Set();
|
const dedupQueue = new Set();
|
||||||
|
|
||||||
const events = await fetcher.fetchLatestEvents(
|
const events = await fetcher.fetchLatestEvents(relayUrls, filter, FETCH_LIMIT, {
|
||||||
relayUrls,
|
asOf: pageParam === 0 ? undefined : pageParam,
|
||||||
{
|
abortSignal: signal,
|
||||||
kinds: [NDKKind.Text, NDKKind.Repost],
|
});
|
||||||
'#t': hashtags,
|
|
||||||
},
|
|
||||||
FETCH_LIMIT,
|
|
||||||
{ asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal }
|
|
||||||
);
|
|
||||||
|
|
||||||
const ndkEvents = events.map((event) => {
|
const ndkEvents = events.map((event) => {
|
||||||
return new NDKEvent(ndk, event);
|
return new NDKEvent(ndk, event);
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { WidgetGroup } from '@utils/types';
|
|
||||||
|
|
||||||
export const FULL_RELAYS = [
|
export const FULL_RELAYS = [
|
||||||
'wss://relay.damus.io',
|
'wss://relay.damus.io',
|
||||||
'wss://relay.primal.net',
|
|
||||||
'wss://relayable.org',
|
'wss://relayable.org',
|
||||||
'wss://relay.nostr.band/all',
|
'wss://relay.nostr.band/all',
|
||||||
'wss://nostr.mutinywallet.com',
|
'wss://nostr.mutinywallet.com',
|
||||||
@ -59,8 +56,6 @@ export const WIDGET_KIND = {
|
|||||||
export const TOPICS = [
|
export const TOPICS = [
|
||||||
{
|
{
|
||||||
title: 'Gaming',
|
title: 'Gaming',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
'#gamestr',
|
'#gamestr',
|
||||||
'#gaming',
|
'#gaming',
|
||||||
@ -74,31 +69,23 @@ export const TOPICS = [
|
|||||||
'#twitch',
|
'#twitch',
|
||||||
'#fortnite',
|
'#fortnite',
|
||||||
'#pc',
|
'#pc',
|
||||||
'#memes',
|
|
||||||
'#pcgaming',
|
'#pcgaming',
|
||||||
'#gamers',
|
'#gamers',
|
||||||
'#gamingcommunity',
|
'#gamingcommunity',
|
||||||
'#youtube',
|
|
||||||
'#switch',
|
'#switch',
|
||||||
'#gamergirl',
|
'#gamergirl',
|
||||||
'#nintendo',
|
'#nintendo',
|
||||||
'#gta',
|
'#gta',
|
||||||
'#callofduty',
|
'#callofduty',
|
||||||
'#streamer',
|
|
||||||
'#follow',
|
|
||||||
'#pubg',
|
'#pubg',
|
||||||
'#videogame',
|
'#videogame',
|
||||||
'#esports',
|
'#esports',
|
||||||
'#bhfyp',
|
|
||||||
'#meme',
|
|
||||||
'#twitchstreamer',
|
|
||||||
'#art',
|
|
||||||
'#genshinimpact',
|
'#genshinimpact',
|
||||||
'#honkaiimpact',
|
'#honkaiimpact',
|
||||||
'#warthunder',
|
'#warthunder',
|
||||||
'#hovoverse',
|
'#hoyoverse',
|
||||||
'#arknights',
|
'#arknights',
|
||||||
'#soul',
|
'#soullike',
|
||||||
'#eldenring',
|
'#eldenring',
|
||||||
'#steam',
|
'#steam',
|
||||||
'#pubg',
|
'#pubg',
|
||||||
@ -108,12 +95,17 @@ export const TOPICS = [
|
|||||||
'#starfield',
|
'#starfield',
|
||||||
'#gta6',
|
'#gta6',
|
||||||
'#gameoftheyear',
|
'#gameoftheyear',
|
||||||
|
'#darksoul',
|
||||||
|
'#batterfield',
|
||||||
|
'#dota',
|
||||||
|
'#rpg',
|
||||||
|
'#thewitcher',
|
||||||
|
'#rogally',
|
||||||
|
'#rog',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Music',
|
title: 'Music',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
'#audiostr',
|
'#audiostr',
|
||||||
'#musicstr',
|
'#musicstr',
|
||||||
@ -125,18 +117,12 @@ export const TOPICS = [
|
|||||||
'#musician',
|
'#musician',
|
||||||
'#artist',
|
'#artist',
|
||||||
'#musica',
|
'#musica',
|
||||||
'#instagood',
|
|
||||||
'#singer',
|
'#singer',
|
||||||
'#dj',
|
'#dj',
|
||||||
'#follow',
|
|
||||||
'#rock',
|
'#rock',
|
||||||
'#like',
|
|
||||||
'#dance',
|
'#dance',
|
||||||
'#guitar',
|
'#guitar',
|
||||||
'#s',
|
|
||||||
'#photography',
|
|
||||||
'#song',
|
'#song',
|
||||||
'#bhfyp',
|
|
||||||
'#newmusic',
|
'#newmusic',
|
||||||
'#producer',
|
'#producer',
|
||||||
'#life',
|
'#life',
|
||||||
@ -146,12 +132,14 @@ export const TOPICS = [
|
|||||||
'#explorepage',
|
'#explorepage',
|
||||||
'#viral',
|
'#viral',
|
||||||
'#beats',
|
'#beats',
|
||||||
|
'#dvd',
|
||||||
|
'#amass',
|
||||||
|
'#bluray',
|
||||||
|
'#Blu_Ray',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Photography',
|
title: 'Photography',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
'#photography',
|
'#photography',
|
||||||
'#photooftheday',
|
'#photooftheday',
|
||||||
@ -159,57 +147,38 @@ export const TOPICS = [
|
|||||||
'#photo',
|
'#photo',
|
||||||
'#nature',
|
'#nature',
|
||||||
'#picoftheday',
|
'#picoftheday',
|
||||||
'#like',
|
|
||||||
'#photographer',
|
'#photographer',
|
||||||
'#beautiful',
|
'#beautiful',
|
||||||
'#follow',
|
|
||||||
'#art',
|
|
||||||
'#fashion',
|
'#fashion',
|
||||||
'#travel',
|
'#travel',
|
||||||
'#bhfyp',
|
|
||||||
'#photoshoot',
|
'#photoshoot',
|
||||||
'#likeforlikes',
|
|
||||||
'#instadaily',
|
|
||||||
'#naturephotography',
|
'#naturephotography',
|
||||||
'#model',
|
'#model',
|
||||||
'#me',
|
'#me',
|
||||||
'#smile',
|
'#smile',
|
||||||
'#style',
|
'#style',
|
||||||
'#instalike',
|
|
||||||
'#happy',
|
'#happy',
|
||||||
'#likes',
|
'#likes',
|
||||||
'#myself',
|
'#myself',
|
||||||
'#followme',
|
|
||||||
'#followforfollowback',
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Art',
|
title: 'Art',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
|
'#nostrdesign',
|
||||||
'#artstr',
|
'#artstr',
|
||||||
'#art',
|
'#art',
|
||||||
'#artist',
|
'#artist',
|
||||||
'#love',
|
|
||||||
'#drawing',
|
'#drawing',
|
||||||
'#photography',
|
|
||||||
'#artwork',
|
'#artwork',
|
||||||
'#instagood',
|
|
||||||
'#photooftheday',
|
|
||||||
'#painting',
|
'#painting',
|
||||||
'#fashion',
|
'#fashion',
|
||||||
'#like',
|
|
||||||
'#artistsoninstagram',
|
|
||||||
'#beautiful',
|
'#beautiful',
|
||||||
'#illustration',
|
'#illustration',
|
||||||
'#digitalart',
|
'#digitalart',
|
||||||
'#follow',
|
|
||||||
'#design',
|
'#design',
|
||||||
'#nature',
|
'#nature',
|
||||||
'#picoftheday',
|
|
||||||
'#photo',
|
'#photo',
|
||||||
'#bhfyp',
|
|
||||||
'#sketch',
|
'#sketch',
|
||||||
'#style',
|
'#style',
|
||||||
'#arte',
|
'#arte',
|
||||||
@ -221,8 +190,6 @@ export const TOPICS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Movie',
|
title: 'Movie',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
'#filmstr',
|
'#filmstr',
|
||||||
'#moviestr',
|
'#moviestr',
|
||||||
@ -233,9 +200,6 @@ export const TOPICS = [
|
|||||||
'#films',
|
'#films',
|
||||||
'#hollywood',
|
'#hollywood',
|
||||||
'#actor',
|
'#actor',
|
||||||
'#love',
|
|
||||||
'#s',
|
|
||||||
'#art',
|
|
||||||
'#cinematography',
|
'#cinematography',
|
||||||
'#actress',
|
'#actress',
|
||||||
'#netflix',
|
'#netflix',
|
||||||
@ -243,7 +207,6 @@ export const TOPICS = [
|
|||||||
'#music',
|
'#music',
|
||||||
'#filmmaking',
|
'#filmmaking',
|
||||||
'#horror',
|
'#horror',
|
||||||
'#instagood',
|
|
||||||
'#bollywood',
|
'#bollywood',
|
||||||
'#movienight',
|
'#movienight',
|
||||||
'#photography',
|
'#photography',
|
||||||
@ -259,8 +222,6 @@ export const TOPICS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Technology',
|
title: 'Technology',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
'#apple',
|
'#apple',
|
||||||
'#xiaomi',
|
'#xiaomi',
|
||||||
@ -276,30 +237,27 @@ export const TOPICS = [
|
|||||||
'#iphone',
|
'#iphone',
|
||||||
'#technews',
|
'#technews',
|
||||||
'#science',
|
'#science',
|
||||||
'#design',
|
|
||||||
'#gadgets',
|
'#gadgets',
|
||||||
'#electronics',
|
'#electronics',
|
||||||
'#android',
|
'#android',
|
||||||
'#software',
|
'#software',
|
||||||
'#programming',
|
'#programming',
|
||||||
'#smartphone',
|
'#smartphone',
|
||||||
'#bhfyp',
|
|
||||||
'#samsung',
|
'#samsung',
|
||||||
'#instagood',
|
|
||||||
'#coding',
|
'#coding',
|
||||||
'#computer',
|
'#computer',
|
||||||
'#pro',
|
|
||||||
'#education',
|
|
||||||
'#security',
|
'#security',
|
||||||
'#gadget',
|
'#gadget',
|
||||||
'#mobile',
|
'#mobile',
|
||||||
'#technologynews',
|
'#technologynews',
|
||||||
|
'#opensource',
|
||||||
|
'#tor',
|
||||||
|
'#bitcoin',
|
||||||
|
'#lightning',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Anime',
|
title: 'Anime',
|
||||||
description: '',
|
|
||||||
kind: WIDGET_KIND.global.topic,
|
|
||||||
content: [
|
content: [
|
||||||
'#animestr',
|
'#animestr',
|
||||||
'#anime',
|
'#anime',
|
||||||
@ -317,78 +275,23 @@ export const TOPICS = [
|
|||||||
'#aot',
|
'#aot',
|
||||||
'#hentai',
|
'#hentai',
|
||||||
'#fanart',
|
'#fanart',
|
||||||
],
|
'#loli',
|
||||||
},
|
'#vocaloid',
|
||||||
];
|
'#vtuber',
|
||||||
|
],
|
||||||
export const DEFAULT_WIDGETS: Array<WidgetGroup> = [
|
},
|
||||||
{
|
{
|
||||||
title: 'Topics',
|
title: 'NSFW',
|
||||||
data: TOPICS,
|
content: [
|
||||||
},
|
'#pornstr',
|
||||||
{
|
'#porn',
|
||||||
title: 'Local',
|
'#nsfw',
|
||||||
data: [
|
'#bdsm',
|
||||||
{
|
'#lewd',
|
||||||
kind: WIDGET_KIND.tmp.xfeed,
|
'#kink',
|
||||||
title: 'Group feeds',
|
'#sexy',
|
||||||
description: 'All posts from specific people you want to keep up with',
|
'#loli',
|
||||||
},
|
'#hentai',
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.local.files,
|
|
||||||
title: 'Files',
|
|
||||||
description: 'All files shared by people you follow',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.local.articles,
|
|
||||||
title: 'Articles',
|
|
||||||
description: 'All articles shared by people you follow',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Global',
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.tmp.xhashtag,
|
|
||||||
title: 'Hashtag',
|
|
||||||
description: 'All posts have a specific hashtag',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.global.files,
|
|
||||||
title: 'Files',
|
|
||||||
description: 'All files shared by people in your current relay set',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.global.articles,
|
|
||||||
title: 'Articles',
|
|
||||||
description: 'All articles shared by people in your current relay set',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'nostr.band',
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.nostrBand.trendingAccounts,
|
|
||||||
title: 'Accounts',
|
|
||||||
description: 'Trending accounts from the last 24 hours',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.nostrBand.trendingNotes,
|
|
||||||
title: 'Notes',
|
|
||||||
description: 'Trending notes from the last 24 hours',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Other',
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
kind: WIDGET_KIND.local.notification,
|
|
||||||
title: 'Notification',
|
|
||||||
description: 'Everything happens around you',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -17,6 +17,32 @@ export function useWidget() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const replaceWidget = useMutation({
|
||||||
|
mutationFn: async ({ currentId, widget }: { currentId: string; widget: Widget }) => {
|
||||||
|
// Cancel any outgoing refetches
|
||||||
|
await queryClient.cancelQueries({ queryKey: ['widgets'] });
|
||||||
|
|
||||||
|
// Snapshot the previous value
|
||||||
|
const prevWidgets = queryClient.getQueryData(['widgets']);
|
||||||
|
|
||||||
|
// create new widget
|
||||||
|
await db.removeWidget(currentId);
|
||||||
|
const newWidget = await db.createWidget(widget.kind, widget.title, widget.content);
|
||||||
|
|
||||||
|
// Optimistically update to the new value
|
||||||
|
queryClient.setQueryData(['widgets'], (prev: Widget[]) => [
|
||||||
|
...prev.filter((t) => t.id !== currentId),
|
||||||
|
newWidget,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Return a context object with the snapshotted value
|
||||||
|
return { prevWidgets };
|
||||||
|
},
|
||||||
|
onSettled: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['widgets'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const removeWidget = useMutation({
|
const removeWidget = useMutation({
|
||||||
mutationFn: async (id: string) => {
|
mutationFn: async (id: string) => {
|
||||||
// Cancel any outgoing refetches
|
// Cancel any outgoing refetches
|
||||||
@ -41,5 +67,5 @@ export function useWidget() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { addWidget, removeWidget };
|
return { addWidget, replaceWidget, removeWidget };
|
||||||
}
|
}
|
||||||
|