mirror of https://github.com/mastodon/mastodon
Use a modern React context for identity in the app (#30098)
parent
6282b6da77
commit
a178ba7cd5
@ -0,0 +1,74 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
import hoistStatics from 'hoist-non-react-statics';
|
||||
|
||||
import type { InitialState } from 'mastodon/initial_state';
|
||||
|
||||
export interface IdentityContextType {
|
||||
signedIn: boolean;
|
||||
accountId: string | undefined;
|
||||
disabledAccountId: string | undefined;
|
||||
accessToken: string | undefined;
|
||||
permissions: number;
|
||||
}
|
||||
|
||||
export const identityContextPropShape = PropTypes.shape({
|
||||
signedIn: PropTypes.bool.isRequired,
|
||||
accountId: PropTypes.string,
|
||||
disabledAccountId: PropTypes.string,
|
||||
accessToken: PropTypes.string,
|
||||
}).isRequired;
|
||||
|
||||
export const createIdentityContext = (state: InitialState) => ({
|
||||
signedIn: !!state.meta.me,
|
||||
accountId: state.meta.me,
|
||||
disabledAccountId: state.meta.disabled_account_id,
|
||||
accessToken: state.meta.access_token,
|
||||
permissions: state.role?.permissions ?? 0,
|
||||
});
|
||||
|
||||
export const IdentityContext = createContext<IdentityContextType>({
|
||||
signedIn: false,
|
||||
permissions: 0,
|
||||
accountId: undefined,
|
||||
disabledAccountId: undefined,
|
||||
accessToken: undefined,
|
||||
});
|
||||
|
||||
export const useIdentity = () => useContext(IdentityContext);
|
||||
|
||||
export interface IdentityProps {
|
||||
ref?: unknown;
|
||||
wrappedComponentRef?: unknown;
|
||||
}
|
||||
|
||||
/* Injects an `identity` props into the wrapped component to be able to use the new context in class components */
|
||||
export function withIdentity<
|
||||
ComponentType extends React.ComponentType<IdentityProps>,
|
||||
>(Component: ComponentType) {
|
||||
const displayName = `withIdentity(${Component.displayName ?? Component.name})`;
|
||||
const C = (props: React.ComponentProps<ComponentType>) => {
|
||||
const { wrappedComponentRef, ...remainingProps } = props;
|
||||
|
||||
return (
|
||||
<IdentityContext.Consumer>
|
||||
{(context) => {
|
||||
return (
|
||||
// @ts-expect-error - Dynamic covariant generic components are tough to type.
|
||||
<Component
|
||||
{...remainingProps}
|
||||
identity={context}
|
||||
ref={wrappedComponentRef}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</IdentityContext.Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
C.displayName = displayName;
|
||||
C.WrappedComponent = Component;
|
||||
|
||||
return hoistStatics(C, Component);
|
||||
}
|
Loading…
Reference in New Issue