PageSourceSearch

https://www.bookmosh.com/assets/ReviewPage-A48YH_vv.js

js bookmosh.com collected 2026-10-02 01:24:35 UTC 17,032 bytes, 1 lines download raw bytes

1import{s as o,j as t,f as R,R as ke,h as ge,i as je}from"./index-CoCDGbkq.js";import{f as ye,u as Ne,c as Se,b as l}from"./vendor-react-DQyBE89E.js";import"./vendor-supabase-XJkir-Zj.js";const Ce="bookmosh-user";function Me(u){if(!u)return"";const f=new Date(u),b=new Date-f,i=Math.floor(b/6e4);if(i<1)return"just now";if(i<60)return`${i}m ago`;const k=Math.floor(i/60);if(k<24)return`${k}h ago`;const s=Math.floor(k/24);return s<7?`${s}d ago`:f.toLocaleDateString()}function Be(){const{reviewId:u}=ye(),f=Ne(),b=Se().state?.feedLikeState??null,[i,k]=l.useState(null),[s,E]=l.useState(null),[B,G]=l.useState(null),[de,L]=l.useState(!0),[W,D]=l.useState(null),[m,S]=l.useState({count:0,likedByMe:!1,users:[]}),[$,C]=l.useState([]),[Y,F]=l.useState({}),[I,M]=l.useState(""),[j,P]=l.useState(null),[me,ue]=l.useState(!1),[J,A]=l.useState(!1),[Q,V]=l.useState(!1),[U,X]=l.useState(""),[T,Z]=l.useState(""),[y,z]=l.useState(0),[H,ee]=l.useState(!1),[O,te]=l.useState(null),[g,se]=l.useState(!1),he=!!i?.id&&String(s?.owner_id)===String(i.id),xe=()=>{X(B||""),Z(s?.review||s?.body||""),z(Number(s?.rating)||0),ee(!!s?.spoiler_warning),te(s?.music||null),se(!!s?.is_favorite),A(!0)},fe=async()=>{if(!(!o||!s?.id||!i?.id)){V(!0);try{const e=y>0?y:null,{error:a}=await o.from("book_reviews").update({body:T.trim(),rating:e,spoiler_warning:H,music:O||null,updated_at:new Date().toISOString()}).eq("id",s.id).eq("owner_id",i.id);if(a)throw a;await o.from("book_events").update({thread_title:U.trim()||null}).eq("review_id",s.id).eq("owner_id",i.id);const r=s.book_title||s.title;r&&await o.from("bookmosh_books").update({is_favorite:g}).eq("owner",s.owner_username||s.reviewer_username||i.username).eq("title",r),E(n=>n&&{...n,body:T.trim(),review:T.trim(),rating:e,spoiler_warning:H,music:O||null,is_favorite:g}),G(U.trim()||null),A(!1)}catch{alert("Failed to save review")}finally{V(!1)}}};l.useEffect(()=>{try{const e=localStorage.getItem(Ce);if(e){const a=JSON.parse(e);a?.id?k(a):a?.user&&k(a.user)}}catch{}},[]),l.useEffect(()=>{u&&we()},[u,i?.id]);const we=async()=>{L(!0),D(null),b&&typeof b=="object"&&S({count:b.count??0,likedByMe:b.likedByMe??!1,users:Array.isArray(b.users)?b.users:[]});try{let{data:e,error:a}=await o.from("book_reviews").select("*").eq("id",u).single(),r="book_reviews";if(a||!e){const{data:c,error:x}=await o.from("bookmosh_books").select("*").eq("id",u).single();if(x||!c){D("Review not found"),L(!1);return}e=c,r="bookmosh_books"}if(E({...e,__reviewTable:r,title:e.book_title||e.title,author:e.book_author||e.author,reviewer_username:e.owner_username||e.reviewer_username||e.owner,review:e.body||e.review}),r==="book_reviews")try{const{data:c}=await o.from("book_events").select("thread_title").eq("review_id",u).not("thread_title","is",null).order("created_at",{ascending:!1}).limit(1);c&&c[0]?.thread_title&&G(c[0].thread_title)}catch{}if(r==="book_reviews"&&(e.owner_username||e.owner)&&e.book_title)try{const{data:c}=await o.from("bookmosh_books").select("is_favorite").eq("owner",e.owner_username||e.owner).eq("title",e.book_title).limit(1).maybeSingle();c?.is_favorite&&E(x=>x&&{...x,is_favorite:!0})}catch{}const n=r==="book_reviews"?"book_review_likes":"review_likes",{data:d}=await o.from(n).select("user_id, username").eq("review_id",u);let h=d||[];if(r==="book_reviews"&&e.owner_username&&e.book_title){const{data:c}=await o.from("book_events").select("id").eq("owner_username",e.owner_username).eq("book_title",e.book_title).in("event_type",["finished_reading","reviewed"]).limit(5),x=(c||[]).map(v=>v.id);if(x.length>0){const{data:v}=await o.from("feed_likes").select("user_id, username").in("book_id",x);if(v?.length){const _=new Set(h.map(w=>w.user_id));for(const w of v)_.has(w.user_id)||(h.push(w),_.add(w.user_id))}}}const p=new Set,K=h.filter(c=>p.has(c.user_id)?!1:(p.add(c.user_id),!0));S({count:K.length,likedByMe:i?K.some(c=>String(c.user_id)===String(i.id)):!1,users:K.map(c=>c.username).filter(Boolean)});const le=r==="book_reviews"?"book_review_comments":"review_comments",{data:q}=await o.from(le).select("*").eq("review_id",u).order("created_at",{ascending:!0});if(C(q||[]),q?.length){const c=q.map(_=>_.id),{data:x}=await o.from("comment_likes").select("*").in("comment_id",c).eq("comment_table",le),v={};for(const _ of q){const w=(x||[]).filter(N=>N.comment_id===_.id);v[_.id]={count:w.length,likedByMe:i?w.some(N=>N.user_id===i.id):!1,users:w.map(N=>N.username)}}F(v)}}catch(e){D(e?.message||"Failed to load review")}finally{L(!1)}},be=async()=>
1{if(!i?.id||!s?.id)return;const e=s.__reviewTable==="book_reviews"?"book_review_likes":"review_likes";let a=[];if(s.__reviewTable==="book_reviews"&&s.owner_username&&(s.book_title||s.title)){const{data:r}=await o.from("book_events").select("id").eq("owner_username",s.owner_username).eq("book_title",s.book_title||s.title).in("event_type",["finished_reading","reviewed"]).limit(5);a=(r||[]).map(n=>n.id)}try{m.likedByMe?(await o.from(e).delete().eq("review_id",s.id).eq("user_id",i.id),a.length>0&&await o.from("feed_likes").delete().in("book_id",a).eq("user_id",i.id),S(r=>({count:Math.max(0,r.count-1),likedByMe:!1,users:r.users.filter(n=>n!==i.username)}))):(await o.from(e).insert({review_id:s.id,user_id:i.id,username:i.username}),a.length>0&&await o.from("feed_likes").upsert({book_id:a[0],user_id:i.id,username:i.username},{onConflict:"book_id,user_id"}),S(r=>({count:r.count+1,likedByMe:!0,users:[...r.users,i.username]})))}catch{}},pe=async e=>{if(!i?.id||!e?.id)return;const a=s.__reviewTable==="book_reviews"?"book_review_comments":"review_comments",r=Y[e.id]||{count:0,likedByMe:!1,users:[]};try{r.likedByMe?(await o.from("comment_likes").delete().eq("comment_id",e.id).eq("comment_table",a).eq("user_id",i.id),F(n=>({...n,[e.id]:{count:Math.max(0,r.count-1),likedByMe:!1,users:r.users.filter(d=>d!==i.username)}}))):(await o.from("comment_likes").insert({comment_id:e.id,comment_table:a,user_id:i.id,username:i.username}),F(n=>({...n,[e.id]:{count:r.count+1,likedByMe:!0,users:[...r.users,i.username]}})))}catch{}},ie=async()=>{if(!i?.id||!s?.id)return;const e=String(I||"").trim();if(!e)return;const a=s.__reviewTable==="book_reviews"?"book_review_comments":"review_comments";try{const r={review_id:s.id,commenter_id:i.id,commenter_username:i.username,body:e};j&&(r.parent_id=j.id);const{error:n}=await o.from(a).insert(r);if(n)throw n;M(""),P(null);const{data:d}=await o.from(a).select("*").eq("review_id",s.id).order("created_at",{ascending:!0});C(d||[])}catch(r){alert(r?.message||"Failed to post comment")}},ve=e=>{P(e),M(`@${e.commenter_username} `)},re=e=>!!i?.id&&String(e?.commenter_id)===String(i.id),ae=e=>e?.is_deleted===!0||e?.body==="[deleted]",_e=async e=>{if(!re(e)||ae(e))return;const a=$.some(d=>d.parent_id===e.id);if(!window.confirm(a?"Delete this comment? Its replies will stay in the thread.":"Delete this comment?"))return;const n=s.__reviewTable==="book_reviews"?"book_review_comments":"review_comments";try{if(a){let{error:d}=await o.from(n).update({body:"[deleted]",is_deleted:!0}).eq("id",e.id);if(d){const h=await o.from(n).update({body:"[deleted]"}).eq("id",e.id);if(h.error)throw h.error}C(h=>h.map(p=>p.id===e.id?{...p,body:"[deleted]",is_deleted:!0}:p))}else{const{error:d}=await o.from(n).delete().eq("id",e.id);if(d)throw d;C(h=>h.filter(p=>p.id!==e.id))}}catch(d){alert(d?.message||"Failed to delete comment")}},ne=(e=>{const a={},r=[];for(const n of e)a[n.id]={...n,replies:[]};for(const n of e)n.parent_id&&a[n.parent_id]?a[n.parent_id].replies.push(a[n.id]):r.push(a[n.id]);return r})($),oe=(e,a=0)=>{const r=Y[e.id]||{count:0,likedByMe:!1},n=ae(e);return t.jsxs("div",{className:`${a>0?"ml-6 border-l-2 border-white/10 pl-4":""}`,children:[t.jsxs("div",{className:"retro-card-inset p-3",children:[t.jsxs("div",{className:"flex items-start justify-between gap-3",children:[t.jsxs("p",{className:"text-sm text-white/80",children:[t.jsx("span",{className:`font-semibold ${n?"text-white/40":"text-white"}`,children:n?"[deleted]":`@${e.commenter_username}`}),t.jsxs("span",{className:"text-white/60",children:[" · ",Me(e.created_at)]})]}),re(e)&&!n&&t.jsx("button",{type:"button",onClick:()=>_e(e),className:"flex-shrink-0 text-[11px] font-semibold text-rose-400/80 hover:text-rose-400 transition",children:"Delete"})]}),t.jsx("p",{className:`mt-2 text-sm whitespace-pre-wrap ${n?"italic text-white/40":"text-white/80"}`,children:n?"[comment deleted]":e.body}),i&&!n&&t.jsxs("div",{className:"mt-2 flex items-center gap-3",children:[t.jsxs("button",{type:"button",onClick:()=>pe(e),className:`flex items-center gap-1.5 text-xs transition ${r.likedByMe?"text-pink-400":"text-white/40 hover:text-pink-400"}`,children:[t.jsx(R,{size:12,color:r.likedByMe?"#f472b6":"rgba(255,255,255,0.4)"}),r.count>0&&t.jsx("span",{children:r.count})]}),t.jsxs("button",{type:"button",onClick:()=>ve(e),className:"flex items-center gap-1.5 text-xs text-white/40 hover:text-purple-400 transition",children:[t.jsx(je,{size:12,color:"rgba(255,255,255,0.4)"}),t.jsx("span",{children:"Reply"})]})]})]}),e.replies?.length>0&&t.jsx("div",{className:"mt-2 space-y-2",children:e.replies.map(d=>oe(d,a+1))})]},e.id)};return de?t.jsx("div",{className:"min-h-screen bg-gradient-to-b from-[#0a0a1a] via-[#050916] to-black text-white flex items-center justify-center",children:t.jsx("p",{className:"text-white/60",children:"Loading review…"})}):W?t.jsx("div",{className:"min-h-screen bg-gradient-to-b from-[#0a0a1a] via-[#050916] to-black text-white flex items-center justify-center",children:t.jsxs("div",{className:"text-center",children:[t.jsx("p",{className:"text-white/60 mb-4",children:W}),t.jsx("button",{type:"button",onClick:()=>f("/"),className:"retro-btn",children:"Go Home"})]})}):t.jsx("div",{className:"min-h-screen bg-gradient-to-b from-[#0a0a1a] via-[#050916] to-black text-white",children:t.jsxs("div",{className:"mx-auto w-full max-w-3xl px-4 py-6",children:[t.jsxs("div",{className:"mb-6",children:[t.jsx("button",{type:"button",onClick:()=>f("/"),className:"text-xs uppercase tracking-[0.3em] text-white/60 hover:text-white transition mb-4",children:"← Back to BookMosh"}),t.jsxs("div",{className:"flex gap-4 items-start",children:[t.jsx("button",{type:"button",onClick:()=>{const e=new URLSearchParams;e.set("bookTitle",s?.title||""),e.set("bookAuthor",s?.author||""),f({pathname:"/book",search:`?${e.toString()}`})},className:"flex-shrink-0 cursor-pointer hover:opacity-80 transition",children:s?.book_cover||s?.cover?t.jsx("img",{src:s?.book_cover||s?.cover,alt:s?.title,className:"w-24 h-36 object-cover rounded-lg shadow-lg border border-white/10"}):t.jsx("div",{className:"w-24 h-36 bg-white/10 rounded-lg flex items-center justify-center text-white/30 text-xs",children:"No cover"})}),t.jsxs("div",{className:"min-w-0",children:[t.jsx("p",{className:"text-xs uppercase tracking-[0.4em] text-white/40",children:"Review Thread"}),t.jsx("button",{type:"button",onClick:()=>{const e=new URLSearchParams;e.set("bookTitle",s?.title||""),e.set("bookAuthor",s?.author||""),f({pathname:"/book",search:`?${e.toString()}`})},className:"text-left hover:text-purple-300 transition",children:t.jsx("h1",{className:"text-2xl font-semibold text-white",children:s?.title})}),t.jsx("p",{className:"text-sm text-white/60",children:s?.author}),t.jsxs("p",{className:"text-xs text-white/50 mt-1",children:["by @",s?.reviewer_username]})]})]})]}),t.jsxs("div",{className:"retro-card p-6 mb-6",children:[t.jsxs("div",{className:"flex items-center justify-between gap-3",children:[t.jsx("p",{className:"text-xs uppercase tracking-[0.3em] text-white/50",children:"Review"}),t.jsxs("div",{className:"flex items-center gap-3",children:[he&&!J&&t.jsx("button",{type:"button",onClick:xe,className:"text-sm font-medium text-aurora hover:underline",children:"Edit"}),t.jsxs("button",{type:"button",onClick:()=>
1{if(!i){f("/");return}be()},className:`flex items-center gap-2 transition ${m.likedByMe?"text-pink-400":"text-white/50 hover:text-pink-400"}`,title:i?m.users?.length?`Liked by ${m.users.join(", ")}`:"Like this review":"Sign in to like",children:[t.jsx(R,{size:16,color:m.likedByMe?"#f472b6":"rgba(255,255,255,0.4)"}),t.jsx("span",{className:"text-sm font-medium",children:m.count>0?m.count:"Like"})]})]})]}),m.users?.length>0&&t.jsxs("div",{className:"mt-2 text-[11px] text-pink-300/80",children:["Liked by ",m.users.slice(0,3).map((e,a)=>t.jsxs("span",{children:[a>0&&(a===m.users.slice(0,3).length-1?" and ":", "),t.jsx("button",{type:"button",onClick:()=>f(`/user/${e}`),className:"hover:underline font-medium",children:e})]},e)),m.users.length>3&&` and ${m.users.length-3} others`]}),J?t.jsxs("div",{className:"mt-3 space-y-3",children:[t.jsxs("div",{className:"flex items-center gap-1",children:[[1,2,3,4,5].map(e=>t.jsx("button",{type:"button",onClick:()=>z(y===e?0:e),className:"p-0.5 text-lg",children:t.jsx("span",{className:e<=y?"text-yellow-400":"text-white/20",children:"★"})},e)),y>0&&t.jsx("button",{type:"button",onClick:()=>z(0),className:"ml-2 text-xs text-white/40 hover:text-white/70",children:"Clear"})]}),t.jsxs("button",{type:"button",onClick:()=>se(e=>!e),"aria-pressed":g,className:"flex w-full items-center gap-2.5 pt-2",children:[t.jsx(R,{size:21,color:g?"#EF4444":"rgba(255,255,255,0.4)"}),t.jsx("span",{className:`text-base font-bold ${g?"text-red-400":"text-white/60"}`,children:g?"Favorited":"Favorite this book"})]}),t.jsx("input",{value:U,onChange:e=>X(e.target.value),placeholder:"Title your review (optional)",className:"w-full retro-input font-semibold"}),t.jsx("textarea",{value:T,onChange:e=>Z(e.target.value),rows:5,placeholder:"Your review…",className:"w-full retro-input resize-none"}),t.jsxs("label",{className:"flex items-center gap-2 text-sm text-white/70",children:[t.jsx("input",{type:"checkbox",checked:H,onChange:e=>ee(e.target.checked)}),"Contains spoilers"]}),t.jsx(ke,{value:O,onChange:te}),t.jsxs("div",{className:"flex gap-2 pt-1",children:[t.jsx("button",{type:"button",onClick:fe,disabled:Q,className:"retro-btn retro-btn-primary text-sm",children:Q?"Saving…":"Save"}),t.jsx("button",{type:"button",onClick:()=>A(!1),className:"retro-btn text-sm",children:"Cancel"})]})]}):t.jsxs(t.Fragment,{children:[B&&t.jsx("h2",{className:"mt-3 text-lg font-bold text-white leading-snug",children:B}),(s?.rating>0||s?.is_favorite)&&t.jsxs("div",{className:"mt-3 flex items-center gap-2",children:[s?.rating>0&&t.jsxs(t.Fragment,{children:[t.jsx("span",{className:"inline-flex text-base","aria-label":`${s.rating} out of 5 stars`,children:[0,1,2,3,4].map(e=>{const a=Math.max(0,Math.min(1,Number(s.rating)-e));return t.jsxs("span",{className:"relative inline-block leading-none",children:[t.jsx("span",{className:"text-white/20",children:"★"}),t.jsx("span",{className:"absolute top-0 left-0 bottom-0 overflow-hidden text-yellow-400",style:{width:`${a*100}%`},children:"★"})]},e)})}),t.jsxs("span",{className:"text-sm font-semibold text-yellow-400",children:[s.rating,"/5"]})]}),s?.is_favorite&&t.jsxs("span",{className:`flex items-center gap-1.5 ${s?.rating>0?"ml-auto":""}`,children:[t.jsx(R,{size:20,color:"#EF4444"}),t.jsx("span",{className:"text-sm font-semibold text-red-400",children:"Favorite"})]})]}),s?.spoiler_warning&&!me?t.jsxs("div",{className:"mt-4 rounded-2xl border border-rose-500/30 bg-rose-500/10 p-4",children:[t.jsx("p",{className:"text-sm text-white/80",children:"This review contains spoilers."}),t.jsx("button",{type:"button",onClick:()=>ue(!0),className:"mt-3 retro-btn",children:"Show spoiler"})]}):t.jsx("p",{className:"mt-4 text-sm text-white/80 whitespace-pre-wrap",children:s?.review||"No review text yet."}),s?.music&&t.jsx(ge,{music:s.music})]})]}),t.jsxs("div",{className:"retro-card p-6",children:[t.jsxs("div",{className:"flex items-center justify-between gap-3 mb-4",children:[t.jsx("p",{className:"text-xs uppercase tracking-[0.3em] text-white/50",children:"Comments"}),t.jsx("p",{className:"text-xs text-white/40",children:$.length})]}),t.jsx("div",{className:"space-y-3",children:ne.length>0?ne.map(e=>oe(e)):t.jsx("p",{className:"text-sm text-white/60",children:"No comments yet."})}),i?t.jsxs("div",{className:"mt-4",children:[j&&t.jsxs("div",{className:"mb-2 flex items-center gap-2 text-xs text-purple-400",children:[t.jsxs("span",{children:["Replying to @",j.commenter_username]}),t.jsx("button",{type:"button",onClick:()=>{P(null),M("")},className:"text-white/40 hover:text-white",children:"✕"})]}),t.jsxs("div",{className:"flex gap-2",children:[t.jsx("input",{value:I,onChange:e=>M(e.target.value),placeholder:j?"Write a reply…":"Write a comment…",className:"flex-1 retro-input text-sm",onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),ie())}}),t.jsx("button",{type:"button",onClick:ie,disabled:!String(I||"").trim(),className:"retro-btn retro-btn-primary disabled:opacity-50",children:"Post"})]})]}):t.jsxs("p",{className:"mt-4 text-sm text-white/60",children:[t.jsx("a",{href:"/",className:"text-aurora hover:underline",children:"Sign in"})," to comment"]})]})]})})}export{Be as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.