style: 🎨
This commit is contained in:
@@ -1,78 +1,78 @@
|
||||
import { useToast } from '@/hooks'
|
||||
import { useBookmarks } from '@/providers/BookmarksProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { BookmarkIcon, Loader } from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
||||
export default function BookmarkButton({ event }: { event: Event }) {
|
||||
const { t } = useTranslation()
|
||||
const { toast } = useToast()
|
||||
const { pubkey: accountPubkey, bookmarkListEvent, checkLogin } = useNostr()
|
||||
const { addBookmark, removeBookmark } = useBookmarks()
|
||||
const [updating, setUpdating] = useState(false)
|
||||
const isBookmarked = useMemo(
|
||||
() => bookmarkListEvent?.tags.some((tag) => tag[0] === 'e' && tag[1] === event.id),
|
||||
[bookmarkListEvent, event]
|
||||
)
|
||||
|
||||
if (!accountPubkey) return null
|
||||
|
||||
const handleBookmark = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
checkLogin(async () => {
|
||||
if (isBookmarked) return
|
||||
|
||||
setUpdating(true)
|
||||
try {
|
||||
await addBookmark(event)
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('Bookmark failed'),
|
||||
description: (error as Error).message,
|
||||
variant: 'destructive'
|
||||
})
|
||||
} finally {
|
||||
setUpdating(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoveBookmark = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
checkLogin(async () => {
|
||||
if (!isBookmarked) return
|
||||
|
||||
setUpdating(true)
|
||||
try {
|
||||
await removeBookmark(event)
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('Remove bookmark failed'),
|
||||
description: (error as Error).message,
|
||||
variant: 'destructive'
|
||||
})
|
||||
} finally {
|
||||
setUpdating(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`flex items-center gap-1 ${
|
||||
isBookmarked ? 'text-rose-400' : 'text-muted-foreground'
|
||||
} enabled:hover:text-rose-400 px-3 h-full`}
|
||||
onClick={isBookmarked ? handleRemoveBookmark : handleBookmark}
|
||||
disabled={updating}
|
||||
title={isBookmarked ? t('Remove bookmark') : t('Bookmark')}
|
||||
>
|
||||
{updating ? (
|
||||
<Loader className="animate-spin" />
|
||||
) : (
|
||||
<BookmarkIcon className={isBookmarked ? 'fill-rose-400' : ''} />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
import { useToast } from '@/hooks'
|
||||
import { useBookmarks } from '@/providers/BookmarksProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { BookmarkIcon, Loader } from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
||||
export default function BookmarkButton({ event }: { event: Event }) {
|
||||
const { t } = useTranslation()
|
||||
const { toast } = useToast()
|
||||
const { pubkey: accountPubkey, bookmarkListEvent, checkLogin } = useNostr()
|
||||
const { addBookmark, removeBookmark } = useBookmarks()
|
||||
const [updating, setUpdating] = useState(false)
|
||||
const isBookmarked = useMemo(
|
||||
() => bookmarkListEvent?.tags.some((tag) => tag[0] === 'e' && tag[1] === event.id),
|
||||
[bookmarkListEvent, event]
|
||||
)
|
||||
|
||||
if (!accountPubkey) return null
|
||||
|
||||
const handleBookmark = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
checkLogin(async () => {
|
||||
if (isBookmarked) return
|
||||
|
||||
setUpdating(true)
|
||||
try {
|
||||
await addBookmark(event)
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('Bookmark failed'),
|
||||
description: (error as Error).message,
|
||||
variant: 'destructive'
|
||||
})
|
||||
} finally {
|
||||
setUpdating(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoveBookmark = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
checkLogin(async () => {
|
||||
if (!isBookmarked) return
|
||||
|
||||
setUpdating(true)
|
||||
try {
|
||||
await removeBookmark(event)
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('Remove bookmark failed'),
|
||||
description: (error as Error).message,
|
||||
variant: 'destructive'
|
||||
})
|
||||
} finally {
|
||||
setUpdating(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`flex items-center gap-1 ${
|
||||
isBookmarked ? 'text-rose-400' : 'text-muted-foreground'
|
||||
} enabled:hover:text-rose-400 px-3 h-full`}
|
||||
onClick={isBookmarked ? handleRemoveBookmark : handleBookmark}
|
||||
disabled={updating}
|
||||
title={isBookmarked ? t('Remove bookmark') : t('Bookmark')}
|
||||
>
|
||||
{updating ? (
|
||||
<Loader className="animate-spin" />
|
||||
) : (
|
||||
<BookmarkIcon className={isBookmarked ? 'fill-rose-400' : ''} />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
import { useFetchEvent } from '@/hooks'
|
||||
import { generateEventIdFromETag } from '@/lib/tag'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { kinds } from 'nostr-tools'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard'
|
||||
|
||||
const SHOW_COUNT = 10
|
||||
|
||||
export default function BookmarkList() {
|
||||
const { t } = useTranslation()
|
||||
const { bookmarkListEvent } = useNostr()
|
||||
const eventIds = useMemo(() => {
|
||||
if (!bookmarkListEvent) return []
|
||||
|
||||
return (
|
||||
bookmarkListEvent.tags
|
||||
.map((tag) => (tag[0] === 'e' ? generateEventIdFromETag(tag) : undefined))
|
||||
.filter(Boolean) as `nevent1${string}`[]
|
||||
).reverse()
|
||||
}, [bookmarkListEvent])
|
||||
const [showCount, setShowCount] = useState(SHOW_COUNT)
|
||||
const bottomRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const options = {
|
||||
root: null,
|
||||
rootMargin: '10px',
|
||||
threshold: 0.1
|
||||
}
|
||||
|
||||
const loadMore = () => {
|
||||
if (showCount < eventIds.length) {
|
||||
setShowCount((prev) => prev + SHOW_COUNT)
|
||||
}
|
||||
}
|
||||
|
||||
const observerInstance = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
loadMore()
|
||||
}
|
||||
}, options)
|
||||
|
||||
const currentBottomRef = bottomRef.current
|
||||
|
||||
if (currentBottomRef) {
|
||||
observerInstance.observe(currentBottomRef)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (observerInstance && currentBottomRef) {
|
||||
observerInstance.unobserve(currentBottomRef)
|
||||
}
|
||||
}
|
||||
}, [showCount, eventIds])
|
||||
|
||||
if (eventIds.length === 0) {
|
||||
return (
|
||||
<div className="mt-2 text-sm text-center text-muted-foreground">
|
||||
{t('no bookmarks found')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{eventIds.slice(0, showCount).map((eventId) => (
|
||||
<BookmarkedNote key={eventId} eventId={eventId} />
|
||||
))}
|
||||
|
||||
{showCount < eventIds.length ? (
|
||||
<div ref={bottomRef}>
|
||||
<NoteCardLoadingSkeleton isPictures={false} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center text-sm text-muted-foreground mt-2">
|
||||
{t('no more bookmarks')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function BookmarkedNote({ eventId }: { eventId: string }) {
|
||||
const { event, isFetching } = useFetchEvent(eventId)
|
||||
|
||||
if (isFetching) {
|
||||
return <NoteCardLoadingSkeleton isPictures={false} />
|
||||
}
|
||||
|
||||
if (!event || event.kind !== kinds.ShortTextNote) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <NoteCard event={event} className="w-full" />
|
||||
}
|
||||
import { useFetchEvent } from '@/hooks'
|
||||
import { generateEventIdFromETag } from '@/lib/tag'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { kinds } from 'nostr-tools'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NoteCard, { NoteCardLoadingSkeleton } from '../NoteCard'
|
||||
|
||||
const SHOW_COUNT = 10
|
||||
|
||||
export default function BookmarkList() {
|
||||
const { t } = useTranslation()
|
||||
const { bookmarkListEvent } = useNostr()
|
||||
const eventIds = useMemo(() => {
|
||||
if (!bookmarkListEvent) return []
|
||||
|
||||
return (
|
||||
bookmarkListEvent.tags
|
||||
.map((tag) => (tag[0] === 'e' ? generateEventIdFromETag(tag) : undefined))
|
||||
.filter(Boolean) as `nevent1${string}`[]
|
||||
).reverse()
|
||||
}, [bookmarkListEvent])
|
||||
const [showCount, setShowCount] = useState(SHOW_COUNT)
|
||||
const bottomRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const options = {
|
||||
root: null,
|
||||
rootMargin: '10px',
|
||||
threshold: 0.1
|
||||
}
|
||||
|
||||
const loadMore = () => {
|
||||
if (showCount < eventIds.length) {
|
||||
setShowCount((prev) => prev + SHOW_COUNT)
|
||||
}
|
||||
}
|
||||
|
||||
const observerInstance = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
loadMore()
|
||||
}
|
||||
}, options)
|
||||
|
||||
const currentBottomRef = bottomRef.current
|
||||
|
||||
if (currentBottomRef) {
|
||||
observerInstance.observe(currentBottomRef)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (observerInstance && currentBottomRef) {
|
||||
observerInstance.unobserve(currentBottomRef)
|
||||
}
|
||||
}
|
||||
}, [showCount, eventIds])
|
||||
|
||||
if (eventIds.length === 0) {
|
||||
return (
|
||||
<div className="mt-2 text-sm text-center text-muted-foreground">
|
||||
{t('no bookmarks found')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{eventIds.slice(0, showCount).map((eventId) => (
|
||||
<BookmarkedNote key={eventId} eventId={eventId} />
|
||||
))}
|
||||
|
||||
{showCount < eventIds.length ? (
|
||||
<div ref={bottomRef}>
|
||||
<NoteCardLoadingSkeleton isPictures={false} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center text-sm text-muted-foreground mt-2">
|
||||
{t('no more bookmarks')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function BookmarkedNote({ eventId }: { eventId: string }) {
|
||||
const { event, isFetching } = useFetchEvent(eventId)
|
||||
|
||||
if (isFetching) {
|
||||
return <NoteCardLoadingSkeleton isPictures={false} />
|
||||
}
|
||||
|
||||
if (!event || event.kind !== kinds.ShortTextNote) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <NoteCard event={event} className="w-full" />
|
||||
}
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { SimpleUserAvatar } from '@/components/UserAvatar'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function NewNotesButton({
|
||||
newEvents = [],
|
||||
onClick
|
||||
}: {
|
||||
newEvents?: Event[]
|
||||
onClick?: () => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const pubkeys = useMemo(() => {
|
||||
const arr: string[] = []
|
||||
for (const event of newEvents) {
|
||||
if (!arr.includes(event.pubkey)) {
|
||||
arr.push(event.pubkey)
|
||||
}
|
||||
if (arr.length >= 3) break
|
||||
}
|
||||
return arr
|
||||
}, [newEvents])
|
||||
|
||||
return (
|
||||
<>
|
||||
{newEvents.length > 0 && (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full flex justify-center z-40 pointer-events-none',
|
||||
isSmallScreen ? 'fixed' : 'absolute bottom-4'
|
||||
)}
|
||||
style={isSmallScreen ? { bottom: 'calc(4rem + env(safe-area-inset-bottom))' } : undefined}
|
||||
>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className="group rounded-full h-fit pl-2 pr-3 hover:bg-primary-hover pointer-events-auto"
|
||||
>
|
||||
{pubkeys.length > 0 && (
|
||||
<div className="flex items-center">
|
||||
{pubkeys.map((pubkey, index) => (
|
||||
<div
|
||||
key={pubkey}
|
||||
className="relative -mr-2.5 last:mr-0"
|
||||
style={{ zIndex: 3 - index }}
|
||||
>
|
||||
<SimpleUserAvatar
|
||||
userId={pubkey}
|
||||
size="small"
|
||||
className="border-primary border-2 group-hover:border-primary-hover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-md font-medium">
|
||||
{t('Show n new notes', { n: newEvents.length > 99 ? '99+' : newEvents.length })}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { SimpleUserAvatar } from '@/components/UserAvatar'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function NewNotesButton({
|
||||
newEvents = [],
|
||||
onClick
|
||||
}: {
|
||||
newEvents?: Event[]
|
||||
onClick?: () => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const pubkeys = useMemo(() => {
|
||||
const arr: string[] = []
|
||||
for (const event of newEvents) {
|
||||
if (!arr.includes(event.pubkey)) {
|
||||
arr.push(event.pubkey)
|
||||
}
|
||||
if (arr.length >= 3) break
|
||||
}
|
||||
return arr
|
||||
}, [newEvents])
|
||||
|
||||
return (
|
||||
<>
|
||||
{newEvents.length > 0 && (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full flex justify-center z-40 pointer-events-none',
|
||||
isSmallScreen ? 'fixed' : 'absolute bottom-4'
|
||||
)}
|
||||
style={isSmallScreen ? { bottom: 'calc(4rem + env(safe-area-inset-bottom))' } : undefined}
|
||||
>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className="group rounded-full h-fit pl-2 pr-3 hover:bg-primary-hover pointer-events-auto"
|
||||
>
|
||||
{pubkeys.length > 0 && (
|
||||
<div className="flex items-center">
|
||||
{pubkeys.map((pubkey, index) => (
|
||||
<div
|
||||
key={pubkey}
|
||||
className="relative -mr-2.5 last:mr-0"
|
||||
style={{ zIndex: 3 - index }}
|
||||
>
|
||||
<SimpleUserAvatar
|
||||
userId={pubkey}
|
||||
size="small"
|
||||
className="border-primary border-2 group-hover:border-primary-hover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-md font-medium">
|
||||
{t('Show n new notes', { n: newEvents.length > 99 ? '99+' : newEvents.length })}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export default function Likes({ event }: { event: Event }) {
|
||||
like(key, emoji)
|
||||
}}
|
||||
>
|
||||
{liking === key ? <Loader className="animate-spin size-5" /> : <Emoji emoji={emoji} />}
|
||||
{liking === key ? <Loader className="animate-spin size-4" /> : <Emoji emoji={emoji} />}
|
||||
<div className="text-sm">{pubkeys.size}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
import { createBookmarkDraftEvent } from '@/lib/draft-event'
|
||||
import client from '@/services/client.service'
|
||||
import { createContext, useContext } from 'react'
|
||||
import { useNostr } from './NostrProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
||||
type TBookmarksContext = {
|
||||
addBookmark: (event: Event) => Promise<void>
|
||||
removeBookmark: (event: Event) => Promise<void>
|
||||
}
|
||||
|
||||
const BookmarksContext = createContext<TBookmarksContext | undefined>(undefined)
|
||||
|
||||
export const useBookmarks = () => {
|
||||
const context = useContext(BookmarksContext)
|
||||
if (!context) {
|
||||
throw new Error('useBookmarks must be used within a BookmarksProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
export function BookmarksProvider({ children }: { children: React.ReactNode }) {
|
||||
const { pubkey: accountPubkey, publish, updateBookmarkListEvent } = useNostr()
|
||||
|
||||
const addBookmark = async (event: Event) => {
|
||||
if (!accountPubkey) return
|
||||
|
||||
const bookmarkListEvent = await client.fetchBookmarkListEvent(accountPubkey)
|
||||
const currentTags = bookmarkListEvent?.tags || []
|
||||
|
||||
if (currentTags.some((tag) => tag[0] === 'e' && tag[1] === event.id)) return
|
||||
|
||||
const newBookmarkDraftEvent = createBookmarkDraftEvent(
|
||||
[...currentTags, ['e', event.id, client.getEventHint(event.id), '', event.pubkey]],
|
||||
bookmarkListEvent?.content
|
||||
)
|
||||
const newBookmarkEvent = await publish(newBookmarkDraftEvent)
|
||||
await updateBookmarkListEvent(newBookmarkEvent)
|
||||
}
|
||||
|
||||
const removeBookmark = async (event: Event) => {
|
||||
if (!accountPubkey) return
|
||||
|
||||
const bookmarkListEvent = await client.fetchBookmarkListEvent(accountPubkey)
|
||||
if (!bookmarkListEvent) return
|
||||
|
||||
const newTags = bookmarkListEvent.tags.filter((tag) => !(tag[0] === 'e' && tag[1] === event.id))
|
||||
if (newTags.length === bookmarkListEvent.tags.length) return
|
||||
|
||||
const newBookmarkDraftEvent = createBookmarkDraftEvent(newTags, bookmarkListEvent.content)
|
||||
const newBookmarkEvent = await publish(newBookmarkDraftEvent)
|
||||
await updateBookmarkListEvent(newBookmarkEvent)
|
||||
}
|
||||
|
||||
return (
|
||||
<BookmarksContext.Provider
|
||||
value={{
|
||||
addBookmark,
|
||||
removeBookmark
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</BookmarksContext.Provider>
|
||||
)
|
||||
}
|
||||
import { createBookmarkDraftEvent } from '@/lib/draft-event'
|
||||
import client from '@/services/client.service'
|
||||
import { createContext, useContext } from 'react'
|
||||
import { useNostr } from './NostrProvider'
|
||||
import { Event } from 'nostr-tools'
|
||||
|
||||
type TBookmarksContext = {
|
||||
addBookmark: (event: Event) => Promise<void>
|
||||
removeBookmark: (event: Event) => Promise<void>
|
||||
}
|
||||
|
||||
const BookmarksContext = createContext<TBookmarksContext | undefined>(undefined)
|
||||
|
||||
export const useBookmarks = () => {
|
||||
const context = useContext(BookmarksContext)
|
||||
if (!context) {
|
||||
throw new Error('useBookmarks must be used within a BookmarksProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
export function BookmarksProvider({ children }: { children: React.ReactNode }) {
|
||||
const { pubkey: accountPubkey, publish, updateBookmarkListEvent } = useNostr()
|
||||
|
||||
const addBookmark = async (event: Event) => {
|
||||
if (!accountPubkey) return
|
||||
|
||||
const bookmarkListEvent = await client.fetchBookmarkListEvent(accountPubkey)
|
||||
const currentTags = bookmarkListEvent?.tags || []
|
||||
|
||||
if (currentTags.some((tag) => tag[0] === 'e' && tag[1] === event.id)) return
|
||||
|
||||
const newBookmarkDraftEvent = createBookmarkDraftEvent(
|
||||
[...currentTags, ['e', event.id, client.getEventHint(event.id), '', event.pubkey]],
|
||||
bookmarkListEvent?.content
|
||||
)
|
||||
const newBookmarkEvent = await publish(newBookmarkDraftEvent)
|
||||
await updateBookmarkListEvent(newBookmarkEvent)
|
||||
}
|
||||
|
||||
const removeBookmark = async (event: Event) => {
|
||||
if (!accountPubkey) return
|
||||
|
||||
const bookmarkListEvent = await client.fetchBookmarkListEvent(accountPubkey)
|
||||
if (!bookmarkListEvent) return
|
||||
|
||||
const newTags = bookmarkListEvent.tags.filter((tag) => !(tag[0] === 'e' && tag[1] === event.id))
|
||||
if (newTags.length === bookmarkListEvent.tags.length) return
|
||||
|
||||
const newBookmarkDraftEvent = createBookmarkDraftEvent(newTags, bookmarkListEvent.content)
|
||||
const newBookmarkEvent = await publish(newBookmarkDraftEvent)
|
||||
await updateBookmarkListEvent(newBookmarkEvent)
|
||||
}
|
||||
|
||||
return (
|
||||
<BookmarksContext.Provider
|
||||
value={{
|
||||
addBookmark,
|
||||
removeBookmark
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</BookmarksContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
hover: 'hsl(var(--primary-hover))',
|
||||
hover: 'hsl(var(--primary-hover))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
|
||||
Reference in New Issue
Block a user