Skip to content

Commit

Permalink
fix: making auth and firestore observable compatible (#4078)
Browse files Browse the repository at this point in the history
* fix: observer compatibility in firestore
* fix: add observer compatibility to auth APIs
* fix: boolean
* fix: tests
* test: add auth test on onAuthStateChange
also fix onSnapshot test
* fix: lint

Co-authored-by: Mike Diarmid <[email protected]>
Co-authored-by: Mike Hardy <[email protected]>
  • Loading branch information
3 people authored Aug 15, 2020
1 parent 62ee961 commit d8410df
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 18 deletions.
36 changes: 36 additions & 0 deletions packages/auth/e2e/auth.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
8 changes: 5 additions & 3 deletions packages/auth/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AuthListenerCallback>): () => void;

/**
* Listen for changes in ID token.
Expand All @@ -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<AuthListenerCallback>): () => void;

/**
* Adds a listener to observe changes to the User object. This is a superset of everything from
Expand All @@ -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<AuthListenerCallback>): () => void;

/**
* Signs the user out.
Expand Down Expand Up @@ -1634,6 +1634,8 @@ export namespace FirebaseAuthTypes {
}
}

type CallbackOrObserver<T extends (...args: any[]) => 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';
Expand Down
15 changes: 12 additions & 3 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(() => {
Expand Down
34 changes: 22 additions & 12 deletions packages/firestore/lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
}
}
Expand Down

0 comments on commit d8410df

Please sign in to comment.