54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
/* Views ping — counts one view per browser session per post (global counter
|
|
on the backend). */
|
|
export function ViewPing({ slug }: { slug: string }) {
|
|
useEffect(() => {
|
|
const key = `anw-viewed:${slug}`
|
|
try {
|
|
if (sessionStorage.getItem(key)) return
|
|
sessionStorage.setItem(key, '1')
|
|
} catch { /* private mode — count anyway */ }
|
|
fetch(`/api/posts/${slug}/view`, { method: 'POST', keepalive: true }).catch(() => null)
|
|
}, [slug])
|
|
return null
|
|
}
|
|
|
|
/* Like toggle — login required; one like per user per post. */
|
|
export function LikeButton({ slug, loggedIn, initialLiked, initialCount }: { slug: string; loggedIn: boolean; initialLiked: boolean; initialCount: number }) {
|
|
const [liked, setLiked] = useState(initialLiked)
|
|
const [count, setCount] = useState(initialCount)
|
|
const [busy, setBusy] = useState(false)
|
|
|
|
async function toggle() {
|
|
if (!loggedIn) {
|
|
location.href = `/login?next=/posts/${slug}`
|
|
return
|
|
}
|
|
setBusy(true)
|
|
const res = await fetch(`/api/posts/${slug}/like`, { method: 'POST' })
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
setLiked(data.liked)
|
|
setCount(data.likes)
|
|
}
|
|
setBusy(false)
|
|
}
|
|
|
|
return (
|
|
<button
|
|
className="like"
|
|
type="button"
|
|
onClick={toggle}
|
|
disabled={busy}
|
|
aria-pressed={liked}
|
|
aria-label={liked ? 'ألغِ الإعجاب' : 'أعجبني'}
|
|
>
|
|
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 13.5 2.6 8.3a3.2 3.2 0 0 1 4.5-4.6L8 4.6l.9-.9a3.2 3.2 0 0 1 4.5 4.6Z" fill="none" stroke="currentColor" strokeWidth="1.4" /></svg>
|
|
<span>{count}</span>
|
|
</button>
|
|
)
|
|
}
|