Compare commits

..

No commits in common. "57306fe7bf1d5af25ded72eb7998226b958976ba" and "5397a34353b8462771aea2809c1e85a9e026ff5b" have entirely different histories.

13 changed files with 28 additions and 466 deletions

View file

@ -1,160 +1,9 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
import { Channel } from './channel';
import { SupaService } from './supa.service';
@Injectable({
providedIn: 'root'
})
export class ChannelService {
channelMap = {};
channels: BehaviorSubject<any> = new BehaviorSubject([]);
isListening: boolean = false;
constructor(
private supa: SupaService,
) { }
/**
* Get Channels from local store.
* Requests data if store is emtpy.
* @returns Observable<Channel[]>
*/
getChannels(): Observable<Channel[]> {
if (Object.values(this.channelMap).length === 0) {
console.log('getChannels - REFRESH')
return this.refreshChannels();
} else {
console.log('getChannels - LOCAL')
return this.channels.asObservable();
}
}
/**
* Listen to realtime events from channel db.
*/
subscribeToChannels() {
if (!this.isListening) {
this.isListening = true;
this.supa.client.from<Channel>('channel').on('*', payload => {
console.log('subscribeToChannels - REALTIME EVENT', payload);
if ((payload.eventType === 'INSERT') || (payload.eventType === 'UPDATE')) {
this.channelMap[payload.new.id] = payload.new;
} else {
delete this.channelMap[payload.old.id];
}
this.next();
}).subscribe();
}
}
/**
* Requests up to date data from API.
* Returns the local copy.
* @returns Observable<Channel>
*/
refreshChannels(): Observable<Channel[]> {
this.subscribeToChannels();
this.supa.client.from<Channel>('channel').select()
.then(channels => this.updateStore(channels.body))
.catch(error => console.log('Error: ', error));
return this.channels.asObservable();
}
/**
* Update the local store with provided channels
* @param channels
*/
updateStore(channels: Channel[]) {
channels.forEach(e => {
this.channelMap[e.id] = e;
});
this.next();
}
/**
* Emits local store.
*/
next = () => this.channels.next(Object.values(this.channelMap));
/**
* Retrieve channel from local store.
* @param id
*/
getOne(id: number) {
if (this.channelMap[id]) {
return of(this.channelMap[id]);
} else {
const subject: Subject<Channel> = new Subject();
this.supa.client.from<Channel>('channel').select('id, name, description')
.filter(<never>'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();
}
}
/**
*
* @param channel
*/
updateOne(channel: Channel): Observable<Channel> {
const subject: Subject<Channel> = new Subject();
this.supa.client.from<Channel>('channel').update(channel)
.match({ id: channel.id })
.then(data => {
subject.next(data.body[0]);
subject.complete();
})
.catch(error => {
subject.error(error);
subject.complete();
});
return subject.asObservable();
}
/**
* Removes one channel from db.
* @param channel
*/
deleteOne(channel: Channel): Observable<Channel> {
const subject: Subject<Channel> = new Subject();
this.supa.client.from<Channel>('channel').delete()
.match({ id: channel.id })
.then(data => {
subject.next(channel);
subject.complete();
})
.catch(error => {
subject.error(error);
subject.complete();
});
return subject.asObservable();
}
/**
* Creates a channel on the db.
* @param channel
*/
addOne(channel: Channel): Observable<Channel> {
const subject: Subject<Channel> = new Subject();
this.supa.client.from<Channel>('channel').insert(channel)
.then(data => {
subject.next(data.body[0]);
subject.complete();
})
.catch(error => {
subject.error(error);
subject.complete();
});
return subject.asObservable();
}
constructor() { }
}

View file

@ -1,13 +0,0 @@
export class Channel {
id: number;
name: string;
description: string;
inserted_at: string | Date;
created_by: string;
constructor(created_by: string, name: string, description?: string) {
this.name = name;
this.description = description;
this.created_by = created_by;
}
}

View file

@ -1,3 +0,0 @@
export class Message {
}

View file

@ -7,6 +7,7 @@ import { SupaService } from './supa.service';
providedIn: 'root'
})
export class StandupService {
// standupMap: Map<number, StandUp> = new Map();
standupMap = {};
standups: BehaviorSubject<any> = new BehaviorSubject([]);
isListening: boolean = false;
@ -67,6 +68,7 @@ export class StandupService {
*/
updateStore(standups: StandUp[]) {
standups.forEach(e => {
// this.standupMap.set(e.id, e);
this.standupMap[e.id] = e;
});
this.next();

View file

@ -1,106 +1 @@
<ng-container *ngIf="channel">
<div class="row headerRow">
<div class="col-7 col-sm-10">
<strong>#</strong><strong #name contenteditable="true" (click)="dataUpdated=true">{{ channel.name }}</strong>
</div>
<div class="col-5 col-sm-2">
<div class="text-right">
<button class="btn btn-sm btn-primary mb-1" [disabled]="!dataUpdated" (click)="updateChannel(name.innerText, desc.innerText)">Save</button>
<button class="btn btn-sm btn-danger ml-1 mb-1" (click)="deleteChannel()">Delete</button>
</div>
</div>
<div class="col-12">
<span #desc contenteditable="true" (click)="dataUpdated=true">{{ channel.description }}</span>
</div>
</div>
<div class="row messageRow">
<div class="col-12 p-0">
<ul class="list-group list-group-flush">
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Morbi leo risus</li>
<li class="list-group-item">Porta ac consectetur ac</li>
<li class="list-group-item">Vestibulum at eros</li>
</ul>
</div>
</div>
<!-- send msg -->
<div class="row inputRow">
<div class="card w-100 m-3 d-flex flex-row">
<input [(ngModel)]="messageInput" type="text" class="input flex-grow-1">
<div class="optionRow">
<button class="btn btn-sm btn-primary btnSend ml-1">Send</button>
</div>
</div>
</div>
</ng-container>
<p>channel works!</p>

View file

@ -1,35 +0,0 @@
.headerRow {
margin-top: -5px;
padding-bottom: 10px;
border-bottom: 1px dashed #343a40;
}
.messageRow {
margin-bottom: 60px;
max-height: calc(100vh - 200px);
overflow: hidden scroll;
}
.inputRow {
position: fixed;
bottom: 0;
// width: calc(100vw - 300px);
.input {
border: 0;
// width: 100%;
padding-left: 5px;
padding-right: 5px;
}
.optionRow {
// background-color: lightgray;
// width: 100%;
height: 40px;
padding: 5px;
.btnSend {
float: right;
background-color: lightseagreen;
border-color: lightseagreen;
&:hover {
border-color: seagreen;
}
}
}
}

View file

@ -1,15 +1,4 @@
import { Component, OnInit } from "@angular/core";
import { DomSanitizer } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
import { Channel } from '../api/supabase/channel';
import { ChannelService } from '../api/supabase/channel.service';
import { Message } from '../api/supabase/message';
import { SupaService } from '../api/supabase/supa.service';
import { User } from '../api/supabase/user';
import { UserService } from '../api/supabase/user.service';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-channel',
@ -17,95 +6,10 @@ import { UserService } from '../api/supabase/user.service';
styleUrls: ['./channel.component.scss']
})
export class ChannelComponent implements OnInit {
channel: Channel;
member: User[] = [];
messages: Message[] = [];
$destroy: Subject<boolean> = new Subject();
dataUpdated: boolean = false;
messageInput: string = '';
constructor(
private channelService: ChannelService,
private modalService: NgbModal,
private supaService: SupaService,
private route: ActivatedRoute,
private sanitizer: DomSanitizer,
private userService: UserService,
private router: Router,
) { }
constructor() { }
ngOnInit() {
this.route.params.subscribe(
params => {
this.reset();
if (params.id) {
this.channelService.getOne(params.id).subscribe(
data => {
console.log('got channel', data);
this.channel = data;
// this.loadMessages(data.id).subscribe(messages => {
// this.messages = messages;
// });
},
error => {
console.error(error);
this.router.navigateByUrl('/dashboard');
}
)
}
}
)
}
ngOnDestroy() {
this.$destroy.next(true);
this.$destroy.complete();
}
reset() {
delete this.channel;
this.member = [];
this.messages = [];
this.messageInput = '';
this.$destroy.next(true);
}
loadChannel(id: number) {
return this.channelService.getOne(id).pipe(takeUntil(this.$destroy.asObservable()));
}
loadMessages(channel_id: number) {
}
loadUser(user_id: string) {
}
updateChannel(name:string, desc:string) {
this.channel.name = name;
this.channel.description = desc;
this.channelService.updateOne(this.channel).pipe(take(1)).subscribe(
data => {
console.log('Success', data);
},
error => {
console.error('Failed', error);
}
);
}
deleteChannel() {
const check = confirm('Do you want to delete this channel?');
if (check == true) {
this.channelService.deleteOne(this.channel).subscribe(
data => {
console.log('Deleted channel', data);
this.router.navigateByUrl('/dashboard');
},
error => console.error(error)
);
}
ngOnInit(): void {
}
}

View file

@ -5,8 +5,8 @@
</div>
<div class="col-12 col-sm-3 order-0 oder-sm-1">
<div class="float-right">
<button class="btn btn-sm btn-primary ml-1 mr-1" [disabled]="!dataUpdated" (click)="updateStandUp(name.innerText, desc.innerText)">Save</button>
<button class="btn btn-sm btn-danger ml-1 mr-1" (click)="deleteStandUp()">Delete</button>
<button class="btn btn-primary ml-1 mr-1" [disabled]="!dataUpdated" (click)="updateStandUp(name.innerText, desc.innerText)">Save</button>
<button class="btn btn-danger ml-1 mr-1" (click)="deleteStandUp()">Delete</button>
</div>
</div>
<div class="col-12 order-2">

View file

@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap";
import { SupabaseAuthUser } from '@supabase/supabase-js';
import { Subject } from 'rxjs';
@ -32,7 +32,6 @@ export class HuddleComponent implements OnInit, OnDestroy {
private route: ActivatedRoute,
private sanitizer: DomSanitizer,
private userService: UserService,
private router: Router,
) {}
ngOnInit() {
@ -50,7 +49,6 @@ export class HuddleComponent implements OnInit, OnDestroy {
},
error => {
console.error(error);
this.router.navigateByUrl('/dashboard');
}
)
}
@ -124,23 +122,19 @@ export class HuddleComponent implements OnInit, OnDestroy {
error => {
console.error('Failed', error);
}
);
)
}
deleteStandUp() {
const check = confirm('Do you want to delete this standup?');
if (check == true) {
this.standupService.deleteOne(this.standup).pipe(take(1)).subscribe(
data => {
console.log('Success', data);
this.router.navigateByUrl('/dashboard')
},
error => {
console.error('Failed', error);
}
);
}
}
async uploadStory(src: string) {
console.log('uploadStory', event);

View file

@ -1,18 +1,18 @@
<form>
<div class="row mt-3 justify-content-md-center">
<div class="form-group col-12 col-md-4">
<div class="form-group col-12 col-sm-4">
<label for="email">Email</label>
<input #email class="form-control" type="email">
</div>
</div>
<div class="row justify-content-md-center">
<div class="form-group col-12 col-md-4">
<div class="form-group col-12 col-sm-4">
<label for="password">Password</label>
<input #password class="form-control" type="password">
</div>
</div>
<div class="row justify-content-md-center">
<div class="col-12 col-md-4">
<div class="col-12 col-sm-4">
<button class="btn btn-primary text-uppercase" type="submit" (click)="login(email.value, password.value)">
Log in
</button> or

View file

@ -1,13 +1,14 @@
<div class="list bg-dark text-white p-3">
<p><strong>Standup</strong> <strong class="newBtn" (click)="createStandUp()" style="vertical-align: text-bottom;">+</strong></p>
<p><strong>Standup</strong> <strong class="newBtn" (click)="createStandUp()">+</strong></p>
<ul class="list-unstyled">
<li *ngFor="let s of standups" [title]="s.description" [routerLink]="'/huddle/'+s.id">{{s.name}}</li>
<li *ngIf="standups && (standups.length === 0)"><small>There are no standups yet.</small></li>
</ul>
<p><strong>Channel</strong> <strong class="newBtn" (click)="createChannel()" style="vertical-align: text-bottom;">+</strong></p>
<p><strong>Channel</strong> <strong class="newBtn">+</strong></p>
<ul class="list-unstyled">
<li *ngFor="let c of channels" [title]="c.description" [routerLink]="'/channel/'+c.id"><span class="channelHash">#</span>{{c.name}}</li>
<li *ngIf="channels && (channels.length === 0)"><small>There are no channels yet.</small></li>
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
<p><strong>Settings</strong></p>
<ul class="list-unstyled">

View file

@ -19,11 +19,4 @@
color: lightseagreen;
}
}
.channelHash {
padding: 3px 6.5px;
background: rgba(255,255,255,0.1);
border-radius: 3px;
margin-right: 5px;
vertical-align: middle;
}
}

