From 93d33464bf2c9e164a5b72c10f78c03fb935fd73 Mon Sep 17 00:00:00 2001 From: kikootwo Date: Thu, 5 Feb 2026 11:51:07 -0500 Subject: [PATCH] Apple-style audiobook card & modal redesign Add Apple-style UI refinements and UX improvements across audiobooks. - Add app icons and update manifest/layout to reference new 180x180 and 1024x1024 app icons. - Add global CSS animations (shimmer and toast slide-in) for improved skeletons/toasts. - Refactor AudiobookCard: new "cover-first" design, accessibility improvements, hover actions, request handling, inline toast/error UI, status helper (status -> badge/state mapping), simplified metadata layout, and polished animations/visuals. Removed some legacy imports and adjusted request UX (shorter toasts, error timing). - Enhance AudiobookDetailsModal: mobile-first sticky header, desktop modal polish, improved status logic, toast/notification helper, ASIN copy behavior, ebook request flows, uses preferences (squareCovers) and various layout/formatting tweaks. - Propagate hideRequestActions from bookdate page (bookdate/page.tsx). These changes focus on visual polish, accessibility, and smoother request/notification UX. Files changed: public manifest & icons, globals.css, app/layout.tsx, bookdate page, AudiobookCard, AudiobookDetailsModal, and PreferencesContext (context usage updated). --- public/RMAB_1024x1024_APPICON.png | Bin 0 -> 58929 bytes public/RMAB_180x180_APPICON.png | Bin 0 -> 9425 bytes public/manifest.json | 7 +- src/app/bookdate/page.tsx | 1 + src/app/globals.css | 26 + src/app/layout.tsx | 3 +- src/components/audiobooks/AudiobookCard.tsx | 377 ++++--- .../audiobooks/AudiobookDetailsModal.tsx | 985 ++++++++---------- src/components/audiobooks/AudiobookGrid.tsx | 84 +- src/contexts/PreferencesContext.tsx | 2 +- 10 files changed, 662 insertions(+), 823 deletions(-) create mode 100644 public/RMAB_1024x1024_APPICON.png create mode 100644 public/RMAB_180x180_APPICON.png diff --git a/public/RMAB_1024x1024_APPICON.png b/public/RMAB_1024x1024_APPICON.png new file mode 100644 index 0000000000000000000000000000000000000000..f65f5c6466095269580d036d341ee0bd44bd8d18 GIT binary patch literal 58929 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 z
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 GsQ&>2It(ZP literal 0 HcmV?d00001 diff --git a/public/RMAB_180x180_APPICON.png b/public/RMAB_180x180_APPICON.png new file mode 100644 index 0000000000000000000000000000000000000000..3598046e0402fe6a555c99038d724f25223e8dc1 GIT binary patch literal 9425 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 ) : null; })()} diff --git a/src/app/globals.css b/src/app/globals.css index 943757b..176c9f6 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -170,3 +170,29 @@ body { -webkit-backface-visibility: hidden; transform-origin: center center; } + +/* Premium Shimmer Animation for Skeletons */ +@keyframes shimmer { + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(100%); + } +} + +/* Smooth Toast Slide In */ +@keyframes toast-slide-in { + 0% { + opacity: 0; + transform: translate(-50%, 20px); + } + 100% { + opacity: 1; + transform: translate(-50%, 0); + } +} + +.animate-toast-in { + animation: toast-slide-in 0.3s ease-out; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index cdfcfe4..d9e751e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -30,7 +30,8 @@ export const metadata: Metadata = { ], shortcut: "/rmab_icon.ico", apple: [ - { url: "/RMAB_1024x1024_ICON.png", sizes: "1024x1024", type: "image/png" }, + { url: "/RMAB_180x180_APPICON.png", sizes: "180x180", type: "image/png" }, + { url: "/RMAB_1024x1024_APPICON.png", sizes: "1024x1024", type: "image/png" }, ], }, appleWebApp: { diff --git a/src/components/audiobooks/AudiobookCard.tsx b/src/components/audiobooks/AudiobookCard.tsx index 5d8675e..4d7c0b6 100644 --- a/src/components/audiobooks/AudiobookCard.tsx +++ b/src/components/audiobooks/AudiobookCard.tsx @@ -1,14 +1,15 @@ /** * Component: Audiobook Card * Documentation: documentation/frontend/components.md + * + * Premium "Cover First" design - Apple-inspired aesthetic + * The cover is the hero. Metadata supports, never overwhelms. */ 'use client'; import React, { useState } from 'react'; import Image from 'next/image'; -import { Button } from '@/components/ui/Button'; -import { StatusBadge } from '@/components/requests/StatusBadge'; import { AudiobookDetailsModal } from '@/components/audiobooks/AudiobookDetailsModal'; import { useCreateRequest } from '@/lib/hooks/useRequests'; import { useAuth } from '@/contexts/AuthContext'; @@ -22,10 +23,31 @@ interface AudiobookCardProps { squareCovers?: boolean; } +// Status configuration for elegant display +const getStatusConfig = (audiobook: Audiobook) => { + if (audiobook.isAvailable || audiobook.requestStatus === 'completed') { + return { type: 'available', label: 'In Library', color: 'emerald' }; + } + + const processingStatuses = ['downloading', 'processing', 'downloaded', 'awaiting_import']; + if (audiobook.requestStatus && processingStatuses.includes(audiobook.requestStatus)) { + return { type: 'processing', label: 'Processing', color: 'amber' }; + } + + const pendingStatuses = ['pending', 'awaiting_search', 'searching', 'awaiting_approval']; + if (audiobook.requestStatus && pendingStatuses.includes(audiobook.requestStatus)) { + return { type: 'pending', label: 'Requested', color: 'blue' }; + } + + if (audiobook.requestStatus === 'denied') { + return { type: 'denied', label: 'Denied', color: 'red' }; + } + + return null; +}; + export function AudiobookCard({ audiobook, - isRequested = false, - requestStatus, onRequestSuccess, squareCovers = false, }: AudiobookCardProps) { @@ -35,223 +57,194 @@ export function AudiobookCard({ const [error, setError] = useState(null); const [showModal, setShowModal] = useState(false); - const handleRequest = async () => { + const status = getStatusConfig(audiobook); + + const handleRequest = async (e: React.MouseEvent) => { + e.stopPropagation(); if (!user) { setError('Please log in to request audiobooks'); + setTimeout(() => setError(null), 3000); return; } try { await createRequest(audiobook); setShowToast(true); - setTimeout(() => setShowToast(false), 3000); + setTimeout(() => setShowToast(false), 2500); onRequestSuccess?.(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to create request'); - setTimeout(() => setError(null), 5000); + setTimeout(() => setError(null), 4000); } }; - const formatDuration = (minutes?: number) => { - if (!minutes) return null; - const hours = Math.floor(minutes / 60); - const mins = minutes % 60; - return `${hours}h ${mins}m`; - }; + // Determine if we can request this book + const canRequest = !status || status.type === 'denied'; return ( <> -
- {/* Cover Art - Clickable */} -
setShowModal(true)} - > - {audiobook.coverArtUrl ? ( - {`Cover - ) : ( -
- - - -
- )} +
setShowModal(true)} + tabIndex={0} + onKeyDown={(e) => e.key === 'Enter' && setShowModal(true)} + role="button" + aria-label={`View details for ${audiobook.title} by ${audiobook.author}`} + > + {/* Cover Container - The Hero */} +
+ {/* Cover Image with Premium Shadow */} +
+ {/* Cover Art */} + {audiobook.coverArtUrl ? ( + + ) : ( +
+ + + +
+ )} - {/* Hover overlay for click hint */} -
-
- - - - + {/* Hover Overlay with Actions - Desktop Only + pointer-events-none by default so taps on mobile pass through to card + Only enable pointer-events on devices that support hover */} +
+
+ {/* Quick Action Button */} + {canRequest ? ( + + ) : status?.type === 'available' ? ( +
+ In Your Library +
+ ) : ( +
+ {status?.type === 'processing' && ( + + + + + + Processing + + )} + {status?.type === 'pending' && 'Requested'} + {status?.type === 'denied' && 'Request Denied'} +
+ )} +
+ + {/* Subtle Status Indicator (visible when not hovered) */} + {status && ( +
+ )} + + {/* Rating Badge - Top Left, Elegant */} + {audiobook.rating && audiobook.rating > 0 && ( +
+ + + + {audiobook.rating.toFixed(1)} +
+ )}
- - {/* Availability Badge */} - {audiobook.isAvailable && ( -
- - - - Available -
- )} - - {/* Processing Badge - show when status is 'downloaded' */} - {audiobook.requestStatus === 'downloaded' && ( -
- - - - - Processing -
- )}
- {/* Content */} -
- {/* Title - Clickable */} -

setShowModal(true)} - > + {/* Metadata - Clean, Minimal */} +
+

{audiobook.title}

- - {/* Author */} -

- By {audiobook.author} -

- - {/* Narrator */} - {audiobook.narrator && ( -

- Narrated by {audiobook.narrator} +

+ {audiobook.author}

- )} - - {/* Metadata Row - Fixed height for alignment */} -
- {/* Rating - Only show if > 0 (0 means no rating) */} - {audiobook.rating && audiobook.rating > 0 && ( -
- - - - {audiobook.rating.toFixed(1)} -
- )}
- {/* Status or Action */} -
- {(() => { - // Check if book is already available in Plex or completed/available status - if (audiobook.isAvailable || audiobook.requestStatus === 'completed') { - return ( -
- - In Your Library - -
- ); - } - - // Check if book is requested and in progress (non-re-requestable statuses) - const inProgressStatuses = ['pending', 'awaiting_search', 'searching', 'downloading', 'processing', 'downloaded', 'awaiting_import', 'awaiting_approval', 'denied']; - if (audiobook.isRequested && audiobook.requestStatus && inProgressStatuses.includes(audiobook.requestStatus)) { - // Determine button text based on status - let buttonText; - let buttonClass = 'w-full cursor-not-allowed opacity-75'; - - if (audiobook.requestStatus === 'downloaded') { - buttonText = 'Processing...'; - } else if (audiobook.requestStatus === 'awaiting_approval') { - buttonText = audiobook.requestedByUsername - ? `Pending Approval (${audiobook.requestedByUsername})` - : 'Pending Approval'; - } else if (audiobook.requestStatus === 'denied') { - buttonText = 'Request Denied'; - buttonClass = 'w-full cursor-not-allowed opacity-75 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/30'; - } else { - buttonText = audiobook.requestedByUsername - ? `Requested by ${audiobook.requestedByUsername}` - : 'Requested'; - } - - return ( - - ); - } - - // For failed/warn/cancelled or no request - show Request button - return ( - - ); - })()} -
- - {/* Error Message */} - {error && ( -

{error}

+ {/* Toast Notifications - Floating */} + {(showToast || error) && ( +
+

+ {showToast ? 'Request created!' : error} +

+
)} +

- {/* Success Toast */} - {showToast && ( -

- ✓ Request created successfully! -

- )} -
-
- - {/* Details Modal */} - setShowModal(false)} - onRequestSuccess={onRequestSuccess} - isRequested={audiobook.isRequested} - requestStatus={audiobook.requestStatus} - isAvailable={audiobook.isAvailable} - requestedByUsername={audiobook.requestedByUsername} - /> + {/* Details Modal */} + setShowModal(false)} + onRequestSuccess={onRequestSuccess} + isRequested={audiobook.isRequested} + requestStatus={audiobook.requestStatus} + isAvailable={audiobook.isAvailable} + requestedByUsername={audiobook.requestedByUsername} + /> ); } diff --git a/src/components/audiobooks/AudiobookDetailsModal.tsx b/src/components/audiobooks/AudiobookDetailsModal.tsx index 243a074..9d0efef 100644 --- a/src/components/audiobooks/AudiobookDetailsModal.tsx +++ b/src/components/audiobooks/AudiobookDetailsModal.tsx @@ -1,6 +1,9 @@ /** * Component: Audiobook Details Modal * Documentation: documentation/frontend/components.md + * + * Premium modal design with mobile-first sticky actions + * Matches the Apple-inspired card aesthetic */ 'use client'; @@ -8,11 +11,10 @@ import React, { useEffect, useState } from 'react'; import Image from 'next/image'; import { createPortal } from 'react-dom'; -import { Button } from '@/components/ui/Button'; -import { StatusBadge } from '@/components/requests/StatusBadge'; import { useAudiobookDetails } from '@/lib/hooks/useAudiobooks'; import { useCreateRequest, useEbookStatus, useFetchEbookByAsin } from '@/lib/hooks/useRequests'; import { useAuth } from '@/contexts/AuthContext'; +import { usePreferences } from '@/contexts/PreferencesContext'; import { InteractiveTorrentSearchModal } from '@/components/requests/InteractiveTorrentSearchModal'; interface AudiobookDetailsModalProps { @@ -24,8 +26,35 @@ interface AudiobookDetailsModalProps { requestStatus?: string | null; isAvailable?: boolean; requestedByUsername?: string | null; + hideRequestActions?: boolean; } +// Status helper +const getStatusInfo = (isAvailable: boolean, requestStatus: string | null, requestedByUsername: string | null) => { + if (isAvailable || requestStatus === 'completed') { + return { type: 'available', label: 'In Your Library', canRequest: false }; + } + + const processingStatuses = ['downloading', 'processing', 'downloaded', 'awaiting_import']; + if (requestStatus && processingStatuses.includes(requestStatus)) { + return { type: 'processing', label: 'Processing', canRequest: false }; + } + + const pendingStatuses = ['pending', 'awaiting_search', 'searching', 'awaiting_approval']; + if (requestStatus && pendingStatuses.includes(requestStatus)) { + const label = requestStatus === 'awaiting_approval' + ? requestedByUsername ? `Pending Approval (${requestedByUsername})` : 'Pending Approval' + : requestedByUsername ? `Requested by ${requestedByUsername}` : 'Requested'; + return { type: 'pending', label, canRequest: false }; + } + + if (requestStatus === 'denied') { + return { type: 'denied', label: 'Request Denied', canRequest: true }; + } + + return { type: 'none', label: '', canRequest: true }; +}; + export function AudiobookDetailsModal({ asin, isOpen, @@ -35,24 +64,25 @@ export function AudiobookDetailsModal({ requestStatus = null, isAvailable = false, requestedByUsername = null, + hideRequestActions = false, }: AudiobookDetailsModalProps) { const { user } = useAuth(); + const { squareCovers } = usePreferences(); const { audiobook, isLoading, error } = useAudiobookDetails(isOpen ? asin : null); const { createRequest, isLoading: isRequesting } = useCreateRequest(); const { ebookStatus, revalidate: revalidateEbookStatus } = useEbookStatus(isOpen && isAvailable ? asin : null); const { fetchEbook, isLoading: isFetchingEbook } = useFetchEbookByAsin(); + const [showToast, setShowToast] = useState(false); - const [toastMessage, setToastMessage] = useState('Request created successfully!'); - const [requestError, setRequestError] = useState(null); + const [toastMessage, setToastMessage] = useState(''); + const [toastType, setToastType] = useState<'success' | 'error'>('success'); const [mounted, setMounted] = useState(false); const [showInteractiveSearch, setShowInteractiveSearch] = useState(false); const [showInteractiveSearchEbook, setShowInteractiveSearchEbook] = useState(false); const [asinCopied, setAsinCopied] = useState(false); - // Determine if ebook buttons should be shown - const canShowEbookButtons = isAvailable && - ebookStatus?.ebookSourcesEnabled && - !ebookStatus?.hasActiveEbookRequest; + const status = getStatusInfo(isAvailable, requestStatus, requestedByUsername); + const canShowEbookButtons = isAvailable && ebookStatus?.ebookSourcesEnabled && !ebookStatus?.hasActiveEbookRequest; useEffect(() => { setMounted(true); @@ -69,115 +99,49 @@ export function AudiobookDetailsModal({ }; }, [isOpen]); + const showNotification = (message: string, type: 'success' | 'error' = 'success') => { + setToastMessage(message); + setToastType(type); + setShowToast(true); + setTimeout(() => setShowToast(false), 3000); + }; + const handleRequest = async () => { if (!user || !audiobook) { - setRequestError('Please log in to request audiobooks'); + showNotification('Please log in to request audiobooks', 'error'); return; } try { await createRequest(audiobook); - setToastMessage('Request created successfully!'); - setShowToast(true); - setTimeout(() => { - setShowToast(false); - onClose(); - }, 2000); + showNotification('Request created!'); + setTimeout(onClose, 1500); onRequestSuccess?.(); } catch (err) { - setRequestError(err instanceof Error ? err.message : 'Failed to create request'); - setTimeout(() => setRequestError(null), 5000); + showNotification(err instanceof Error ? err.message : 'Failed to create request', 'error'); } }; const handleInteractiveSearch = () => { if (!user || !audiobook) { - setRequestError('Please log in to request audiobooks'); + showNotification('Please log in to request audiobooks', 'error'); return; } - - // Just show the interactive search modal - no request created yet setShowInteractiveSearch(true); }; - const handleInteractiveSearchClose = () => { - // Clean up state - setShowInteractiveSearch(false); - - // Close the details modal too - onClose(); - }; - - const handleInteractiveSearchSuccess = () => { - // Request was created and torrent was selected successfully - onRequestSuccess?.(); - }; - const handleFetchEbook = async () => { if (!user) { - setRequestError('Please log in to request ebooks'); + showNotification('Please log in to request ebooks', 'error'); return; } try { const result = await fetchEbook(asin); revalidateEbookStatus(); - - if (result.needsApproval) { - setToastMessage('Ebook request submitted for approval!'); - } else { - setToastMessage('Ebook search started!'); - } - setShowToast(true); - setTimeout(() => { - setShowToast(false); - }, 3000); + showNotification(result.needsApproval ? 'Ebook request submitted for approval!' : 'Ebook search started!'); } catch (err) { - setRequestError(err instanceof Error ? err.message : 'Failed to request ebook'); - setTimeout(() => setRequestError(null), 5000); - } - }; - - const handleInteractiveSearchEbook = () => { - if (!user) { - setRequestError('Please log in to request ebooks'); - return; - } - setShowInteractiveSearchEbook(true); - }; - - const handleInteractiveSearchEbookClose = () => { - setShowInteractiveSearchEbook(false); - revalidateEbookStatus(); - }; - - const handleInteractiveSearchEbookSuccess = () => { - revalidateEbookStatus(); - setToastMessage('Ebook download started!'); - setShowToast(true); - setTimeout(() => { - setShowToast(false); - }, 3000); - }; - - const formatDuration = (minutes?: number) => { - if (!minutes) return null; - const hours = Math.floor(minutes / 60); - const mins = minutes % 60; - return `${hours} hr ${mins} min`; - }; - - const formatDate = (dateString?: string) => { - if (!dateString) return null; - try { - const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric', - }); - } catch { - return dateString; + showNotification(err instanceof Error ? err.message : 'Failed to request ebook', 'error'); } }; @@ -191,203 +155,229 @@ export function AudiobookDetailsModal({ } }; + const formatDuration = (minutes?: number) => { + if (!minutes) return null; + const hours = Math.floor(minutes / 60); + const mins = minutes % 60; + return `${hours}h ${mins}m`; + }; + + const formatDate = (dateString?: string) => { + if (!dateString) return null; + try { + return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); + } catch { + return dateString; + } + }; + if (!isOpen || !mounted) return null; const modalContent = (
+ {/* Modal Container - uses dvh for PWA support */}
e.stopPropagation()} > - {/* Close Button */} + {/* Mobile: Sticky Header with Close */} +
+ Audiobook Details + +
+ + {/* Desktop: Close Button */} - {/* Loading State */} - {isLoading && ( -
-
-
- )} - - {/* Error State */} - {error && !isLoading && ( -
-
-

- Failed to load audiobook details -

-

- Please try again later -

+ {/* Scrollable Content */} +
+ {/* Loading State */} + {isLoading && ( +
+
-
- )} + )} - {/* Content */} - {audiobook && !isLoading && ( -
- {/* Header Section */} -
- {/* Cover Art */} -
-
- {audiobook.coverArtUrl ? ( - {`Cover - ) : ( -
- - - + {/* Error State */} + {error && !isLoading && ( +
+
+ + + +
+

Failed to load details

+

Please try again later

+
+ )} + + {/* Content */} + {audiobook && !isLoading && ( +
+ {/* Hero Section - Cover + Title/Author */} +
+ {/* Cover Art */} +
+
+ {audiobook.coverArtUrl ? ( + + ) : ( +
+ + + +
+ )} + + {/* Rating Badge */} + {audiobook.rating && audiobook.rating > 0 && ( +
+ + + + {audiobook.rating.toFixed(1)} +
+ )} +
+
+ + {/* Title & Author */} +
+

+ {audiobook.title} +

+

+ {audiobook.author} +

+ {audiobook.narrator && ( +

+ Narrated by {audiobook.narrator} +

+ )} + + {/* Status Badge */} + {status.type !== 'none' && ( +
+ + {status.type === 'available' && ( + + + + )} + {status.type === 'processing' && ( + + + + + )} + {status.label} +
)} + + {/* Quick Metadata */} +
+ {audiobook.durationMinutes && ( + + + + + {formatDuration(audiobook.durationMinutes)} + + )} + {audiobook.releaseDate && ( + {formatDate(audiobook.releaseDate)} + )} +
- {/* Metadata */} -
- {/* Title */} -
-

- {audiobook.title} -

+ {/* Genres */} + {audiobook.genres && audiobook.genres.length > 0 && ( +
+ {audiobook.genres.map((genre: string) => ( + + {genre} + + ))}
+ )} - {/* Author */} -
-

By

-

- {audiobook.author} + {/* Description */} + {audiobook.description && ( +

+

+ Summary +

+

+ {audiobook.description}

+ )} - {/* Narrator */} - {audiobook.narrator && ( -
-

Narrated by

-

- {audiobook.narrator} -

-
- )} - - {/* Metadata Grid */} -
- {/* Rating - Always show header, display 'Not Found' if no rating */} -
-

Rating

- {audiobook.rating && audiobook.rating > 0 ? ( -
-
- {[...Array(5)].map((_, i) => ( - - - - ))} -
- - {Number(audiobook.rating).toFixed(1)} - -
- ) : ( -

Not Found

- )} -
- - {/* Duration */} - {audiobook.durationMinutes && ( -
-

Length

-

- {formatDuration(audiobook.durationMinutes)} -

-
- )} - - {/* Release Date */} - {audiobook.releaseDate && ( -
-

Release Date

-

- {formatDate(audiobook.releaseDate)} -

-
- )} - + {/* Details Grid */} +
+

+ Details +

+
{/* ASIN */}
-

ASIN

+

ASIN

@@ -395,328 +385,158 @@ export function AudiobookDetailsModal({ {/* Audible Link */}
-

View Details

+

Source

- Audible.com - - + Audible + +
- - {/* Availability Status */} - {isAvailable && ( -
-

Status

-
- - - - In Your Library -
-
- )}
+
- {/* Genres */} - {audiobook.genres && audiobook.genres.length > 0 && ( -
-

Genres

-
- {audiobook.genres.map((genre: string) => ( - - {genre} - - ))} -
+ {/* Ebook Status */} + {ebookStatus?.hasActiveEbookRequest && ( +
+
+ + + + + Ebook: {ebookStatus.existingEbookStatus === 'awaiting_approval' + ? 'Pending Approval' + : ebookStatus.existingEbookStatus === 'available' || ebookStatus.existingEbookStatus === 'downloaded' + ? 'Available' + : 'In Progress'} +
+
+ )} +
+ )} +
+ + {/* Sticky Action Bar - hidden when opened from bookdate */} + {audiobook && !isLoading && !hideRequestActions && ( +
+
+ {/* Main Action */} +
+ {status.type === 'available' ? ( + + ) : status.canRequest ? ( + + ) : ( + )}
-
- {/* Description */} - {audiobook.description && ( -
-

- Publisher's Summary -

-
- {audiobook.description} -
-
- )} - - {/* Action Buttons */} -
- {(() => { - // Use props from card instead of fetched audiobook data for request status - // Check if book is already available in library or completed status - if (isAvailable || requestStatus === 'completed') { - return ( - <> -
-
- - Available in Your Library - -
-
- - {/* Ebook Buttons - Only shown when audiobook is available and ebook sources enabled */} - {canShowEbookButtons && user && ( - <> - {/* Grab Ebook Button */} - - - {/* Interactive Search Ebook Button */} - - - )} - - {/* Show ebook request status if one exists */} - {ebookStatus?.hasActiveEbookRequest && ( -
- - - - - Ebook: {ebookStatus.existingEbookStatus === 'awaiting_approval' - ? 'Pending Approval' - : ebookStatus.existingEbookStatus === 'available' || ebookStatus.existingEbookStatus === 'downloaded' - ? 'Available' - : 'In Progress'} - -
- )} - - ); - } - - // Check if book is requested and in progress - const inProgressStatuses = [ - 'pending', - 'awaiting_search', - 'searching', - 'downloading', - 'processing', - 'downloaded', - 'awaiting_import', - 'awaiting_approval', - 'denied', - ]; - if ( - isRequested && - requestStatus && - inProgressStatuses.includes(requestStatus) - ) { - // Determine button text and styling based on status - let buttonText; - let buttonClass = 'w-full cursor-not-allowed opacity-75'; - - if (requestStatus === 'downloaded') { - buttonText = 'Processing...'; - } else if (requestStatus === 'awaiting_approval') { - buttonText = requestedByUsername - ? `Pending Approval (${requestedByUsername})` - : 'Pending Approval'; - } else if (requestStatus === 'denied') { - buttonText = 'Request Denied'; - buttonClass = 'w-full cursor-not-allowed opacity-75 bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/30'; - } else { - buttonText = requestedByUsername - ? `Requested by ${requestedByUsername}` - : 'Already Requested'; - } - - return ( -
- -
- ); - } - - // For failed/warn/cancelled or no request - show Request button - return ( -
- -
- ); - })()} - - {/* Interactive Search Button - only show if not already available */} - {!isAvailable && requestStatus !== 'completed' && ( + {/* Interactive Search - only if not available */} + {status.type !== 'available' && ( )} - + {/* Ebook Buttons - only when available and enabled */} + {canShowEbookButtons && user && ( + <> + + + + )}
+
+ )} - {/* Error Message */} - {requestError && ( -
-

{requestError}

-
- )} - - {/* Success Toast */} - {showToast && ( -
-

- ✓ {toastMessage} -

-
- )} + {/* Toast Notification */} + {showToast && ( +
+

{toastMessage}

)}
@@ -726,40 +546,49 @@ export function AudiobookDetailsModal({ return ( <> {createPortal(modalContent, document.body)} - {/* Interactive Search Modal (Audiobook) - render with higher z-index to appear above details modal */} + + {/* Interactive Search Modal (Audiobook) */} {showInteractiveSearch && audiobook && createPortal( -
-
- -
+
+ { + setShowInteractiveSearch(false); + onClose(); + }} + onSuccess={() => { + onRequestSuccess?.(); + }} + audiobook={{ + title: audiobook.title, + author: audiobook.author, + }} + fullAudiobook={audiobook} + />
, document.body )} - {/* Interactive Search Modal (Ebook) - render with higher z-index to appear above details modal */} + + {/* Interactive Search Modal (Ebook) */} {showInteractiveSearchEbook && audiobook && createPortal( -
-
- -
+
+ { + setShowInteractiveSearchEbook(false); + revalidateEbookStatus(); + }} + onSuccess={() => { + revalidateEbookStatus(); + showNotification('Ebook download started!'); + }} + asin={asin} + audiobook={{ + title: audiobook.title, + author: audiobook.author, + }} + searchMode="ebook" + />
, document.body )} diff --git a/src/components/audiobooks/AudiobookGrid.tsx b/src/components/audiobooks/AudiobookGrid.tsx index 4c671b3..f6c1da1 100644 --- a/src/components/audiobooks/AudiobookGrid.tsx +++ b/src/components/audiobooks/AudiobookGrid.tsx @@ -1,6 +1,8 @@ /** * Component: Audiobook Grid * Documentation: documentation/frontend/components.md + * + * Premium grid layout with generous spacing and elegant skeletons */ 'use client'; @@ -18,21 +20,20 @@ interface AudiobookGridProps { squareCovers?: boolean; // true = square (1:1), false = rectangle (2:3) } -// Helper function to get grid classes based on card size -// IMPORTANT: Classes must be explicit strings (not template literals) for Tailwind purging +// Grid classes with generous spacing for premium feel +// IMPORTANT: Classes must be explicit strings for Tailwind purging function getGridClasses(size: number): string { const sizeMap: Record = { - 1: 'grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10', // Smallest + 1: 'grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10', 2: 'grid-cols-3 md:grid-cols-5 lg:grid-cols-7 xl:grid-cols-9', 3: 'grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8', 4: 'grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7', - 5: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5', // Default + 5: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5', 6: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-4', 7: 'grid-cols-2 md:grid-cols-3', 8: 'grid-cols-2', - 9: 'grid-cols-1', // Largest + 9: 'grid-cols-1', }; - return sizeMap[size] || sizeMap[5]; } @@ -48,9 +49,9 @@ export function AudiobookGrid({ if (isLoading) { return ( -
- {Array.from({ length: 8 }).map((_, i) => ( - +
+ {Array.from({ length: 10 }).map((_, i) => ( + ))}
); @@ -58,27 +59,19 @@ export function AudiobookGrid({ if (audiobooks.length === 0) { return ( -
- - - -

{emptyMessage}

+
+
+ + + +
+

{emptyMessage}

); } return ( -
+
{audiobooks.map((audiobook) => ( - {/* Cover Art Skeleton */} -
+
+ {/* Cover Skeleton */} +
+ {/* Shimmer overlay */} +
+
- {/* Content Skeleton */} -
- {/* Title */} -
-
- - {/* Author */} -
- - {/* Metadata */} -
- - {/* Button */} -
+ {/* Text Skeleton */} +
+
+
); diff --git a/src/contexts/PreferencesContext.tsx b/src/contexts/PreferencesContext.tsx index e7325bd..0a8a5b4 100644 --- a/src/contexts/PreferencesContext.tsx +++ b/src/contexts/PreferencesContext.tsx @@ -23,7 +23,7 @@ const PreferencesContext = createContext(und const DEFAULT_PREFERENCES: Preferences = { cardSize: 5, - squareCovers: false, + squareCovers: true, }; const STORAGE_KEY = 'preferences';