Skip to content

Commit a57ddaa

Browse files
authored
adjust input validation timing (code4recovery#474)
1 parent 01312d8 commit a57ddaa

File tree

7 files changed

+77
-49
lines changed

7 files changed

+77
-49
lines changed

index.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ type MapMarker = {
55
height: number;
66
width: number;
77
};
8+
type Mode = 'search' | 'location' | 'me';
9+
type View = 'table' | 'map';
810

911
interface TSMLReactConfig {
1012
cache: boolean;
@@ -14,12 +16,12 @@ interface TSMLReactConfig {
1416
defaults: {
1517
distance?: number;
1618
meeting?: string;
17-
mode: 'search' | 'location' | 'me';
19+
mode: Mode;
1820
region: string[];
1921
search: string;
2022
time: TSMLReactConfig['times'];
2123
type: string[];
22-
view: 'table' | 'map';
24+
view: View;
2325
weekday: TSMLReactConfig['weekdays'];
2426
};
2527
distance_default: number;
@@ -46,6 +48,7 @@ interface TSMLReactConfig {
4648
url: string;
4749
};
4850
};
51+
modes: Array<Mode>;
4952
now_offset: number;
5053
params: Array<'search' | 'mode' | 'view' | 'meeting'>;
5154
show: {
@@ -56,6 +59,7 @@ interface TSMLReactConfig {
5659
[lang in Lang]: Translation;
5760
};
5861
times: Array<'morning' | 'midday' | 'evening' | 'night' | 'appointment'>;
62+
views: Array<View>;
5963
weekdays: string[];
6064
}
6165

public/app.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/Controls.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ export default function Controls() {
2828
const searchInput = useRef<HTMLInputElement>(null);
2929

3030
// get available search options based on capabilities
31-
const allModes = ['search', 'location', 'me'] as const;
32-
const modes = allModes
31+
const modes = settings.modes
3332
.filter(mode => mode !== 'location' || capabilities.coordinates)
3433
.filter(
3534
mode =>
@@ -42,8 +41,7 @@ export default function Controls() {
4241
.filter(filter => filter !== 'region' || input.mode === 'search');
4342

4443
// get available views
45-
const allViews = ['table', 'map'] as const;
46-
const views = allViews.filter(
44+
const views = settings.views.filter(
4745
view => view !== 'map' || capabilities.coordinates
4846
);
4947

src/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from './load-meeting-data';
1616
export * from './streamline-regions-index';
1717
export * from './translate-google-sheet';
1818
export * from './user-agent';
19+
export * from './validate-input';

src/helpers/validate-input.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { defaults } from '../hooks/settings';
2+
import { formatSearch } from './format-search';
3+
4+
export const validateInput = (
5+
params: URLSearchParams
6+
): TSMLReactConfig['defaults'] => {
7+
const { defaults: defaultInput } = defaults;
8+
9+
const modeParam = params.get('mode');
10+
const mode = isMode(modeParam) ? modeParam : defaultInput.mode;
11+
12+
const viewParam = params.get('view');
13+
const view = isView(viewParam) ? viewParam : defaultInput.view;
14+
15+
const search = formatSearch(
16+
params.get('search')?.toString() ?? defaultInput.search
17+
);
18+
19+
const region = params.has('region')
20+
? `${params.get('region')}`.split('/')
21+
: defaultInput.region;
22+
23+
const time = params.has('time')
24+
? (`${params.get('time')}`.split('/') as Array<
25+
'morning' | 'midday' | 'evening' | 'night' | 'appointment'
26+
>)
27+
: defaultInput.time;
28+
29+
const weekday = params.has('weekday')
30+
? `${params.get('weekday')}`.split('/')
31+
: defaultInput.weekday;
32+
33+
const type = params.has('type')
34+
? `${params.get('type')}`.split('/')
35+
: defaultInput.type;
36+
37+
const meeting = params.get('meeting') ?? defaultInput.meeting;
38+
39+
const distance = params.has('distance')
40+
? parseInt(params.get('distance') ?? '')
41+
: defaultInput.distance;
42+
43+
return {
44+
distance,
45+
meeting,
46+
mode,
47+
region,
48+
search,
49+
time,
50+
type,
51+
view,
52+
weekday,
53+
};
54+
};
55+
56+
const isMode = (mode: string | null): mode is Mode =>
57+
defaults.modes.includes(mode as Mode);
58+
59+
const isView = (view: string | null): view is View =>
60+
defaults.views.includes(view as View);

src/hooks/input.tsx

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from 'react';
88
import { useSearchParams } from 'react-router-dom';
99

10-
import { formatSearch, formatString } from '../helpers';
10+
import { formatString, validateInput } from '../helpers';
1111
import { useError } from './error';
1212
import { defaults, useSettings } from './settings';
1313

@@ -43,7 +43,7 @@ export const InputProvider = ({ children }: PropsWithChildren) => {
4343
});
4444

4545
const [input, setInput] = useState<TSMLReactConfig['defaults']>(
46-
defaults.defaults
46+
validateInput(searchParams)
4747
);
4848

4949
const [coordinates, setCoordinates] = useState<Coordinates>({
@@ -52,45 +52,7 @@ export const InputProvider = ({ children }: PropsWithChildren) => {
5252

5353
// detect input from URL search params
5454
useEffect(() => {
55-
const mode =
56-
searchParams.get('mode') === 'location'
57-
? 'location'
58-
: searchParams.get('mode') === 'me'
59-
? 'me'
60-
: 'search';
61-
const view = searchParams.get('view') === 'map' ? 'map' : 'table';
62-
const search = formatSearch(searchParams.get('search')?.toString() ?? '');
63-
const region = searchParams.has('region')
64-
? `${searchParams.get('region')}`.split('/')
65-
: [];
66-
const time = searchParams.has('time')
67-
? (`${searchParams.get('time')}`.split('/') as Array<
68-
'morning' | 'midday' | 'evening' | 'night' | 'appointment'
69-
>)
70-
: [];
71-
const weekday = searchParams.has('weekday')
72-
? `${searchParams.get('weekday')}`.split('/')
73-
: [];
74-
const type = searchParams.has('type')
75-
? `${searchParams.get('type')}`.split('/')
76-
: [];
77-
const meeting = searchParams.get('meeting') ?? undefined;
78-
const distance = searchParams.has('distance')
79-
? parseInt(searchParams.get('distance') ?? '')
80-
: undefined;
81-
82-
setInput(input => ({
83-
...input,
84-
distance,
85-
meeting,
86-
mode,
87-
region,
88-
search,
89-
time,
90-
type,
91-
view,
92-
weekday,
93-
}));
55+
setInput(validateInput(searchParams));
9456
}, [searchParams]);
9557

9658
// handle geocoding or geolocation requests

src/hooks/settings.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const defaults: TSMLReactConfig = {
3535
},
3636
defaults: {
3737
distance: undefined,
38+
meeting: undefined,
3839
mode: 'search',
3940
region: [],
4041
search: '',
@@ -86,6 +87,7 @@ export const defaults: TSMLReactConfig = {
8687
url: 'https://{s}s.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
8788
},
8889
},
90+
modes: ['search', 'location', 'me'],
8991
now_offset: -10, // "now" includes meetings that started in the last 10 minutes
9092
params: ['search', 'mode', 'view', 'meeting'], //input other than filters
9193
show: {
@@ -103,6 +105,7 @@ export const defaults: TSMLReactConfig = {
103105
sv,
104106
},
105107
times: ['morning', 'midday', 'evening', 'night', 'appointment'],
108+
views: ['table', 'map'],
106109
weekdays: [
107110
'sunday',
108111
'monday',

0 commit comments

Comments
 (0)