Refactor homepage, reading modal, matching feature, and user profile for improved UX #VERCEL_SKIP Co-authored-by: undefined <undefined+undefined@users.noreply.github.com>
126 lines
4.9 KiB
TypeScript
126 lines
4.9 KiB
TypeScript
"use client"
|
||
|
||
import { useState, useEffect } from "react"
|
||
import { X, Lock, Sparkles } from "lucide-react"
|
||
import { useStore } from "@/lib/store"
|
||
import { getFullBookPrice } from "@/lib/book-data"
|
||
|
||
interface ReadingModalProps {
|
||
section: { id: string; title: string; filePath: string }
|
||
onClose: () => void
|
||
onPurchase: (sectionId: string, title: string, price: number) => void
|
||
}
|
||
|
||
export function ReadingModal({ section, onClose, onPurchase }: ReadingModalProps) {
|
||
const [content, setContent] = useState("")
|
||
const [isLoading, setIsLoading] = useState(true)
|
||
const { isLoggedIn, hasPurchased } = useStore()
|
||
|
||
const isFree = section.id === "preface" || section.id === "epilogue"
|
||
const canAccess = isFree || (isLoggedIn && hasPurchased(section.id))
|
||
const fullBookPrice = getFullBookPrice()
|
||
|
||
useEffect(() => {
|
||
async function loadContent() {
|
||
try {
|
||
const response = await fetch(`/api/content?path=${encodeURIComponent(section.filePath)}`)
|
||
if (response.ok) {
|
||
const data = await response.json()
|
||
setContent(data.content || "")
|
||
}
|
||
} catch (error) {
|
||
console.error("Failed to load content:", error)
|
||
} finally {
|
||
setIsLoading(false)
|
||
}
|
||
}
|
||
loadContent()
|
||
}, [section.filePath])
|
||
|
||
// 计算显示内容
|
||
const displayContent = canAccess ? content : content.slice(0, Math.floor(content.length * 0.3))
|
||
const showPaywall = !canAccess && content.length > 0
|
||
|
||
return (
|
||
<div className="fixed inset-0 z-50 bg-black/90 modal-overlay">
|
||
<div className="h-full flex flex-col">
|
||
{/* Header */}
|
||
<header className="flex-shrink-0 glass-nav px-4 py-3 flex items-center justify-between safe-top">
|
||
<button
|
||
onClick={onClose}
|
||
className="w-9 h-9 rounded-full bg-[var(--app-bg-secondary)] flex items-center justify-center touch-feedback"
|
||
>
|
||
<X className="w-5 h-5" />
|
||
</button>
|
||
<h1 className="text-white font-semibold text-sm truncate flex-1 mx-4 text-center">{section.title}</h1>
|
||
<div className="w-9" />
|
||
</header>
|
||
|
||
{/* Content */}
|
||
<div className="flex-1 overflow-y-auto scrollbar-hide">
|
||
<div className="max-w-2xl mx-auto px-5 py-6">
|
||
{isLoading ? (
|
||
<div className="space-y-4">
|
||
{[...Array(10)].map((_, i) => (
|
||
<div key={i} className="skeleton h-4 rounded" style={{ width: `${Math.random() * 40 + 60}%` }} />
|
||
))}
|
||
</div>
|
||
) : (
|
||
<>
|
||
<article className="book-content relative">
|
||
<div className="text-[var(--app-text-secondary)] leading-[1.9] text-[17px]">
|
||
{displayContent.split("\n").map(
|
||
(paragraph, index) =>
|
||
paragraph.trim() && (
|
||
<p key={index} className="mb-6 text-justify">
|
||
{paragraph}
|
||
</p>
|
||
),
|
||
)}
|
||
</div>
|
||
|
||
{/* 付费墙渐变 */}
|
||
{showPaywall && (
|
||
<div className="absolute bottom-0 left-0 right-0 h-48 bg-gradient-to-t from-black to-transparent" />
|
||
)}
|
||
</article>
|
||
|
||
{/* 付费提示 - 在阅读中途触发 */}
|
||
{showPaywall && (
|
||
<div className="mt-8 glass-card-heavy p-8 text-center">
|
||
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-[var(--app-bg-secondary)] flex items-center justify-center">
|
||
<Lock className="w-8 h-8 text-[var(--app-text-tertiary)]" />
|
||
</div>
|
||
<h3 className="text-xl font-semibold mb-2">解锁完整内容</h3>
|
||
<p className="text-[var(--app-text-secondary)] mb-6">您已阅读30%,解锁后可阅读完整内容</p>
|
||
|
||
<div className="flex flex-col gap-3">
|
||
<button
|
||
onClick={() => onPurchase(section.id, section.title, 1)}
|
||
className="btn-ios-secondary py-3"
|
||
>
|
||
购买本节 ¥1
|
||
</button>
|
||
<button
|
||
onClick={() => onPurchase(section.id, section.title, fullBookPrice)}
|
||
className="btn-ios py-3 glow flex items-center justify-center gap-2"
|
||
>
|
||
<Sparkles className="w-4 h-4" />
|
||
购买全书 ¥{fullBookPrice} 省82%
|
||
</button>
|
||
</div>
|
||
|
||
<p className="text-[var(--app-text-tertiary)] text-xs mt-4">
|
||
分享给好友,他人购买你可获得 90% 返利
|
||
</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|