import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; import { filter, map, switchMap, take, tap } from 'rxjs/operators'; import { Contact, Country, Tag } from 'app/modules/admin/apps/contacts/contacts.types'; @Injectable({ providedIn: 'root' }) export class ContactsService { // Private private _contact: BehaviorSubject = new BehaviorSubject(null); private _contacts: BehaviorSubject = new BehaviorSubject(null); private _countries: BehaviorSubject = new BehaviorSubject(null); private _tags: BehaviorSubject = new BehaviorSubject(null); /** * Constructor */ constructor(private _httpClient: HttpClient) { } // ----------------------------------------------------------------------------------------------------- // @ Accessors // ----------------------------------------------------------------------------------------------------- /** * Getter for contact */ get contact$(): Observable { return this._contact.asObservable(); } /** * Getter for contacts */ get contacts$(): Observable { return this._contacts.asObservable(); } /** * Getter for countries */ get countries$(): Observable { return this._countries.asObservable(); } /** * Getter for tags */ get tags$(): Observable { return this._tags.asObservable(); } // ----------------------------------------------------------------------------------------------------- // @ Public methods // ----------------------------------------------------------------------------------------------------- /** * Get contacts */ getContacts(): Observable { return this._httpClient.get('api/apps/contacts/all').pipe( tap((contacts) => { this._contacts.next(contacts); }) ); } /** * Search contacts with given query * * @param query */ searchContacts(query: string): Observable { return this._httpClient.get('api/apps/contacts/search', { params: {query} }).pipe( tap((contacts) => { this._contacts.next(contacts); }) ); } /** * Get contact by id */ getContactById(id: string): Observable { return this._contacts.pipe( take(1), map((contacts) => { // Find the contact const contact = contacts.find(item => item.id === id) || null; // Update the contact this._contact.next(contact); // Return the contact return contact; }), switchMap((contact) => { if ( !contact ) { return throwError('Could not found contact with id of ' + id + '!'); } return of(contact); }) ); } /** * Create contact */ createContact(): Observable { return this.contacts$.pipe( take(1), switchMap((contacts) => this._httpClient.post('api/apps/contacts/contact', {}).pipe( map((newContact) => { // Update the contacts with the new contact this._contacts.next([newContact, ...contacts]); // Return the new contact return newContact; }) )) ); } /** * Update contact * * @param id * @param contact */ updateContact(id: string, contact: Contact): Observable { return this.contacts$.pipe( take(1), switchMap(contacts => this._httpClient.patch('api/apps/contacts/contact', { id, contact }).pipe( map((updatedContact) => { // Find the index of the updated contact const index = contacts.findIndex(item => item.id === id); // Update the contact contacts[index] = updatedContact; // Update the contacts this._contacts.next(contacts); // Return the updated contact return updatedContact; }), switchMap(updatedContact => this.contact$.pipe( take(1), filter(item => item && item.id === id), tap(() => { // Update the contact if it's selected this._contact.next(updatedContact); // Return the updated contact return updatedContact; }) )) )) ); } /** * Delete the contact * * @param id */ deleteContact(id: string): Observable { return this.contacts$.pipe( take(1), switchMap(contacts => this._httpClient.delete('api/apps/contacts/contact', {params: {id}}).pipe( map((isDeleted: boolean) => { // Find the index of the deleted contact const index = contacts.findIndex(item => item.id === id); // Delete the contact contacts.splice(index, 1); // Update the contacts this._contacts.next(contacts); // Return the deleted status return isDeleted; }) )) ); } /** * Get countries */ getCountries(): Observable { return this._httpClient.get('api/apps/contacts/countries').pipe( tap((countries) => { this._countries.next(countries); }) ); } /** * Get tags */ getTags(): Observable { return this._httpClient.get('api/apps/contacts/tags').pipe( tap((tags) => { this._tags.next(tags); }) ); } /** * Create tag * * @param tag */ createTag(tag: Tag): Observable { return this.tags$.pipe( take(1), switchMap(tags => this._httpClient.post('api/apps/contacts/tag', {tag}).pipe( map((newTag) => { // Update the tags with the new tag this._tags.next([...tags, newTag]); // Return new tag from observable return newTag; }) )) ); } /** * Update the tag * * @param id * @param tag */ updateTag(id: string, tag: Tag): Observable { return this.tags$.pipe( take(1), switchMap(tags => this._httpClient.patch('api/apps/contacts/tag', { id, tag }).pipe( map((updatedTag) => { // Find the index of the updated tag const index = tags.findIndex(item => item.id === id); // Update the tag tags[index] = updatedTag; // Update the tags this._tags.next(tags); // Return the updated tag return updatedTag; }) )) ); } /** * Delete the tag * * @param id */ deleteTag(id: string): Observable { return this.tags$.pipe( take(1), switchMap(tags => this._httpClient.delete('api/apps/contacts/tag', {params: {id}}).pipe( map((isDeleted: boolean) => { // Find the index of the deleted tag const index = tags.findIndex(item => item.id === id); // Delete the tag tags.splice(index, 1); // Update the tags this._tags.next(tags); // Return the deleted status return isDeleted; }), filter(isDeleted => isDeleted), switchMap(isDeleted => this.contacts$.pipe( take(1), map((contacts) => { // Iterate through the contacts contacts.forEach((contact) => { const tagIndex = contact.tags.findIndex(tag => tag === id); // If the contact has the tag, remove it if ( tagIndex > -1 ) { contact.tags.splice(tagIndex, 1); } }); // Return the deleted status return isDeleted; }) )) )) ); } /** * Update the avatar of the given contact * * @param id * @param avatar */ uploadAvatar(id: string, avatar: File): Observable { return this.contacts$.pipe( take(1), switchMap(contacts => this._httpClient.post('api/apps/contacts/avatar', { id, avatar }, { headers: { 'Content-Type': avatar.type } }).pipe( map((updatedContact) => { // Find the index of the updated contact const index = contacts.findIndex(item => item.id === id); // Update the contact contacts[index] = updatedContact; // Update the contacts this._contacts.next(contacts); // Return the updated contact return updatedContact; }), switchMap(updatedContact => this.contact$.pipe( take(1), filter(item => item && item.id === id), tap(() => { // Update the contact if it's selected this._contact.next(updatedContact); // Return the updated contact return updatedContact; }) )) )) ); } }