From ef07db581899eb649297c8f8ee94a0921a66db18 Mon Sep 17 00:00:00 2001 From: jgerstbe Date: Fri, 30 Oct 2020 14:42:44 +0100 Subject: [PATCH] [A] user.service, edit username in profile --- src/app/api/supabase/supa.service.ts | 37 ++++- src/app/api/supabase/user.service.spec.ts | 16 +++ src/app/api/supabase/user.service.ts | 159 ++++++++++++++++++++++ src/app/api/supabase/user.ts | 11 ++ src/app/app.module.ts | 2 + src/app/profile/profile.component.html | 18 ++- src/app/profile/profile.component.ts | 18 ++- 7 files changed, 249 insertions(+), 12 deletions(-) create mode 100644 src/app/api/supabase/user.service.spec.ts create mode 100644 src/app/api/supabase/user.service.ts create mode 100644 src/app/api/supabase/user.ts diff --git a/src/app/api/supabase/supa.service.ts b/src/app/api/supabase/supa.service.ts index 78a6a5f..ce52fdf 100644 --- a/src/app/api/supabase/supa.service.ts +++ b/src/app/api/supabase/supa.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; import { createClient, SupabaseAuthUser, SupabaseClient } from '@supabase/supabase-js' -import { BehaviorSubject } from 'rxjs'; +import { Subject } from 'rxjs'; import { environment } from '../../../environments/environment' +import { User } from './user'; @Injectable({ @@ -9,7 +10,9 @@ import { environment } from '../../../environments/environment' }) export class SupaService { client: SupabaseClient; - user: BehaviorSubject = new BehaviorSubject(null); + user: Subject = new Subject(); + supabaseUser: SupabaseAuthUser; + userProfile: User; constructor() { // Create a single supabase client for interacting with your database @@ -20,9 +23,39 @@ export class SupaService { async getUser() { const user = await this.client.auth.user(); console.log('user', user); + this.supabaseUser = user; + this.getUserProfile(); this.user.next(user); } + getUserProfile(user_id: string = this.supabaseUser.id) { + const subject: Subject = new Subject(); + if (!this.userProfile) { + this.client.from('user').select().match({id: user_id}) + .then(data => { + console.log('getUserProfile', data) + if (data.body.length === 0) { + // create default user profile + this.client.from('user').insert(new User(user_id, this.supabaseUser.email.split('@')[0])) + .then(data => { + console.log('created UserProfile', data.body[0]); + this.userProfile = data.body[0]; + subject.next(this.userProfile); + }) + .catch(error => console.error('Error creating UserProfile', error)) + } else { + console.log('loaded UserProfile', data.body[0]); + this.userProfile = data.body[0]; + subject.next(this.userProfile); + } + }) + .catch(error => console.error('getUserProfile', error)) + } else { + setTimeout(() =>subject.next(this.userProfile), 100); + } + return subject.asObservable(); + } + async login(email: string, password: string): Promise { try { const res = await this.client.auth.login( diff --git a/src/app/api/supabase/user.service.spec.ts b/src/app/api/supabase/user.service.spec.ts new file mode 100644 index 0000000..3f804c9 --- /dev/null +++ b/src/app/api/supabase/user.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UserService } from './user.service'; + +describe('UserService', () => { + let service: UserService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UserService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/api/supabase/user.service.ts b/src/app/api/supabase/user.service.ts new file mode 100644 index 0000000..1c6e287 --- /dev/null +++ b/src/app/api/supabase/user.service.ts @@ -0,0 +1,159 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; +import { map } from 'rxjs/internal/operators/map'; +import { SupaService } from './supa.service'; +import { User } from './user'; + +@Injectable({ + providedIn: 'root' +}) +export class UserService { + userMap = {}; + users: BehaviorSubject = new BehaviorSubject(Object.values(this.userMap)); + isListening: boolean = true; + + constructor( + private supa: SupaService, + ) { } + + /** + * Get Users from local store. + * Requests data if store is emtpy. + * @returns Observable + */ + getStories(): Observable { + if (Object.values(this.userMap).length === 0) { + this.subscribeToUsers(); + console.log('getUsers- REFRESH') + this.supa.client.from('user').select() + .then(data => { + this.updateStore(data.body); + }) + .catch(error => { + console.error(error); + }); + } + return this.users.asObservable(); + } + + /** + * Listen to realtime events from users db. + */ + subscribeToUsers() { + if (!this.isListening) { + this.isListening = true; + this.supa.client.from('user').on('*', payload => { + console.log('subscribeToStories - REALTIME EVENT', payload) + if ((payload.eventType === 'INSERT') || (payload.eventType === 'UPDATE')) { + this.userMap[payload.new.id] = payload.new; + } else { + delete this.userMap[payload.old.id]; + } + this.next(); + }).subscribe(); + } + } + + /** + * Update the local store with provided users + * @param users + */ + updateStore(users: User[]) { + users.forEach(e => { + // this.userMap.set(e.id, e); + this.userMap[e.id] = e; + }); + console.log('update users', this.userMap) + this.next(); + } + + /** + * Emits local store. + */ + next = () => { + console.log('next', Object.values(this.userMap)) + this.users.next(Object.values(this.userMap)) + }; + + /** + * Retrieve users from local store. + * @param id + */ + getOne(id: number) { + if (this.userMap[id]) { + return of(this.userMap[id]); + } else { + const subject: Subject = new Subject(); + this.supa.client.from('user').select('id, name, description') + .filter('id', 'eq', id) + .then(data => { + this.updateStore([data.body[0]]); + subject.next(data.body[0]); + subject.complete(); + }) + .catch(error => { + subject.error(error); + subject.complete(); + }); + return subject.asObservable(); + } + } + + /** + * Update a users data. + * @param users + */ + updateOne(user: User): Observable { + const subject: Subject = new Subject(); + this.supa.client.from('user').update(user) + .match({ id: user.id }) + .then(data => { + subject.next(data.body[0]); + subject.complete(); + }) + .catch(error => { + subject.error(error); + subject.complete(); + }); + return subject.asObservable(); + } + + + /** + * Removes one user from db. + * @param user + */ + deleteOne(user: User): Observable { + const subject: Subject = new Subject(); + this.supa.client.from('user').delete() + .match({ id: user.id }) + .then(data => { + subject.next(user); + subject.complete(); + }) + .catch(error => { + subject.error(error); + subject.complete(); + }); + return subject.asObservable(); + } + + /** + * Creates a user on the db. + * @param users + */ + addOne(user: User): Observable { + const subject: Subject = new Subject(); + this.supa.client.from('user').insert(user) + .then(data => { + subject.next(data.body[0]); + subject.complete(); + }) + .catch(error => { + subject.error(error); + subject.complete(); + }); + return subject.asObservable(); + } + +} diff --git a/src/app/api/supabase/user.ts b/src/app/api/supabase/user.ts new file mode 100644 index 0000000..243d5ef --- /dev/null +++ b/src/app/api/supabase/user.ts @@ -0,0 +1,11 @@ +export class User { + id: string; + status: 'OFFLINE' |'ONLINE'; + username: string; + + constructor(id: string, username: string, status: 'OFFLINE' | 'ONLINE' = 'ONLINE') { + this.id = id; + this.username = username; + this.status = status; + } +} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3164f90..3d074bd 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,6 +8,7 @@ import { FormsModule } from '@angular/forms'; import { SupaService } from './api/supabase/supa.service'; import { StandupService } from './api/supabase/standup.service'; import { StoryService } from './api/supabase/story.service'; +import { UserService } from './api/supabase/user.service'; import { AuthGuard } from './authguard.service'; import { AppComponent } from "./app.component"; @@ -42,6 +43,7 @@ import { DashboardComponent } from './dashboard/dashboard.component'; SupaService, StandupService, StoryService, + UserService, AuthGuard, ], bootstrap: [ diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index 797181d..69bab14 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -1,6 +1,12 @@ -Profile -
-    
-        {{ user | json }}
-    
-
\ No newline at end of file +
+
+ Profile +
+
+ + +
+ +
+
+
\ No newline at end of file diff --git a/src/app/profile/profile.component.ts b/src/app/profile/profile.component.ts index 939e62e..9cb3229 100644 --- a/src/app/profile/profile.component.ts +++ b/src/app/profile/profile.component.ts @@ -1,6 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { SupabaseAuthUser } from '@supabase/supabase-js'; import { SupaService } from '../api/supabase/supa.service'; +import { User } from '../api/supabase/user'; +import { UserService } from '../api/supabase/user.service'; @Component({ selector: 'app-profile', @@ -8,17 +10,25 @@ import { SupaService } from '../api/supabase/supa.service'; styleUrls: ['./profile.component.scss'] }) export class ProfileComponent implements OnInit { - user: SupabaseAuthUser; + user: User; constructor( - public supa: SupaService, + private supaService: SupaService, + private userService: UserService, ) { } ngOnInit(): void { - this.supa.client.auth.user().then(user => { - console.log('user', user) + this.supaService.getUserProfile().subscribe((user:User) => { + console.log('user', user); this.user = user; }); } + updateUser() { + this.userService.updateOne(this.user).subscribe( + data => console.log('Success', data), + error => console.error(error) + ) + } + }