web: disable hardcoded tenor gifs and add configured fallback
All checks were successful
CI / test (push) Successful in 21s
All checks were successful
CI / test (push) Successful in 21s
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user