Skip to content

Commit

Permalink
Merge pull request #556 from margelo/@chrispader/fix-and-improve-onyx…
Browse files Browse the repository at this point in the history
…-input-method-types
  • Loading branch information
mountiny authored May 31, 2024
2 parents a367599 + 72bea51 commit 990f53e
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 23 deletions.
20 changes: 10 additions & 10 deletions lib/Onyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ import Storage from './storage';
import utils from './utils';
import DevTools from './DevTools';
import type {
Collection,
CollectionKeyBase,
ConnectOptions,
InitOptions,
KeyValueMapping,
Mapping,
NonUndefined,
NullableKeyValueMapping,
NullishDeep,
OnyxCollection,
OnyxEntry,
OnyxKey,
OnyxMergeCollectionInput,
OnyxMergeInput,
OnyxMultiSetInput,
OnyxSetInput,
OnyxUpdate,
OnyxValue,
} from './types';
Expand Down Expand Up @@ -209,7 +209,7 @@ function disconnect(connectionID: number, keyToRemoveFromEvictionBlocklist?: Ony
* @param key ONYXKEY to set
* @param value value to store
*/
function set<TKey extends OnyxKey>(key: TKey, value: NonUndefined<OnyxEntry<KeyValueMapping[TKey]>>): Promise<void> {
function set<TKey extends OnyxKey>(key: TKey, value: OnyxSetInput<TKey>): Promise<void> {
// When we use Onyx.set to set a key we want to clear the current delta changes from Onyx.merge that were queued
// before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
if (OnyxUtils.hasPendingMergeForKey(key)) {
Expand Down Expand Up @@ -273,7 +273,7 @@ function set<TKey extends OnyxKey>(key: TKey, value: NonUndefined<OnyxEntry<KeyV
*
* @param data object keyed by ONYXKEYS and the values to set
*/
function multiSet(data: Partial<NullableKeyValueMapping>): Promise<void> {
function multiSet(data: OnyxMultiSetInput): Promise<void> {
const allKeyValuePairs = OnyxUtils.prepareKeyValuePairsForStorage(data, true);

// When a key is set to null, we need to remove the remove the key from storage using "OnyxUtils.remove".
Expand Down Expand Up @@ -321,7 +321,7 @@ function multiSet(data: Partial<NullableKeyValueMapping>): Promise<void> {
* Onyx.merge(ONYXKEYS.POLICY, {id: 1}); // -> {id: 1}
* Onyx.merge(ONYXKEYS.POLICY, {name: 'My Workspace'}); // -> {id: 1, name: 'My Workspace'}
*/
function merge<TKey extends OnyxKey>(key: TKey, changes: NonUndefined<OnyxEntry<NullishDeep<KeyValueMapping[TKey]>>>): Promise<void> {
function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>): Promise<void> {
const mergeQueue = OnyxUtils.getMergeQueue();
const mergeQueuePromise = OnyxUtils.getMergeQueuePromise();

Expand Down Expand Up @@ -430,7 +430,7 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: NonUndefined<OnyxEntry<
* @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
* @param collection Object collection keyed by individual collection member keys and values
*/
function mergeCollection<TKey extends CollectionKeyBase, TMap>(collectionKey: TKey, collection: Collection<TKey, TMap, NullishDeep<KeyValueMapping[TKey]>>): Promise<void> {
function mergeCollection<TKey extends CollectionKeyBase, TMap>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey, TMap>): Promise<void> {
if (typeof collection !== 'object' || Array.isArray(collection) || utils.isEmptyObject(collection)) {
Logger.logInfo('mergeCollection() called with invalid or empty value. Skipping this update.');
return Promise.resolve();
Expand Down Expand Up @@ -564,7 +564,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {

const keysToBeClearedFromStorage: OnyxKey[] = [];
const keyValuesToResetAsCollection: Record<OnyxKey, OnyxCollection<KeyValueMapping[OnyxKey]>> = {};
const keyValuesToResetIndividually: NullableKeyValueMapping = {};
const keyValuesToResetIndividually: KeyValueMapping = {};

// The only keys that should not be cleared are:
// 1. Anything specifically passed in keysToPreserve (because some keys like language preferences, offline
Expand Down Expand Up @@ -619,7 +619,7 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
const defaultKeyValuePairs = Object.entries(
Object.keys(defaultKeyStates)
.filter((key) => !keysToPreserve.includes(key))
.reduce((obj: NullableKeyValueMapping, key) => {
.reduce((obj: KeyValueMapping, key) => {
// eslint-disable-next-line no-param-reassign
obj[key] = defaultKeyStates[key];
return obj;
Expand Down
3 changes: 1 addition & 2 deletions lib/OnyxUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import type {
DefaultConnectOptions,
KeyValueMapping,
Mapping,
NullableKeyValueMapping,
OnyxCollection,
OnyxEntry,
OnyxKey,
Expand Down Expand Up @@ -103,7 +102,7 @@ function getDefaultKeyStates(): Record<OnyxKey, OnyxValue<OnyxKey>> {
* @param initialKeyStates - initial data to set when `init()` and `clear()` are called
* @param safeEvictionKeys - This is an array of keys (individual or collection patterns) that when provided to Onyx are flagged as "safe" for removal.
*/
function initStoreValues(keys: DeepRecord<string, OnyxKey>, initialKeyStates: Partial<NullableKeyValueMapping>, safeEvictionKeys: OnyxKey[]): void {
function initStoreValues(keys: DeepRecord<string, OnyxKey>, initialKeyStates: Partial<KeyValueMapping>, safeEvictionKeys: OnyxKey[]): void {
// We need the value of the collection keys later for checking if a
// key is a collection. We store it in a map for faster lookup.
const collectionValues = Object.values(keys.COLLECTION ?? {});
Expand Down
21 changes: 20 additions & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import type {ConnectOptions, OnyxUpdate} from './Onyx';
import Onyx from './Onyx';
import type {CustomTypeOptions, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxValue, Selector} from './types';
import type {
CustomTypeOptions,
KeyValueMapping,
NullishDeep,
OnyxCollection,
OnyxEntry,
OnyxInput,
OnyxKey,
OnyxValue,
Selector,
OnyxSetInput,
OnyxMultiSetInput,
OnyxMergeInput,
OnyxMergeCollectionInput,
} from './types';
import type {FetchStatus, ResultMetadata, UseOnyxResult} from './useOnyx';
import useOnyx from './useOnyx';
import withOnyx from './withOnyx';
Expand All @@ -16,7 +30,12 @@ export type {
NullishDeep,
OnyxCollection,
OnyxEntry,
OnyxInput,
OnyxKey,
OnyxSetInput,
OnyxMultiSetInput,
OnyxMergeInput,
OnyxMergeCollectionInput,
OnyxUpdate,
OnyxValue,
ResultMetadata,
Expand Down
48 changes: 40 additions & 8 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ type KeyValueMapping = {
* It's very similar to `KeyValueMapping` but this type accepts using `null` as well.
*/
type NullableKeyValueMapping = {
[TKey in OnyxKey]: OnyxValue<TKey>;
[TKey in OnyxKey]: NonUndefined<OnyxValue<TKey>> | null;
};

/**
Expand Down Expand Up @@ -180,6 +180,13 @@ type Selector<TKey extends OnyxKey, TOnyxProps, TReturnType> = (value: OnyxEntry
*/
type OnyxEntry<TOnyxValue> = TOnyxValue | undefined;

/**
* Represents an input value that can be passed to Onyx methods, that can be either `TOnyxValue` or `null`.
* Setting a key to `null` will remove the key from the store.
* `undefined` is not allowed for setting values, because it will have no effect on the data.
*/
type OnyxInput<TOnyxValue> = TOnyxValue | null;

/**
* Represents an Onyx collection of entries, that can be either a record of `TOnyxValue`s or `null` / `undefined` if it is empty or doesn't exist.
*
Expand Down Expand Up @@ -261,7 +268,7 @@ type NullishObjectDeep<ObjectType extends object> = {
* Also, the `TMap` type is inferred automatically in `mergeCollection()` method and represents
* the object of collection keys/values specified in the second parameter of the method.
*/
type Collection<TKey extends CollectionKeyBase, TMap, TValue> = {
type Collection<TKey extends CollectionKeyBase, TValue, TMap = never> = {
[MapK in keyof TMap]: MapK extends `${TKey}${string}`
? MapK extends `${TKey}`
? never // forbids empty id
Expand Down Expand Up @@ -321,6 +328,26 @@ type Mapping<TKey extends OnyxKey> = ConnectOptions<TKey> & {
connectionID: number;
};

/**
* This represents the value that can be passed to `Onyx.set` and to `Onyx.update` with the method "SET"
*/
type OnyxSetInput<TKey extends OnyxKey> = OnyxInput<KeyValueMapping[TKey]>;

/**
* This represents the value that can be passed to `Onyx.multiSet` and to `Onyx.update` with the method "MULTI_SET"
*/
type OnyxMultiSetInput = Partial<NullableKeyValueMapping>;

/**
* This represents the value that can be passed to `Onyx.merge` and to `Onyx.update` with the method "MERGE"
*/
type OnyxMergeInput<TKey extends OnyxKey> = OnyxInput<NullishDeep<KeyValueMapping[TKey]>>;

/**
* This represents the value that can be passed to `Onyx.merge` and to `Onyx.update` with the method "MERGE"
*/
type OnyxMergeCollectionInput<TKey extends OnyxKey, TMap = object> = Collection<TKey, NullishDeep<KeyValueMapping[TKey]>, TMap>;

/**
* Represents different kinds of updates that can be passed to `Onyx.update()` method. It is a discriminated union of
* different update methods (`SET`, `MERGE`, `MERGE_COLLECTION`), each with their own key and value structure.
Expand All @@ -331,17 +358,17 @@ type OnyxUpdate =
| {
onyxMethod: typeof OnyxUtils.METHOD.SET;
key: TKey;
value: NonUndefined<OnyxEntry<KeyValueMapping[TKey]>>;
value: OnyxSetInput<TKey>;
}
| {
onyxMethod: typeof OnyxUtils.METHOD.MERGE;
onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET;
key: TKey;
value: NonUndefined<OnyxEntry<NullishDeep<KeyValueMapping[TKey]>>>;
value: OnyxMultiSetInput;
}
| {
onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET;
onyxMethod: typeof OnyxUtils.METHOD.MERGE;
key: TKey;
value: Partial<NullableKeyValueMapping>;
value: OnyxMergeInput<TKey>;
}
| {
onyxMethod: typeof OnyxUtils.METHOD.CLEAR;
Expand All @@ -353,7 +380,7 @@ type OnyxUpdate =
[TKey in CollectionKeyBase]: {
onyxMethod: typeof OnyxUtils.METHOD.MERGE_COLLECTION;
key: TKey;
value: Record<`${TKey}${string}`, NullishDeep<KeyValueMapping[TKey]>>;
value: OnyxMergeCollectionInput<TKey>;
};
}[CollectionKeyBase];

Expand Down Expand Up @@ -417,7 +444,12 @@ export type {
NullishDeep,
OnyxCollection,
OnyxEntry,
OnyxInput,
OnyxKey,
OnyxSetInput,
OnyxMultiSetInput,
OnyxMergeInput,
OnyxMergeCollectionInput,
OnyxUpdate,
OnyxValue,
Selector,
Expand Down
4 changes: 2 additions & 2 deletions lib/withOnyx/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {ForwardedRef} from 'react';
import type {IsEqual} from 'type-fest';
import type {CollectionKeyBase, ExtractOnyxCollectionValue, KeyValueMapping, NullableKeyValueMapping, OnyxCollection, OnyxEntry, OnyxKey, OnyxValue, Selector} from '../types';
import type {CollectionKeyBase, ExtractOnyxCollectionValue, KeyValueMapping, OnyxCollection, OnyxEntry, OnyxKey, OnyxValue, Selector} from '../types';

/**
* Represents the base mapping options between an Onyx key and the component's prop.
Expand Down Expand Up @@ -162,7 +162,7 @@ type WithOnyxState<TOnyxProps> = TOnyxProps & {
/**
* Represents the `withOnyx` internal component instance.
*/
type WithOnyxInstance = React.Component<unknown, WithOnyxState<NullableKeyValueMapping>> & {
type WithOnyxInstance = React.Component<unknown, WithOnyxState<KeyValueMapping>> & {
setStateProxy: (modifier: Record<string, OnyxCollection<KeyValueMapping[OnyxKey]>> | ((state: Record<string, OnyxCollection<KeyValueMapping[OnyxKey]>>) => OnyxValue<OnyxKey>)) => void;
setWithOnyxState: (statePropertyName: OnyxKey, value: OnyxValue<OnyxKey>) => void;
};
Expand Down

0 comments on commit 990f53e

Please sign in to comment.