web: disable hardcoded tenor gifs and add configured fallback
All checks were successful
CI / test (push) Successful in 21s

This commit is contained in:
2026-03-08 13:55:24 +03:00
parent bc9d943d11
commit b6175352d0
2 changed files with 22 additions and 3 deletions

View File

@@ -1,2 +1,5 @@
VITE_API_BASE_URL=http://localhost:8000/api/v1 VITE_API_BASE_URL=http://localhost:8000/api/v1
VITE_WS_URL=ws://localhost:8000/api/v1/realtime/ws VITE_WS_URL=ws://localhost:8000/api/v1/realtime/ws
VITE_GIF_PROVIDER=
VITE_TENOR_API_KEY=
VITE_TENOR_CLIENT_KEY=benya_messenger_web

View File

@@ -31,8 +31,10 @@ const GIF_PRESETS: Array<{ name: string; url: string }> = [
const STICKER_FAVORITES_KEY = "bm_sticker_favorites_v1"; const STICKER_FAVORITES_KEY = "bm_sticker_favorites_v1";
const GIF_FAVORITES_KEY = "bm_gif_favorites_v1"; const GIF_FAVORITES_KEY = "bm_gif_favorites_v1";
const TENOR_API_KEY = "LIVDSRZULELA"; const GIF_PROVIDER = (import.meta.env.VITE_GIF_PROVIDER ?? "").toLowerCase();
const TENOR_CLIENT_KEY = "benya_messenger_web"; const TENOR_API_KEY = (import.meta.env.VITE_TENOR_API_KEY ?? "").trim();
const TENOR_CLIENT_KEY = (import.meta.env.VITE_TENOR_CLIENT_KEY ?? "benya_messenger_web").trim();
const GIF_SEARCH_ENABLED = GIF_PROVIDER === "tenor" && TENOR_API_KEY.length > 0;
function loadFavorites(key: string): Set<string> { function loadFavorites(key: string): Set<string> {
try { try {
@@ -102,6 +104,7 @@ export function MessageComposer() {
const [gifQuery, setGifQuery] = useState(""); const [gifQuery, setGifQuery] = useState("");
const [gifResults, setGifResults] = useState<Array<{ name: string; url: string }>>([]); const [gifResults, setGifResults] = useState<Array<{ name: string; url: string }>>([]);
const [gifLoading, setGifLoading] = useState(false); const [gifLoading, setGifLoading] = useState(false);
const [gifSearchError, setGifSearchError] = useState<string | null>(null);
const [favoriteStickers, setFavoriteStickers] = useState<Set<string>>(() => loadFavorites(STICKER_FAVORITES_KEY)); const [favoriteStickers, setFavoriteStickers] = useState<Set<string>>(() => loadFavorites(STICKER_FAVORITES_KEY));
const [favoriteGifs, setFavoriteGifs] = useState<Set<string>>(() => loadFavorites(GIF_FAVORITES_KEY)); const [favoriteGifs, setFavoriteGifs] = useState<Set<string>>(() => loadFavorites(GIF_FAVORITES_KEY));
const [captionDraft, setCaptionDraft] = useState(""); const [captionDraft, setCaptionDraft] = useState("");
@@ -321,11 +324,19 @@ export function MessageComposer() {
if (!showGifMenu || term.length < 2) { if (!showGifMenu || term.length < 2) {
setGifResults([]); setGifResults([]);
setGifLoading(false); setGifLoading(false);
setGifSearchError(null);
return;
}
if (!GIF_SEARCH_ENABLED) {
setGifResults([]);
setGifLoading(false);
setGifSearchError("GIF search is not configured. Using preset GIFs.");
return; return;
} }
let cancelled = false; let cancelled = false;
const timer = window.setTimeout(() => { const timer = window.setTimeout(() => {
setGifLoading(true); setGifLoading(true);
setGifSearchError(null);
void (async () => { void (async () => {
try { try {
const params = new URLSearchParams({ const params = new URLSearchParams({
@@ -338,6 +349,9 @@ export function MessageComposer() {
}); });
const response = await fetch(`https://tenor.googleapis.com/v2/search?${params.toString()}`); const response = await fetch(`https://tenor.googleapis.com/v2/search?${params.toString()}`);
if (!response.ok) { if (!response.ok) {
if (response.status === 400) {
throw new Error("GIF provider rejected the request. Configure your own API key.");
}
throw new Error("gif search failed"); throw new Error("gif search failed");
} }
const data = (await response.json()) as { const data = (await response.json()) as {
@@ -355,6 +369,7 @@ export function MessageComposer() {
} catch { } catch {
if (!cancelled) { if (!cancelled) {
setGifResults([]); setGifResults([]);
setGifSearchError("GIF search is unavailable right now. Using preset GIFs.");
} }
} finally { } finally {
if (!cancelled) { if (!cancelled) {
@@ -902,7 +917,7 @@ export function MessageComposer() {
) : null} ) : null}
{showStickerMenu ? ( {showStickerMenu ? (
<div className="mb-2 rounded-xl border border-slate-700/80 bg-slate-900/95 p-2"> <div className="mb-2 rounded-xl border border-slate-700/80 bg-slate-900/95 p-2">
<div className="mb-2 flex items-center justify-between"> <div className="mb-2 flex items-center justify-between">
<p className="text-xs font-semibold text-slate-200">Stickers</p> <p className="text-xs font-semibold text-slate-200">Stickers</p>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
@@ -1008,6 +1023,7 @@ export function MessageComposer() {
))} ))}
</div> </div>
{gifLoading ? <p className="mt-2 text-xs text-slate-400">Searching GIF...</p> : null} {gifLoading ? <p className="mt-2 text-xs text-slate-400">Searching GIF...</p> : null}
{gifSearchError ? <p className="mt-2 text-xs text-amber-300">{gifSearchError}</p> : null}
</div> </div>
) : null} ) : null}