Richard
Richard

Reputation: 8935

Firebase how to unsubscribe

I am using Ionic2 with Angularfire2 to access Firebase Authentication.

I access the following rxjs/Observable:

chats.ts

  this.firelist = this.dataService.findChats();
  this.subscription = this.firelist.subscribe(chatItems => {
     ...
  });

  ngDestroy() {
    this.subscription.unsubscribe();
  }

dataService.ts

findChats(): Observable<any[]> {
        this.firebaseDataService.findChats().forEach(firebaseItems => {
           ...
           observer.next(mergedItems);
        });
}

firebaseDataService.ts

findChats(): Observable<any[]> { // populates the firelist
    return this.af.database.list('/chat/', {
        query: {
            orderByChild: 'negativtimestamp'
        }
    }).map(items => {
        const filtered = items.filter(
            item => (item.memberId1 === this.me.uid || item.memberId2 === this.me.uid)
        );
        return filtered;
    });
}

Also, in Firebase Authentication, I have the following Realtime Database Rules:

  "rules": {
      ".read": "auth != null",
      ".write": "auth != null",
  }

Problem

This works perfectly while a user is logged in (i.e. auth != null), but a soon as a user logs out, and auth == null, I get the following error:

ERROR Error: Uncaught (in promise): Error: permission_denied at /chat: Client doesn't have permission to access the desired data. Error: permission_denied at /chat: Client doesn't have permission to access the desired data.

Question

As you can see in the above code, I try to unsubscribe from the Firebase Authentication Service, but must be doing it incorrectly. If anyone can advise how I should unsubscribe in order to stop my app querying the Firebase database, I would appreciate the help.

Upvotes: 3

Views: 10222

Answers (2)

Vamshi
Vamshi

Reputation: 9331

This is not an exact answer to the question, but my argument is we will never come to this issue if we do this other way.

P.S : I never used firebase , I use AWS so the firebase code might not be precise, we discuss about it .

Here is my version:

findChats(): Observable<any[]> { // populates the firelist
    return 
      this.authService.getCurrentUser()
        .flatMap(user => user === null ? 
          Observable.empty():        
          this.af.database.list('/chat/', {
             query: {
                orderByChild: 'negativtimestamp'
             }
           })
          .map(items => 
             return items.filter(
               item => (
                  item.memberId1 === this.me.uid || 
                  item.memberId2 === this.me.uid
               )
             ) 
          )
     );
}

Auth Service

public currentUser = new BehaviorSubject(null);

constructor(){

    firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
           // User is signed in.
           currentUser.next(user);
        } else {
         // No user is signed in.
           currentUser.next(null);
        }
    });

}    

The code is very raw but I hope it conveys the idea. Here I am observing on the user authentication status and doing flatMap to fetch data. If user logs out, auth status change to null so in the UI data changes to empty list. Even if the user is not logged out , still he cannot see the data.


UPDATE :

Refactoring other answer:

findChats(){
   return Observable.merge(
       this.firebaseDataService
           .findChats()
           .flatMap(chats => Observable.from(chats)),
       this.localDataService
           .findChats()
           .flatMap(chats => Observable.from(chats))
   )
   .filter(chat => !!chat)
   .toArray()
   .map(chats => [
      ...chats.sort(
        (a, b) => 
           parseFloat(a.negativtimestamp) - parseFloat(b.negativtimestamp))
      ]
   );  
}

Except when you are caching response using Subject never use subscribe inside service.

Upvotes: 1

Richard
Richard

Reputation: 8935

The unsubscribe is working correctly. The problem turns out that the following function I used, is causing it to subscribe twice for some reason:

findChats(): Observable<any[]> {
    return Observable.create((observer) => {
        this.chatSubscription2 = this.firebaseDataService.findChats().subscribe(firebaseItems => {
            this.localDataService.findChats().then((localItems: any[]) => {
                let mergedItems: any[] = [];
                if (localItems && localItems != null && firebaseItems && firebaseItems != null) {
                    for (let i: number = 0; i < localItems.length; i++) {
                        if (localItems[i] === null) {
                            localItems.splice(i, 1);
                        }
                    }
                    mergedItems = this.arrayUnique(firebaseItems.concat(localItems), true);
                } else if (firebaseItems && firebaseItems != null) {
                    mergedItems = firebaseItems;
                } else if (localItems && localItems != null) {
                    mergedItems = localItems;
                }
                mergedItems.sort((a, b) => {
                    return parseFloat(a.negativtimestamp) - parseFloat(b.negativtimestamp);
                });
                observer.next(mergedItems);
                this.checkChats(firebaseItems, localItems);
            });
        });
    });
}

For some reason the 3rd line above is called twice (even though the function is only called once). This is creating a second subscription, and the first subscription is never unsubscribed.

So it appears that the creation of the Observable is incorrect. I'm not sure what;s the correct way to create the Observable though.

Upvotes: 0

Related Questions