From 08138a77145fbdf74ad44b9a72b9a0b13d9df550 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Sat, 1 Mar 2025 15:49:31 +0100 Subject: [PATCH 1/3] Add migrations for watchlist table. --- apps/web/src/lib/database.types.ts | 156 ++++++++++++++++++ .../20250301153337_addWatchlist.sql | 31 ++++ 2 files changed, 187 insertions(+) create mode 100644 supabase/migrations/20250301153337_addWatchlist.sql diff --git a/apps/web/src/lib/database.types.ts b/apps/web/src/lib/database.types.ts index a7a3c0b..2a5fa28 100644 --- a/apps/web/src/lib/database.types.ts +++ b/apps/web/src/lib/database.types.ts @@ -166,6 +166,38 @@ export type Database = { } Relationships: [] } + watchlist: { + Row: { + bookmark_id: string + created_at: string + id: string + updated_at: string + user_id: string + } + Insert: { + bookmark_id: string + created_at?: string + id?: string + updated_at?: string + user_id?: string + } + Update: { + bookmark_id?: string + created_at?: string + id?: string + updated_at?: string + user_id?: string + } + Relationships: [ + { + foreignKeyName: 'watchlist_bookmark_id_fkey' + columns: ['bookmark_id'] + isOneToOne: false + referencedRelation: 'bookmarks' + referencedColumns: ['id'] + }, + ] + } } Views: { [_ in never]: never @@ -248,6 +280,7 @@ export type Database = { created_at: string | null id: string last_accessed_at: string | null + level: number | null metadata: Json | null name: string | null owner: string | null @@ -262,6 +295,7 @@ export type Database = { created_at?: string | null id?: string last_accessed_at?: string | null + level?: number | null metadata?: Json | null name?: string | null owner?: string | null @@ -276,6 +310,7 @@ export type Database = { created_at?: string | null id?: string last_accessed_at?: string | null + level?: number | null metadata?: Json | null name?: string | null owner?: string | null @@ -295,6 +330,38 @@ export type Database = { }, ] } + prefixes: { + Row: { + bucket_id: string + created_at: string | null + level: number + name: string + updated_at: string | null + } + Insert: { + bucket_id: string + created_at?: string | null + level?: number + name: string + updated_at?: string | null + } + Update: { + bucket_id?: string + created_at?: string | null + level?: number + name?: string + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: 'prefixes_bucketId_fkey' + columns: ['bucket_id'] + isOneToOne: false + referencedRelation: 'buckets' + referencedColumns: ['id'] + }, + ] + } s3_multipart_uploads: { Row: { bucket_id: string @@ -398,6 +465,13 @@ export type Database = { [_ in never]: never } Functions: { + add_prefixes: { + Args: { + _bucket_id: string + _name: string + } + Returns: undefined + } can_insert_object: { Args: { bucketid: string @@ -407,6 +481,13 @@ export type Database = { } Returns: undefined } + delete_prefix: { + Args: { + _bucket_id: string + _name: string + } + Returns: boolean + } extension: { Args: { name: string @@ -425,6 +506,24 @@ export type Database = { } Returns: string[] } + get_level: { + Args: { + name: string + } + Returns: number + } + get_prefix: { + Args: { + name: string + } + Returns: string + } + get_prefixes: { + Args: { + name: string + } + Returns: string[] + } get_size_by_bucket: { Args: Record Returns: { @@ -487,6 +586,63 @@ export type Database = { metadata: Json }[] } + search_legacy_v1: { + Args: { + prefix: string + bucketname: string + limits?: number + levels?: number + offsets?: number + search?: string + sortcolumn?: string + sortorder?: string + } + Returns: { + name: string + id: string + updated_at: string + created_at: string + last_accessed_at: string + metadata: Json + }[] + } + search_v1_optimised: { + Args: { + prefix: string + bucketname: string + limits?: number + levels?: number + offsets?: number + search?: string + sortcolumn?: string + sortorder?: string + } + Returns: { + name: string + id: string + updated_at: string + created_at: string + last_accessed_at: string + metadata: Json + }[] + } + search_v2: { + Args: { + prefix: string + bucket_name: string + limits?: number + levels?: number + start_after?: string + } + Returns: { + key: string + name: string + id: string + updated_at: string + created_at: string + metadata: Json + }[] + } } Enums: { [_ in never]: never diff --git a/supabase/migrations/20250301153337_addWatchlist.sql b/supabase/migrations/20250301153337_addWatchlist.sql new file mode 100644 index 0000000..cf58e10 --- /dev/null +++ b/supabase/migrations/20250301153337_addWatchlist.sql @@ -0,0 +1,31 @@ +create table if not exists watchlist ( + "id" uuid not null default uuid_generate_v4() primary key, + "bookmark_id" uuid not null references bookmarks(id) on delete cascade, + "user_id" uuid not null references auth.users(id) on delete cascade default auth.uid(), + "created_at" timestamp not null default now(), + "updated_at" timestamp not null default now() +); + +-- Enable RLS +alter table watchlist enable row level security; + +-- Create indexes for better performance +create index if not exists watchlist_bookmark_id_idx on watchlist(bookmark_id); +create index if not exists watchlist_user_id_idx on watchlist(user_id); + +-- RLS Policies +create policy "Users can view their own watchlist entries" on watchlist + for select to authenticated + using (auth.uid() = user_id); + +create policy "Users can insert their own watchlist entries" on watchlist + for insert to authenticated + with check (auth.uid() = user_id); + +create policy "Users can delete their own watchlist entries" on watchlist + for delete to authenticated + using (auth.uid() = user_id); + +-- Add unique constraint to prevent duplicate entries +alter table watchlist + add constraint unique_user_bookmark unique (user_id, bookmark_id); From 4e7650471a348dbfa6ac37c9afe51fb2ff4fb30b Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Sun, 2 Mar 2025 01:19:34 +0100 Subject: [PATCH 2/3] Add logic for detecting a video bookmark. --- .../app/(dashboard)/bookmarks/new/action.ts | 34 ++++++-- apps/web/src/lib/utils/watchable.ts | 44 ++++++++++ apps/web/src/lib/utils/watchlist.ts | 82 +++++++++++++++++++ 3 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 apps/web/src/lib/utils/watchable.ts create mode 100644 apps/web/src/lib/utils/watchlist.ts diff --git a/apps/web/src/app/(dashboard)/bookmarks/new/action.ts b/apps/web/src/app/(dashboard)/bookmarks/new/action.ts index 3c0eb86..a10d53d 100644 --- a/apps/web/src/app/(dashboard)/bookmarks/new/action.ts +++ b/apps/web/src/app/(dashboard)/bookmarks/new/action.ts @@ -1,27 +1,49 @@ 'use server' import type { IdName } from '@/src/components/edit-pages-for-bookmark' +import { isWatchable } from '@/src/lib/utils/watchable' +import { addToWatchlist } from '@/src/lib/utils/watchlist' import { createClient } from '@/src/utils/supabase/server' import { revalidatePath } from 'next/cache' export const save = async (name: string, url: string, tagIds: IdName[]) => { const supabase = await createClient() + // Get the current user + const { + data: { user }, + } = await supabase.auth.getUser() + if (!user) { + throw new Error('User not authenticated') + } + + // Create new tags if needed const tagNames = tagIds.filter(t => !t.id).map(t => ({ name: t.name })) const { data: newTags } = await supabase.from('tags').insert(tagNames).select() - const { data: bookmarks } = await supabase + // Create the bookmark + const { data: bookmark, error: bookmarkError } = await supabase .from('bookmarks') - .insert({ - name: name, - url: url, - }) + .insert({ name, url }) .select() + .single() + + if (bookmarkError) { + throw bookmarkError + } - const bookmark = bookmarks![0] + if (!bookmark) { + throw new Error('Failed to create bookmark') + } + // Add bookmark-tag relations const existingTags = tagIds.filter(t => t.id) const relations = [...(newTags || []), ...existingTags].map(r => ({ tag_id: r.id!, bookmark_id: bookmark.id })) await supabase.from('bookmarks_tags').insert(relations) + // If the URL is watchable, add it to the watchlist + if (isWatchable(url)) { + await addToWatchlist(bookmark.id) + } + revalidatePath('/bookmarks') } diff --git a/apps/web/src/lib/utils/watchable.ts b/apps/web/src/lib/utils/watchable.ts new file mode 100644 index 0000000..7194f5a --- /dev/null +++ b/apps/web/src/lib/utils/watchable.ts @@ -0,0 +1,44 @@ +// Video hosting domains that we consider watchable +const VIDEO_DOMAINS = new Set(['youtube.com', 'youtu.be', 'vimeo.com', 'dailymotion.com', 'twitch.tv']) + +// Common video file extensions +const VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.ogg', '.mov', '.avi', '.wmv', '.m4v', '.mpg', '.mpeg']) + +/** + * Extracts domain from a URL string + */ +function extractDomain(url: string): string { + try { + const { hostname } = new URL(url) + return hostname.toLowerCase() + } catch { + return '' + } +} + +/** + * Checks if a URL points to a video hosting platform + */ +function isVideoHostingPlatform(url: string): boolean { + const domain = extractDomain(url) + return VIDEO_DOMAINS.has(domain) || VIDEO_DOMAINS.has(`www.${domain}`) +} + +/** + * Checks if a URL points to a direct video file + */ +function isVideoFile(url: string): boolean { + const lowercaseUrl = url.toLowerCase() + return Array.from(VIDEO_EXTENSIONS).some(ext => lowercaseUrl.endsWith(ext)) +} + +/** + * Determines if a URL points to watchable content + */ +export function isWatchable(url: string | URL): boolean { + const urlString = typeof url === 'string' ? url : url.toString() + + if (!urlString) return false + + return isVideoHostingPlatform(urlString) || isVideoFile(urlString) +} diff --git a/apps/web/src/lib/utils/watchlist.ts b/apps/web/src/lib/utils/watchlist.ts new file mode 100644 index 0000000..4d47247 --- /dev/null +++ b/apps/web/src/lib/utils/watchlist.ts @@ -0,0 +1,82 @@ +import { type Database } from '@/src/lib/database.types' +import { createClient } from '@/src/utils/supabase/client' + +const supabase = createClient() + +type WatchlistEntry = Database['public']['Tables']['watchlist']['Row'] + +/** + * Add a bookmark to user's watchlist + */ +export async function addToWatchlist(bookmarkId: string): Promise { + try { + const { data: user } = await supabase.auth.getUser() + if (!user.user) return null + + const { data, error } = await supabase.from('watchlist').insert({ bookmark_id: bookmarkId }).select().single() + + if (error) { + console.error('Error adding to watchlist:', error) + return null + } + + return data + } catch (error) { + console.error('Error in addToWatchlist:', error) + return null + } +} + +/** + * Remove a bookmark from user's watchlist + */ +export async function removeFromWatchlist(bookmarkId: string): Promise { + try { + const { data: user } = await supabase.auth.getUser() + if (!user.user) return false + + const { error } = await supabase.from('watchlist').delete().match({ + bookmark_id: bookmarkId, + user_id: user.user.id, + }) + + if (error) { + console.error('Error removing from watchlist:', error) + return false + } + + return true + } catch (error) { + console.error('Error in removeFromWatchlist:', error) + return false + } +} + +/** + * Check if a bookmark is in user's watchlist + */ +export async function isInWatchlist(bookmarkId: string): Promise { + try { + const { data: user } = await supabase.auth.getUser() + if (!user.user) return false + + const { data, error } = await supabase + .from('watchlist') + .select('id') + .match({ + bookmark_id: bookmarkId, + user_id: user.user.id, + }) + .maybeSingle() + + if (error) { + console.error('Error checking watchlist:', error) + return false + } + + return !!data + } catch (error) { + console.error('Error in isInWatchlist:', error) + return false + } +} From ed823db89f4741c705c661a1d2cd0abe21cb001d Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Sun, 2 Mar 2025 22:05:41 +0100 Subject: [PATCH 3/3] Add UI for the watchlist. --- .../app/(dashboard)/bookmarks/new/action.ts | 2 +- .../(dashboard)/bookmarks/new/component.tsx | 34 ++------ .../list-watchlist-bookmarks-query.ts | 82 +++++++++++++++++++ .../src/app/(dashboard)/watchlist/page.tsx | 29 +++++++ .../watchlist/watchlist-bookmarks.tsx | 12 +++ apps/web/src/components/app-sidebar/index.tsx | 8 +- apps/web/src/components/bookmark/bookmark.tsx | 49 ++++++++++- apps/web/src/lib/utils/watchable.ts | 4 +- apps/web/src/lib/utils/watchlist.ts | 36 ++------ 9 files changed, 197 insertions(+), 59 deletions(-) create mode 100644 apps/web/src/app/(dashboard)/watchlist/list-watchlist-bookmarks-query.ts create mode 100644 apps/web/src/app/(dashboard)/watchlist/page.tsx create mode 100644 apps/web/src/app/(dashboard)/watchlist/watchlist-bookmarks.tsx diff --git a/apps/web/src/app/(dashboard)/bookmarks/new/action.ts b/apps/web/src/app/(dashboard)/bookmarks/new/action.ts index a10d53d..65410d0 100644 --- a/apps/web/src/app/(dashboard)/bookmarks/new/action.ts +++ b/apps/web/src/app/(dashboard)/bookmarks/new/action.ts @@ -42,7 +42,7 @@ export const save = async (name: string, url: string, tagIds: IdName[]) => { // If the URL is watchable, add it to the watchlist if (isWatchable(url)) { - await addToWatchlist(bookmark.id) + await addToWatchlist(supabase, bookmark.id) } revalidatePath('/bookmarks') diff --git a/apps/web/src/app/(dashboard)/bookmarks/new/component.tsx b/apps/web/src/app/(dashboard)/bookmarks/new/component.tsx index cb42bfb..d637653 100644 --- a/apps/web/src/app/(dashboard)/bookmarks/new/component.tsx +++ b/apps/web/src/app/(dashboard)/bookmarks/new/component.tsx @@ -24,6 +24,7 @@ import { import { toast } from 'sonner' import { EditPagesForBookmark } from '../../../../components/edit-pages-for-bookmark' import { createClient } from '../../../../utils/supabase/client' +import { save } from './action' const formId = 'create-new-bookmark' @@ -56,34 +57,15 @@ export const NewBookmarkComponent = () => { }) const onSubmit: SubmitHandler> = async values => { - const tagNames = values.tagIds.filter(t => !t.id).map(t => ({ name: t.name })) - const { data: newTags } = await supabase.from('tags').insert(tagNames).select() - - const { data, error } = await supabase - .from('bookmarks') - .insert({ - name: values.name, - url: values.url, - read: values.read, + try { + await save(values.name, values.url, values.tagIds) + toast.success('Bookmark created') + router.push('/bookmarks') + } catch (error) { + toast.error('Failed to create bookmark', { + description: error instanceof Error ? error.message : 'Please try again', }) - .select() - - if (error || data.length !== 1) { - // TODO: handle this case - return - } - const bookmark = data[0] - - const existingTags = values.tagIds.filter(t => t.id) - const relations = [...(newTags || []), ...existingTags].map(r => ({ tag_id: r.id!, bookmark_id: bookmark.id })) - - await supabase.from('bookmarks_tags').delete().eq('bookmark_id', bookmark.id) - if (relations.length > 0) { - await supabase.from('bookmarks_tags').insert(relations) } - - toast.success('Bookmark created') - router.push(`/bookmarks`) } return ( diff --git a/apps/web/src/app/(dashboard)/watchlist/list-watchlist-bookmarks-query.ts b/apps/web/src/app/(dashboard)/watchlist/list-watchlist-bookmarks-query.ts new file mode 100644 index 0000000..f32dd94 --- /dev/null +++ b/apps/web/src/app/(dashboard)/watchlist/list-watchlist-bookmarks-query.ts @@ -0,0 +1,82 @@ +import { BookmarkType } from '@/src/lib/supabase' +import { createClient } from '@/src/utils/supabase/client' +import { GetNextPageParamFunction, InfiniteData, useInfiniteQuery } from '@tanstack/react-query' + +const queryKey = (searchQuery: string | null) => ['watchlist-bookmarks', { searchQuery }] + +const PAGE_SIZE = 19 + +const queryFn = async ({ + pageParam: skip, + queryKey, +}: { + pageParam: number + queryKey: (string | { searchQuery: string | null })[] +}) => { + let searchQuery: string | undefined = undefined + if (queryKey.length === 2 && typeof queryKey[1] !== 'string') { + searchQuery = queryKey[1].searchQuery || undefined + } + + let query = createClient() + .from('watchlist') + .select( + ` + bookmark:bookmarks!inner ( + * + ) + `, + { count: 'exact' }, + ) + + if (searchQuery && searchQuery.length > 0) { + query = query.ilike('bookmark.name', `%${searchQuery}%`) + } + + const { data: watchlistItems, count } = await query + .order('created_at', { ascending: false }) + // secondary sort by id to avoid pagination issues + .order('id', { ascending: false }) + .range(skip, skip + PAGE_SIZE) + .throwOnError() + + const data = watchlistItems?.map(item => ({ + ...item.bookmark, + })) + + return { data, count } as { data: NonNullable; count: number } +} + +const getNextPageParam: GetNextPageParamFunction< + number, + { + data: NonNullable + count: number + } +> = (_, pages) => { + const bookmarks = pages.flatMap(p => p.data) + return bookmarks.length +} + +const selectData = ( + data: InfiniteData< + { + data: NonNullable + count: number + }, + number + >, +) => { + return { bookmarks: data.pages.flatMap(p => p.data), count: data.pages[0].count } +} + +export const useListWatchlistBookmarksQuery = (searchQuery: string | null) => { + return useInfiniteQuery({ + queryKey: queryKey(searchQuery), + queryFn: queryFn, + staleTime: 5000, + getNextPageParam: getNextPageParam, + initialPageParam: 0, + select: selectData, + }) +} diff --git a/apps/web/src/app/(dashboard)/watchlist/page.tsx b/apps/web/src/app/(dashboard)/watchlist/page.tsx new file mode 100644 index 0000000..6d9e943 --- /dev/null +++ b/apps/web/src/app/(dashboard)/watchlist/page.tsx @@ -0,0 +1,29 @@ +'use server' + +import { MainContentLayout } from '@/src/components/main-content-layout' +import { getQueryClient } from '@/src/lib/react-query-client' +import { checkAuthentication } from '@/src/lib/supabase' +import { HydrationBoundary, dehydrate } from '@tanstack/react-query' +import { Video } from 'lucide-react' +import { WatchlistBookmarks } from './watchlist-bookmarks' + +const WatchlistPage = async () => { + await checkAuthentication('/watchlist') + const queryClient = getQueryClient() + + return ( + + +
+
+
+
+ +
+
+ ) +} + +export default WatchlistPage diff --git a/apps/web/src/app/(dashboard)/watchlist/watchlist-bookmarks.tsx b/apps/web/src/app/(dashboard)/watchlist/watchlist-bookmarks.tsx new file mode 100644 index 0000000..42807ea --- /dev/null +++ b/apps/web/src/app/(dashboard)/watchlist/watchlist-bookmarks.tsx @@ -0,0 +1,12 @@ +'use client' + +import { Bookmarks } from '@/src/components/bookmarks' +import { useQueryState } from 'nuqs' +import { useListWatchlistBookmarksQuery } from './list-watchlist-bookmarks-query' + +export function WatchlistBookmarks() { + const [searchQuery] = useQueryState('q') + const query = useListWatchlistBookmarksQuery(searchQuery) + + return +} diff --git a/apps/web/src/components/app-sidebar/index.tsx b/apps/web/src/components/app-sidebar/index.tsx index 1bd53ae..d8d5a3e 100644 --- a/apps/web/src/components/app-sidebar/index.tsx +++ b/apps/web/src/components/app-sidebar/index.tsx @@ -14,7 +14,7 @@ import { SidebarMenuItem, SidebarMenuSkeleton, } from '@rememr/ui' -import { ChevronDown, Home, Inbox } from 'lucide-react' +import { ChevronDown, Home, Inbox, Video } from 'lucide-react' import Link from 'next/link' import { Suspense } from 'react' import { createClient } from '../../utils/supabase/server' @@ -55,6 +55,12 @@ export async function AppSidebar() { Reading list + + + + diff --git a/apps/web/src/components/bookmark/bookmark.tsx b/apps/web/src/components/bookmark/bookmark.tsx index 4976fe1..2e9eb9b 100755 --- a/apps/web/src/components/bookmark/bookmark.tsx +++ b/apps/web/src/components/bookmark/bookmark.tsx @@ -1,6 +1,9 @@ 'use client' import { BookmarkType } from '@/src/lib/supabase' +import { isWatchable } from '@/src/lib/utils/watchable' +import { addToWatchlist, isInWatchlist, removeFromWatchlist } from '@/src/lib/utils/watchlist' +import { createClient } from '@/src/utils/supabase/client' import { Button, Card, @@ -11,15 +14,19 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '@rememr/ui' -import { EllipsisVertical, Pen, Trash2 } from 'lucide-react' -import { useState } from 'react' +import { EllipsisVertical, Pen, Trash2, Video } from 'lucide-react' +import { useEffect, useState } from 'react' import { DeleteBookmarkDialog } from '../delete-bookmark' import { EditBookmarkDialog } from '../edit-bookmark' import { ChatSheet } from './chat-sheet' +const supabase = createClient() + export const Bookmark = (props: { bookmark: BookmarkType }) => { const [editBookmarkDialogShown, setEditBookmarkDialogShown] = useState(false) const [deleteBookmarkDialogShown, setDeleteBookmarkDialogShown] = useState(false) + const [isInWatchlistState, setIsInWatchlistState] = useState(false) + const [isLoading, setIsLoading] = useState(false) const bookmark = props.bookmark let hostname = '' @@ -28,6 +35,32 @@ export const Bookmark = (props: { bookmark: BookmarkType }) => { hostname = new URL(bookmark.url).hostname } catch {} + const isBookmarkWatchable = isWatchable(bookmark.url) + + useEffect(() => { + if (isBookmarkWatchable) { + isInWatchlist(supabase, bookmark.id).then(setIsInWatchlistState) + } + }, [bookmark.id, isBookmarkWatchable]) + + const handleWatchlistToggle = async () => { + if (isLoading) return + setIsLoading(true) + try { + if (isInWatchlistState) { + await removeFromWatchlist(supabase, bookmark.id) + setIsInWatchlistState(false) + } else { + await addToWatchlist(supabase, bookmark.id) + setIsInWatchlistState(true) + } + } catch (error) { + console.error('Error toggling watchlist:', error) + } finally { + setIsLoading(false) + } + } + return (
@@ -50,6 +83,18 @@ export const Bookmark = (props: { bookmark: BookmarkType }) => {
+ {isBookmarkWatchable && ( + + )} + diff --git a/apps/web/src/lib/utils/watchable.ts b/apps/web/src/lib/utils/watchable.ts index 7194f5a..d97d171 100644 --- a/apps/web/src/lib/utils/watchable.ts +++ b/apps/web/src/lib/utils/watchable.ts @@ -20,8 +20,8 @@ function extractDomain(url: string): string { * Checks if a URL points to a video hosting platform */ function isVideoHostingPlatform(url: string): boolean { - const domain = extractDomain(url) - return VIDEO_DOMAINS.has(domain) || VIDEO_DOMAINS.has(`www.${domain}`) + const domain = extractDomain(url).replace(/^www\./, '') + return VIDEO_DOMAINS.has(domain) } /** diff --git a/apps/web/src/lib/utils/watchlist.ts b/apps/web/src/lib/utils/watchlist.ts index 4d47247..8ebe55c 100644 --- a/apps/web/src/lib/utils/watchlist.ts +++ b/apps/web/src/lib/utils/watchlist.ts @@ -1,18 +1,16 @@ import { type Database } from '@/src/lib/database.types' -import { createClient } from '@/src/utils/supabase/client' - -const supabase = createClient() +import { SupabaseClient } from '@supabase/supabase-js' type WatchlistEntry = Database['public']['Tables']['watchlist']['Row'] /** * Add a bookmark to user's watchlist */ -export async function addToWatchlist(bookmarkId: string): Promise { +export async function addToWatchlist( + supabase: SupabaseClient, + bookmarkId: string, +): Promise { try { - const { data: user } = await supabase.auth.getUser() - if (!user.user) return null - const { data, error } = await supabase.from('watchlist').insert({ bookmark_id: bookmarkId }).select().single() if (error) { @@ -30,15 +28,9 @@ export async function addToWatchlist(bookmarkId: string): Promise { +export async function removeFromWatchlist(supabase: SupabaseClient, bookmarkId: string): Promise { try { - const { data: user } = await supabase.auth.getUser() - if (!user.user) return false - - const { error } = await supabase.from('watchlist').delete().match({ - bookmark_id: bookmarkId, - user_id: user.user.id, - }) + const { error } = await supabase.from('watchlist').delete().eq('bookmark_id', bookmarkId) if (error) { console.error('Error removing from watchlist:', error) @@ -55,19 +47,9 @@ export async function removeFromWatchlist(bookmarkId: string): Promise /** * Check if a bookmark is in user's watchlist */ -export async function isInWatchlist(bookmarkId: string): Promise { +export async function isInWatchlist(supabase: SupabaseClient, bookmarkId: string): Promise { try { - const { data: user } = await supabase.auth.getUser() - if (!user.user) return false - - const { data, error } = await supabase - .from('watchlist') - .select('id') - .match({ - bookmark_id: bookmarkId, - user_id: user.user.id, - }) - .maybeSingle() + const { data, error } = await supabase.from('watchlist').select('id').eq('bookmark_id', bookmarkId).maybeSingle() if (error) { console.error('Error checking watchlist:', error)