import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ViewChild } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Store } from '@ngxs/store'; import { Observable, Subscription } from 'rxjs'; import { faWindowClose, faReply, faRetweet, faStar, faEllipsisH, faLock } from "@fortawesome/free-solid-svg-icons"; import { faWindowClose as faWindowCloseRegular } from "@fortawesome/free-regular-svg-icons"; import { ContextMenuComponent, ContextMenuService } from 'ngx-contextmenu'; import { MastodonService } from '../../../../services/mastodon.service'; import { AccountInfo } from '../../../../states/accounts.state'; import { Status, Account } from '../../../../services/models/mastodon.interfaces'; import { ToolsService } from '../../../../services/tools.service'; import { NotificationService } from '../../../../services/notification.service'; import { StatusWrapper } from '../../../../models/common.model'; import { NavigationService } from '../../../../services/navigation.service'; @Component({ selector: 'app-action-bar', templateUrl: './action-bar.component.html', styleUrls: ['./action-bar.component.scss'] }) export class ActionBarComponent implements OnInit, OnDestroy { faWindowClose = faWindowClose; faReply = faReply; faRetweet = faRetweet; faStar = faStar; faWindowCloseRegular = faWindowCloseRegular; faEllipsisH = faEllipsisH; faLock = faLock; @ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent; public items = [ { name: 'John', otherProperty: 'Foo' }, { name: 'Joe', otherProperty: 'Bar' } ]; @Input() statusWrapper: StatusWrapper; @Output() replyEvent = new EventEmitter(); @Output() cwIsActiveEvent = new EventEmitter(); isFavorited: boolean; isBoosted: boolean; isBoostLocked: boolean; isLocked: boolean; favoriteIsLoading: boolean; boostIsLoading: boolean; isContentWarningActive: boolean = false; private isProviderSelected: boolean; private selectedAccounts: AccountInfo[]; private favoriteStatePerAccountId: { [id: string]: boolean; } = {}; private bootedStatePerAccountId: { [id: string]: boolean; } = {}; private accounts$: Observable; private accountSub: Subscription; constructor( private readonly navigationService: NavigationService, private readonly contextMenuService: ContextMenuService, private readonly store: Store, private readonly toolsService: ToolsService, private readonly mastodonService: MastodonService, private readonly notificationService: NotificationService) { this.accounts$ = this.store.select(state => state.registeredaccounts.accounts); } username: string; private fullHandle: string; private displayedStatus: Status; private loadedAccounts: AccountInfo[]; ngOnInit() { const status = this.statusWrapper.status; const account = this.statusWrapper.provider; if (status.reblog) { this.favoriteStatePerAccountId[account.id] = status.reblog.favourited; this.bootedStatePerAccountId[account.id] = status.reblog.reblogged; this.extractHandle(status.reblog.account); this.displayedStatus = status.reblog; } else { this.favoriteStatePerAccountId[account.id] = status.favourited; this.bootedStatePerAccountId[account.id] = status.reblogged; this.extractHandle(status.account); this.displayedStatus = status; } this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => { this.loadedAccounts = accounts; this.checkStatus(accounts); }); } private extractHandle(account: Account) { this.username = account.acct.split('@')[0]; this.fullHandle = account.acct.toLowerCase(); if (!this.fullHandle.includes('@')) { this.fullHandle += `@${account.url.replace('https://', '').split('/')[0]}`; } this.fullHandle = `@${this.fullHandle}`; } ngOnDestroy(): void { this.accountSub.unsubscribe(); } private checkStatus(accounts: AccountInfo[]): void { const status = this.statusWrapper.status; const provider = this.statusWrapper.provider; this.selectedAccounts = accounts.filter(x => x.isSelected); this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0; if (status.visibility === 'direct' || status.visibility === 'private') { this.isBoostLocked = true; } else { this.isBoostLocked = false; } if ((status.visibility === 'direct' || status.visibility === 'private') && !this.isProviderSelected) { this.isLocked = true; } else { this.isLocked = false; } if (status.sensitive || status.spoiler_text) { this.isContentWarningActive = true; } this.checkIfFavorited(); this.checkIfBoosted(); } showContent(): boolean { this.isContentWarningActive = false; this.cwIsActiveEvent.next(false); return false; } hideContent(): boolean { this.isContentWarningActive = true; this.cwIsActiveEvent.next(true); return false; } reply(): boolean { this.replyEvent.emit(); return false; } boost(): boolean { if (this.boostIsLoading) return; this.boostIsLoading = true; const account = this.toolsService.getSelectedAccounts()[0]; const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper); usableStatus .then((status: Status) => { if (this.isBoosted && status.reblogged) { return this.mastodonService.unreblog(account, status); } else if (!this.isBoosted && !status.reblogged) { return this.mastodonService.reblog(account, status); } else { return Promise.resolve(status); } }) .then((boostedStatus: Status) => { if (boostedStatus.pleroma) { this.bootedStatePerAccountId[account.id] = boostedStatus.reblog !== null; //FIXME: when Pleroma will return the good status } else { this.bootedStatePerAccountId[account.id] = boostedStatus.reblogged; } this.checkIfBoosted(); }) .catch((err: HttpErrorResponse) => { this.notificationService.notifyHttpError(err); }) .then(() => { this.boostIsLoading = false; }); return false; } favorite(): boolean { if (this.favoriteIsLoading) return; this.favoriteIsLoading = true; const account = this.toolsService.getSelectedAccounts()[0]; const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper); usableStatus .then((status: Status) => { if (this.isFavorited && status.favourited) { return this.mastodonService.unfavorite(account, status); } else if (!this.isFavorited && !status.favourited) { return this.mastodonService.favorite(account, status); } else { return Promise.resolve(status); } }) .then((favoritedStatus: Status) => { this.favoriteStatePerAccountId[account.id] = favoritedStatus.favourited; this.checkIfFavorited(); // this.isFavorited = !this.isFavorited; }) .catch((err: HttpErrorResponse) => { this.notificationService.notifyHttpError(err); }) .then(() => { this.favoriteIsLoading = false; }); return false; } private checkIfBoosted() { const selectedAccount = this.selectedAccounts[0]; if (selectedAccount) { this.isBoosted = this.bootedStatePerAccountId[selectedAccount.id]; } else { this.isBoosted = false; } } private checkIfFavorited() { const selectedAccount = this.selectedAccounts[0]; if (selectedAccount) { this.isFavorited = this.favoriteStatePerAccountId[selectedAccount.id]; } else { this.isFavorited = false; } } more(): boolean { console.warn('more'); //TODO return false; } public onContextMenu($event: MouseEvent): void { this.contextMenuService.show.next({ // Optional - if unspecified, all context menu components will open contextMenu: this.contextMenu, event: $event, item: null }); $event.preventDefault(); $event.stopPropagation(); } expandStatus(): boolean { return false; } copyStatusLink(): boolean { let selBox = document.createElement('textarea'); selBox.style.position = 'fixed'; selBox.style.left = '0'; selBox.style.top = '0'; selBox.style.opacity = '0'; selBox.value = this.displayedStatus.url; document.body.appendChild(selBox); selBox.focus(); selBox.select(); document.execCommand('copy'); document.body.removeChild(selBox); return false; } mentionAccount(): boolean { this.navigationService.replyToUser(this.fullHandle, false); return false; } dmAccount(): boolean { this.navigationService.replyToUser(this.fullHandle, true); return false; } muteAccount(): boolean { this.loadedAccounts.forEach(acc => { this.toolsService.findAccount(acc, this.fullHandle) .then((target: Account) => { this.mastodonService.mute(acc, target.id); return target; }) .then((target: Account) => { this.notificationService.hideAccount(target); }) .catch(err => { this.notificationService.notifyHttpError(err); }); }); return false; } blockAccount(): boolean { this.loadedAccounts.forEach(acc => { this.toolsService.findAccount(acc, this.fullHandle) .then((target: Account) => { this.mastodonService.block(acc, target.id); return target; }) .then((target: Account) => { this.notificationService.hideAccount(target); }) .catch(err => { this.notificationService.notifyHttpError(err); }); }); return false; } }