Skip to content

Commit d341be2

Browse files
committed
wip: Switch to react-router 6
Currently broken because regex pattern support has been removed: remix-run/react-router#8254
1 parent 18e99da commit d341be2

12 files changed

+235
-338
lines changed

assets/js/templates/App.jsx

+114-92
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import PropTypes from 'prop-types';
33
import nullable from 'prop-types-nullable';
44
import {
55
BrowserRouter as Router,
6-
Switch,
6+
Routes,
77
Route,
88
Link,
99
Redirect,
1010
useHistory,
1111
useLocation,
12-
useRouteMatch,
12+
useMatch,
13+
useNavigate,
1314
} from 'react-router-dom';
1415
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
1516
import Collapse from '@kunukn/react-collapse';
@@ -147,10 +148,15 @@ function PureApp({
147148
}, []);
148149

149150
// TODO: move stuff that depends on this to the App.
150-
const history = useHistory();
151+
const navigate = useNavigate();
152+
React.useEffect(() => {
153+
selfoss.navigate = navigate;
154+
}, [navigate]);
155+
156+
const entriesPageMatch = useMatch(ENTRIES_ROUTE_PATTERN);
151157
React.useEffect(() => {
152-
selfoss.history = history;
153-
}, [history]);
158+
selfoss.entriesPageMatch = entriesPageMatch;
159+
}, [entriesPageMatch]);
154160

