Skip to content

Commit c253867

Browse files
committed
use react router for navigation
1 parent 57df22a commit c253867

File tree

11 files changed

+82
-87
lines changed

11 files changed

+82
-87
lines changed

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/Alert.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { getIndexByKey, formatString as i18n } from '../helpers';
1+
import { useNavigate } from 'react-router-dom';
2+
import { formatUrl, getIndexByKey, formatString as i18n } from '../helpers';
23
import { useData, useError, useFilter, useInput, useSettings } from '../hooks';
34
import { alertCss, errorCss } from '../styles';
45

@@ -8,7 +9,8 @@ export default function Alert() {
89
const { indexes } = useData();
910
const { error } = useError();
1011
const { alert } = useFilter();
11-
const { input, setInput } = useInput();
12+
const { input } = useInput();
13+
const navigate = useNavigate();
1214
const { settings, strings } = useSettings();
1315
return error ? (
1416
<div css={errorCss}>{error}</div>
@@ -17,7 +19,9 @@ export default function Alert() {
1719
<div css={alertCss}>{alert}</div>
1820
{alert === strings.no_results && input.search && (
1921
<Button
20-
onClick={() => setInput(input => ({ ...input, search: '' }))}
22+
onClick={() =>
23+
navigate(formatUrl({ ...input, search: '' }, settings))
24+
}
2125
text={i18n(strings.remove, { filter: input.search })}
2226
icon="close"
2327
/>
@@ -29,10 +33,15 @@ export default function Alert() {
2933
key={value}
3034
icon="close"
3135
onClick={() => {
32-
setInput(input => ({
33-
...input,
34-
[filter]: input[filter].filter(item => item !== value),
35-
}));
36+
navigate(
37+
formatUrl(
38+
{
39+
...input,
40+
[filter]: input[filter].filter(item => item !== value),
41+
},
42+
settings
43+
)
44+
);
3645
}}
3746
text={i18n(strings.remove, {
3847
filter: getIndexByKey(indexes[filter], value)?.name,

src/components/Controls.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { FormEvent, MouseEvent, useEffect, useRef, useState } from 'react';
22

3-
import { analyticsEvent } from '../helpers';
3+
import { useNavigate } from 'react-router-dom';
4+
5+
import { analyticsEvent, formatUrl } from '../helpers';
46
import { useData, useInput, useSettings } from '../hooks';
57
import {
68
controlsCss,
@@ -18,9 +20,10 @@ import Dropdown from './Dropdown';
1820

1921
export default function Controls() {
2022
const { capabilities, meetings, slugs } = useData();
23+
const navigate = useNavigate();
2124
const { settings, strings } = useSettings();
2225
const [dropdown, setDropdown] = useState<string>();
23-
const { input, setInput } = useInput();
26+
const { input } = useInput();
2427
const [search, setSearch] = useState(input.search);
2528
const searchInput = useRef<HTMLInputElement>(null);
2629

@@ -78,7 +81,7 @@ export default function Controls() {
7881

7982
if (value === input.search) return;
8083

81-
setInput(input => ({ ...input, search: value }));
84+
navigate(formatUrl({ ...input, search: value }, settings));
8285
}, [searchInput.current?.value]);
8386

8487
// update search when global state changes
@@ -106,7 +109,7 @@ export default function Controls() {
106109
});
107110
}
108111

109-
setInput(input => ({ ...input, search }));
112+
navigate(formatUrl({ ...input, search }, settings));
110113
};
111114

112115
// set search mode dropdown and reset distances
@@ -127,7 +130,7 @@ export default function Controls() {
127130
// focus after waiting for disabled to clear
128131
setTimeout(() => searchInput.current?.focus(), 100);
129132

130-
setInput(input => ({
133+
const newInput = {
131134
...input,
132135
distance:
133136
mode === 'search'
@@ -136,13 +139,9 @@ export default function Controls() {
136139
mode,
137140
region: mode === 'search' ? input.region : [],
138141
search,
139-
}));
140-
};
142+
};
141143

142-
// toggle list/map view
143-
const setView = (e: MouseEvent, view: 'table' | 'map') => {
144-
e.preventDefault();
145-
setInput(input => ({ ...input, view }));
144+
navigate(formatUrl(newInput, settings));
146145
};
147146

148147
return !slugs.length ? null : (
@@ -224,7 +223,7 @@ export default function Controls() {
224223
css={index ? controlsGroupLastCss : controlsGroupFirstCss}
225224
data-active={input.view === view}
226225
key={view}
227-
onClick={e => setView(e, view)}
226+
onClick={() => navigate(formatUrl({ ...input, view }, settings))}
228227
type="button"
229228
>
230229
{strings.views[view]}

src/components/Dropdown.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
useState,
77
} from 'react';
88

9-
import { getIndexByKey, formatString as i18n } from '../helpers';
9+
import { useNavigate } from 'react-router-dom';
10+
import { formatUrl, getIndexByKey, formatString as i18n } from '../helpers';
1011
import { type Data, useData, useInput, useSettings } from '../hooks';
1112
import { dropdownButtonCss, dropdownCss } from '../styles';
1213
import type { Index } from '../types';
@@ -23,8 +24,9 @@ export default function Dropdown({
2324
setDropdown: Dispatch<SetStateAction<string | undefined>>;
2425
}) {
2526
const { indexes } = useData();
26-
const { strings } = useSettings();
27-
const { input, setInput, waitingForInput } = useInput();
27+
const navigate = useNavigate();
28+
const { settings, strings } = useSettings();
29+
const { input, waitingForInput } = useInput();
2830
const options = indexes[filter];
2931
const values =
3032
filter === 'distance'
@@ -54,10 +56,12 @@ export default function Dropdown({
5456
e.preventDefault();
5557

5658
if (filter === 'distance') {
57-
setInput(input => ({
58-
...input,
59-
distance: value ? parseInt(value) : undefined,
60-
}));
59+
navigate(
60+
formatUrl(
61+
{ ...input, distance: value ? parseInt(value) : undefined },
62+
settings
63+
)
64+
);
6165
} else {
6266
// add or remove from filters
6367
let currentValues = input[filter] as string[];
@@ -80,7 +84,7 @@ export default function Dropdown({
8084
// Remove the filter from search params if no value is provided
8185
currentValues = [];
8286
}
83-
setInput(input => ({ ...input, [filter]: currentValues }));
87+
navigate(formatUrl({ ...input, [filter]: currentValues }, settings));
8488
}
8589
};
8690

src/components/Link.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { formatUrl } from '../helpers';
22
import { useFilter, useInput, useSettings } from '../hooks';
33
import type { Meeting } from '../types';
44

5+
import { Link as RouterLink } from 'react-router-dom';
6+
57
export default function Link({ meeting }: { meeting: Meeting }) {
68
const { meeting: thisMeeting } = useFilter();
79
const { settings, strings } = useSettings();
8-
const { input, setInput } = useInput();
10+
const { input } = useInput();
911

1012
const flags =
1113
settings.flags
@@ -27,15 +29,9 @@ export default function Link({ meeting }: { meeting: Meeting }) {
2729

2830
return (
2931
<>
30-
<a
31-
href={formatUrl({ ...input, meeting: meeting.slug }, settings)}
32-
onClick={e => {
33-
e.preventDefault();
34-
setInput(input => ({ ...input, meeting: meeting.slug }));
35-
}}
36-
>
32+
<RouterLink to={formatUrl({ ...input, meeting: meeting.slug }, settings)}>
3733
{meeting.name}
38-
</a>
34+
</RouterLink>
3935
{flags && <small>{flags}</small>}
4036
</>
4137
);

src/components/Meeting.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useState } from 'react';
22

33
import { DateTime, Info } from 'luxon';
4+
import { Link as RouterLink } from 'react-router-dom';
45

56
import {
67
formatDirectionsUrl,
@@ -29,7 +30,7 @@ import type { Meeting as MeetingType } from '../types';
2930

3031
export default function Meeting({ meeting }: { meeting: MeetingType }) {
3132
const { settings, strings } = useSettings();
32-
const { input, setInput } = useInput();
33+
const { input } = useInput();
3334

3435
// open types
3536
const [define, setDefine] = useState<string | undefined>();
@@ -258,15 +259,9 @@ export default function Meeting({ meeting }: { meeting: MeetingType }) {
258259
</h1>
259260
<div css={meetingBackCss}>
260261
<Icon icon="back" />
261-
<a
262-
href={formatUrl({ ...input, meeting: undefined }, settings)}
263-
onClick={e => {
264-
e.preventDefault();
265-
setInput(input => ({ ...input, meeting: undefined }));
266-
}}
267-
>
262+
<RouterLink to={formatUrl({ ...input, meeting: undefined }, settings)}>
268263
{strings.back_to_meetings}
269-
</a>
264+
</RouterLink>
270265
</div>
271266
<div css={meetingColumnsCss}>
272267
<div>

src/components/Table.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { useState } from 'react';
22

33
import InfiniteScroll from 'react-infinite-scroller';
44

5-
import { formatString as i18n } from '../helpers';
5+
import { useNavigate } from 'react-router-dom';
6+
7+
import { formatUrl, formatString as i18n } from '../helpers';
68
import { useData, useError, useFilter, useInput, useSettings } from '../hooks';
79
import {
810
tableChicletCss,
@@ -20,7 +22,8 @@ export default function Table() {
2022
const { error } = useError();
2123
const { filteredSlugs, inProgress } = useFilter();
2224
const { settings, strings } = useSettings();
23-
const { latitude, longitude, setInput } = useInput();
25+
const { input, latitude, longitude } = useInput();
26+
const navigate = useNavigate();
2427
const meetingsPerPage = 10;
2528
const supported_columns = [
2629
'address',
@@ -130,7 +133,9 @@ export default function Table() {
130133
const meeting = meetings[slug];
131134
return (
132135
<tr
133-
onClick={() => setInput(input => ({ ...input, meeting: meeting.slug }))}
136+
onClick={() =>
137+
navigate(formatUrl({ ...input, meeting: meeting.slug }, settings))
138+
}
134139
>
135140
{columns.map((column, index) => (
136141
<td className={`tsml-${column}`} key={index}>

src/helpers/format-feedback-email.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function formatFeedbackEmail({
1616
strings: Translation;
1717
}) {
1818
// remove extra query params from meeting URL
19-
const meetingUrl = formatUrl({ meeting: meeting.slug }, settings);
19+
const meetingUrl = formatUrl({ meeting: meeting.slug }, settings, true);
2020

2121
// build message
2222
const lines = [

src/helpers/format-url.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// format an internal link with correct query params
22
export function formatUrl(
33
input: Partial<TSMLReactConfig['defaults']>,
4-
settings: TSMLReactConfig
4+
settings: TSMLReactConfig,
5+
includeDomain = false
56
) {
67
const query = {};
78

8-
// distance, region, time, type, and weekday
9+
// region, time, type, and weekday
910
settings.filters
1011
.filter(filter => typeof input[filter] !== 'undefined')
1112
.filter(filter => input[filter]?.length)
@@ -14,6 +15,12 @@ export function formatUrl(
1415
query[filter] = input[filter].join('/');
1516
});
1617

18+
// distance
19+
if (typeof input.distance !== 'undefined') {
20+
// @ts-expect-error TODO
21+
query.distance = input.distance.toString();
22+
}
23+
1724
// meeting, mode, search, view
1825
settings.params
1926
.filter(param => typeof input[param] !== 'undefined')
@@ -30,7 +37,9 @@ export function formatUrl(
3037
.replace(/%20/g, '+')
3138
.replace(/%2C/g, ',');
3239

33-
const [path] = window.location.href.split('?');
40+
const base = includeDomain ? `${window.location.origin}` : '';
41+
42+
const [path] = window.location.pathname.split('?');
3443

35-
return `${path}${queryString.length ? `?${queryString}` : ''}`;
44+
return `${base}${path}${queryString.length ? `?${queryString}` : ''}`;
3645
}

src/hooks/input.tsx

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {
1+
import {
22
createContext,
33
PropsWithChildren,
44
useContext,
@@ -22,13 +22,12 @@ const InputContext = createContext<
2222
input: TSMLReactConfig['defaults'];
2323
latitude?: number;
2424
longitude?: number;
25-
setInput: React.Dispatch<React.SetStateAction<TSMLReactConfig['defaults']>>;
2625
} & Coordinates
27-
>({ input: defaults.defaults, setInput: () => {}, waitingForInput: false });
26+
>({ input: defaults.defaults, waitingForInput: false });
2827

2928
export const InputProvider = ({ children }: PropsWithChildren) => {
3029
const { setError } = useError();
31-
const [searchParams, setSearchParams] = useSearchParams();
30+
const [searchParams] = useSearchParams();
3231
const { settings, strings } = useSettings();
3332

3433
const [input, setInput] = useState<TSMLReactConfig['defaults']>(
@@ -39,7 +38,7 @@ export const InputProvider = ({ children }: PropsWithChildren) => {
3938
waitingForInput: input.mode !== 'search',
4039
});
4140

42-
// detect initial input from URL search params
41+
// detect input from URL search params
4342
useEffect(() => {
4443
const mode =
4544
searchParams.get('mode') === 'location'
@@ -80,27 +79,7 @@ export const InputProvider = ({ children }: PropsWithChildren) => {
8079
view,
8180
weekday,
8281
}));
83-
}, []);
84-
85-
// update URL search params when input changes
86-
useEffect(() => {
87-
if (input === defaults.defaults) return;
88-
const params = {
89-
distance: input.distance,
90-
meeting: input.meeting,
91-
mode: input.mode == defaults.defaults.mode ? '' : input.mode,
92-
region: input.region.join('/'),
93-
search: input.search,
94-
time: input.time.join('/'),
95-
type: input.type.join('/'),
96-
view: input.view === defaults.defaults.view ? '' : input.view,
97-
weekday: input.weekday.join('/'),
98-
};
99-
const filteredParams = Object.fromEntries(
100-
Object.entries(params).filter(([, value]) => value)
101-
) as { [key: string]: string };
102-
setSearchParams(filteredParams);
103-
}, [input]);
82+
}, [searchParams]);
10483

10584
// handle geocoding or geolocation requests
10685
useEffect(() => {
@@ -166,7 +145,7 @@ export const InputProvider = ({ children }: PropsWithChildren) => {
166145
}, [input.mode, input.search]);
167146

168147
return (
169-
<InputContext.Provider value={{ input, setInput, ...coordinates }}>
148+
<InputContext.Provider value={{ input, ...coordinates }}>
170149
{children}
171150
</InputContext.Provider>
172151
);

0 commit comments

Comments
 (0)