Fix performance and map tilt glitch
This commit is contained in:
parent
5ae40e7787
commit
fa5f176004
3 changed files with 39 additions and 8 deletions
|
|
@ -128,6 +128,11 @@ export default function App() {
|
|||
const labels = useMemo(() => buildLabels(places), [places]);
|
||||
const placeById = useMemo(() => new Map(realPlaces.map((p) => [p.id, p])), [realPlaces]);
|
||||
|
||||
const savedCounts = useMemo(() => {
|
||||
const c: Record<Verdict, number> = { match: 0, partial: 0, conflict: 0, incomplete: 0, fail: 0 };
|
||||
for (const a of assessments.values()) c[a.verdict]++;
|
||||
return c;
|
||||
}, [assessments]);
|
||||
const previewCounts = useCallback((p: Profile): Record<Verdict, number> => {
|
||||
const c: Record<Verdict, number> = { match: 0, partial: 0, conflict: 0, incomplete: 0, fail: 0 };
|
||||
if (index) for (const pl of realPlaces) c[assessPlace(pl, p, index, reports).verdict]++;
|
||||
|
|
@ -440,6 +445,7 @@ export default function App() {
|
|||
onDone={() => setView('places')}
|
||||
onSignOut={signOut}
|
||||
preview={previewCounts}
|
||||
savedCounts={savedCounts}
|
||||
>
|
||||
{!needsProfile && (
|
||||
<SettingsPanel
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ export function MapView({ places, showPlaces, selectedId, onSelect, route, route
|
|||
center: [(w + e) / 2, (s + n) / 2],
|
||||
zoom: 15,
|
||||
minZoom: 12,
|
||||
maxPitch: 0, // flat map only: no two-finger tilt, no ctrl/right-drag tilt
|
||||
dragRotate: false,
|
||||
touchPitch: false,
|
||||
maxBounds: [[w - 0.04, s - 0.03], [e + 0.04, n + 0.03]],
|
||||
attributionControl: { compact: true },
|
||||
locale: {
|
||||
|
|
@ -153,6 +156,8 @@ export function MapView({ places, showPlaces, selectedId, onSelect, route, route
|
|||
'AttributionControl.ToggleAttribution': t('map.attrToggle'),
|
||||
},
|
||||
});
|
||||
map.touchZoomRotate.disableRotation();
|
||||
map.keyboard.disableRotation();
|
||||
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
|
||||
map.on('load', () => {
|
||||
map.addSource('places', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { Account } from '../lib/auth';
|
||||
import { useI18n, type Key } from '../lib/i18n';
|
||||
import { DEFAULT_PROFILE, PRESETS, activePreset, bucket } from '../lib/profile';
|
||||
|
|
@ -13,23 +13,43 @@ interface Props {
|
|||
onDone: () => void;
|
||||
onSignOut: () => void;
|
||||
preview: (p: Profile) => Record<Verdict, number>;
|
||||
/** Verdict counts for the saved profile, already computed by the app: saves a second pass over every place. */
|
||||
savedCounts: Record<Verdict, number> | null;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const VERDICTS: Verdict[] = ['match', 'partial', 'conflict', 'incomplete', 'fail'];
|
||||
|
||||
export function ProfileView({ account, profile, onChange, onDone, onSignOut, preview, children }: Props) {
|
||||
export function ProfileView({ account, profile, onChange, onDone, onSignOut, preview, savedCounts, children }: Props) {
|
||||
const { t } = useI18n();
|
||||
const [draft, setDraft] = useState<Profile>(profile ?? DEFAULT_PROFILE[account.preset ?? 'wheelchair']);
|
||||
const update = (p: Profile) => { setDraft(p); if (profile) onChange(p); };
|
||||
// The draft follows the control instantly; the saved profile (which re-assesses every place) only catches up once changes pause.
|
||||
const pending = useRef<Profile | null>(null);
|
||||
const timer = useRef<number | undefined>(undefined);
|
||||
const onChangeRef = useRef(onChange);
|
||||
onChangeRef.current = onChange;
|
||||
const flush = () => {
|
||||
window.clearTimeout(timer.current);
|
||||
if (pending.current) { onChangeRef.current(pending.current); pending.current = null; }
|
||||
};
|
||||
const update = (p: Profile) => {
|
||||
setDraft(p);
|
||||
if (!profile) return;
|
||||
pending.current = p;
|
||||
window.clearTimeout(timer.current);
|
||||
timer.current = window.setTimeout(flush, 300);
|
||||
};
|
||||
useEffect(() => flush, []); // leaving the page must not drop the last change
|
||||
const set = <K extends keyof Profile>(k: K, v: Profile[K]) => update({ ...draft, [k]: v });
|
||||
|
||||
// Debounced live preview: how many places fall into each verdict with these limits.
|
||||
const [counts, setCounts] = useState<Record<Verdict, number> | null>(null);
|
||||
// Live preview: how many places fall into each verdict with these limits. A saved profile reuses the app's own assessments.
|
||||
const [onboardCounts, setOnboardCounts] = useState<Record<Verdict, number> | null>(null);
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setCounts(preview(draft)), 250);
|
||||
if (profile) return;
|
||||
const id = setTimeout(() => setOnboardCounts(preview(draft)), 250);
|
||||
return () => clearTimeout(id);
|
||||
}, [draft, preview]);
|
||||
}, [profile, draft, preview]);
|
||||
const counts = profile ? savedCounts : onboardCounts;
|
||||
|
||||
const active = activePreset(draft);
|
||||
const presets = PRESETS.filter((p) => p.kind === draft.kind);
|
||||
|
|
@ -151,7 +171,7 @@ export function ProfileView({ account, profile, onChange, onDone, onSignOut, pre
|
|||
<p className="font-sans-xs text-base-dark margin-top-2">
|
||||
<Icon name="lock" size={2} className="margin-right-05" />{t('profile.privacy')} {profile && t('profile.autosave')}
|
||||
</p>
|
||||
<button type="button" className="usa-button usa-button--big margin-top-2" onClick={() => { onChange(draft); onDone(); }}>
|
||||
<button type="button" className="usa-button usa-button--big margin-top-2" onClick={() => { pending.current = null; window.clearTimeout(timer.current); onChange(draft); onDone(); }}>
|
||||
{t('profile.done')}
|
||||
<Icon name="arrow_forward" size={3} className="margin-left-1" />
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue