98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
import { matchPath } from 'react-router-dom';
|
|
import {
|
|
BOTS_PATH,
|
|
CHANNELS_PATH,
|
|
CHANNELS_ROOM_EVENT_PATH,
|
|
CHANNELS_ROOM_PATH,
|
|
CHANNELS_SPACE_PATH,
|
|
CHANNELS_THREAD_PATH,
|
|
DIRECT_PATH,
|
|
EXPLORE_PATH,
|
|
HOME_PATH,
|
|
SPACE_PATH,
|
|
} from '../pages/paths';
|
|
import {
|
|
getBotsPath,
|
|
getChannelsPath,
|
|
getChannelsRoomPath,
|
|
getChannelsSpacePath,
|
|
getDirectPath,
|
|
getExplorePath,
|
|
getHomePath,
|
|
getSpacePath,
|
|
} from '../pages/pathUtils';
|
|
|
|
/**
|
|
* Returns the section-root parent path for the given pathname, or null
|
|
* when pathname is not under a known section or is already at the root
|
|
* of its section. Shared by the UI back arrow (BackRouteHandler) and the
|
|
* Android hardware back button (useAndroidBackButton) so they can't drift.
|
|
*/
|
|
export const getRouteSectionParent = (pathname: string): string | null => {
|
|
const atRoot = (path: string) =>
|
|
matchPath({ path, caseSensitive: true, end: true }, pathname) !== null;
|
|
const under = (path: string) =>
|
|
matchPath({ path, caseSensitive: true, end: false }, pathname) !== null;
|
|
|
|
if (under(HOME_PATH)) return atRoot(HOME_PATH) ? null : getHomePath();
|
|
if (under(DIRECT_PATH)) return atRoot(DIRECT_PATH) ? null : getDirectPath();
|
|
if (under(BOTS_PATH)) return atRoot(BOTS_PATH) ? null : getBotsPath();
|
|
|
|
if (under(CHANNELS_PATH)) {
|
|
// Thread URL collapses to its parent room view (drawer closed) — must
|
|
// match before the room-level matcher because thread is a strict
|
|
// subpath of room (`/channels/:s/:r/thread/:rootId/`).
|
|
const threadMatch = matchPath(
|
|
{ path: CHANNELS_THREAD_PATH, caseSensitive: true, end: false },
|
|
pathname
|
|
);
|
|
if (threadMatch?.params.spaceIdOrAlias && threadMatch?.params.roomIdOrAlias) {
|
|
return getChannelsRoomPath(
|
|
decodeURIComponent(threadMatch.params.spaceIdOrAlias),
|
|
decodeURIComponent(threadMatch.params.roomIdOrAlias)
|
|
);
|
|
}
|
|
// Event-anchored permalink collapses to the same parent room view as
|
|
// thread does — so back from a search/notification permalink lands on
|
|
// the room timeline first, not on the space root.
|
|
const eventMatch = matchPath(
|
|
{ path: CHANNELS_ROOM_EVENT_PATH, caseSensitive: true, end: false },
|
|
pathname
|
|
);
|
|
if (eventMatch?.params.spaceIdOrAlias && eventMatch?.params.roomIdOrAlias) {
|
|
return getChannelsRoomPath(
|
|
decodeURIComponent(eventMatch.params.spaceIdOrAlias),
|
|
decodeURIComponent(eventMatch.params.roomIdOrAlias)
|
|
);
|
|
}
|
|
const roomMatch = matchPath(
|
|
{ path: CHANNELS_ROOM_PATH, caseSensitive: true, end: false },
|
|
pathname
|
|
);
|
|
const roomSpace = roomMatch?.params.spaceIdOrAlias;
|
|
if (roomSpace) {
|
|
return getChannelsSpacePath(decodeURIComponent(roomSpace));
|
|
}
|
|
const spaceMatch = matchPath(
|
|
{ path: CHANNELS_SPACE_PATH, caseSensitive: true, end: false },
|
|
pathname
|
|
);
|
|
const channelsSpace = spaceMatch?.params.spaceIdOrAlias;
|
|
if (channelsSpace) {
|
|
const channelsSpacePath = getChannelsSpacePath(decodeURIComponent(channelsSpace));
|
|
return pathname === channelsSpacePath ? getChannelsPath() : channelsSpacePath;
|
|
}
|
|
return atRoot(CHANNELS_PATH) ? null : getChannelsPath();
|
|
}
|
|
|
|
const spaceMatch = matchPath({ path: SPACE_PATH, caseSensitive: true, end: false }, pathname);
|
|
const encodedSpaceIdOrAlias = spaceMatch?.params.spaceIdOrAlias;
|
|
if (encodedSpaceIdOrAlias) {
|
|
const spacePath = getSpacePath(decodeURIComponent(encodedSpaceIdOrAlias));
|
|
return pathname === spacePath ? null : spacePath;
|
|
}
|
|
|
|
if (under(EXPLORE_PATH)) return atRoot(EXPLORE_PATH) ? null : getExplorePath();
|
|
|
|
return null;
|
|
};
|