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 {
|
||||
ArticleWidget,
|
||||
FileWidget,
|
||||
GroupWidget,
|
||||
HashtagWidget,
|
||||
NewsfeedWidget,
|
||||
NotificationWidget,
|
||||
ThreadWidget,
|
||||
ToggleWidgetList,
|
||||
TopicWidget,
|
||||
TrendingAccountsWidget,
|
||||
TrendingNotesWidget,
|
||||
UserWidget,
|
||||
WidgetList,
|
||||
} from '@shared/widgets';
|
||||
|
||||
@ -54,6 +63,24 @@ export function HomeScreen() {
|
||||
return <NotificationWidget key={widget.id} />;
|
||||
case WIDGET_KIND.newsfeed:
|
||||
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:
|
||||
return <WidgetList key={widget.id} widget={widget} />;
|
||||
default:
|
||||
|
@ -15,7 +15,7 @@ interface Response {
|
||||
profiles: Array<{ pubkey: string }>;
|
||||
}
|
||||
|
||||
export function TrendingAccountsWidget({ params }: { params: Widget }) {
|
||||
export function TrendingAccountsWidget({ widget }: { widget: Widget }) {
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ['trending-profiles-widget'],
|
||||
queryFn: async () => {
|
||||
@ -35,7 +35,7 @@ export function TrendingAccountsWidget({ params }: { params: Widget }) {
|
||||
|
||||
return (
|
||||
<WidgetWrapper>
|
||||
<TitleBar id={params.id} title="Trending Accounts" />
|
||||
<TitleBar id={widget.id} title="Trending Accounts" />
|
||||
<div className="flex-1">
|
||||
{status === 'pending' ? (
|
||||
<div className="flex h-full w-full items-center justify-center ">
|
||||
|
@ -16,7 +16,7 @@ export function ToggleWidgetList() {
|
||||
onClick={() =>
|
||||
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" />
|
||||
<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 { WidgetWrapper } from '@shared/widgets';
|
||||
|
||||
import { TOPICS, WIDGET_KIND } from '@stores/constants';
|
||||
|
||||
import { useWidget } from '@utils/hooks/useWidget';
|
||||
import { Widget } from '@utils/types';
|
||||
|
||||
export function WidgetList({ widget }: { widget: Widget }) {
|
||||
const { replaceWidget } = useWidget();
|
||||
|
||||
return (
|
||||
<WidgetWrapper>
|
||||
<TitleBar id={widget.id} title="Add widgets" />
|
||||
<div className="flex-1 overflow-y-auto pb-10 scrollbar-none">
|
||||
<div className="flex flex-col gap-6 px-3">
|
||||
<div className="border-t border-neutral-200 pt-6 dark:border-neutral-800">
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
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"
|
||||
>
|
||||
Build your own widget{' '}
|
||||
<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">
|
||||
<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">
|
||||
Coming soon
|
||||
</span>
|
||||
<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">
|
||||
Topics
|
||||
</h3>
|
||||
<div className="flex flex-col gap-3">
|
||||
{TOPICS.sort((a, b) => a.title.localeCompare(b.title)).map(
|
||||
(topic, index) => (
|
||||
<div
|
||||
key={index}
|
||||
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="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>
|
||||
</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>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import { FetchFilter } from 'nostr-fetch';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { VList } from 'virtua';
|
||||
|
||||
@ -33,18 +34,18 @@ export function TopicWidget({ widget }: { widget: Widget }) {
|
||||
pageParam: number;
|
||||
}) => {
|
||||
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 dedupQueue = new Set();
|
||||
|
||||
const events = await fetcher.fetchLatestEvents(
|
||||
relayUrls,
|
||||
{
|
||||
kinds: [NDKKind.Text, NDKKind.Repost],
|
||||
'#t': hashtags,
|
||||
},
|
||||
FETCH_LIMIT,
|
||||
{ asOf: pageParam === 0 ? undefined : pageParam, abortSignal: signal }
|
||||
);
|
||||
const events = await fetcher.fetchLatestEvents(relayUrls, filter, FETCH_LIMIT, {
|
||||
asOf: pageParam === 0 ? undefined : pageParam,
|
||||
abortSignal: signal,
|
||||
});
|
||||
|
||||
const ndkEvents = events.map((event) => {
|
||||
return new NDKEvent(ndk, event);
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { WidgetGroup } from '@utils/types';
|
||||
|
||||
export const FULL_RELAYS = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.primal.net',
|
||||
'wss://relayable.org',
|
||||
'wss://relay.nostr.band/all',
|
||||
'wss://nostr.mutinywallet.com',
|
||||
@ -59,8 +56,6 @@ export const WIDGET_KIND = {
|
||||
export const TOPICS = [
|
||||
{
|
||||
title: 'Gaming',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#gamestr',
|
||||
'#gaming',
|
||||
@ -74,31 +69,23 @@ export const TOPICS = [
|
||||
'#twitch',
|
||||
'#fortnite',
|
||||
'#pc',
|
||||
'#memes',
|
||||
'#pcgaming',
|
||||
'#gamers',
|
||||
'#gamingcommunity',
|
||||
'#youtube',
|
||||
'#switch',
|
||||
'#gamergirl',
|
||||
'#nintendo',
|
||||
'#gta',
|
||||
'#callofduty',
|
||||
'#streamer',
|
||||
'#follow',
|
||||
'#pubg',
|
||||
'#videogame',
|
||||
'#esports',
|
||||
'#bhfyp',
|
||||
'#meme',
|
||||
'#twitchstreamer',
|
||||
'#art',
|
||||
'#genshinimpact',
|
||||
'#honkaiimpact',
|
||||
'#warthunder',
|
||||
'#hovoverse',
|
||||
'#hoyoverse',
|
||||
'#arknights',
|
||||
'#soul',
|
||||
'#soullike',
|
||||
'#eldenring',
|
||||
'#steam',
|
||||
'#pubg',
|
||||
@ -108,12 +95,17 @@ export const TOPICS = [
|
||||
'#starfield',
|
||||
'#gta6',
|
||||
'#gameoftheyear',
|
||||
'#darksoul',
|
||||
'#batterfield',
|
||||
'#dota',
|
||||
'#rpg',
|
||||
'#thewitcher',
|
||||
'#rogally',
|
||||
'#rog',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Music',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#audiostr',
|
||||
'#musicstr',
|
||||
@ -125,18 +117,12 @@ export const TOPICS = [
|
||||
'#musician',
|
||||
'#artist',
|
||||
'#musica',
|
||||
'#instagood',
|
||||
'#singer',
|
||||
'#dj',
|
||||
'#follow',
|
||||
'#rock',
|
||||
'#like',
|
||||
'#dance',
|
||||
'#guitar',
|
||||
'#s',
|
||||
'#photography',
|
||||
'#song',
|
||||
'#bhfyp',
|
||||
'#newmusic',
|
||||
'#producer',
|
||||
'#life',
|
||||
@ -146,12 +132,14 @@ export const TOPICS = [
|
||||
'#explorepage',
|
||||
'#viral',
|
||||
'#beats',
|
||||
'#dvd',
|
||||
'#amass',
|
||||
'#bluray',
|
||||
'#Blu_Ray',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Photography',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#photography',
|
||||
'#photooftheday',
|
||||
@ -159,57 +147,38 @@ export const TOPICS = [
|
||||
'#photo',
|
||||
'#nature',
|
||||
'#picoftheday',
|
||||
'#like',
|
||||
'#photographer',
|
||||
'#beautiful',
|
||||
'#follow',
|
||||
'#art',
|
||||
'#fashion',
|
||||
'#travel',
|
||||
'#bhfyp',
|
||||
'#photoshoot',
|
||||
'#likeforlikes',
|
||||
'#instadaily',
|
||||
'#naturephotography',
|
||||
'#model',
|
||||
'#me',
|
||||
'#smile',
|
||||
'#style',
|
||||
'#instalike',
|
||||
'#happy',
|
||||
'#likes',
|
||||
'#myself',
|
||||
'#followme',
|
||||
'#followforfollowback',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Art',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#nostrdesign',
|
||||
'#artstr',
|
||||
'#art',
|
||||
'#artist',
|
||||
'#love',
|
||||
'#drawing',
|
||||
'#photography',
|
||||
'#artwork',
|
||||
'#instagood',
|
||||
'#photooftheday',
|
||||
'#painting',
|
||||
'#fashion',
|
||||
'#like',
|
||||
'#artistsoninstagram',
|
||||
'#beautiful',
|
||||
'#illustration',
|
||||
'#digitalart',
|
||||
'#follow',
|
||||
'#design',
|
||||
'#nature',
|
||||
'#picoftheday',
|
||||
'#photo',
|
||||
'#bhfyp',
|
||||
'#sketch',
|
||||
'#style',
|
||||
'#arte',
|
||||
@ -221,8 +190,6 @@ export const TOPICS = [
|
||||
},
|
||||
{
|
||||
title: 'Movie',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#filmstr',
|
||||
'#moviestr',
|
||||
@ -233,9 +200,6 @@ export const TOPICS = [
|
||||
'#films',
|
||||
'#hollywood',
|
||||
'#actor',
|
||||
'#love',
|
||||
'#s',
|
||||
'#art',
|
||||
'#cinematography',
|
||||
'#actress',
|
||||
'#netflix',
|
||||
@ -243,7 +207,6 @@ export const TOPICS = [
|
||||
'#music',
|
||||
'#filmmaking',
|
||||
'#horror',
|
||||
'#instagood',
|
||||
'#bollywood',
|
||||
'#movienight',
|
||||
'#photography',
|
||||
@ -259,8 +222,6 @@ export const TOPICS = [
|
||||
},
|
||||
{
|
||||
title: 'Technology',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#apple',
|
||||
'#xiaomi',
|
||||
@ -276,30 +237,27 @@ export const TOPICS = [
|
||||
'#iphone',
|
||||
'#technews',
|
||||
'#science',
|
||||
'#design',
|
||||
'#gadgets',
|
||||
'#electronics',
|
||||
'#android',
|
||||
'#software',
|
||||
'#programming',
|
||||
'#smartphone',
|
||||
'#bhfyp',
|
||||
'#samsung',
|
||||
'#instagood',
|
||||
'#coding',
|
||||
'#computer',
|
||||
'#pro',
|
||||
'#education',
|
||||
'#security',
|
||||
'#gadget',
|
||||
'#mobile',
|
||||
'#technologynews',
|
||||
'#opensource',
|
||||
'#tor',
|
||||
'#bitcoin',
|
||||
'#lightning',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Anime',
|
||||
description: '',
|
||||
kind: WIDGET_KIND.global.topic,
|
||||
content: [
|
||||
'#animestr',
|
||||
'#anime',
|
||||
@ -317,78 +275,23 @@ export const TOPICS = [
|
||||
'#aot',
|
||||
'#hentai',
|
||||
'#fanart',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const DEFAULT_WIDGETS: Array<WidgetGroup> = [
|
||||
{
|
||||
title: 'Topics',
|
||||
data: TOPICS,
|
||||
},
|
||||
{
|
||||
title: 'Local',
|
||||
data: [
|
||||
{
|
||||
kind: WIDGET_KIND.tmp.xfeed,
|
||||
title: 'Group feeds',
|
||||
description: 'All posts from specific people you want to keep up with',
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
'#loli',
|
||||
'#vocaloid',
|
||||
'#vtuber',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'NSFW',
|
||||
content: [
|
||||
'#pornstr',
|
||||
'#porn',
|
||||
'#nsfw',
|
||||
'#bdsm',
|
||||
'#lewd',
|
||||
'#kink',
|
||||
'#sexy',
|
||||
'#loli',
|
||||
'#hentai',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
@ -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({
|
||||
mutationFn: async (id: string) => {
|
||||
// Cancel any outgoing refetches
|
||||
@ -41,5 +67,5 @@ export function useWidget() {
|
||||
},
|
||||
});
|
||||
|
||||
return { addWidget, removeWidget };
|
||||
return { addWidget, replaceWidget, removeWidget };
|
||||
}
|
||||
|