A personal media tracker built on the AT Protocol opnshelf.xyz
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: person page watch behavior and poster card positioning

- Remove date picker from person filmography page; items now mark as watched immediately with current date (matching mobile behavior)
- Add slide animation to watched button on MediaPosterCard: starts at list button position and slides left on hover to make room

+14 -55
+3 -1
apps/web/src/components/MediaPosterCard.tsx
··· 103 103 }} 104 104 isLoading={isShelfPending} 105 105 className={cn( 106 - "shadow-lg ring-1 ring-black/10", 106 + "shadow-lg ring-1 ring-black/10 transition-transform duration-200 ease-out", 107 107 isOnShelf 108 108 ? "bg-(--md-sys-color-tertiary) hover:bg-(--md-sys-color-error)" 109 109 : HOVER_REVEAL, 110 + // When watched, start at list button position, slide left on hover 111 + isOnShelf && "translate-x-10 group-hover:translate-x-0", 110 112 )} 111 113 > 112 114 {isOnShelf ? (
+11 -54
apps/web/src/routes/person.$personId.$name.tsx
··· 22 22 import { createFileRoute, useRouter } from "@tanstack/react-router"; 23 23 import { Calendar, MapPin, Star } from "lucide-react"; 24 24 import { useMemo, useState } from "react"; 25 - import { DatePickerModal } from "@/components/DatePickerModal"; 26 25 import { DetailHero } from "@/components/detail"; 27 26 import { MediaPosterCard } from "@/components/MediaPosterCard"; 28 27 import { useTheme } from "@/components/theme-provider"; ··· 346 345 }, 347 346 }); 348 347 349 - // Modal states 350 - const [datePickerModal, setDatePickerModal] = useState<{ 351 - mediaType: "movie" | "show"; 352 - mediaId: string; 353 - title: string; 354 - isWatched: boolean; 355 - } | null>(null); 356 - 357 348 const handleToggleWatched = (item: PersonFilmographyItemDto) => { 358 349 if (!user) return; 359 350 ··· 377 368 }); 378 369 } 379 370 } else { 380 - // Open date picker for marking 381 - setDatePickerModal({ 382 - mediaType: item.media_type as "movie" | "show", 383 - mediaId, 384 - title: item.title, 385 - isWatched: false, 386 - }); 387 - } 388 - }; 389 - 390 - const handleMarkWithDate = (date: Date) => { 391 - if (!datePickerModal || !user) return; 392 - 393 - const { mediaType, mediaId } = datePickerModal; 394 - 395 - if (mediaType === "movie") { 396 - markMovieMutation.mutate({ 397 - body: { 398 - movieId: mediaId, 399 - watchedAt: date.toISOString(), 400 - }, 401 - }); 402 - } else { 403 - markShowMutation.mutate({ 404 - body: { 405 - showId: mediaId, 406 - watchedAt: date.toISOString(), 407 - }, 408 - }); 371 + // Mark with current date 372 + const now = new Date().toISOString(); 373 + if (isMovie) { 374 + markMovieMutation.mutate({ 375 + body: { movieId: mediaId, watchedAt: now }, 376 + }); 377 + } else { 378 + markShowMutation.mutate({ 379 + body: { showId: mediaId, watchedAt: now }, 380 + }); 381 + } 409 382 } 410 - 411 - setDatePickerModal(null); 412 383 }; 413 384 414 385 return ( ··· 630 601 </div> 631 602 </div> 632 603 </div> 633 - 634 - {/* Date Picker Modal */} 635 - {datePickerModal && user && ( 636 - <DatePickerModal 637 - open={!!datePickerModal} 638 - onClose={() => setDatePickerModal(null)} 639 - {...(datePickerModal.mediaType === "movie" 640 - ? { mode: "movie" as const, movieId: datePickerModal.mediaId } 641 - : { mode: "show" as const, showId: datePickerModal.mediaId })} 642 - userDid={user.did} 643 - modalTitle={`Mark "${datePickerModal.title}" as watched`} 644 - onSelect={handleMarkWithDate} 645 - /> 646 - )} 647 604 648 605 {isPersonLoading && ( 649 606 <div