import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { AccountInfo } from '../states/accounts.state'; import { Attachment } from './models/mastodon.interfaces'; import { MastodonWrapperService } from './mastodon-wrapper.service'; import { NotificationService } from './notification.service'; @Injectable({ providedIn: 'root' }) export class MediaService { mediaSubject: BehaviorSubject = new BehaviorSubject([]); fileCache: { [url: string]: File } = {}; constructor( private readonly notificationService: NotificationService, private readonly mastodonService: MastodonWrapperService) { } uploadMedia(account: AccountInfo, files: File[]) { for (let file of files) { this.postMedia(account, file); } } private postMedia(account: AccountInfo, file: File) { const uniqueId = `${file.name}${file.size}${Math.random()}`; const wrapper = new MediaWrapper(uniqueId, file, null); let medias = this.mediaSubject.value; medias.push(wrapper); if (medias.length > 4) { medias.splice(0, 1); } this.mediaSubject.next(medias); this.mastodonService.uploadMediaAttachment(account, file, null) .then((attachment: Attachment) => { this.fileCache[attachment.url] = file; let currentMedias = this.mediaSubject.value; let currentMedia = currentMedias.filter(x => x.id === uniqueId)[0]; if (currentMedia) { currentMedia.attachment = attachment; this.mediaSubject.next(currentMedias); } }) .catch((err) => { this.remove(wrapper); this.notificationService.notifyHttpError(err, account); }); } loadMedia(attachments: Attachment[]) { const wrappers: MediaWrapper[] = []; for (const att of attachments) { const uniqueId = `${att.id}${Math.random()}`; const wrapper = new MediaWrapper(uniqueId, null, att); wrapper.description = att.description; wrapper.isEdited = true; wrappers.push(wrapper); } this.mediaSubject.next(wrappers); } update(account: AccountInfo, media: MediaWrapper): Promise { if (media.attachment.description === media.description) return; if (media.isEdited) { media.attachment.description = media.description; let medias = this.mediaSubject.value; let updatedMedia = medias.filter(x => x.id === media.id)[0]; updatedMedia.attachment.description = media.attachment.description; this.mediaSubject.next(medias); } else { return this.mastodonService.updateMediaAttachment(account, media.attachment.id, media.description) .then((att: Attachment) => { let medias = this.mediaSubject.value; let updatedMedia = medias.filter(x => x.id === media.id)[0]; updatedMedia.attachment.description = att.description; this.mediaSubject.next(medias); }) .catch((err) => { console.warn('failing update'); this.notificationService.notifyHttpError(err, account); }); } } async retrieveUpToDateMedia(account: AccountInfo): Promise { const allMedia = this.mediaSubject.value; let allPromises: Promise[] = []; for (const m of allMedia) { let t = this.update(account, m); allPromises.push(t); } await Promise.all(allPromises); return allMedia; } addExistingMedia(media: MediaWrapper) { if (!this.fileCache[media.attachment.url]) return; media.file = this.fileCache[media.attachment.url]; let medias = this.mediaSubject.value; medias.push(media); this.mediaSubject.next(medias); } remove(media: MediaWrapper) { let medias = this.mediaSubject.value; let filteredMedias = medias.filter(x => x.id !== media.id); this.mediaSubject.next(filteredMedias); } clearMedia() { this.mediaSubject.next([]); } migrateMedias(account: AccountInfo) { let medias = this.mediaSubject.value; medias.forEach(media => { if (!media.isEdited) { media.isMigrating = true; } }); this.mediaSubject.next(medias); for (let media of medias) { if (media.isEdited) continue; this.mastodonService.uploadMediaAttachment(account, media.file, media.description) .then((attachment: Attachment) => { this.fileCache[attachment.url] = media.file; let currentMedias = this.mediaSubject.value; let currentMedia = currentMedias.filter(x => x.id === media.id)[0]; if (currentMedia) { currentMedia.attachment = attachment; currentMedia.isMigrating = false; this.mediaSubject.next(currentMedias); } }) .catch((err) => { this.remove(media); this.notificationService.notifyHttpError(err, account); }); } } } export class MediaWrapper { constructor( public id: string, public file: File, attachment: Attachment) { this.attachment = attachment; } private _attachment: Attachment; public get attachment(): Attachment { return this._attachment; } public set attachment(value: Attachment) { if (value && value.meta && value.meta.audio_encode) { this.audioType = `audio/${value.meta.audio_encode}`; } else if (value && value.pleroma && value.pleroma.mime_type) { this.audioType = value.pleroma.mime_type; } this._attachment = value; } public description: string; public isMigrating: boolean; public audioType: string; public isEdited: boolean; }