[U] huddle.compo upload stories, get stories for correct standup
This commit is contained in:
parent
412a542e03
commit
ccde75f72a
5 changed files with 101 additions and 92 deletions
|
|
@ -1,5 +1,6 @@
|
|||
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';
|
||||
|
||||
|
|
@ -27,25 +28,20 @@ export class StoryService {
|
|||
if (!this.subscribedStandUpIds.includes(standup_id)) {
|
||||
this.subscribeToStories(standup_id);
|
||||
console.log('getStories - REFRESH', standup_id)
|
||||
const subject: Subject<Story[]> = new Subject();
|
||||
this.supa.client.from<Story>('story').select()
|
||||
.filter(<never>'standup_id', 'eq', standup_id)
|
||||
.then(data => {
|
||||
this.updateStore(data.body);
|
||||
subject.next(data.body);
|
||||
subject.complete();
|
||||
})
|
||||
.catch(error => {
|
||||
subject.error(error);
|
||||
subject.complete();
|
||||
console.error(error);
|
||||
});
|
||||
return subject.asObservable();
|
||||
} else {
|
||||
} else {
|
||||
console.log('getStories - LOCAL', standup_id)
|
||||
const stories = Object.values<Story>(this.storyMap).filter(e => e.standup_id === standup_id)
|
||||
console.log('filteredStories', stories)
|
||||
return of(stories);
|
||||
}
|
||||
return this.stories.asObservable().pipe(
|
||||
map(stories => stories.filter(e => e.standup_id === standup_id))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,13 +85,17 @@ export class StoryService {
|
|||
// this.storyMap.set(e.id, e);
|
||||
this.storyMap[e.id] = e;
|
||||
});
|
||||
console.log('update stories', this.storyMap)
|
||||
this.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits local store.
|
||||
*/
|
||||
next = () => this.stories.next(Object.values(this.storyMap));
|
||||
next = () => {
|
||||
console.log('next', Object.values(this.storyMap))
|
||||
this.stories.next(Object.values(this.storyMap))
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve story from local store.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
export class Story {
|
||||
id: number;
|
||||
id: string;
|
||||
standup_id: number;
|
||||
user_id: string
|
||||
base64: string;
|
||||
src: string;
|
||||
created_at: string | Date;
|
||||
|
||||
constructor(user_id: string, standup_id: number, base64: string) {
|
||||
constructor(user_id: string, standup_id: number, src: string) {
|
||||
this.user_id = user_id;
|
||||
this.standup_id = standup_id;
|
||||
this.base64 = base64;
|
||||
this.src = src;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,12 +12,12 @@
|
|||
<div class="col-12">
|
||||
<ah-recorder (recordingEnded)="uploadStory($event)"></ah-recorder>
|
||||
</div>
|
||||
<div *ngFor="let user of users" class="col p-3">
|
||||
<div class="text-center" (click)="playStory(user)">
|
||||
<div class="storyImage" style="background-image: url({{user.image}})"
|
||||
[ngClass]="{'hasStory': user.story_link && user.submit_time}"></div>
|
||||
<p class="mt-2 mb-0"><strong>{{user.name}}</strong></p>
|
||||
<p><small>{{user.submit_time | date :'dd.MM. HH:mm' }}</small></p>
|
||||
<div *ngFor="let story of stories" class="col p-3">
|
||||
<div class="text-center" (click)="playStory(story)">
|
||||
<div class="storyImage" style="background-image: url('https://placeimg.com/640/480/people')"
|
||||
[ngClass]="{'hasStory': story.id && story.src}"></div>
|
||||
<p class="mt-2 mb-0"><strong>{{story.user_id}}</strong></p>
|
||||
<p><small>{{story.created_at | date :'dd.MM. HH:mm' }}</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -46,9 +46,9 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class="text-center m-0">
|
||||
<strong>{{selectedUser.name}} -
|
||||
<small>{{selectedUser.submit_time | date :'dd.MM. HH:mm' }}</small></strong>
|
||||
<strong>{{selectedStory.user_id}} - <small>{{selectedStory.created_at | date :'dd.MM. HH:mm' }}</small></strong>
|
||||
</p>
|
||||
<video [src]="selectedUser.story_link" (ended)="nextUser(modal)" autoplay></video>
|
||||
<video [src]="base64ToSafeURL(selectedStory.src)" (ended)="nextUser(modal)" autoplay>
|
||||
</video>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
|
@ -1,46 +1,27 @@
|
|||
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { SupabaseAuthUser } from '@supabase/supabase-js';
|
||||
import { Subject } from 'rxjs';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
import { StandUp } from '../api/supabase/standup';
|
||||
import { StandupService } from '../api/supabase/standup.service';
|
||||
import { Story } from '../api/supabase/story';
|
||||
import { StoryService } from '../api/supabase/story.service';
|
||||
import { SupaService } from '../api/supabase/supa.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-huddle',
|
||||
templateUrl: './huddle.component.html',
|
||||
styleUrls: ['./huddle.component.scss']
|
||||
})
|
||||
export class HuddleComponent implements OnInit {
|
||||
export class HuddleComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("content") content;
|
||||
@ViewChild("activeStory") video;
|
||||
users = [
|
||||
{
|
||||
name: "Jan",
|
||||
image: "https://www.supercardating.com/doc/image.rhtm/profile-pic2.jpg",
|
||||
submit_time: 1601655386668,
|
||||
story_link: "https://erjb.s3.nl-ams.scw.cloud/ttk_beagle.mp4"
|
||||
},
|
||||
{
|
||||
name: "Bob",
|
||||
image:
|
||||
"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse1.mm.bing.net%2Fth%3Fid%3DOIP.4oYqJqInuQd2TAlPPdggLgHaHa%26pid%3DApi&f=1",
|
||||
submit_time: null,
|
||||
story_link: "https://erjb.s3.nl-ams.scw.cloud/ttk_golden.mp4"
|
||||
},
|
||||
{
|
||||
name: "Angela",
|
||||
image:
|
||||
"https://writestylesonline.com/wp-content/uploads/2019/01/What-To-Wear-For-Your-Professional-Profile-Picture-or-Headshot.jpg",
|
||||
submit_time: 1601655386668,
|
||||
story_link: "https://erjb.s3.nl-ams.scw.cloud/ttk_frenchie.mp4"
|
||||
}
|
||||
];
|
||||
selectedUser;
|
||||
standup: StandUp;
|
||||
selectedStory: Story;
|
||||
stories: Story[] = [];
|
||||
unsubscribe: Subject<boolean> = new Subject();
|
||||
|
||||
constructor(
|
||||
private modalService: NgbModal,
|
||||
|
|
@ -48,17 +29,23 @@ export class HuddleComponent implements OnInit {
|
|||
private storyService: StoryService,
|
||||
private supaService: SupaService,
|
||||
private route: ActivatedRoute,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.params.subscribe(
|
||||
params => {
|
||||
console.warn('PARAMS', params)
|
||||
this.reset();
|
||||
if (params.id) {
|
||||
this.standupService.getOne(params.id).subscribe(
|
||||
data => {
|
||||
console.log(data);
|
||||
this.standup = data;
|
||||
this.loadStories(data.id).subscribe(stories => this.stories = stories);
|
||||
this.loadStories(data.id).subscribe(stories => {
|
||||
console.log('GOT STORIES', stories);
|
||||
this.stories = stories;
|
||||
});
|
||||
},
|
||||
error => {
|
||||
console.error(error);
|
||||
|
|
@ -67,62 +54,70 @@ export class HuddleComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
)
|
||||
this.unsubscribe.subscribe(data => console.warn('UNSUBSCRIBE'))
|
||||
}
|
||||
|
||||
playStory(user) {
|
||||
if (!user.submit_time || !user.story_link) {
|
||||
ngOnDestroy() {
|
||||
console.error('HUDDLE COMP DESTROYED')
|
||||
this.unsubscribe.next(true);
|
||||
this.unsubscribe.complete();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.stories = [];
|
||||
delete this.standup;
|
||||
delete this.selectedStory;
|
||||
this.unsubscribe.next(true);
|
||||
}
|
||||
|
||||
playStory(story) {
|
||||
if (!story.id || !story.src) {
|
||||
return;
|
||||
}
|
||||
this.modalService.open(this.content, { centered: true });
|
||||
this.selectedUser = user;
|
||||
this.selectedStory = story;
|
||||
}
|
||||
|
||||
prevUser(modal: NgbModalRef) {
|
||||
let index: number = this.users.findIndex(
|
||||
(u) => u.name === this.selectedUser.name
|
||||
let index: number = this.stories.findIndex(
|
||||
(u) => u.id === this.selectedStory.id
|
||||
);
|
||||
if (index < 1) {
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
while (
|
||||
!this.users[index - 1].story_link ||
|
||||
!this.users[index - 1].submit_time
|
||||
) {
|
||||
while (!this.stories[index - 1].id) {
|
||||
index--;
|
||||
if (index === 0) {
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.selectedUser = this.users[index - 1];
|
||||
this.selectedStory = this.stories[index - 1];
|
||||
}
|
||||
|
||||
nextUser(modal: NgbModalRef) {
|
||||
let index: number = this.users.findIndex(
|
||||
(u) => u.name === this.selectedUser.name
|
||||
let index: number = this.stories.findIndex(
|
||||
(u) => u.id === this.selectedStory.id
|
||||
);
|
||||
if (index === -1 || index === this.users.length - 1) {
|
||||
if (index === -1 || index === this.stories.length - 1) {
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
while (
|
||||
!this.users[index + 1].story_link ||
|
||||
!this.users[index + 1].submit_time
|
||||
) {
|
||||
if (index === -1 || index === this.users.length - 1) {
|
||||
while (!this.stories[index + 1].id) {
|
||||
if (index === -1 || index === this.stories.length - 1) {
|
||||
modal.close();
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
this.selectedUser = this.users[index + 1];
|
||||
this.selectedStory = this.stories[index + 1];
|
||||
}
|
||||
|
||||
updateStandUp(name:string, desc:string) {
|
||||
this.standup.name = name;
|
||||
this.standup.description = desc;
|
||||
this.standupService.updateOne(this.standup).subscribe(
|
||||
this.standupService.updateOne(this.standup).pipe(take(1)).subscribe(
|
||||
data => {
|
||||
console.log('Success', data);
|
||||
},
|
||||
|
|
@ -133,7 +128,7 @@ export class HuddleComponent implements OnInit {
|
|||
}
|
||||
|
||||
deleteStandUp() {
|
||||
this.standupService.deleteOne(this.standup).subscribe(
|
||||
this.standupService.deleteOne(this.standup).pipe(take(1)).subscribe(
|
||||
data => {
|
||||
console.log('Success', data);
|
||||
},
|
||||
|
|
@ -143,12 +138,11 @@ export class HuddleComponent implements OnInit {
|
|||
);
|
||||
}
|
||||
|
||||
async uploadStory(base64: string) {
|
||||
async uploadStory(src: string) {
|
||||
console.log('uploadStory', event);
|
||||
const user: SupabaseAuthUser = await this.supaService.client.auth.user();
|
||||
// TODO ID muss in DB noch autoincremented werden.
|
||||
const story = new Story(user.id, this.standup.id, base64);
|
||||
this.storyService.addOne(story).subscribe(
|
||||
const story = new Story(user.id, this.standup.id, src);
|
||||
this.storyService.addOne(story).pipe(take(1)).subscribe(
|
||||
data => {
|
||||
console.log('Success', data);
|
||||
},
|
||||
|
|
@ -159,7 +153,28 @@ export class HuddleComponent implements OnInit {
|
|||
}
|
||||
|
||||
loadStories(id: number) {
|
||||
return this.storyService.getStories(id);
|
||||
return this.storyService.getStories(id).pipe(takeUntil(this.unsubscribe.asObservable()));
|
||||
}
|
||||
|
||||
base64ToSafeURL(b64Data: string, contentType: string ='video/webm', sliceSize: number=512): SafeUrl {
|
||||
// https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript#16245768
|
||||
const byteCharacters = atob(b64Data.split('base64,')[1]);
|
||||
const byteArrays = [];
|
||||
|
||||
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
||||
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
||||
|
||||
const byteNumbers = new Array(slice.length);
|
||||
for (let i = 0; i < slice.length; i++) {
|
||||
byteNumbers[i] = slice.charCodeAt(i);
|
||||
}
|
||||
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
byteArrays.push(byteArray);
|
||||
}
|
||||
|
||||
const blob = new Blob(byteArrays, {type: contentType});
|
||||
return this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,13 @@ export class RecorderComponent implements OnInit {
|
|||
}
|
||||
|
||||
startRecording() {
|
||||
if (!this.stream) {
|
||||
return;
|
||||
}
|
||||
this.isRecording = true;
|
||||
this.recorder = new rrtc.RecordRTCPromisesHandler(this.stream, {
|
||||
type: 'video',
|
||||
mimeType: 'video/webm;codecs=vp8',
|
||||
});
|
||||
this.recorder.startRecording();
|
||||
}
|
||||
|
|
@ -50,21 +54,11 @@ export class RecorderComponent implements OnInit {
|
|||
const that = this;
|
||||
this.isRecording = false;
|
||||
await this.recorder.stopRecording();
|
||||
let blob = await this.recorder.getBlob();
|
||||
// read as b64
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
reader.onloadend = function() {
|
||||
const base64data:string = String(reader.result);
|
||||
console.log(base64data);
|
||||
that.recordingEnded.emit(base64data);
|
||||
that.modal.close();
|
||||
}
|
||||
const handleError = (error) => {
|
||||
console.error(error);
|
||||
}
|
||||
reader.onerror = handleError;
|
||||
reader.onabort = handleError;
|
||||
let blob = await this.recorder.getDataURL();
|
||||
that.recordingEnded.emit(blob);
|
||||
that.modal.close();
|
||||
// rrtc.invokeSaveAsDialog(blob, 'Recorded-Video.webm');
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue