0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00
logto/packages/console/src/hooks/use-tenant-pathname.ts
simeng-li 1c3ada0123
feat(console,phrases): add third party app list page (#5149)
* feat(console,phrases): add new third-party applicaiton to the guide library

add new third-party applicaiton to the guide library

* feat(console,phrases): add third party app list page

add third party app list page

* fix(console): fix page size

fix page size

* fix(console): fix rebase issue

fix rebase issue

* fix(test): fix rebase issue

fix rebase issue
2024-01-05 12:27:27 +08:00

134 lines
3.8 KiB
TypeScript

import { ossConsolePath } from '@logto/schemas';
import { appendPath, joinPath } from '@silverhand/essentials';
import { useCallback, useContext, useMemo } from 'react';
import {
type NavigateOptions,
type To,
matchPath,
useLocation,
useNavigate,
useHref,
type NavigateFunction,
} from 'react-router-dom';
import { isCloud } from '@/consts/env';
import { TenantsContext } from '@/contexts/TenantsProvider';
type TenantPathname = {
/**
* A function that can be used to match a specific tenant pathname with
* the current location.
*
* @example
* ```ts
* // Current location: `/tenant-1/console`
* const match = useMatchTenantPath();
* match('/console'); // true
* match('/tenant-1/console'); // false
* match('/another-page'); // false
* ```
*
* @param pathname Pathname to match
* @param exact Whether to match exactly, defaults to `false`
*/
match: (pathname: string, exact?: boolean) => boolean;
/**
* Returns the pathname with the current tenant ID prepended if the pathname
* is an absolute pathname; otherwise, returns the pathname directly.
*/
getPathname: (pathname: string) => string;
/**
* Returns the `to` object with the current tenant ID prepended if the
* pathname is an absolute pathname; otherwise, returns the `to` object
* or the string directly.
*/
getTo: (to: To) => To;
/** Navigate to the given pathname in the current tenant. */
navigate: NavigateFunction;
/** Returns the full URL with the current tenant ID prepended. */
getUrl: (pathname: string) => URL;
};
/**
* Returns a `TenantPathname` object that contains utilities for matching
* and generating tenant-specific pathnames.
*
* @see {@link TenantPathname}
*/
function useTenantPathname(): TenantPathname {
const location = useLocation();
const { currentTenantId } = useContext(TenantsContext);
const tenantSegment = useMemo(
() => (isCloud ? currentTenantId : ossConsolePath.slice(1)),
[currentTenantId]
);
const navigate = useNavigate();
const href = useHref('/');
const match = useCallback(
(pathname: string, exact = false) => {
// Match relative pathnames directly
if (!pathname.startsWith('/')) {
return (
matchPath(joinPath(location.pathname, pathname, exact ? '' : '*'), location.pathname) !==
null
);
}
// Match absolute pathnames with the tenant segment
return (
matchPath(joinPath(':tenantId', pathname, exact ? '' : '*'), location.pathname) !== null
);
},
[location.pathname]
);
/** Returns the pathname with the current tenant ID prepended. */
const getPathname = useCallback(
(pathname: string) => {
if (pathname.startsWith('/') && !pathname.startsWith(`/${tenantSegment}`)) {
return joinPath(tenantSegment, pathname);
}
// Directly return the pathname if it's a relative pathname
return pathname;
},
[tenantSegment]
);
const getTo = useCallback(
(to: To): To => {
if (typeof to === 'string') {
return getPathname(to);
}
return { ...to, pathname: getPathname(to.pathname ?? '') };
},
[getPathname]
);
const getUrl = useCallback(
(pathname = '/') => appendPath(new URL(window.location.origin), href, tenantSegment, pathname),
[href, tenantSegment]
);
const data = useMemo(
() => ({
match,
navigate: (to: To | number, options?: NavigateOptions) => {
// Navigate to the given index in the history stack
if (typeof to === 'number') {
navigate(to);
return;
}
navigate(getTo(to), options);
},
getPathname,
getTo,
getUrl,
}),
[match, getPathname, getTo, navigate, getUrl]
);
return data;
}
export default useTenantPathname;