155161
// Prepare path of the homepage for redirecting from /
156162
let homePagePath = configuration.homepage.split('/');
@@ -181,97 +187,113 @@ function PureApp({
181187
<React.StrictMode>
182188
<Message message={globalMessage} />
183189

184-
<Switch>
185-
<Route path="/sign/in">
186-
{/* menu open for smartphone */}
187-
<div id="loginform" role="main">
188-
<LoginForm
189-
{...{offlineEnabled}}
190-
/>
191-
</div>
192-
</Route>
193-
194-
<Route path="/">
195-
<CheckAuthorization
196-
isAllowed={selfoss.isAllowedToRead()}
197-
_={_}
198-
>
199-
<div id="nav-mobile" role="navigation">
200-
<div id="nav-mobile-logo">
201-
<div id="nav-mobile-count" className={classNames({'unread-count': true, offline: offlineState, online: !offlineState, unread: unreadItemsCount > 0})}>
202-
<span className={classNames({'offline-count': true, offline: offlineState, online: !offlineState, diff: unreadItemsCount !== unreadItemsOfflineCount && unreadItemsOfflineCount})}>{unreadItemsOfflineCount > 0 ? unreadItemsOfflineCount : ''}</span>
203-
<span className="count">{unreadItemsCount}</span>
190+
<Routes>
191+
<Route
192+
path="/sign/in"
193+
element={
194+
/* menu open for smartphone */
195+
<div id="loginform" role="main">
196+
<LoginForm
197+
{...{offlineEnabled}}
198+
/>
199+
</div>
200+
}
201+
/>
202+
203+
<Route
204+
path="/"
205+
element={
206+
<CheckAuthorization
207+
isAllowed={selfoss.isAllowedToRead()}
208+
_={_}
209+
>
210+
<div id="nav-mobile" role="navigation">
211+
<div id="nav-mobile-logo">
212+
<div id="nav-mobile-count" className={classNames({'unread-count': true, offline: offlineState, online: !offlineState, unread: unreadItemsCount > 0})}>
213+
<span className={classNames({'offline-count': true, offline: offlineState, online: !offlineState, diff: unreadItemsCount !== unreadItemsOfflineCount && unreadItemsOfflineCount})}>{unreadItemsOfflineCount > 0 ? unreadItemsOfflineCount : ''}</span>
214+
<span className="count">{unreadItemsCount}</span>
215+
</div>
204216
</div>
217+
<button
218+
id="nav-mobile-settings"
219+
accessKey="t"
220+
aria-label={_('settingsbutton')}
221+
onClick={menuButtonOnClick}
222+
>
223+
<FontAwesomeIcon icon={icons.menu} size="2x" />
224+
</button>
205225
</div>
206-
<button
207-
id="nav-mobile-settings"
208-
accessKey="t"
209-
aria-label={_('settingsbutton')}
210-
onClick={menuButtonOnClick}
211-
>
212-
<FontAwesomeIcon icon={icons.menu} size="2x" />
213-
</button>
214-
</div>
215226

216-
{/* navigation */}
217-
<Collapse isOpen={!smartphone || navExpanded} className="collapse-css-transition">
218-
<div id="nav" role="navigation">
219-
<Navigation
220-
entriesPage={entriesPage}
221-
setNavExpanded={setNavExpanded}
222-
navSourcesExpanded={navSourcesExpanded}
223-
setNavSourcesExpanded={setNavSourcesExpanded}
224-
offlineState={offlineState}
225-
allItemsCount={allItemsCount}
226-
allItemsOfflineCount={allItemsOfflineCount}
227-
unreadItemsCount={unreadItemsCount}
228-
unreadItemsOfflineCount={unreadItemsOfflineCount}
229-
starredItemsCount={starredItemsCount}
230-
starredItemsOfflineCount={starredItemsOfflineCount}
231-
sourcesState={sourcesState}
232-
setSourcesState={setSourcesState}
233-
sources={sources}
234-
setSources={setSources}
235-
tags={tags}
236-
reloadAll={reloadAll}
237-
/>
227+
{/* navigation */}
228+
<Collapse isOpen={!smartphone || navExpanded} className="collapse-css-transition">
229+
<div id="nav" role="navigation">
230+
<Navigation
231+
entriesPage={entriesPage}
232+
setNavExpanded={setNavExpanded}
233+
navSourcesExpanded={navSourcesExpanded}
234+
setNavSourcesExpanded={setNavSourcesExpanded}
235+
offlineState={offlineState}
236+
allItemsCount={allItemsCount}
237+
allItemsOfflineCount={allItemsOfflineCount}
238+
unreadItemsCount={unreadItemsCount}
239+
unreadItemsOfflineCount={unreadItemsOfflineCount}
240+
starredItemsCount={starredItemsCount}
241+
starredItemsOfflineCount={starredItemsOfflineCount}
242+
sourcesState={sourcesState}
243+
setSourcesState={setSourcesState}
244+
sources={sources}
245+
setSources={setSources}
246+
tags={tags}
247+
reloadAll={reloadAll}
248+
/>
249+
</div>
250+
</Collapse>
251+
252+
<ul id="search-list">
253+
<SearchList />
254+
</ul>
255+
256+
{/* content */}
257+
<div id="content" role="main">
258+
<Routes>
259+
<Route
260+
path="/"
261+
element={
262+
<Redirect to={`/${homePagePath.join('/')}`} />
263+
}
264+
/>
265+
<Route
266+
/* TODO: regex pattern does not work https://github.com/remix-run/react-router/issues/8254 */
267+
path={ENTRIES_ROUTE_PATTERN}
268+
element={
269+
<EntriesPage
270+
ref={entriesRef}
271+
setNavExpanded={setNavExpanded}
272+
configuration={configuration}
273+
navSourcesExpanded={navSourcesExpanded}
274+
setTitle={setTitle}
275+
unreadItemsCount={unreadItemsCount}
276+
/>
277+
}
278+
/>
279+
<Route
280+
path="/manage/sources"
281+
element={
282+
<SourcesPage />
283+
}
284+
/>
285+
<Route
286+
path="*"
287+
element={
288+
<NotFound />
289+
}
290+
/>
291+
</Routes>
238292
</div>
239-
</Collapse>
240-
241-
<ul id="search-list">
242-
<SearchList />
243-
</ul>
244-
245-
{/* content */}
246-
<div id="content" role="main">
247-
<Switch>
248-
<Route exact path="/">
249-
<Redirect to={`/${homePagePath.join('/')}`} />
250-
</Route>
251-
<Route path={ENTRIES_ROUTE_PATTERN}>
252-
{(routeProps) => (
253-
<EntriesPage
254-
{...routeProps}
255-
ref={entriesRef}
256-
setNavExpanded={setNavExpanded}
257-
configuration={configuration}
258-
navSourcesExpanded={navSourcesExpanded}
259-
setTitle={setTitle}
260-
unreadItemsCount={unreadItemsCount}
261-
/>
262-
)}
263-
</Route>
264-
<Route path="/manage/sources">
265-
<SourcesPage />
266-
</Route>
267-
<Route path="*">
268-
<NotFound />
269-
</Route>
270-
</Switch>
271-
</div>
272-
</CheckAuthorization>
273-
</Route>
274-
</Switch>
293+
</CheckAuthorization>
294+
}
295+
/>
296+
</Routes>
275297
</React.StrictMode>
276298
);
277299
}

