summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/search-page.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/search-page.js70
1 files changed, 59 insertions, 11 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
index 032d26a..5ac3ab6 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
@@ -10,6 +10,7 @@ import {
import { FilesPanel, FilePreview } from './files-app.js';
import { VideoApp, bumpMediaMetaGeneration, bumpThumbGeneration } from './video-app.js';
import { MusicApp, bumpMusicMetaGeneration } from './music-app.js';
+import { PhotosApp } from './photos-app.js';
import { VideoPlayer } from './video-player.js';
import { transfers } from './transfers.js';
@@ -18,6 +19,7 @@ const MAX_POOL_SIZE = 3;
const DEBOUNCE_MS = 200;
const SEARCH_VIDEO_ROOT = '__search__';
const SEARCH_AUDIO_ROOT = '__search__';
+const SEARCH_PHOTO_ROOTS = ['__search_photos__'];
// -- Connection pool ----------------------------------------------------------
@@ -235,18 +237,15 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
return entry;
}, [token, username, userId]);
- // Eagerly connect to groups when entering Videos or Music view
+ // Pre-connect to all groups as soon as indexing finishes so thumbnails
+ // start loading before the user switches views. The pool evicts old
+ // connections but _thumbBlobCache keeps fetched thumbnails across evictions.
useEffect(() => {
- if (viewMode !== 'videos' && viewMode !== 'music') return;
+ if (fetching || indexedGroups.size === 0) return;
let cancelled = false;
(async () => {
- const groupIds = [...indexedGroups.keys()].filter((gid) => {
- const data = indexedGroups.get(gid);
- if (viewMode === 'videos') return !!data.roots.videoRoot;
- return !!data.roots.audioRoot;
- });
-
+ const groupIds = [...indexedGroups.keys()];
for (let i = 0; i < groupIds.length; i += BATCH_SIZE) {
if (cancelled) break;
const batch = groupIds.slice(i, i + BATCH_SIZE);
@@ -257,7 +256,7 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
})();
return () => { cancelled = true; };
- }, [viewMode, indexedGroups, connectGroup]);
+ }, [fetching, indexedGroups, connectGroup]);
// -- Entry preparation --
@@ -364,6 +363,32 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
return result;
}, [indexedGroups, q, matchesQuery, connectionGen]);
+ // Photos view: pre-filtered by photoRoots, path-prefixed
+ const photoEntries = useMemo(() => {
+ const result = [];
+ for (const [groupId, data] of indexedGroups) {
+ const roots = data.roots.photoRoots;
+ if (!roots || !roots.length) continue;
+ const conn = groupConns.current.get(groupId);
+ for (const e of data.entries) {
+ if (e.type !== 'image') continue;
+ const p = e.path || '';
+ if (!roots.some((r) => p === r || p.startsWith(r + '/'))) continue;
+ if (q && !matchesQuery(e)) continue;
+ result.push({
+ ...e,
+ path: '__search_photos__/' + e.path,
+ groupId,
+ groupName: data.groupName,
+ groupOwner: data.groupOwner,
+ _tRef: conn ? conn.tRef : null,
+ _gRef: conn ? conn.gRef : null,
+ });
+ }
+ }
+ return result;
+ }, [indexedGroups, q, matchesQuery, connectionGen]);
+
// -- Callbacks --
const onPreview = useCallback(async (entry) => {
@@ -417,7 +442,12 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
await downloadEntry(transfers, transport, modalGekRef.current, entry);
}, []);
- // No-op setters for FilesPanel (read-only mode)
+ const getTransport = useCallback(async (entry) => {
+ const conn = await connectGroup(entry.groupId);
+ return { transport: conn.transport, gek: conn.gek };
+ }, [connectGroup]);
+
+ // No-op setters for FilesPanel
const noop = useCallback(() => {}, []);
// -- Render --
@@ -456,6 +486,10 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
onClick=${() => setViewMode('music')}
title=${t('search.view_music')}>
<${Icon} name="music" /></button>
+ <button class=${viewMode === 'photos' ? 'active' : ''}
+ onClick=${() => setViewMode('photos')}
+ title=${t('search.view_photos')}>
+ <${Icon} name="image" /></button>
</div>
`}
</div>
@@ -497,7 +531,8 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
setError=${noop}
onPreview=${onPreview}
showGroup=${true}
- readOnly=${true} />
+ readOnly=${true}
+ getTransport=${getTransport} />
`}
${viewMode === 'videos' && hasResults && html`
@@ -527,6 +562,19 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
hideFilter=${true} />
`}
+ ${viewMode === 'photos' && hasResults && html`
+ <${PhotosApp}
+ groupId="search"
+ transportRef=${defaultTRef}
+ gekRef=${defaultGRef}
+ status="connected"
+ entries=${photoEntries}
+ photoRoots=${SEARCH_PHOTO_ROOTS}
+ setError=${noop}
+ hideFilter=${true}
+ readOnly=${true} />
+ `}
+
${!hasResults && !fetching && html`
<p class="page-message">${t('search.hint')}</p>
`}