feat(desktop): view-once image attachments (sender toggle + recipient lightbox + tombstone)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-05-16 18:18:39 +02:00
parent 915569db39
commit 95156a65eb
5 changed files with 157 additions and 5 deletions
+23 -1
View File
@@ -15,6 +15,7 @@ import {
ArrowRightIcon,
ChevronDownIcon,
ChevronUpIcon,
EyeOffIcon,
PlusIcon,
PollIcon,
ReplyIcon,
@@ -183,6 +184,10 @@ export function ConversationPage() {
const [mentionState, setMentionState] = useState<{ query: string; start: number } | null>(null);
const [emojiOpen, setEmojiOpen] = useState(false);
const [gifPickerOpen, setGifPickerOpen] = useState(false);
// Sticky toggle: when on, the next image(s) sent are marked view-once.
// Auto-clears on a successful send so the composer doesn't accidentally
// burn the message-after-next.
const [viewOnceNext, setViewOnceNext] = useState(false);
const pins = usePinnedMessages(id);
const [pinnedPanelOpen, setPinnedPanelOpen] = useState(false);
const pinnedIds = useMemo(() => new Set(pins.map((p) => p.messageId)), [pins]);
@@ -536,10 +541,13 @@ export function ConversationPage() {
setSending(true);
setSendError(null);
try {
await send(text, attachments, replyTo?.id ?? null);
await send(text, attachments, replyTo?.id ?? null, { viewOnce: viewOnceNext });
setText('');
setAttachments([]);
setReplyTo(null);
// Reset the sticky view-once flag so it only applies to the message
// the user explicitly armed it for — Snapchat / WhatsApp parity.
setViewOnceNext(false);
if (fileInputRef.current) fileInputRef.current.value = '';
setStickToBottom(true);
notifyStopTyping();
@@ -1012,6 +1020,20 @@ export function ConversationPage() {
onPick={(gif) => void handleGifPick(gif)}
/>
</div>
<button
type="button"
onClick={() => setViewOnceNext((v) => !v)}
aria-pressed={viewOnceNext}
title={viewOnceNext ? 'Nächstes Bild: einmal ansehen' : 'Nächstes Bild: normal'}
className={
'flex h-9 w-9 cursor-pointer items-center justify-center rounded-md transition ' +
(viewOnceNext
? 'bg-accent/20 text-accent'
: 'text-fg-muted hover:bg-surface-3 hover:text-fg')
}
>
<EyeOffIcon className="h-4 w-4" />
</button>
<VoiceRecorder
disabled={sending}
onComplete={async (file) => {