assets/js/templates/EntriesPage.jsx

+23-22
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function reloadList({
115115
}
116116

117117
if (error instanceof HttpError && error.response.status === 403) {
118-
selfoss.history.push('/sign/in', {
118+
selfoss.navigage('/sign/in', {
119119
error: selfoss.app._('error_session_expired')
120120
});
121121
return;
@@ -673,13 +673,6 @@ export default class StateHolder extends React.Component {
673673
return params.category?.startsWith('source-') ? parseInt(params.category.replace(/^source-/, ''), 10) : null;
674674
}
675675

676-
getActiveFilter() {
677-
if (!this.props.match) {
678-
return null;
679-
}
680-
return this.props.match.params.filter;
681-
}
682-
683676
/**
684677
* Mark all visible items as read
685678
*/
@@ -721,8 +714,8 @@ export default class StateHolder extends React.Component {
721714
// close opened entry and list
722715
this.setExpandedEntries({});
723716

724-
if (ids.length !== 0 && this.props.match.filter === FilterType.UNREAD) {
725-
markedEntries = markedEntries.filter(({ id }) => ids.includes(id));
717+
if (this.entriesPageMatch.filter === FilterType.UNREAD) {
718+
markedEntries = [];
726719
}
727720

728721
this.setLoadingState(LoadingState.LOADING);
@@ -747,8 +740,10 @@ export default class StateHolder extends React.Component {
747740
selfoss.dbOffline.enqueueStatuses(statuses);
748741
}).catch((error) => {
749742
if (error instanceof HttpError && error.response.status === 403) {
750-
selfoss.history.push('/sign/in', {
751-
error: selfoss.app._('error_session_expired')
743+
selfoss.navigate('/sign/in', {
744+
state: {
745+
error: selfoss.app._('error_session_expired'),
746+
},
752747
});
753748
return;
754749
}
@@ -809,8 +804,10 @@ export default class StateHolder extends React.Component {
809804
selfoss.dbOffline.enqueueStatus(id, 'unread', !markRead);
810805
}).catch(function(error) {
811806
if (error instanceof HttpError && error.response.status === 403) {
812-
selfoss.history.push('/sign/in', {
813-
error: selfoss.app._('error_session_expired')
807+
selfoss.navigate('/sign/in', {
808+
state: {
809+
error: selfoss.app._('error_session_expired'),
810+
},
814811
});
815812
return;
816813
}
@@ -859,8 +856,10 @@ export default class StateHolder extends React.Component {
859856
selfoss.dbOffline.enqueueStatus(id, 'starred', markStarred);
860857
}).catch(function(error) {
861858
if (error instanceof HttpError && error.response.status === 403) {
862-
selfoss.history.push('/sign/in', {
863-
error: selfoss.app._('error_session_expired')
859+
selfoss.navigate('/sign/in', {
860+
state: {
861+
error: selfoss.app._('error_session_expired'),
862+
},
864863
});
865864
return;
866865
}
@@ -877,11 +876,14 @@ export default class StateHolder extends React.Component {
877876
/**
878877
* HACK: A counter that is increased every time reload action (r key) is triggered.
879878
*/
880-
selfoss.history.replace({
881-
...this.props.location,
882-
...makeEntriesLinkLocation(this.props.location, { id: null }),
883-
state: forceReload(this.props.location),
884-
});
879+
selfoss.navigate(
880+
{
881+
...this.props.location,
882+
...makeEntriesLinkLocation(this.props.location, { id: null }),
883+
state: forceReload(this.props.location),
884+
},
885+
{ replace: true }
886+
);
885887
}
886888

887889
/**
@@ -1042,7 +1044,6 @@ export default class StateHolder extends React.Component {
10421044

10431045
StateHolder.propTypes = {
10441046
configuration: PropTypes.object.isRequired,
1045-
location: PropTypes.object.isRequired,
10461047
match: PropTypes.object.isRequired,
10471048
setNavExpanded: PropTypes.func.isRequired,
10481049
navSourcesExpanded: PropTypes.bool.isRequired,

0 commit comments

Comments
 (0)