119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
|
|
import { useStore } from '@nanostores/react'
|
||
|
|
import { $interactions, $interactionsData, $hasInteractionChanges, $saveInteractions, $changedInteractionFields } from '../stores/interactions'
|
||
|
|
import { addToast } from '../stores/app'
|
||
|
|
import { SaveBar, EngagementPageSkeleton, PageHeader } from '../components/shared'
|
||
|
|
import { Toggle, Input } from '../components/ui'
|
||
|
|
|
||
|
|
export default function EngagementPage() {
|
||
|
|
const config = useStore($interactions)
|
||
|
|
const { data } = useStore($interactionsData)
|
||
|
|
const hasChanges = useStore($hasInteractionChanges)
|
||
|
|
const changedFields = useStore($changedInteractionFields)
|
||
|
|
const saveInteractions = useStore($saveInteractions)
|
||
|
|
|
||
|
|
const handleSave = async () => {
|
||
|
|
try {
|
||
|
|
await saveInteractions.mutate(config)
|
||
|
|
addToast('Settings saved', 'success')
|
||
|
|
} catch {
|
||
|
|
addToast('Failed to save settings', 'error')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!data) return <EngagementPageSkeleton />
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="pb-20">
|
||
|
|
<PageHeader />
|
||
|
|
|
||
|
|
{/* Panel container - full-bleed borders */}
|
||
|
|
<div className="-mx-6 lg:-mx-10 mt-6">
|
||
|
|
{/* Comments - single row toggle */}
|
||
|
|
<div className="px-6 lg:px-10 py-5 flex items-center justify-between gap-4">
|
||
|
|
<div>
|
||
|
|
<div className="text-sm font-medium">Comments</div>
|
||
|
|
<div className="text-xs text-muted mt-0.5">Allow readers to comment on your posts</div>
|
||
|
|
</div>
|
||
|
|
<Toggle
|
||
|
|
checked={config.comments_enabled}
|
||
|
|
onChange={v => $interactions.setKey('comments_enabled', v)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="border-t border-border" />
|
||
|
|
|
||
|
|
{/* Reactions - toggle with expandable options */}
|
||
|
|
<div className="px-6 lg:px-10 py-5 flex items-center justify-between gap-4">
|
||
|
|
<div>
|
||
|
|
<div className="text-sm font-medium">Reactions</div>
|
||
|
|
<div className="text-xs text-muted mt-0.5">Let readers react to your posts</div>
|
||
|
|
</div>
|
||
|
|
<Toggle
|
||
|
|
checked={config.reactions_enabled}
|
||
|
|
onChange={v => $interactions.setKey('reactions_enabled', v)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{config.reactions_enabled && (
|
||
|
|
<div className="px-6 lg:px-10 py-6 space-y-4">
|
||
|
|
<div className="space-y-1">
|
||
|
|
<label className="label">Reaction Mode</label>
|
||
|
|
<div className="flex gap-2">
|
||
|
|
{['emoji', 'upvote'].map(mode => (
|
||
|
|
<button
|
||
|
|
key={mode}
|
||
|
|
onClick={() => $interactions.setKey('reaction_mode', mode)}
|
||
|
|
className={`flex-1 p-3 border capitalize transition-colors ${
|
||
|
|
config.reaction_mode === mode
|
||
|
|
? 'border-accent bg-accent/5'
|
||
|
|
: 'border-border hover:border-muted'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{mode}
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{config.reaction_mode === 'emoji' && (
|
||
|
|
<div className="space-y-1">
|
||
|
|
<label className="label">Reaction Emojis</label>
|
||
|
|
<Input
|
||
|
|
value={config.reaction_emojis}
|
||
|
|
onChange={v => $interactions.setKey('reaction_emojis', v)}
|
||
|
|
placeholder="👍 ❤️ 🎉 🤔"
|
||
|
|
/>
|
||
|
|
<p className="text-xs text-muted">Space-separated list of emoji reactions</p>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{config.reaction_mode === 'upvote' && (
|
||
|
|
<div className="space-y-1">
|
||
|
|
<label className="label">Upvote Icon</label>
|
||
|
|
<Input
|
||
|
|
value={config.upvote_icon}
|
||
|
|
onChange={v => $interactions.setKey('upvote_icon', v)}
|
||
|
|
placeholder="👍"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="flex items-center justify-between gap-4 pt-2">
|
||
|
|
<div>
|
||
|
|
<div className="text-sm font-medium">Require Authentication</div>
|
||
|
|
<div className="text-xs text-muted mt-0.5">Users must be logged in to react</div>
|
||
|
|
</div>
|
||
|
|
<Toggle
|
||
|
|
checked={config.reactions_require_auth}
|
||
|
|
onChange={v => $interactions.setKey('reactions_require_auth', v)}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{hasChanges && <SaveBar onSave={handleSave} loading={saveInteractions.loading} changes={changedFields} />}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|