Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: making auth and firestore observable compatible #4078

Merged
merged 10 commits into from
Aug 15, 2020
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 };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No very sure on where to put this type


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;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also not sure where to put this utils function.


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