1
1
/* eslint-disable @typescript-eslint/ban-types */
2
2
import { storage as sdkStorage } from '@cardano-sdk/wallet' ;
3
- import { EMPTY , filter , from , map , mergeMap , Observable , of , share } from 'rxjs' ;
3
+ import { EMPTY , filter , from , map , mergeMap , Observable , of , share , firstValueFrom } from 'rxjs' ;
4
4
import { Logger } from 'ts-log' ;
5
5
import { contextLogger , fromSerializableObject , toSerializableObject } from '@cardano-sdk/util' ;
6
6
import { ExtensionStore } from './extension-store' ;
7
+ import isEqual from 'lodash/isEqual' ;
7
8
8
9
export type DocumentChange < T > = {
9
10
oldValue ?: T ;
10
11
newValue ?: T ;
11
12
} ;
12
13
13
- const undefinedIfEmpty = < T > ( value : T | undefined ) : T | undefined => {
14
- if ( typeof value === 'object' && ( value === null || Object . keys ( value ) . length === 0 ) ) return undefined ;
14
+ const undefinedIfEmptyObj = < T > ( value : T | undefined ) : T | undefined => {
15
+ if ( typeof value === 'object' && ! Array . isArray ( value ) && ( value === null || Object . keys ( value ) . length === 0 ) )
16
+ return undefined ;
15
17
// eslint-disable-next-line consistent-return
16
18
return value ;
17
19
} ;
@@ -34,8 +36,8 @@ export class ExtensionDocumentStore<T extends {}> extends ExtensionStore impleme
34
36
filter ( ( { key } ) => key === docId ) ,
35
37
map (
36
38
( { change } ) : DocumentChange < T > => ( {
37
- oldValue : undefinedIfEmpty ( change . oldValue ) ,
38
- newValue : undefinedIfEmpty ( change . newValue )
39
+ oldValue : undefinedIfEmptyObj ( change . oldValue ) ,
40
+ newValue : undefinedIfEmptyObj ( change . newValue )
39
41
} )
40
42
) ,
41
43
share ( )
@@ -55,11 +57,25 @@ export class ExtensionDocumentStore<T extends {}> extends ExtensionStore impleme
55
57
set ( doc : T ) : Observable < void > {
56
58
this . logger . debug ( 'set' , doc ) ;
57
59
return from (
58
- ( this . idle = this . idle . then ( ( ) =>
59
- this . storage . set ( {
60
+ ( this . idle = this . idle . then ( async ( ) => {
61
+ const previousValueMap = await this . storage . get ( this . docId ) ;
62
+ const previousValue = fromSerializableObject ( previousValueMap [ this . docId ] ) ;
63
+ if ( isEqual ( previousValue , doc ) ) {
64
+ // if values are equal, then we would set to the same value and
65
+ // this.documentChange$ won't emit and this promise will never resolve
66
+ return ;
67
+ }
68
+
69
+ const storageChange = firstValueFrom ( this . documentChange$ ) ;
70
+ await this . storage . set ( {
60
71
[ this . docId ] : toSerializableObject ( doc )
61
- } )
62
- ) )
72
+ } ) ;
73
+ // do not emit until documentChange$ emits
74
+ // in order to avoid race conditions:
75
+ // users expect `observeAll` to emit new value when subscribing
76
+ // to it immediatelly after `set` emits
77
+ await storageChange ;
78
+ } ) )
63
79
) ;
64
80
}
65
81
0 commit comments