feat(frontend): load systems from PocketBase and add system focus to region page
This commit is contained in:
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { AhoCorasick } from '@/lib/aho';
|
||||
import pb from '@/lib/pocketbase';
|
||||
|
||||
interface SearchResult {
|
||||
system: string;
|
||||
@@ -10,21 +11,25 @@ interface SearchResult {
|
||||
}
|
||||
|
||||
async function loadAllSystems(): Promise<Array<SearchResult>> {
|
||||
// Fetch the list of regions from universe.json then fetch each region JSON for systems
|
||||
const res = await fetch('/universe.json');
|
||||
if (!res.ok) return [];
|
||||
const regions: Array<{ regionName: string } & Record<string, any>> = await res.json();
|
||||
// Fetch system names with regions from PocketBase, paginated and minimal fields
|
||||
const perPage = 1000;
|
||||
let page = 1;
|
||||
const out: Array<SearchResult> = [];
|
||||
await Promise.all(regions.map(async (r) => {
|
||||
try {
|
||||
const rr = await fetch(`/${encodeURIComponent(r.regionName)}.json`);
|
||||
if (!rr.ok) return;
|
||||
const systems: Array<{ solarSystemName: string } & Record<string, any>> = await rr.json();
|
||||
for (const s of systems) {
|
||||
out.push({ system: s.solarSystemName, region: r.regionName });
|
||||
const seen = new Set<string>();
|
||||
// loop pages until fewer than perPage items
|
||||
while (true) {
|
||||
const res = await pb.collection('regionview').getList(page, perPage, { fields: 'sysname,sysregion' });
|
||||
for (const item of res.items as any[]) {
|
||||
const system: string = item.sysname;
|
||||
const region: string = item.sysregion;
|
||||
if (!seen.has(system)) {
|
||||
seen.add(system);
|
||||
out.push({ system, region });
|
||||
}
|
||||
}
|
||||
if (res.items.length < perPage) break;
|
||||
page += 1;
|
||||
}
|
||||
} catch (_) { /* noop */ }
|
||||
}));
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -36,7 +41,6 @@ export const SearchDialog: React.FC = () => {
|
||||
const [results, setResults] = useState<Array<SearchResult>>([]);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
// Build AC automaton of single query to enable contains search efficiently (case-insensitive)
|
||||
const automaton = useMemo(() => {
|
||||
const ac = new AhoCorasick();
|
||||
if (query.trim().length > 0) ac.add(query.toLowerCase());
|
||||
@@ -60,17 +64,14 @@ export const SearchDialog: React.FC = () => {
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
if (all.length === 0) {
|
||||
// lazy load once
|
||||
loadAllSystems().then(setAll);
|
||||
}
|
||||
}, [open, all.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (query.trim().length === 0) { setResults([]); return; }
|
||||
const q = query.toLowerCase();
|
||||
const out: Array<SearchResult> = [];
|
||||
for (const r of all) {
|
||||
// contains via Aho–Corasick over the single query pattern
|
||||
if (automaton.searchHas(r.system.toLowerCase())) {
|
||||
out.push(r);
|
||||
if (out.length >= 10) break;
|
||||
@@ -82,7 +83,7 @@ export const SearchDialog: React.FC = () => {
|
||||
const onSelect = (r: SearchResult) => {
|
||||
setOpen(false);
|
||||
setQuery('');
|
||||
navigate(`/regions/${encodeURIComponent(r.region)}/${encodeURIComponent(r.system)}`);
|
||||
navigate(`/regions/${encodeURIComponent(r.region)}?focus=${encodeURIComponent(r.system)}`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { RegionMap } from '@/components/RegionMap';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
@@ -6,6 +6,8 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const RegionPage = () => {
|
||||
const { region } = useParams<{ region: string }>();
|
||||
const [sp] = useSearchParams();
|
||||
const focus = sp.get('focus') || undefined;
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!region) {
|
||||
@@ -31,6 +33,7 @@ export const RegionPage = () => {
|
||||
<RegionMap
|
||||
regionName={region}
|
||||
isWormholeRegion={region === "Wormhole"}
|
||||
focusSystem={focus}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
Reference in New Issue
Block a user