diff --git a/packages/auth/e2e/auth.e2e.js b/packages/auth/e2e/auth.e2e.js index c30d62c950..b6820bf001 100644 --- a/packages/auth/e2e/auth.e2e.js +++ b/packages/auth/e2e/auth.e2e.js @@ -187,6 +187,42 @@ describe('auth()', () => { unsubscribe(); }); + it('accept observer instead callback as well', async () => { + await firebase.auth().signInAnonymously(); + + await Utils.sleep(50); + + // Test + const observer = { + next(user) { + // Test this access + this.onNext(); + this.user = user; + }, + }; + + let unsubscribe; + await new Promise(resolve => { + observer.onNext = resolve; + unsubscribe = firebase.auth().onAuthStateChanged(observer); + }); + should.exist(observer.user); + + // Sign out + + await firebase.auth().signOut(); + + // Assertions + + await Utils.sleep(50); + + should.not.exist(observer.user); + + // Tear down + + unsubscribe(); + }); + it('stops listening when unsubscribed', async () => { await firebase.auth().signInAnonymously(); diff --git a/packages/auth/lib/index.d.ts b/packages/auth/lib/index.d.ts index 4eb53171d6..20673d73eb 100644 --- a/packages/auth/lib/index.d.ts +++ b/packages/auth/lib/index.d.ts @@ -1254,7 +1254,7 @@ export namespace FirebaseAuthTypes { * * @param listener A listener function which triggers when auth state changed (for example signing out). */ - onAuthStateChanged(listener: AuthListenerCallback): () => void; + onAuthStateChanged(listener: CallbackOrObserver): () => void; /** * Listen for changes in ID token. @@ -1276,7 +1276,7 @@ export namespace FirebaseAuthTypes { * * @param listener A listener function which triggers when the users ID token changes. */ - onIdTokenChanged(listener: AuthListenerCallback): () => void; + onIdTokenChanged(listener: CallbackOrObserver): () => void; /** * Adds a listener to observe changes to the User object. This is a superset of everything from @@ -1302,7 +1302,7 @@ export namespace FirebaseAuthTypes { * @react-native-firebase * @param listener A listener function which triggers when the users data changes. */ - onUserChanged(listener: AuthListenerCallback): () => void; + onUserChanged(listener: CallbackOrObserver): () => void; /** * Signs the user out. @@ -1634,6 +1634,8 @@ export namespace FirebaseAuthTypes { } } +type CallbackOrObserver any> = T | { next: T }; + declare module '@react-native-firebase/auth' { // tslint:disable-next-line:no-duplicate-imports required otherwise doesn't work import { ReactNativeFirebase } from '@react-native-firebase/app'; diff --git a/packages/auth/lib/index.js b/packages/auth/lib/index.js index 24110ce978..d020699dc6 100644 --- a/packages/auth/lib/index.js +++ b/packages/auth/lib/index.js @@ -129,7 +129,14 @@ class FirebaseAuthModule extends FirebaseModule { }; } - onAuthStateChanged(listener) { + _parseListener(listenerOrObserver) { + return typeof listenerOrObserver === 'object' + ? listenerOrObserver.next.bind(listenerOrObserver) + : listenerOrObserver; + } + + onAuthStateChanged(listenerOrObserver) { + const listener = this._parseListener(listenerOrObserver); const subscription = this.emitter.addListener( this.eventNameForApp('onAuthStateChanged'), listener, @@ -143,7 +150,8 @@ class FirebaseAuthModule extends FirebaseModule { return () => subscription.remove(); } - onIdTokenChanged(listener) { + onIdTokenChanged(listenerOrObserver) { + const listener = this._parseListener(listenerOrObserver); const subscription = this.emitter.addListener( this.eventNameForApp('onIdTokenChanged'), listener, @@ -157,7 +165,8 @@ class FirebaseAuthModule extends FirebaseModule { return () => subscription.remove(); } - onUserChanged(listener) { + onUserChanged(listenerOrObserver) { + const listener = this._parseListener(listenerOrObserver); const subscription = this.emitter.addListener(this.eventNameForApp('onUserChanged'), listener); if (this._authResult) { Promise.resolve().then(() => { diff --git a/packages/firestore/lib/utils/index.js b/packages/firestore/lib/utils/index.js index abd4752a5b..9e0154fd3d 100644 --- a/packages/firestore/lib/utils/index.js +++ b/packages/firestore/lib/utils/index.js @@ -141,6 +141,13 @@ export function parseSetOptions(options) { // }; // } +function isPartialObserver(input) { + if (input == null) { + return false; + } + return input.next != null || input.error != null || input.complete != null; +} + export function parseSnapshotArgs(args) { if (args.length === 0) { throw new Error('expected at least one argument.'); @@ -174,20 +181,22 @@ export function parseSnapshotArgs(args) { /** * .onSnapshot({ complete: () => {}, error: (e) => {}, next: (snapshot) => {} }) */ - if (isObject(args[0]) && args[0].includeMetadataChanges === undefined) { - if (args[0].error) { - onError = args[0].error; + if (isObject(args[0]) && isPartialObserver(args[0])) { + const observer = args[0]; + if (observer.error) { + onError = isFunction(observer.error) ? observer.error.bind(observer) : observer.error; } - if (args[0].next) { - onNext = args[0].next; + if (observer.next) { + onNext = isFunction(observer.next) ? observer.next.bind(observer) : observer.next; } } /** * .onSnapshot(SnapshotListenOptions, ... */ - if (isObject(args[0]) && args[0].includeMetadataChanges !== undefined) { - snapshotListenOptions.includeMetadataChanges = args[0].includeMetadataChanges; + if (isObject(args[0]) && !isPartialObserver(args[0])) { + snapshotListenOptions.includeMetadataChanges = + args[0].includeMetadataChanges == null ? false : args[0].includeMetadataChanges; if (isFunction(args[1])) { /** * .onSnapshot(SnapshotListenOptions, Function); @@ -204,15 +213,16 @@ export function parseSnapshotArgs(args) { */ callback = args[1]; } - } else if (isObject(args[1])) { + } else if (isPartialObserver(args[1])) { /** * .onSnapshot(SnapshotListenOptions, { complete: () => {}, error: (e) => {}, next: (snapshot) => {} }); */ - if (isFunction(args[1].error)) { - onError = args[1].error; + const observer = args[1]; + if (observer.error) { + onError = isFunction(observer.error) ? observer.error.bind(observer) : observer.error; } - if (isFunction(args[1].next)) { - onNext = args[1].next; + if (observer.next) { + onNext = isFunction(observer.next) ? observer.next.bind(observer) : observer.next; } } }