View file

@ -3,10 +3,6 @@ import { Observable, throwError } from 'rxjs';
import { StandUp } from '../api/supabase/standup';
import { StandupService } from '../api/supabase/standup.service';
import { environment } from '../../environments/environment';
import { Channel } from '../api/supabase/channel';
import { ChannelService } from '../api/supabase/channel.service';
import { SupaService } from '../api/supabase/supa.service';
import { User } from '../api/supabase/user';
@Component({
selector: 'app-sidebar',
@ -15,13 +11,10 @@ import { User } from '../api/supabase/user';
})
export class SidebarComponent implements OnInit {
standups: StandUp[] = [];
channels: Channel[] = [];
environment = environment;
constructor(
private supaService: SupaService,
private standupSevice: StandupService,
private channelService: ChannelService,
) { }
ngOnInit() {
@ -29,20 +22,12 @@ export class SidebarComponent implements OnInit {
console.log('ChannelListComponent - StandUps', standups);
this.standups = standups;
});
this.loadChannels().subscribe(channels => {
console.log('ChannelListComponent - Channels', channels);
this.channels = channels;
});
}
loadStandUps(): Observable<StandUp[]> {
return this.standupSevice.getStandUps();
}
loadChannels(): Observable<Channel[]> {
return this.channelService.getChannels();
}
createStandUp() {
this.standupSevice.addOne(new StandUp('test', 'test')).subscribe(
data => console.log(data),
@ -50,14 +35,4 @@ export class SidebarComponent implements OnInit {
)
}
createChannel() {
if (!this.supaService.userProfile.id) {
return;
}
this.channelService.addOne(new Channel(this.supaService.userProfile.id, 'test', 'test')).subscribe(
data => console.log(data),
error => console.error(error)
)
}
}