From be9798cfa54a39ffa0abca91a6007ad0c84c9f51 Mon Sep 17 00:00:00 2001 From: v0 Date: Sat, 7 Feb 2026 01:45:52 +0000 Subject: [PATCH] fix: share function and list navigation improvements\n\nReplace Web Share API with clipboard copy and add separate list tab. --- app/(app)/lists/page.tsx | 221 ++++++++++++++++++++++++++++++++++++ components/bottom-nav.tsx | 5 +- components/movie-detail.tsx | 45 ++++---- 3 files changed, 250 insertions(+), 21 deletions(-) create mode 100644 app/(app)/lists/page.tsx diff --git a/app/(app)/lists/page.tsx b/app/(app)/lists/page.tsx new file mode 100644 index 0000000..bc99912 --- /dev/null +++ b/app/(app)/lists/page.tsx @@ -0,0 +1,221 @@ +"use client" + +import React from "react" + +import { useState, useEffect } from "react" +import Link from "next/link" +import { createClient } from "@/lib/supabase/client" +import { ListIcon, Plus, Loader2, Film, Trash2 } from "lucide-react" + +interface ListWithCount { + id: string + name: string + description: string | null + created_at: string + list_items: { count: number }[] +} + +export default function ListsPage() { + const [lists, setLists] = useState([]) + const [loading, setLoading] = useState(true) + const [showNewList, setShowNewList] = useState(false) + const [newListName, setNewListName] = useState("") + const [newListDesc, setNewListDesc] = useState("") + const [creatingList, setCreatingList] = useState(false) + + useEffect(() => { + loadLists() + }, []) + + async function loadLists() { + const supabase = createClient() + const { + data: { user }, + } = await supabase.auth.getUser() + if (!user) return + + const { data } = await supabase + .from("lists") + .select("*, list_items(count)") + .eq("user_id", user.id) + .order("created_at", { ascending: false }) + + setLists(data || []) + setLoading(false) + } + + async function createList(e: React.FormEvent) { + e.preventDefault() + if (!newListName.trim()) return + setCreatingList(true) + + const supabase = createClient() + const { + data: { user }, + } = await supabase.auth.getUser() + if (!user) return + + const { data, error } = await supabase + .from("lists") + .insert({ + user_id: user.id, + name: newListName.trim(), + description: newListDesc.trim() || null, + }) + .select("*, list_items(count)") + .single() + + if (!error && data) { + setLists([data, ...lists]) + setNewListName("") + setNewListDesc("") + setShowNewList(false) + } + setCreatingList(false) + } + + async function deleteList(id: string) { + const supabase = createClient() + await supabase.from("lists").delete().eq("id", id) + setLists(lists.filter((l) => l.id !== id)) + } + + if (loading) { + return ( +
+
+

+ Meine Listen +

+
+
+ +
+
+ ) + } + + return ( +
+
+
+

+ Meine Listen +

+ + {lists.length} + +
+
+ +
+ {/* New list button */} + + + {/* New list form */} + {showNewList && ( +
+ setNewListName(e.target.value)} + placeholder="Listenname..." + className="h-10 rounded-lg border border-border bg-secondary px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" + autoFocus + /> + setNewListDesc(e.target.value)} + placeholder="Beschreibung (optional)..." + className="h-10 rounded-lg border border-border bg-secondary px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" + /> +
+ + +
+
+ )} + + {/* Lists */} + {lists.length === 0 && !showNewList ? ( +
+ +
+

+ Keine Listen vorhanden +

+

+ Erstelle Listen wie "Beste Weihnachtsfilme" oder "Familienabend-Favoriten" +

+
+
+ ) : ( +
+ {lists.map((list) => ( +
+ +
+
+ +
+
+

+ {list.name} +

+ {list.description && ( +

+ {list.description} +

+ )} +
+
+ + {list.list_items?.[0]?.count || 0} Filme + + + +
+ ))} +
+ )} +
+ +
+
+ ) +} diff --git a/components/bottom-nav.tsx b/components/bottom-nav.tsx index 2cb40f2..667b622 100644 --- a/components/bottom-nav.tsx +++ b/components/bottom-nav.tsx @@ -2,13 +2,14 @@ import Link from "next/link" import { usePathname } from "next/navigation" -import { Home, Search, PlusCircle, BookOpen, User } from "lucide-react" +import { Home, Search, PlusCircle, BookOpen, User, ListIcon } from "lucide-react" const navItems = [ { href: "/feed", label: "Feed", icon: Home }, { href: "/search", label: "Suche", icon: Search }, { href: "/log", label: "Loggen", icon: PlusCircle }, - { href: "/diary", label: "Tagebuch", icon: BookOpen }, + { href: "/diary", label: "Filme", icon: BookOpen }, + { href: "/lists", label: "Listen", icon: ListIcon }, { href: "/profile", label: "Profil", icon: User }, ] diff --git a/components/movie-detail.tsx b/components/movie-detail.tsx index fa2c09b..1027d62 100644 --- a/components/movie-detail.tsx +++ b/components/movie-detail.tsx @@ -15,7 +15,8 @@ import { PenLine, Clock, Film, - Share2, + Link2, + Check, } from "lucide-react" interface FamilyEntry { @@ -39,6 +40,7 @@ export function MovieDetail({ }: MovieDetailProps) { const [inWatchlist, setInWatchlist] = useState(initialWatchlist) const [saving, setSaving] = useState(false) + const [copied, setCopied] = useState(false) const router = useRouter() const backdrop = backdropUrl(movie.backdrop_path, "w1280") const poster = posterUrl(movie.poster_path, "w500") @@ -70,20 +72,21 @@ export function MovieDetail({ setSaving(false) } - async function handleShare() { - const shareData = { - title: movie.title, - text: `Schau dir "${movie.title}" an - ${movie.tagline || movie.overview?.slice(0, 100)}`, - url: window.location.href, - } - if (navigator.share) { - try { - await navigator.share(shareData) - } catch { - // User cancelled - } - } else { + async function handleCopyLink() { + try { await navigator.clipboard.writeText(window.location.href) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } catch { + // Fallback for older browsers + const textArea = document.createElement("textarea") + textArea.value = window.location.href + document.body.appendChild(textArea) + textArea.select() + document.execCommand("copy") + document.body.removeChild(textArea) + setCopied(true) + setTimeout(() => setCopied(false), 2000) } } @@ -115,14 +118,18 @@ export function MovieDetail({ - {/* Share button */} + {/* Copy link button */}