[U] huddle.compo upload stories, get stories for correct standup

This commit is contained in:
Jan 2020-10-30 13:16:40 +01:00
parent 412a542e03
commit ccde75f72a
5 changed files with 101 additions and 92 deletions

View file

@ -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.

View file

@ -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;
}
}

View file

@ -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>

View file

@ -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));
}
}

View file

@ -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;
}
}