feat(desktop): GIF button + picker wired into composer

This commit is contained in:
byGalax
2026-05-16 18:07:40 +02:00
parent 7726498218
commit cd79b77e6f
@@ -8,6 +8,7 @@ import { ConversationHeader } from '../components/ConversationHeader';
import { EmojiPicker } from '../components/EmojiPicker';
import { EmptyState } from '../components/EmptyState';
import { ForwardDialog } from '../components/ForwardDialog';
import { GifPicker } from '../components/GifPicker';
import { GroupInfoPanel } from '../components/GroupInfoPanel';
import {
AlertIcon,
@@ -43,6 +44,7 @@ import { compressImages } from '../lib/imageCompress';
import { ensureInstallId } from '../lib/installId';
import { searchCachedMessages } from '../lib/messageCache';
import { supabase } from '../lib/supabase';
import { type GifResult } from '../lib/tenor';
import type { OutboxItem } from '../lib/messageOutbox';
import { useConversationMessages } from '../lib/useConversationMessages';
import { useMessageReactions } from '../lib/useMessageReactions';
@@ -180,6 +182,7 @@ export function ConversationPage() {
} | null>(null);
const [mentionState, setMentionState] = useState<{ query: string; start: number } | null>(null);
const [emojiOpen, setEmojiOpen] = useState(false);
const [gifPickerOpen, setGifPickerOpen] = useState(false);
const pins = usePinnedMessages(id);
const [pinnedPanelOpen, setPinnedPanelOpen] = useState(false);
const pinnedIds = useMemo(() => new Set(pins.map((p) => p.messageId)), [pins]);
@@ -199,6 +202,23 @@ export function ConversationPage() {
},
[id, myId, pinnedIds],
);
const handleGifPick = useCallback(
async (gif: GifResult) => {
try {
// Download the GIF bytes once and feed them into the existing
// image-attachment pipeline so the result is end-to-end-encrypted
// like any normal image.
const res = await fetch(gif.url);
const blob = await res.blob();
const file = new File([blob], `tenor-${gif.id}.gif`, { type: 'image/gif' });
await send('', [file], null);
} catch (err) {
console.warn('GIF send failed', err);
}
},
[send],
);
const scrollRef = useRef<HTMLDivElement>(null);
const loadMoreSentinelRef = useRef<HTMLDivElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -976,6 +996,22 @@ export function ConversationPage() {
onClose={() => setEmojiOpen(false)}
/>
</div>
<div className="relative">
<button
type="button"
onClick={() => setGifPickerOpen((v) => !v)}
className="flex h-9 w-9 cursor-pointer items-center justify-center rounded-md text-fg-muted hover:bg-surface-3 hover:text-fg"
aria-label="GIF"
title="GIF einfügen"
>
<span className="text-xs font-bold">GIF</span>
</button>
<GifPicker
open={gifPickerOpen}
onClose={() => setGifPickerOpen(false)}
onPick={(gif) => void handleGifPick(gif)}
/>
</div>
<VoiceRecorder
disabled={sending}
onComplete={async (file) => {