Fix time filter refresh and polish search UI

This commit is contained in:
Your Name 2025-12-30 18:01:11 -05:00
parent d7700b154b
commit 5cc21194a0
5 changed files with 31 additions and 9 deletions

View File

@ -1,10 +1,25 @@
const API_BASE = "/api"; function apiBase() {
const raw =
(import.meta?.env?.VITE_API_BASE_URL || "")
.toString()
.replace(/\/+$/, "");
if (raw) return raw + "/api";
try {
if (typeof window !== "undefined") {
const host = window.location.hostname || "";
if (host.startsWith("www.")) {
return `https://${host.replace(/^www\./, "")}/api`;
}
}
} catch {}
return "/api";
}
const cache = new Map(); // key -> Promise const cache = new Map(); // key -> Promise
export async function fetchDefaultTemplate(typeKey = "news") { export async function fetchDefaultTemplate(typeKey = "news") {
const t = (typeKey || "news").toLowerCase().trim(); const t = (typeKey || "news").toLowerCase().trim();
const res = await fetch(`${API_BASE}/templates/default?type=${encodeURIComponent(t)}`, { const res = await fetch(`${apiBase()}/templates/default?type=${encodeURIComponent(t)}`, {
credentials: "include", credentials: "include",
}); });
if (!res.ok) { if (!res.ok) {

View File

@ -5,7 +5,7 @@ export default function TimeFilterButtons({ active, onSelect }) {
{ code: "NOW", label: "Now", icon: "fa-solid fa-bolt" }, { code: "NOW", label: "Now", icon: "fa-solid fa-bolt" },
{ code: "TODAY", label: "Today", icon: "fa-solid fa-sun" }, { code: "TODAY", label: "Today", icon: "fa-solid fa-sun" },
{ code: "RECENT", label: "Recent", icon: "fa-solid fa-clock-rotate-left" }, { code: "RECENT", label: "Recent", icon: "fa-solid fa-clock-rotate-left" },
{ code: "PAST", label: "Past", icon: "fa-solid fa-hourglass-half" }, { code: "PAST", label: "Old", icon: "fa-solid fa-hourglass-half" },
]; ];
return ( return (

View File

@ -813,7 +813,6 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
<div class="sw-modal-badge">MAIN NEWS</div> <div class="sw-modal-badge">MAIN NEWS</div>
<div class="sw-modal-meta"> <div class="sw-modal-meta">
${author ? `<div class="sw-modal-author-avatar" data-username="${escapeHtml(author)}"></div>` : ""} ${author ? `<div class="sw-modal-author-avatar" data-username="${escapeHtml(author)}"></div>` : ""}
${author ? `<div class="sw-inline-avatar" data-username="${escapeHtml(author)}"></div>` : ""}
${metaLeft ? `<span class="sw-author-link" data-author="${escapeHtml(author)}" style="cursor: pointer; text-decoration: underline;">${escapeHtml(metaLeft)}</span>` : ""} ${metaLeft ? `<span class="sw-author-link" data-author="${escapeHtml(author)}" style="cursor: pointer; text-decoration: underline;">${escapeHtml(metaLeft)}</span>` : ""}
${metaRight ? `<span>• ${escapeHtml(metaRight)}</span>` : ""} ${metaRight ? `<span>• ${escapeHtml(metaRight)}</span>` : ""}
<span> ${escapeHtml(relTime)}</span> <span> ${escapeHtml(relTime)}</span>
@ -893,7 +892,6 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
<div class="sw-modal-badge">${escapeHtml(cat)}</div> <div class="sw-modal-badge">${escapeHtml(cat)}</div>
<div class="sw-modal-meta"> <div class="sw-modal-meta">
${author ? `<div class="sw-modal-author-avatar" data-username="${escapeHtml(author)}"></div>` : ""} ${author ? `<div class="sw-modal-author-avatar" data-username="${escapeHtml(author)}"></div>` : ""}
${author ? `<div class="sw-inline-avatar" data-username="${escapeHtml(author)}"></div>` : ""}
${metaLeft ? `<span class="sw-author-link" data-author="${escapeHtml(author)}" style="cursor: pointer; text-decoration: underline;">${escapeHtml(metaLeft)}</span>` : ""} ${metaLeft ? `<span class="sw-author-link" data-author="${escapeHtml(author)}" style="cursor: pointer; text-decoration: underline;">${escapeHtml(metaLeft)}</span>` : ""}
${metaRight ? `<span>• ${escapeHtml(metaRight)}</span>` : ""} ${metaRight ? `<span>• ${escapeHtml(metaRight)}</span>` : ""}
<span> ${escapeHtml(relTime)}</span> <span> ${escapeHtml(relTime)}</span>

View File

@ -858,9 +858,8 @@ export function usePostsEngine({
if (pc !== catCode) return false; if (pc !== catCode) return false;
} }
if (!matchesSubFilter(p, subFilter)) return false; if (!matchesSubFilter(p, subFilter)) return false;
const skipTimeFilter = const postTf = (p?._swTimeFilter || "").toString();
tf === "RECENT" && lastFetchRef.current?.timeFilter === "RECENT"; if (postTf !== tf && !matchesTimeFilter(effectivePostTime(p), tf)) return false;
if (!skipTimeFilter && !matchesTimeFilter(effectivePostTime(p), tf)) return false;
if (!searchResultsRef.current && !matchesSearch(p, searchQuery)) return false; if (!searchResultsRef.current && !matchesSearch(p, searchQuery)) return false;
return true; return true;
}); });
@ -1075,6 +1074,7 @@ export function usePostsEngine({
const normalized = normalizePostId(p); const normalized = normalizePostId(p);
const key = getPostKey(normalized); const key = getPostKey(normalized);
if (!key) continue; if (!key) continue;
if (timeFilter) normalized._swTimeFilter = timeFilter;
const prior = byId.get(key); const prior = byId.get(key);
if (prior) { if (prior) {
byId.set(key, { ...prior, ...normalized }); byId.set(key, { ...prior, ...normalized });

View File

@ -75,6 +75,7 @@ export default function SmartSearchBar({
const [active, setActive] = useState(-1); const [active, setActive] = useState(-1);
const [err, setErr] = useState(""); const [err, setErr] = useState("");
const [hasSearched, setHasSearched] = useState(false); const [hasSearched, setHasSearched] = useState(false);
const [focused, setFocused] = useState(false);
const abortRef = useRef(null); const abortRef = useRef(null);
const skipSearchRef = useRef(false); const skipSearchRef = useRef(false);
@ -98,6 +99,7 @@ export default function SmartSearchBar({
abortRef.current = ctrl; abortRef.current = ctrl;
setLoading(true); setLoading(true);
setErr(""); setErr("");
setOpen(true);
try { try {
console.log('[SmartSearchBar] Searching for:', searchQuery); console.log('[SmartSearchBar] Searching for:', searchQuery);
@ -112,6 +114,7 @@ export default function SmartSearchBar({
console.error('[SmartSearchBar] Search error:', e); console.error('[SmartSearchBar] Search error:', e);
setErr((e && e.message) ? e.message : "Search error"); setErr((e && e.message) ? e.message : "Search error");
setItems([]); setItems([]);
setOpen(true);
} finally { } finally {
if (!ctrl.signal.aborted) setLoading(false); if (!ctrl.signal.aborted) setLoading(false);
} }
@ -125,6 +128,8 @@ export default function SmartSearchBar({
return; return;
} }
if (query) setOpen(true);
if (abortRef.current) abortRef.current.abort(); if (abortRef.current) abortRef.current.abort();
const t = setTimeout(() => { const t = setTimeout(() => {
@ -137,7 +142,7 @@ export default function SmartSearchBar({
}; };
}, [q]); }, [q]);
const showList = open && (items.length > 0 || loading || err); const showList = (open || focused) && (items.length > 0 || loading || err || q.trim());
const clearOnFocusRef = useRef(false); const clearOnFocusRef = useRef(false);
const pick = (it, reason = "click") => { const pick = (it, reason = "click") => {
@ -214,6 +219,7 @@ export default function SmartSearchBar({
} }
}} }}
onFocus={() => { onFocus={() => {
setFocused(true);
if (clearOnFocusRef.current) { if (clearOnFocusRef.current) {
clearOnFocusRef.current = false; clearOnFocusRef.current = false;
setQ(""); setQ("");
@ -228,6 +234,9 @@ export default function SmartSearchBar({
triggerSearch(""); triggerSearch("");
} }
}} }}
onBlur={() => {
setTimeout(() => setFocused(false), 120);
}}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
placeholder={placeholder} placeholder={placeholder}
aria-label="Search" aria-label="Search"