From ab38f8fc054f019de0480495d2e384134dfd19ec Mon Sep 17 00:00:00 2001 From: Nicolas Constant Date: Sun, 10 Mar 2019 14:36:22 -0400 Subject: [PATCH] added media edition #56 --- src/app/app.component.ts | 2 +- .../create-status/media/media.component.html | 6 ++- .../create-status/media/media.component.scss | 2 +- .../create-status/media/media.component.ts | 17 ++++-- src/app/services/mastodon.service.ts | 52 +++++++++++------- src/app/services/media.service.ts | 53 +++++++++++++------ src/app/services/models/api.settings.ts | 1 + .../services/models/mastodon.interfaces.ts | 2 + 8 files changed, 91 insertions(+), 44 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3b2da8be..68bce313 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -110,7 +110,7 @@ export class AppComponent implements OnInit, OnDestroy { let files = event.dataTransfer.files; const selectedAccount = this.toolsService.getSelectedAccounts()[0]; - this.mediaService.uploadMedia(files, selectedAccount); + this.mediaService.uploadMedia(selectedAccount, files); return false; } } diff --git a/src/app/components/create-status/media/media.component.html b/src/app/components/create-status/media/media.component.html index 840a534f..70773f48 100644 --- a/src/app/components/create-status/media/media.component.html +++ b/src/app/components/create-status/media/media.component.html @@ -2,12 +2,14 @@
-
+
- +
diff --git a/src/app/components/create-status/media/media.component.scss b/src/app/components/create-status/media/media.component.scss index 9e0440bd..6d4c9dc1 100644 --- a/src/app/components/create-status/media/media.component.scss +++ b/src/app/components/create-status/media/media.component.scss @@ -41,7 +41,7 @@ height: 10px; position: absolute; top:5px; - right:7px; + right:8px; color: white; } diff --git a/src/app/components/create-status/media/media.component.ts b/src/app/components/create-status/media/media.component.ts index 3f2473d4..d7886564 100644 --- a/src/app/components/create-status/media/media.component.ts +++ b/src/app/components/create-status/media/media.component.ts @@ -1,8 +1,9 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { faTimes } from "@fortawesome/free-solid-svg-icons"; +import { Subscription } from 'rxjs'; import { MediaService, MediaWrapper } from '../../../services/media.service'; -import { Subscription } from 'rxjs'; +import { ToolsService } from '../../../services/tools.service'; @Component({ selector: 'app-media', @@ -14,7 +15,9 @@ export class MediaComponent implements OnInit, OnDestroy { media: MediaWrapper[] = []; private mediaSub: Subscription; - constructor(private readonly mediaService: MediaService) { } + constructor( + private readonly toolsService: ToolsService, + private readonly mediaService: MediaService) { } ngOnInit() { this.mediaSub = this.mediaService.mediaSubject.subscribe((media: MediaWrapper[]) => { @@ -26,10 +29,14 @@ export class MediaComponent implements OnInit, OnDestroy { this.mediaSub.unsubscribe(); } - removeMedia(media: MediaWrapper): boolean{ - console.warn('delete'); - console.warn(media); + removeMedia(media: MediaWrapper): boolean { + this.mediaService.remove(media); + return false; + } + updateMedia(media: MediaWrapper): boolean { + const account = this.toolsService.getSelectedAccounts()[0]; + this.mediaService.update(account, media); return false; } } diff --git a/src/app/services/mastodon.service.ts b/src/app/services/mastodon.service.ts index 8d131d93..f3f92881 100644 --- a/src/app/services/mastodon.service.ts +++ b/src/app/services/mastodon.service.ts @@ -2,18 +2,18 @@ import { Injectable } from '@angular/core'; import { HttpHeaders, HttpClient } from '@angular/common/http'; import { ApiRoutes } from './models/api.settings'; -import { Account, Status, Results, Context, Relationship, Instance } from "./models/mastodon.interfaces"; +import { Account, Status, Results, Context, Relationship, Instance, Attachment } from "./models/mastodon.interfaces"; import { AccountInfo } from '../states/accounts.state'; import { StreamTypeEnum } from '../states/streams.state'; @Injectable() -export class MastodonService { +export class MastodonService { private apiRoutes = new ApiRoutes(); constructor(private readonly httpClient: HttpClient) { } - getInstance(instance: string): Promise{ - const route = `https://${instance}${this.apiRoutes.getInstance}`; + getInstance(instance: string): Promise { + const route = `https://${instance}${this.apiRoutes.getInstance}`; return this.httpClient.get(route).toPromise(); } @@ -114,25 +114,25 @@ export class MastodonService { return this.httpClient.post(url, formData, { headers: headers }).toPromise(); } - search(account: AccountInfo, query: string, resolve: boolean = false): Promise{ - if(query[0] === '#') query = query.substr(1); + search(account: AccountInfo, query: string, resolve: boolean = false): Promise { + if (query[0] === '#') query = query.substr(1); const route = `https://${account.instance}${this.apiRoutes.search}?q=${query}&resolve=${resolve}`; const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.get(route, { headers: headers }).toPromise() } - getAccountStatuses(account: AccountInfo, targetAccountId: number, onlyMedia: boolean, onlyPinned: boolean, excludeReplies: boolean, maxId: string, sinceId: string, limit: number = 20): Promise{ + getAccountStatuses(account: AccountInfo, targetAccountId: number, onlyMedia: boolean, onlyPinned: boolean, excludeReplies: boolean, maxId: string, sinceId: string, limit: number = 20): Promise { const route = `https://${account.instance}${this.apiRoutes.getAccountStatuses}`.replace('{0}', targetAccountId.toString()); let params = `?only_media=${onlyMedia}&pinned=${onlyPinned}&exclude_replies=${excludeReplies}&limit=${limit}`; - if(maxId) params += `&max_id=${maxId}`; - if(sinceId) params += `&since_id=${sinceId}`; + if (maxId) params += `&max_id=${maxId}`; + if (sinceId) params += `&since_id=${sinceId}`; const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); - return this.httpClient.get(route+params, { headers: headers }).toPromise(); + return this.httpClient.get(route + params, { headers: headers }).toPromise(); } - getStatusContext(account: AccountInfo, targetStatusId: string): Promise{ + getStatusContext(account: AccountInfo, targetStatusId: string): Promise { const params = this.apiRoutes.getStatusContext.replace('{0}', targetStatusId); const route = `https://${account.instance}${params}`; @@ -140,7 +140,7 @@ export class MastodonService { return this.httpClient.get(route, { headers: headers }).toPromise(); } - searchAccount(account: AccountInfo, query: string, limit: number = 40, following: boolean = false): Promise{ + searchAccount(account: AccountInfo, query: string, limit: number = 40, following: boolean = false): Promise { const route = `https://${account.instance}${this.apiRoutes.searchForAccounts}?q=${query}&limit=${limit}&following=${following}`; const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.get(route, { headers: headers }).toPromise() @@ -152,7 +152,7 @@ export class MastodonService { return this.httpClient.post(route, null, { headers: headers }).toPromise() } - unreblog(account: AccountInfo, status: Status): Promise { + unreblog(account: AccountInfo, status: Status): Promise { const route = `https://${account.instance}${this.apiRoutes.unreblogStatus}`.replace('{0}', status.id); const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.post(route, null, { headers: headers }).toPromise() @@ -163,7 +163,7 @@ export class MastodonService { const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); return this.httpClient.post(route, null, { headers: headers }).toPromise() } - + unfavorite(account: AccountInfo, status: Status): any { const route = `https://${account.instance}${this.apiRoutes.unfavouritingStatus}`.replace('{0}', status.id); const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); @@ -173,7 +173,7 @@ export class MastodonService { getRelationships(account: AccountInfo, accountsToRetrieve: Account[]): Promise { let params = "?"; accountsToRetrieve.forEach(x => { - if(params.includes('id')) params += '&'; + if (params.includes('id')) params += '&'; params += `id[]=${x.id}`; }); @@ -182,11 +182,11 @@ export class MastodonService { return this.httpClient.get(route, { headers: headers }).toPromise(); } - follow(currentlyUsedAccount: AccountInfo, account: Account): Promise { + follow(currentlyUsedAccount: AccountInfo, account: Account): Promise { const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.follow}`.replace('{0}', account.id.toString()); const headers = new HttpHeaders({ 'Authorization': `Bearer ${currentlyUsedAccount.token.access_token}` }); return this.httpClient.post(route, null, { headers: headers }).toPromise(); - } + } unfollow(currentlyUsedAccount: AccountInfo, account: Account): Promise { const route = `https://${currentlyUsedAccount.instance}${this.apiRoutes.unfollow}`.replace('{0}', account.id.toString()); @@ -194,7 +194,23 @@ export class MastodonService { return this.httpClient.post(route, null, { headers: headers }).toPromise(); } - + + uploadMediaAttachment(account: AccountInfo, file: File): Promise { + let input = new FormData(); + input.append('file', file); + const route = `https://${account.instance}${this.apiRoutes.uploadMediaAttachment}`; + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); + return this.httpClient.post(route, input, { headers: headers }).toPromise(); + } + + //TODO: add focus support + updateMediaAttachment(account: AccountInfo, mediaId: string, description: string): Promise { + let input = new FormData(); + input.append('description', description); + const route = `https://${account.instance}${this.apiRoutes.updateMediaAttachment.replace('{0}', mediaId)}`; + const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); + return this.httpClient.put(route, input, { headers: headers }).toPromise(); + } } export enum VisibilityEnum { diff --git a/src/app/services/media.service.ts b/src/app/services/media.service.ts index f3deb8ba..f7bdb2c1 100644 --- a/src/app/services/media.service.ts +++ b/src/app/services/media.service.ts @@ -5,37 +5,37 @@ import { BehaviorSubject, Subject } from 'rxjs'; import { AccountInfo } from '../states/accounts.state'; import { ApiRoutes } from './models/api.settings'; import { Attachment } from './models/mastodon.interfaces'; +import { MastodonService } from './mastodon.service'; +import { NotificationService } from './notification.service'; @Injectable({ providedIn: 'root' }) -export class MediaService { +export class MediaService { private apiRoutes = new ApiRoutes(); mediaSubject: BehaviorSubject = new BehaviorSubject([]); - constructor(private readonly httpClient: HttpClient) { } + constructor( + private readonly notificationService: NotificationService, + private readonly mastodonService: MastodonService) { } - uploadMedia(files: File[], account: AccountInfo){ + uploadMedia(account: AccountInfo, files: File[]){ for (let file of files) { - this.postMedia(file, account); + this.postMedia(account, file); } } - private postMedia(file: File, account: AccountInfo){ + private postMedia(account: AccountInfo, file: File){ const uniqueId = `${file.name}${file.size}${Math.random()}`; - const wrapper = new MediaWrapper(uniqueId, file, null); + const wrapper = new MediaWrapper(uniqueId, file, null, null); let medias = this.mediaSubject.value; medias.push(wrapper); this.mediaSubject.next(medias); - let input = new FormData(); - input.append('file', file); - const route = `https://${account.instance}${this.apiRoutes.uploadMediaAttachment}`; - const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` }); - this.httpClient.post(route, input, { headers: headers }).toPromise() + this.mastodonService.uploadMediaAttachment(account, file) .then((attachment: Attachment) => { let currentMedias = this.mediaSubject.value; let currentMedia = currentMedias.filter(x => x.id === uniqueId)[0]; @@ -45,18 +45,37 @@ export class MediaService { } }) .catch((err)=>{ - let currentMedias = this.mediaSubject.value; - let currentMedia = currentMedias.filter(x => x.id !== uniqueId); - this.mediaSubject.next(currentMedia); - - //TODO: notify + this.remove(wrapper); + this.notificationService.notifyHttpError(err); }); } + + update( account: AccountInfo, media: MediaWrapper): any { + if(media.attachment.description === media.description) 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) => { + this.notificationService.notifyHttpError(err); + }); + } + + remove(media: MediaWrapper): any { + let medias = this.mediaSubject.value; + let filteredMedias = medias.filter(x => x.id !== media.id); + this.mediaSubject.next(filteredMedias); + } } export class MediaWrapper { constructor( public id: string, public file: File, - public attachment: Attachment) {} + public attachment: Attachment, + public description: string) {} } diff --git a/src/app/services/models/api.settings.ts b/src/app/services/models/api.settings.ts index edf68194..0e0f6978 100644 --- a/src/app/services/models/api.settings.ts +++ b/src/app/services/models/api.settings.ts @@ -23,6 +23,7 @@ export class ApiRoutes { followRemote = '/api/v1/follows'; getInstance = '/api/v1/instance'; uploadMediaAttachment = '/api/v1/media'; + updateMediaAttachment = '/api/v1/media/{0}'; getMutes = '/api/v1/mutes'; getNotifications = '/api/v1/notifications'; getSingleNotifications = '/api/v1/notifications/{0}'; diff --git a/src/app/services/models/mastodon.interfaces.ts b/src/app/services/models/mastodon.interfaces.ts index deb3d3a4..d439a7c9 100644 --- a/src/app/services/models/mastodon.interfaces.ts +++ b/src/app/services/models/mastodon.interfaces.ts @@ -61,6 +61,8 @@ export interface Attachment { remote_url: string; preview_url: string; text_url: string; + meta: any; + description: string; } export interface Card {