import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; import { map } from 'rxjs/operators'; import { Story } from './story'; import { SupaService } from './supa.service'; @Injectable({ providedIn: 'root' }) export class StoryService { // storyMap: Map = new Map(); storyMap = {}; stories: BehaviorSubject = new BehaviorSubject([]); subscribedStandUpIds: number[] = []; isListening: boolean = false; constructor( private supa: SupaService, ) { } /** * Get Storys from local store. * Requests data if store is emtpy. * @param standup_id * @returns Observable */ getStories(standup_id: number): Observable { if (!this.subscribedStandUpIds.includes(standup_id)) { this.subscribeToStories(standup_id); console.log('getStories - REFRESH', standup_id) this.supa.client.from('story').select() .filter('standup_id', 'eq', standup_id) .then( data => { this.updateStore(data.body); }, error => { console.error(error); } ); } else { console.log('getStories - LOCAL', standup_id) } return this.stories.asObservable().pipe( map(stories => stories.filter(e => e.standup_id === standup_id)) ); } /** * Listen to realtime events from story db. */ subscribeToStories(standup_id: number) { // TODO check for this.subscribedStandUpIds.includes(standup_id) if (!this.isListening) { this.subscribedStandUpIds.push(standup_id); this.supa.client.from('story').on('*', payload => { console.log('subscribeToStories - REALTIME EVENT', payload) if ((payload.eventType === 'INSERT') || (payload.eventType === 'UPDATE')) { this.storyMap[payload.new.id] = payload.new; } else { delete this.storyMap[payload.old.id]; } this.next(); }).subscribe(); } } /** * Requests up to date data from API. * Returns the local copy. * @returns Observable */ // refreshStorys(): Observable { // this.subscribeToStories(); // this.supa.client.from('story').select() // .then(stories => this.updateStore(stories.body)) // .catch(error => console.log('Error: ', error)); // return this.stories.asObservable(); // } /** * Update the local store with provided stories * @param stories */ updateStore(stories: Story[]) { stories.forEach(e => { // this.storyMap.set(e.id, e); this.storyMap[e.id] = e; }); console.log('update stories', this.storyMap) this.next(); } /** * Emits local store. */ next = () => { console.log('next', Object.values(this.storyMap)) this.stories.next(Object.values(this.storyMap)) }; /** * Retrieve story from local store. * @param id */ getOne(id: number) { if (this.storyMap[id]) { return of(this.storyMap[id]); } else { const subject: Subject = new Subject(); this.supa.client.from('story').select('id, name, description') .filter('id', 'eq', id) .then( data => { this.updateStore([data.body[0]]); subject.next(data.body[0]); subject.complete(); }, error => { subject.error(error); subject.complete(); } ); return subject.asObservable(); } } /** * * @param story */ updateOne(story: Story): Observable { const subject: Subject = new Subject(); this.supa.client.from('story').update(story) .match({ id: story.id }) .then( data => { subject.next(data.body[0]); subject.complete(); }, error => { subject.error(error); subject.complete(); } ); return subject.asObservable(); } /** * Removes one story from db. * @param story */ deleteOne(story: Story): Observable { const subject: Subject = new Subject(); this.supa.client.from('story').delete() .match({ id: story.id }) .then( data => { subject.next(story); subject.complete(); }, error => { subject.error(error); subject.complete(); } ); return subject.asObservable(); } /** * Creates a story on the db. * @param story */ addOne(story: Story): Observable { const subject: Subject = new Subject(); this.supa.client.from('story').insert(story) .then( data => { subject.next(data.body[0]); subject.complete(); }, error => { subject.error(error); subject.complete(); } ); return subject.asObservable(); } }