Morning improvements
This commit is contained in:
parent
3e9fba5303
commit
53fff3efaa
41 changed files with 2438 additions and 637 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import { useState, useCallback, useRef, useEffect } from 'react';
|
||||
import pb from '../lib/pocketbase';
|
||||
import { apiUrl, authHeaders } from '../lib/api';
|
||||
import { trackEvent } from '../lib/analytics';
|
||||
|
|
@ -12,39 +12,94 @@ export interface SavedSearch {
|
|||
created: string;
|
||||
}
|
||||
|
||||
const POLL_INTERVAL_MS = 2000;
|
||||
const MAX_POLL_ATTEMPTS = 15;
|
||||
|
||||
export function useSavedSearches(userId: string | null) {
|
||||
const [searches, setSearches] = useState<SavedSearch[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const pollTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const pollAttemptsRef = useRef(0);
|
||||
const userIdRef = useRef(userId);
|
||||
userIdRef.current = userId;
|
||||
|
||||
const stopPolling = useCallback(() => {
|
||||
if (pollTimerRef.current) {
|
||||
clearInterval(pollTimerRef.current);
|
||||
pollTimerRef.current = null;
|
||||
}
|
||||
pollAttemptsRef.current = 0;
|
||||
}, []);
|
||||
|
||||
// Clean up polling on unmount or userId change
|
||||
useEffect(() => stopPolling, [userId, stopPolling]);
|
||||
|
||||
const fetchRecords = useCallback(async (uid: string): Promise<SavedSearch[]> => {
|
||||
const records = await pb.collection('saved_searches').getFullList({
|
||||
sort: '-created',
|
||||
filter: `user = "${uid}"`,
|
||||
});
|
||||
return records.map((r) => ({
|
||||
id: r.id,
|
||||
name: (r as Record<string, unknown>).name as string,
|
||||
params: (r as Record<string, unknown>).params as string,
|
||||
screenshotUrl: (r as Record<string, unknown>).screenshot
|
||||
? pb.files.getURL(r, (r as Record<string, unknown>).screenshot as string)
|
||||
: '',
|
||||
notes: ((r as Record<string, unknown>).notes as string) || '',
|
||||
created: r.created,
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const startPolling = useCallback(() => {
|
||||
if (pollTimerRef.current) return;
|
||||
pollAttemptsRef.current = 0;
|
||||
pollTimerRef.current = setInterval(async () => {
|
||||
const uid = userIdRef.current;
|
||||
if (!uid) {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
pollAttemptsRef.current++;
|
||||
if (pollAttemptsRef.current >= MAX_POLL_ATTEMPTS) {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const mapped = await fetchRecords(uid);
|
||||
setSearches(mapped);
|
||||
if (!mapped.some((s) => !s.screenshotUrl)) {
|
||||
stopPolling();
|
||||
}
|
||||
} catch {
|
||||
// Silent — background poll errors don't surface to UI
|
||||
}
|
||||
}, POLL_INTERVAL_MS);
|
||||
}, [stopPolling, fetchRecords]);
|
||||
|
||||
const fetchSearches = useCallback(async () => {
|
||||
if (!userId) return;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const records = await pb.collection('saved_searches').getFullList({
|
||||
sort: '-created',
|
||||
filter: `user = "${userId}"`,
|
||||
});
|
||||
setSearches(
|
||||
records.map((r) => ({
|
||||
id: r.id,
|
||||
name: (r as Record<string, unknown>).name as string,
|
||||
params: (r as Record<string, unknown>).params as string,
|
||||
screenshotUrl: (r as Record<string, unknown>).screenshot
|
||||
? pb.files.getURL(r, (r as Record<string, unknown>).screenshot as string)
|
||||
: '',
|
||||
notes: ((r as Record<string, unknown>).notes as string) || '',
|
||||
created: r.created,
|
||||
}))
|
||||
);
|
||||
const mapped = await fetchRecords(userId);
|
||||
setSearches(mapped);
|
||||
|
||||
// Poll for missing screenshots so they appear without a page refresh
|
||||
if (mapped.some((s) => !s.screenshotUrl)) {
|
||||
startPolling();
|
||||
} else {
|
||||
stopPolling();
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load searches');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [userId]);
|
||||
}, [userId, fetchRecords, startPolling, stopPolling]);
|
||||
|
||||
const saveSearch = useCallback(
|
||||
async (name: string) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue