lgtm
This commit is contained in:
parent
5e73287eaf
commit
e2b85fe819
73 changed files with 1180 additions and 2028 deletions
|
|
@ -12,7 +12,7 @@ import {
|
|||
} from './lib/seoRoutes';
|
||||
import Header, { type Page } from './components/ui/Header';
|
||||
import type { FeatureMeta, FeatureGroup, POICategoriesResponse, POICategoryGroup } from './types';
|
||||
import { fetchWithRetry, apiUrl, logNonAbortError, readDemoChoice, setDemoCenter } from './lib/api';
|
||||
import { fetchWithRetry, apiUrl, logNonAbortError } from './lib/api';
|
||||
import { trackEvent } from './lib/analytics';
|
||||
import { parseUrlState } from './lib/url-state';
|
||||
import pb from './lib/pocketbase';
|
||||
|
|
@ -209,34 +209,6 @@ export default function App() {
|
|||
}, []);
|
||||
const initialRoute = useMemo(() => pathToPage(window.location.pathname), []);
|
||||
const [mapUrlState, setMapUrlState] = useState(urlState);
|
||||
// Demo location: a fresh visitor (no explicit URL view) is offered the "check
|
||||
// your area" prompt (in MapPage). A choice made earlier this session is reused —
|
||||
// GPS coords re-centre the map and are sent to the server (via lib/api), while
|
||||
// 'declined' keeps Canary Wharf. Resolve it once, before children fetch, so the
|
||||
// very first data request already carries the centre.
|
||||
// Read license synchronously from the persisted session so we never apply the
|
||||
// demo location for a returning licensed user (they bypass the free zone, and we
|
||||
// don't want their location in request URLs).
|
||||
const licensedAtMount = useMemo(
|
||||
() =>
|
||||
pb.authStore.isValid &&
|
||||
(pb.authStore.record?.subscription === 'licensed' || pb.authStore.record?.is_admin === true),
|
||||
[]
|
||||
);
|
||||
const initialDemoChoice = useMemo(() => {
|
||||
const choice = readDemoChoice();
|
||||
if (choice && choice !== 'declined' && !licensedAtMount) setDemoCenter(choice);
|
||||
return choice;
|
||||
}, [licensedAtMount]);
|
||||
const [demoView] = useState<typeof INITIAL_VIEW_STATE | null>(() =>
|
||||
initialDemoChoice && initialDemoChoice !== 'declined' && !licensedAtMount
|
||||
? {
|
||||
...INITIAL_VIEW_STATE,
|
||||
latitude: initialDemoChoice.lat,
|
||||
longitude: initialDemoChoice.lon,
|
||||
}
|
||||
: null
|
||||
);
|
||||
const [dashboardRouteKey, setDashboardRouteKey] = useState(() =>
|
||||
window.location.pathname === '/dashboard' ? window.location.search : ''
|
||||
);
|
||||
|
|
@ -248,8 +220,8 @@ export default function App() {
|
|||
);
|
||||
const activePageRef = useRef<Page>('home');
|
||||
const initialViewState = useMemo(
|
||||
() => (mapUrlState.hasExplicitView ? mapUrlState.viewState : (demoView ?? INITIAL_VIEW_STATE)),
|
||||
[mapUrlState.hasExplicitView, mapUrlState.viewState, demoView]
|
||||
() => (mapUrlState.hasExplicitView ? mapUrlState.viewState : INITIAL_VIEW_STATE),
|
||||
[mapUrlState.hasExplicitView, mapUrlState.viewState]
|
||||
);
|
||||
|
||||
const isScreenshotMode = useMemo(() => {
|
||||
|
|
@ -302,16 +274,6 @@ export default function App() {
|
|||
refreshAuth,
|
||||
clearError,
|
||||
} = useAuth();
|
||||
// Only offer the "check your area" demo prompt to non-licensed visitors on a
|
||||
// fresh visit. `user` is hydrated synchronously from the persisted PocketBase
|
||||
// session, so a returning premium user is recognised at mount (no flash).
|
||||
const offerDemoLocation =
|
||||
!urlState.hasExplicitView && initialDemoChoice == null && !hasFullAccess(user);
|
||||
// Stop transmitting the demo location once the user is licensed (in-session
|
||||
// upgrade, cross-tab sign-in, or a stale session that refreshed to licensed).
|
||||
useEffect(() => {
|
||||
if (hasFullAccess(user)) setDemoCenter(null);
|
||||
}, [user]);
|
||||
const { startCheckout: startPostAuthCheckout } = useLicense();
|
||||
const [showAuthModal, setShowAuthModal] = useState(false);
|
||||
const [authModalTab, setAuthModalTab] = useState<'login' | 'register'>('login');
|
||||
|
|
@ -351,8 +313,8 @@ export default function App() {
|
|||
if (!completed) {
|
||||
setPostAuthIntent(null);
|
||||
postAuthCheckoutReturnPathRef.current = null;
|
||||
// Only protected pages bounce home; the dashboard stays open in demo
|
||||
// mode (server-enforced free zone) when the modal is dismissed.
|
||||
// Only protected pages bounce home; the dashboard stays open when the
|
||||
// modal is dismissed.
|
||||
if (isProtectedPage(activePageRef.current)) {
|
||||
window.history.replaceState({ page: 'home', hash: '' }, '', '/');
|
||||
setRouteHash('');
|
||||
|
|
@ -611,9 +573,8 @@ export default function App() {
|
|||
|
||||
const isProtectedPageActive = isProtectedPage(activePage);
|
||||
// Only protected pages (account/saved) prompt for login on entry. The
|
||||
// dashboard opens straight into demo mode (server-enforced free zone) so
|
||||
// visitors can try it without logging in; the upgrade modal still appears
|
||||
// when they pan outside the free zone.
|
||||
// dashboard opens straight away so visitors can try it without logging in;
|
||||
// the upgrade modal appears when they hit the free filter cap.
|
||||
useEffect(() => {
|
||||
if (authLoading) return;
|
||||
if (isProtectedPageActive && !user) {
|
||||
|
|
@ -768,7 +729,6 @@ export default function App() {
|
|||
poiCategoryGroups={poiCategoryGroups}
|
||||
initialFilters={mapUrlState.filters}
|
||||
initialViewState={initialViewState}
|
||||
offerDemoLocation={offerDemoLocation}
|
||||
initialPOICategories={mapUrlState.poiCategories}
|
||||
initialOverlays={mapUrlState.overlays}
|
||||
initialCrimeTypes={mapUrlState.crimeTypes}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue