feat(desktop): GIF button + picker wired into composer
This commit is contained in:
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user