87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
import type { PropsWithChildren } from 'react';
|
|
import React from 'react';
|
|
|
|
import { Router as OriginalRouter, useHistory } from 'react-router';
|
|
|
|
import type {
|
|
LocationDescriptor,
|
|
LocationDescriptorObject,
|
|
Path,
|
|
} from 'history';
|
|
import { createBrowserHistory } from 'history';
|
|
|
|
import { layoutFromWindow } from 'mastodon/is_mobile';
|
|
import { isDevelopment } from 'mastodon/utils/environment';
|
|
|
|
interface MastodonLocationState {
|
|
fromMastodon?: boolean;
|
|
mastodonModalKey?: string;
|
|
}
|
|
|
|
type LocationState = MastodonLocationState | null | undefined;
|
|
|
|
type HistoryPath = Path | LocationDescriptor<LocationState>;
|
|
|
|
const browserHistory = createBrowserHistory<LocationState>();
|
|
const originalPush = browserHistory.push.bind(browserHistory);
|
|
const originalReplace = browserHistory.replace.bind(browserHistory);
|
|
|
|
export function useAppHistory() {
|
|
return useHistory<LocationState>();
|
|
}
|
|
|
|
function normalizePath(
|
|
path: HistoryPath,
|
|
state?: LocationState,
|
|
): LocationDescriptorObject<LocationState> {
|
|
const location = typeof path === 'string' ? { pathname: path } : { ...path };
|
|
|
|
if (location.state === undefined && state !== undefined) {
|
|
location.state = state;
|
|
} else if (
|
|
location.state !== undefined &&
|
|
state !== undefined &&
|
|
isDevelopment()
|
|
) {
|
|
// eslint-disable-next-line no-console
|
|
console.log(
|
|
'You should avoid providing a 2nd state argument to push when the 1st argument is a location-like object that already has state; it is ignored',
|
|
);
|
|
}
|
|
|
|
if (
|
|
layoutFromWindow() === 'multi-column' &&
|
|
!location.pathname?.startsWith('/deck')
|
|
) {
|
|
location.pathname = `/deck${location.pathname}`;
|
|
}
|
|
|
|
return location;
|
|
}
|
|
|
|
browserHistory.push = (path: HistoryPath, state?: MastodonLocationState) => {
|
|
const location = normalizePath(path, state);
|
|
|
|
location.state = location.state ?? {};
|
|
location.state.fromMastodon = true;
|
|
|
|
originalPush(location);
|
|
};
|
|
|
|
browserHistory.replace = (path: HistoryPath, state?: MastodonLocationState) => {
|
|
const location = normalizePath(path, state);
|
|
|
|
if (!location.pathname) return;
|
|
|
|
if (browserHistory.location.state?.fromMastodon) {
|
|
location.state = location.state ?? {};
|
|
location.state.fromMastodon = true;
|
|
}
|
|
|
|
originalReplace(location);
|
|
};
|
|
|
|
export const Router: React.FC<PropsWithChildren> = ({ children }) => {
|
|
return <OriginalRouter history={browserHistory}>{children}</OriginalRouter>;
|
|
};
|