mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-16 20:20:29 +03:00
Add some explanatory comments
This commit is contained in:
parent
458b1c0172
commit
f9b0d8c86f
3 changed files with 56 additions and 5 deletions
|
|
@ -9,12 +9,23 @@ import React, {
|
|||
import { deepCompare } from 'matrix-js-sdk/lib/utils';
|
||||
import { ExtendedProfile } from '../../../../hooks/useExtendedProfile';
|
||||
|
||||
/// These types ensure the element functions are actually able to manipulate
|
||||
/// the profile fields they're mapped to. The <C> generic parameter represents
|
||||
/// extra "context" props which are passed to every element.
|
||||
|
||||
// strip the index signature from ExtendedProfile using mapped type magic.
|
||||
// keeping the index signature causes weird typechecking issues further down the line
|
||||
// plus there should never be field elements passed with keys which don't exist in ExtendedProfile.
|
||||
type ExtendedProfileKeys = keyof {
|
||||
[Property in keyof ExtendedProfile as string extends Property
|
||||
? never
|
||||
: Property]: ExtendedProfile[Property];
|
||||
};
|
||||
|
||||
// these are the props which all field elements must accept.
|
||||
// this is split into `RawProps` and `Props` so we can type `V` instead of
|
||||
// spraying `ExtendedProfile[K]` all over the place.
|
||||
// don't use this directly, use the `ProfileFieldElementProps` type instead
|
||||
type ProfileFieldElementRawProps<V, C> = {
|
||||
defaultValue: V;
|
||||
value: V;
|
||||
|
|
@ -26,6 +37,7 @@ export type ProfileFieldElementProps<
|
|||
C
|
||||
> = ProfileFieldElementRawProps<ExtendedProfile[K], C>;
|
||||
|
||||
// the map of extended profile keys to field element functions
|
||||
type ProfileFieldElements<C> = {
|
||||
[Property in ExtendedProfileKeys]?: FunctionComponent<ProfileFieldElementProps<Property, C>>;
|
||||
};
|
||||
|
|
@ -42,6 +54,12 @@ type ProfileFieldContextProps<C> = {
|
|||
context: C;
|
||||
};
|
||||
|
||||
/// This element manages the pending state of the profile field widgets.
|
||||
/// It takes the default values of each field, as well as a map associating a profile field key
|
||||
/// with an element _function_ (not a rendered element!) that will be used to edit that field.
|
||||
/// It renders the editor elements internally using React.createElement and passes the rendered
|
||||
/// elements into the child UI. This allows it to handle the pending state entirely by itself,
|
||||
/// and provides strong typechecking.
|
||||
export function ProfileFieldContext<C>({
|
||||
fieldDefaults,
|
||||
fieldElements: fieldElementConstructors,
|
||||
|
|
@ -49,11 +67,14 @@ export function ProfileFieldContext<C>({
|
|||
context,
|
||||
}: ProfileFieldContextProps<C>): ReactNode {
|
||||
const [fields, setFields] = useState<ExtendedProfile>(fieldDefaults);
|
||||
|
||||
|
||||
// this callback also runs when fieldDefaults changes,
|
||||
// which happens when the profile is saved and the pending fields become the new defaults
|
||||
const reset = useCallback(() => {
|
||||
setFields(fieldDefaults);
|
||||
}, [fieldDefaults]);
|
||||
|
||||
// set the pending values to the defaults on the first render
|
||||
useEffect(() => {
|
||||
reset();
|
||||
}, [reset]);
|
||||
|
|
@ -72,6 +93,7 @@ export function ProfileFieldContext<C>({
|
|||
() =>
|
||||
Object.entries(fields).find(
|
||||
([key, value]) =>
|
||||
// deep comparison is necessary here because field values can be any JSON type
|
||||
deepCompare(fieldDefaults[key as keyof ExtendedProfile], value)
|
||||
) !== undefined,
|
||||
[fields, fieldDefaults]
|
||||
|
|
@ -86,6 +108,8 @@ export function ProfileFieldContext<C>({
|
|||
setValue: (value) => setField(key, value),
|
||||
key,
|
||||
};
|
||||
// element can be undefined if the field defaults didn't include its key,
|
||||
// which means the HS doesn't support setting that field
|
||||
if (element !== undefined) {
|
||||
return React.createElement(element, props);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue