@@ -24,7 +24,6 @@ import type {
2424 OnyxValue ,
2525 OnyxInput ,
2626 OnyxMethodMap ,
27- MultiMergeReplaceNullPatches ,
2827} from './types' ;
2928import OnyxUtils from './OnyxUtils' ;
3029import logMessages from './logMessages' ;
@@ -345,7 +344,7 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
345344}
346345
347346/**
348- * Merges a collection based on their keys
347+ * Merges a collection based on their keys.
349348 *
350349 * @example
351350 *
@@ -356,125 +355,9 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
356355 *
357356 * @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
358357 * @param collection Object collection keyed by individual collection member keys and values
359- * @param mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
360- * tuples that we'll use to replace the nested objects of that collection member record with something else.
361358 */
362- function mergeCollection < TKey extends CollectionKeyBase , TMap > (
363- collectionKey : TKey ,
364- collection : OnyxMergeCollectionInput < TKey , TMap > ,
365- mergeReplaceNullPatches ?: MultiMergeReplaceNullPatches ,
366- ) : Promise < void > {
367- if ( ! OnyxUtils . isValidNonEmptyCollectionForMerge ( collection ) ) {
368- Logger . logInfo ( 'mergeCollection() called with invalid or empty value. Skipping this update.' ) ;
369- return Promise . resolve ( ) ;
370- }
371-
372- let resultCollection : OnyxInputKeyValueMapping = collection ;
373- let resultCollectionKeys = Object . keys ( resultCollection ) ;
374-
375- // Confirm all the collection keys belong to the same parent
376- if ( ! OnyxUtils . doAllCollectionItemsBelongToSameParent ( collectionKey , resultCollectionKeys ) ) {
377- return Promise . resolve ( ) ;
378- }
379-
380- const skippableCollectionMemberIDs = OnyxUtils . getSkippableCollectionMemberIDs ( ) ;
381- if ( skippableCollectionMemberIDs . size ) {
382- resultCollection = resultCollectionKeys . reduce ( ( result : OnyxInputKeyValueMapping , key ) => {
383- try {
384- const [ , collectionMemberID ] = OnyxUtils . splitCollectionMemberKey ( key , collectionKey ) ;
385- // If the collection member key is a skippable one we set its value to null.
386- // eslint-disable-next-line no-param-reassign
387- result [ key ] = ! skippableCollectionMemberIDs . has ( collectionMemberID ) ? resultCollection [ key ] : null ;
388- } catch {
389- // Something went wrong during split, so we assign the data to result anyway.
390- // eslint-disable-next-line no-param-reassign
391- result [ key ] = resultCollection [ key ] ;
392- }
393-
394- return result ;
395- } , { } ) ;
396- }
397- resultCollectionKeys = Object . keys ( resultCollection ) ;
398-
399- return OnyxUtils . getAllKeys ( )
400- . then ( ( persistedKeys ) => {
401- // Split to keys that exist in storage and keys that don't
402- const keys = resultCollectionKeys . filter ( ( key ) => {
403- if ( resultCollection [ key ] === null ) {
404- OnyxUtils . remove ( key ) ;
405- return false ;
406- }
407- return true ;
408- } ) ;
409-
410- const existingKeys = keys . filter ( ( key ) => persistedKeys . has ( key ) ) ;
411-
412- const cachedCollectionForExistingKeys = OnyxUtils . getCachedCollection ( collectionKey , existingKeys ) ;
413-
414- const existingKeyCollection = existingKeys . reduce ( ( obj : OnyxInputKeyValueMapping , key ) => {
415- const { isCompatible, existingValueType, newValueType} = utils . checkCompatibilityWithExistingValue ( resultCollection [ key ] , cachedCollectionForExistingKeys [ key ] ) ;
416-
417- if ( ! isCompatible ) {
418- Logger . logAlert ( logMessages . incompatibleUpdateAlert ( key , 'mergeCollection' , existingValueType , newValueType ) ) ;
419- return obj ;
420- }
421-
422- // eslint-disable-next-line no-param-reassign
423- obj [ key ] = resultCollection [ key ] ;
424- return obj ;
425- } , { } ) as Record < OnyxKey , OnyxInput < TKey > > ;
426-
427- const newCollection : Record < OnyxKey , OnyxInput < TKey > > = { } ;
428- keys . forEach ( ( key ) => {
429- if ( persistedKeys . has ( key ) ) {
430- return ;
431- }
432- newCollection [ key ] = resultCollection [ key ] ;
433- } ) ;
434-
435- // When (multi-)merging the values with the existing values in storage,
436- // we don't want to remove nested null values from the data that we pass to the storage layer,
437- // because the storage layer uses them to remove nested keys from storage natively.
438- const keyValuePairsForExistingCollection = OnyxUtils . prepareKeyValuePairsForStorage ( existingKeyCollection , false , mergeReplaceNullPatches ) ;
439-
440- // We can safely remove nested null values when using (multi-)set,
441- // because we will simply overwrite the existing values in storage.
442- const keyValuePairsForNewCollection = OnyxUtils . prepareKeyValuePairsForStorage ( newCollection , true ) ;
443-
444- const promises = [ ] ;
445-
446- // We need to get the previously existing values so we can compare the new ones
447- // against them, to avoid unnecessary subscriber updates.
448- const previousCollectionPromise = Promise . all ( existingKeys . map ( ( key ) => OnyxUtils . get ( key ) . then ( ( value ) => [ key , value ] ) ) ) . then ( Object . fromEntries ) ;
449-
450- // New keys will be added via multiSet while existing keys will be updated using multiMerge
451- // This is because setting a key that doesn't exist yet with multiMerge will throw errors
452- if ( keyValuePairsForExistingCollection . length > 0 ) {
453- promises . push ( Storage . multiMerge ( keyValuePairsForExistingCollection ) ) ;
454- }
455-
456- if ( keyValuePairsForNewCollection . length > 0 ) {
457- promises . push ( Storage . multiSet ( keyValuePairsForNewCollection ) ) ;
458- }
459-
460- // finalMergedCollection contains all the keys that were merged, without the keys of incompatible updates
461- const finalMergedCollection = { ...existingKeyCollection , ...newCollection } ;
462-
463- // Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
464- // and update all subscribers
465- const promiseUpdate = previousCollectionPromise . then ( ( previousCollection ) => {
466- cache . merge ( finalMergedCollection ) ;
467- return OnyxUtils . scheduleNotifyCollectionSubscribers ( collectionKey , finalMergedCollection , previousCollection ) ;
468- } ) ;
469-
470- return Promise . all ( promises )
471- . catch ( ( error ) => OnyxUtils . evictStorageAndRetry ( error , mergeCollection , collectionKey , resultCollection ) )
472- . then ( ( ) => {
473- OnyxUtils . sendActionToDevTools ( OnyxUtils . METHOD . MERGE_COLLECTION , undefined , resultCollection ) ;
474- return promiseUpdate ;
475- } ) ;
476- } )
477- . then ( ( ) => undefined ) ;
359+ function mergeCollection < TKey extends CollectionKeyBase , TMap > ( collectionKey : TKey , collection : OnyxMergeCollectionInput < TKey , TMap > ) : Promise < void > {
360+ return OnyxUtils . mergeCollectionWithPatches ( collectionKey , collection ) ;
478361}
479362
480363/**
@@ -708,7 +591,11 @@ function update(data: OnyxUpdate[]): Promise<void> {
708591
709592 if ( ! utils . isEmptyObject ( batchedCollectionUpdates . merge ) ) {
710593 promises . push ( ( ) =>
711- mergeCollection ( collectionKey , batchedCollectionUpdates . merge as Collection < CollectionKey , unknown , unknown > , batchedCollectionUpdates . mergeReplaceNullPatches ) ,
594+ OnyxUtils . mergeCollectionWithPatches (
595+ collectionKey ,
596+ batchedCollectionUpdates . merge as Collection < CollectionKey , unknown , unknown > ,
597+ batchedCollectionUpdates . mergeReplaceNullPatches ,
598+ ) ,
712599 ) ;
713600 }
714601 if ( ! utils . isEmptyObject ( batchedCollectionUpdates . set ) ) {
0 commit comments