1- import { NativeModules , Platform } from 'react-native' ;
1+ import { NativeEventEmitter , NativeModules , Platform } from 'react-native' ;
22import {
33 CollectionChangeListenerArgs ,
44 ICoreEngine ,
55 ListenerCallback ,
6- ListenerHandle ,
76 CollectionArgs ,
87 CollectionCreateIndexArgs ,
98 CollectionDeleteDocumentArgs ,
@@ -57,6 +56,25 @@ export class CblReactNativeEngine implements ICoreEngine {
5756 _defaultCollectionName = '_default' ;
5857 _defaultScopeName = '_default' ;
5958
59+ //event name mapping for the native side of the module
60+ _eventReplicatorStatusChange = 'replicatorStatusChange' ;
61+ _eventReplicatorDocumentChange = 'replicatorDocumentChange' ;
62+ _eventCollectionChange = 'collectionChange' ;
63+ _eventCollectionDocumentChange = 'collectionDocumentChange' ;
64+ _eventQueryChange = 'queryChange' ;
65+
66+ //used to listen to replicator change events for both status and document changes
67+ private _isReplicatorStatusChangeEventSetup : boolean = false ;
68+ private _replicatorChangeListeners : Map < string , ListenerCallback > = new Map ( ) ;
69+ private _replicatorStatusChangeStopListener : ( ) => void | undefined =
70+ undefined ;
71+
72+ private _replicatorDocumentChangeListeners : Map < string , ListenerCallback > =
73+ new Map ( ) ;
74+ private _replicatorDocumentChangeStopListener : ( ) => void | undefined =
75+ undefined ;
76+ private _isReplicatorDocumentChangeEventSetup : boolean = false ;
77+
6078 private static readonly LINKING_ERROR =
6179 `The package 'cbl-reactnative' doesn't seem to be linked. Make sure: \n\n` +
6280 Platform . select ( { ios : "- You have run 'pod install'\n" , default : '' } ) +
@@ -74,10 +92,18 @@ export class CblReactNativeEngine implements ICoreEngine {
7492 }
7593 ) ;
7694
95+ private _eventEmitter = new NativeEventEmitter ( this . CblReactNative ) ;
96+
7797 constructor ( ) {
7898 EngineLocator . registerEngine ( EngineLocator . key , this ) ;
7999 }
80100
101+ //startListeningEvents - used to listen to events from the native side of the module. Implements Native change listeners for Couchbase Lite
102+ startListeningEvents = ( event : string , callback : any ) => {
103+ const subscription = this . _eventEmitter . addListener ( event , callback ) ;
104+ return ( ) => subscription . remove ( ) ;
105+ } ;
106+
81107 collection_AddChangeListener (
82108 args : CollectionChangeListenerArgs ,
83109 lcb : ListenerCallback
@@ -748,18 +774,55 @@ export class CblReactNativeEngine implements ICoreEngine {
748774 args : ReplicationChangeListenerArgs ,
749775 lcb : ListenerCallback
750776 ) : Promise < void > {
777+ //need to track the listener callback for later use due to how React Native events work. Events are global so we need to first find which callback to call, we could have multiple replicators registered
778+ //https://reactnative.dev/docs/native-modules-ios#sending-events-to-javascript
779+ if ( this . _replicatorChangeListeners . has ( args . changeListenerToken ) ) {
780+ throw new Error (
781+ 'ERROR: changeListenerToken already exists and is registered to listen to callbacks, cannot add a new one'
782+ ) ;
783+ }
784+ //if the event listener is not setup, then set up the listener.
785+ //Event listener only needs to be setup once for any replicators in memory
786+ if ( ! this . _isReplicatorStatusChangeEventSetup ) {
787+ this . _replicatorDocumentChangeStopListener = this . startListeningEvents (
788+ this . _eventReplicatorStatusChange ,
789+ ( results : any [ ] ) => {
790+ const token = results [ 0 ] as string ;
791+ const data = results [ 1 ] ;
792+ const error = results [ 2 ] ;
793+ if ( token === undefined || token === null || token . length === 0 ) {
794+ throw new Error (
795+ 'ERROR: No token to resolve back to proper callback'
796+ ) ;
797+ }
798+ const callback = this . _replicatorChangeListeners . get ( token ) ;
799+ if ( callback !== undefined ) {
800+ callback ( data , error ) ;
801+ } else {
802+ throw new Error (
803+ `Error: Could not found callback method for token: ${ token } .`
804+ ) ;
805+ }
806+ }
807+ ) ;
808+ this . _isReplicatorStatusChangeEventSetup = true ;
809+ }
751810 return new Promise ( ( resolve , reject ) => {
752811 this . CblReactNative . replicator_AddChangeListener (
753812 args . changeListenerToken ,
754- args . replicatorId ,
755- ( results : [ any , any ] ) => {
756- lcb ( results [ 0 ] , results [ 1 ] ) ;
757- }
813+ args . replicatorId
758814 ) . then (
759815 ( ) => {
816+ //add token to change listener map
817+ this . _replicatorChangeListeners . set ( args . changeListenerToken , lcb ) ;
760818 resolve ( ) ;
761819 } ,
762820 ( error : any ) => {
821+ //stop the event listening if there is an error and no other tokens are present, thus no need to listen to events
822+ if ( this . _replicatorChangeListeners . size === 0 ) {
823+ this . _replicatorStatusChangeStopListener ( ) ;
824+ this . _isReplicatorStatusChangeEventSetup = false ;
825+ }
763826 reject ( error ) ;
764827 }
765828 ) ;
@@ -856,7 +919,23 @@ export class CblReactNativeEngine implements ICoreEngine {
856919 replicator_RemoveChangeListener (
857920 args : ReplicationChangeListenerArgs
858921 ) : Promise < void > {
859- return Promise . resolve ( undefined ) ;
922+ return new Promise ( ( resolve , reject ) => {
923+ this . CblReactNative . replicator_RemoveChangeListener (
924+ args . replicatorId ,
925+ args . changeListenerToken
926+ ) . then (
927+ ( ) => {
928+ //remove the listener callback from the map
929+ if ( this . _replicatorChangeListeners . has ( args . changeListenerToken ) ) {
930+ this . _replicatorChangeListeners . delete ( args . changeListenerToken ) ;
931+ }
932+ resolve ( ) ;
933+ } ,
934+ ( error : any ) => {
935+ reject ( error ) ;
936+ }
937+ ) ;
938+ } ) ;
860939 }
861940
862941 replicator_ResetCheckpoint ( args : ReplicatorArgs ) : Promise < void > {
0 commit comments