lgtm fe
This commit is contained in:
parent
fe46cb3379
commit
dd9f00b105
8 changed files with 338 additions and 103 deletions
|
|
@ -5,6 +5,7 @@ import {
|
|||
type TravelTimeEntry,
|
||||
type TravelTimeInitial,
|
||||
} from '../hooks/useTravelTime';
|
||||
import { INITIAL_VIEW_STATE } from './consts';
|
||||
import {
|
||||
SCHOOL_FILTER_NAME,
|
||||
createSchoolFilterKey,
|
||||
|
|
@ -21,13 +22,56 @@ import {
|
|||
isSpecificCrimeFeatureName,
|
||||
isSpecificCrimeFilterName,
|
||||
} from './crime-filter';
|
||||
import {
|
||||
ETHNICITIES_FILTER_NAME,
|
||||
createEthnicityFilterKey,
|
||||
getEthnicityFeatureName,
|
||||
isEthnicityFeatureName,
|
||||
isEthnicityFilterName,
|
||||
} from './ethnicity-filter';
|
||||
import {
|
||||
POI_DISTANCE_FILTER_NAME,
|
||||
POI_COUNT_2KM_FILTER_NAME,
|
||||
POI_COUNT_5KM_FILTER_NAME,
|
||||
createPoiFilterKey,
|
||||
createPoiDistanceFilterKey,
|
||||
getPoiDistanceFeatureName,
|
||||
getPoiFilterName,
|
||||
isPoiDistanceFeatureName,
|
||||
isPoiDistanceFilterName,
|
||||
type PoiFilterName,
|
||||
} from './poi-distance-filter';
|
||||
|
||||
function parseFilters(params: URLSearchParams): FeatureFilters | undefined {
|
||||
const POI_NONE_PARAM = '__none';
|
||||
|
||||
export interface UrlState {
|
||||
viewState: ViewState;
|
||||
filters: FeatureFilters;
|
||||
poiCategories: Set<string>;
|
||||
tab: 'properties' | 'area';
|
||||
travelTime?: TravelTimeInitial;
|
||||
postcode?: string;
|
||||
share?: string;
|
||||
}
|
||||
|
||||
function parseFilters(params: URLSearchParams): FeatureFilters {
|
||||
const filterParams = params.getAll('filter');
|
||||
const schoolParams = params.getAll('school');
|
||||
const crimeParams = params.getAll('crime');
|
||||
if (filterParams.length === 0 && schoolParams.length === 0 && crimeParams.length === 0) {
|
||||
return undefined;
|
||||
const ethnicityParams = params.getAll('ethnicity');
|
||||
const poiDistanceParams = params.getAll('poiDistance');
|
||||
const poiCount2KmParams = params.getAll('poiCount2km');
|
||||
const poiCount5KmParams = params.getAll('poiCount5km');
|
||||
if (
|
||||
filterParams.length === 0 &&
|
||||
schoolParams.length === 0 &&
|
||||
crimeParams.length === 0 &&
|
||||
ethnicityParams.length === 0 &&
|
||||
poiDistanceParams.length === 0 &&
|
||||
poiCount2KmParams.length === 0 &&
|
||||
poiCount5KmParams.length === 0
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const filters: FeatureFilters = {};
|
||||
|
|
@ -82,20 +126,65 @@ function parseFilters(params: URLSearchParams): FeatureFilters | undefined {
|
|||
filters[createSpecificCrimeFilterKey(featureName, index)] = [min, max];
|
||||
});
|
||||
|
||||
return Object.keys(filters).length > 0 ? filters : undefined;
|
||||
ethnicityParams.forEach((entry, index) => {
|
||||
const parts = entry.split(':');
|
||||
if (parts.length < 3) return;
|
||||
const featureName = parts.slice(0, -2).join(':');
|
||||
const min = Number(parts[parts.length - 2]);
|
||||
const max = Number(parts[parts.length - 1]);
|
||||
if (!isEthnicityFeatureName(featureName) || isNaN(min) || isNaN(max)) {
|
||||
return;
|
||||
}
|
||||
filters[createEthnicityFilterKey(featureName, index)] = [min, max];
|
||||
});
|
||||
|
||||
poiDistanceParams.forEach((entry, index) => {
|
||||
const parts = entry.split(':');
|
||||
if (parts.length < 3) return;
|
||||
const featureName = decodeURIComponent(parts.slice(0, -2).join(':'));
|
||||
const min = Number(parts[parts.length - 2]);
|
||||
const max = Number(parts[parts.length - 1]);
|
||||
if (!isPoiDistanceFeatureName(featureName) || isNaN(min) || isNaN(max)) {
|
||||
return;
|
||||
}
|
||||
filters[createPoiDistanceFilterKey(featureName, index)] = [min, max];
|
||||
});
|
||||
|
||||
const parsePoiCountParams = (
|
||||
entries: string[],
|
||||
filterName: PoiFilterName,
|
||||
startIndex: number
|
||||
) => {
|
||||
entries.forEach((entry, index) => {
|
||||
const parts = entry.split(':');
|
||||
if (parts.length < 3) return;
|
||||
const featureName = decodeURIComponent(parts.slice(0, -2).join(':'));
|
||||
const min = Number(parts[parts.length - 2]);
|
||||
const max = Number(parts[parts.length - 1]);
|
||||
if (getPoiFilterName(featureName) !== filterName || isNaN(min) || isNaN(max)) {
|
||||
return;
|
||||
}
|
||||
filters[createPoiFilterKey(filterName, featureName, startIndex + index)] = [min, max];
|
||||
});
|
||||
};
|
||||
parsePoiCountParams(poiCount2KmParams, POI_COUNT_2KM_FILTER_NAME, poiDistanceParams.length);
|
||||
parsePoiCountParams(
|
||||
poiCount5KmParams,
|
||||
POI_COUNT_5KM_FILTER_NAME,
|
||||
poiDistanceParams.length + poiCount2KmParams.length
|
||||
);
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
export function parseUrlState(): {
|
||||
viewState?: ViewState;
|
||||
filters?: FeatureFilters;
|
||||
poiCategories?: Set<string>;
|
||||
tab?: 'properties' | 'area';
|
||||
travelTime?: TravelTimeInitial;
|
||||
postcode?: string;
|
||||
share?: string;
|
||||
} {
|
||||
export function parseUrlState(): UrlState {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const result: ReturnType<typeof parseUrlState> = {};
|
||||
const result: UrlState = {
|
||||
viewState: INITIAL_VIEW_STATE,
|
||||
filters: parseFilters(params),
|
||||
poiCategories: new Set(),
|
||||
tab: 'area',
|
||||
};
|
||||
|
||||
// Share-link code: grants bbox-scoped access to the area the link references
|
||||
// even for unlicensed users. The backend looks the code up against PocketBase.
|
||||
|
|
@ -117,13 +206,16 @@ export function parseUrlState(): {
|
|||
}
|
||||
}
|
||||
|
||||
// Filters: repeated `filter` params
|
||||
result.filters = parseFilters(params);
|
||||
|
||||
// POI categories: repeated `poi` params
|
||||
const poiParams = params.getAll('poi');
|
||||
if (poiParams.length > 0) {
|
||||
result.poiCategories = new Set(poiParams.filter(Boolean));
|
||||
if (poiParams.includes(POI_NONE_PARAM)) {
|
||||
result.poiCategories = new Set();
|
||||
} else {
|
||||
result.poiCategories = new Set(
|
||||
poiParams.filter((value) => value && value !== POI_NONE_PARAM)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Tab: full name
|
||||
|
|
@ -209,6 +301,27 @@ export function stateToParams(
|
|||
continue;
|
||||
}
|
||||
|
||||
const ethnicityFeatureName = getEthnicityFeatureName(name);
|
||||
if (ethnicityFeatureName && isEthnicityFilterName(name)) {
|
||||
const [min, max] = value as [number, number];
|
||||
params.append('ethnicity', `${ethnicityFeatureName}:${min}:${max}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const poiDistanceFeatureName = getPoiDistanceFeatureName(name);
|
||||
if (poiDistanceFeatureName && isPoiDistanceFilterName(name)) {
|
||||
const [min, max] = value as [number, number];
|
||||
const filterName = getPoiFilterName(name);
|
||||
const paramName =
|
||||
filterName === POI_COUNT_2KM_FILTER_NAME
|
||||
? 'poiCount2km'
|
||||
: filterName === POI_COUNT_5KM_FILTER_NAME
|
||||
? 'poiCount5km'
|
||||
: 'poiDistance';
|
||||
params.append(paramName, `${encodeURIComponent(poiDistanceFeatureName)}:${min}:${max}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const meta = features.find((f) => f.name === name);
|
||||
if (meta?.type === 'enum') {
|
||||
params.append('filter', `${name}:${(value as string[]).join('|')}`);
|
||||
|
|
@ -218,8 +331,12 @@ export function stateToParams(
|
|||
}
|
||||
}
|
||||
|
||||
for (const category of selectedPOICategories) {
|
||||
params.append('poi', category);
|
||||
if (selectedPOICategories.size === 0) {
|
||||
params.append('poi', POI_NONE_PARAM);
|
||||
} else {
|
||||
for (const category of selectedPOICategories) {
|
||||
params.append('poi', category);
|
||||
}
|
||||
}
|
||||
|
||||
if (rightPaneTab === 'properties') {
|
||||
|
|
@ -255,18 +372,45 @@ export function summarizeParams(queryString: string): string {
|
|||
const filterParams = params.getAll('filter');
|
||||
const schoolParams = params.getAll('school');
|
||||
const crimeParams = params.getAll('crime');
|
||||
if (filterParams.length > 0 || schoolParams.length > 0 || crimeParams.length > 0) {
|
||||
const ethnicityParams = params.getAll('ethnicity');
|
||||
const poiDistanceParams = params.getAll('poiDistance');
|
||||
const poiCount2KmParams = params.getAll('poiCount2km');
|
||||
const poiCount5KmParams = params.getAll('poiCount5km');
|
||||
if (
|
||||
filterParams.length > 0 ||
|
||||
schoolParams.length > 0 ||
|
||||
crimeParams.length > 0 ||
|
||||
ethnicityParams.length > 0 ||
|
||||
poiDistanceParams.length > 0 ||
|
||||
poiCount2KmParams.length > 0 ||
|
||||
poiCount5KmParams.length > 0
|
||||
) {
|
||||
const filterNames = filterParams
|
||||
.map((entry) => {
|
||||
const colonIdx = entry.indexOf(':');
|
||||
const name = colonIdx > 0 ? entry.substring(0, colonIdx) : entry;
|
||||
return isSpecificCrimeFeatureName(name) ? SPECIFIC_CRIMES_FILTER_NAME : name;
|
||||
if (isSpecificCrimeFeatureName(name)) return SPECIFIC_CRIMES_FILTER_NAME;
|
||||
if (isEthnicityFeatureName(name)) return ETHNICITIES_FILTER_NAME;
|
||||
if (isPoiDistanceFeatureName(name)) return POI_DISTANCE_FILTER_NAME;
|
||||
return name;
|
||||
})
|
||||
.filter((n) => n);
|
||||
for (let i = 0; i < schoolParams.length; i++) filterNames.push(SCHOOL_FILTER_NAME);
|
||||
for (let i = 0; i < crimeParams.length; i++) {
|
||||
filterNames.push(SPECIFIC_CRIMES_FILTER_NAME);
|
||||
}
|
||||
for (let i = 0; i < ethnicityParams.length; i++) {
|
||||
filterNames.push(ETHNICITIES_FILTER_NAME);
|
||||
}
|
||||
for (let i = 0; i < poiDistanceParams.length; i++) {
|
||||
filterNames.push(POI_DISTANCE_FILTER_NAME);
|
||||
}
|
||||
for (let i = 0; i < poiCount2KmParams.length; i++) {
|
||||
filterNames.push(POI_COUNT_2KM_FILTER_NAME);
|
||||
}
|
||||
for (let i = 0; i < poiCount5KmParams.length; i++) {
|
||||
filterNames.push(POI_COUNT_5KM_FILTER_NAME);
|
||||
}
|
||||
if (filterNames.length > 0) {
|
||||
parts.push(
|
||||
filterNames.length <= 2
|
||||
|
|
@ -278,7 +422,7 @@ export function summarizeParams(queryString: string): string {
|
|||
|
||||
const poiParams = params.getAll('poi');
|
||||
if (poiParams.length > 0) {
|
||||
const count = poiParams.filter(Boolean).length;
|
||||
const count = poiParams.filter((value) => value && value !== POI_NONE_PARAM).length;
|
||||
if (count > 0) {
|
||||
parts.push(
|
||||
count === 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue