import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } 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, faEnvelope, faBookmark } from "@fortawesome/free-solid-svg-icons"; import { faWindowClose as faWindowCloseRegular } from "@fortawesome/free-regular-svg-icons"; import { MastodonWrapperService } from '../../../../services/mastodon-wrapper.service'; import { AccountInfo } from '../../../../states/accounts.state'; import { Status, Account, Results } from '../../../../services/models/mastodon.interfaces'; import { ToolsService, OpenThreadEvent, InstanceInfo } from '../../../../services/tools.service'; import { NotificationService } from '../../../../services/notification.service'; import { StatusWrapper } from '../../../../models/common.model'; import { StatusesStateService, StatusState } from '../../../../services/statuses-state.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; faEnvelope = faEnvelope; faBookmark = faBookmark; @Input() statusWrapper: StatusWrapper; @Output() replyEvent = new EventEmitter(); @Output() cwIsActiveEvent = new EventEmitter(); @Output() browseThreadEvent = new EventEmitter(); isBookmarked: boolean; isFavorited: boolean; isBoosted: boolean; isDM: boolean; isBoostLocked: boolean; isLocked: boolean; isBookmarksAvailable: boolean; bookmarkingIsLoading: boolean; favoriteIsLoading: boolean; boostIsLoading: boolean; isContentWarningActive: boolean = false; displayedStatus: Status; private isProviderSelected: boolean; private selectedAccounts: AccountInfo[]; private favoriteStatePerAccountId: { [id: string]: boolean; } = {}; private bootedStatePerAccountId: { [id: string]: boolean; } = {}; private bookmarkStatePerAccountId: { [id: string]: boolean; } = {}; private accounts$: Observable; private accountSub: Subscription; private statusStateSub: Subscription; constructor( private readonly store: Store, private readonly toolsService: ToolsService, private readonly statusStateService: StatusesStateService, private readonly mastodonService: MastodonWrapperService, private readonly notificationService: NotificationService) { this.accounts$ = this.store.select(state => state.registeredaccounts.accounts); } ngOnInit() { this.displayedStatus = this.statusWrapper.status; const account = this.statusWrapper.provider; let accountId = 'remote'; if (account) { accountId = account.id; } if (this.displayedStatus.reblog) { this.displayedStatus = this.displayedStatus.reblog; } this.favoriteStatePerAccountId[accountId] = this.displayedStatus.favourited; this.bootedStatePerAccountId[accountId] = this.displayedStatus.reblogged; this.bookmarkStatePerAccountId[accountId] = this.displayedStatus.bookmarked; this.analyseMemoryStatus(); if (this.displayedStatus.visibility === 'direct') { this.isDM = true; } this.accountSub = this.accounts$.subscribe((accounts: AccountInfo[]) => { this.checkStatus(accounts); }); this.statusStateSub = this.statusStateService.stateNotification.subscribe((state: StatusState) => { if (state && state.statusId === this.displayedStatus.url) { if (state.isFavorited) { this.favoriteStatePerAccountId[state.accountId] = state.isFavorited; } if (state.isRebloged) { this.bootedStatePerAccountId[state.accountId] = state.isRebloged; } if (state.isBookmarked) { this.bookmarkStatePerAccountId[state.accountId] = state.isBookmarked; } this.checkIfFavorited(); this.checkIfBoosted(); this.checkIfBookmarked(); } }); } ngOnDestroy(): void { this.accountSub.unsubscribe(); this.statusStateSub.unsubscribe(); } private analyseMemoryStatus() { let memoryStatusState = this.statusStateService.getStateForStatus(this.displayedStatus.url); if (!memoryStatusState) return; memoryStatusState.forEach((state: StatusState) => { this.favoriteStatePerAccountId[state.accountId] = state.isFavorited; this.bootedStatePerAccountId[state.accountId] = state.isRebloged; this.bookmarkStatePerAccountId[state.accountId] = state.isBookmarked; }); } private checkStatus(accounts: AccountInfo[]): void { const status = this.statusWrapper.status; const provider = this.statusWrapper.provider; this.selectedAccounts = accounts.filter(x => x.isSelected); if (!this.statusWrapper.isRemote) { this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0; } else { this.isProviderSelected = false; } 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; } this.isContentWarningActive = this.statusWrapper.applyCw; this.checkIfBookmarksAreAvailable(this.selectedAccounts[0]); this.checkIfFavorited(); this.checkIfBoosted(); this.checkIfBookmarked(); } 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 false; 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 { let reblogged = boostedStatus.reblogged; //FIXME: when pixelfed will return the good status if (reblogged === null) { reblogged = !this.bootedStatePerAccountId[account.id]; } this.bootedStatePerAccountId[account.id] = reblogged; } this.checkIfBoosted(); }) .catch((err: HttpErrorResponse) => { this.notificationService.notifyHttpError(err, account); }) .then(() => { this.statusStateService.statusReblogStatusChanged(this.displayedStatus.url, account.id, this.bootedStatePerAccountId[account.id]); this.boostIsLoading = false; }); return false; } favorite(): boolean { if (this.favoriteIsLoading) return false; 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) => { let favourited = favoritedStatus.favourited; //FIXME: when pixelfed will return the good status if (favourited === null) { favourited = !this.favoriteStatePerAccountId[account.id]; } this.favoriteStatePerAccountId[account.id] = favourited; this.checkIfFavorited(); }) .catch((err: HttpErrorResponse) => { this.notificationService.notifyHttpError(err, account); }) .then(() => { this.statusStateService.statusFavoriteStatusChanged(this.displayedStatus.url, account.id, this.favoriteStatePerAccountId[account.id]); this.favoriteIsLoading = false; }); return false; } bookmark(): boolean { if (this.bookmarkingIsLoading) return false; this.bookmarkingIsLoading = true; const account = this.toolsService.getSelectedAccounts()[0]; const usableStatus = this.toolsService.getStatusUsableByAccount(account, this.statusWrapper); usableStatus .then((status: Status) => { if (this.isBookmarked && status.bookmarked) { return this.mastodonService.unbookmark(account, status); } else if (!this.isBookmarked && !status.bookmarked) { return this.mastodonService.bookmark(account, status); } else { return Promise.resolve(status); } }) .then((bookmarkedStatus: Status) => { let bookmarked = bookmarkedStatus.bookmarked; //FIXME: when pixelfed will return the good status if (bookmarked === null) { bookmarked = !this.bookmarkStatePerAccountId[account.id]; } this.bookmarkStatePerAccountId[account.id] = bookmarked; this.checkIfBookmarked(); }) .catch((err: HttpErrorResponse) => { this.notificationService.notifyHttpError(err, account); }) .then(() => { this.statusStateService.statusBookmarkStatusChanged(this.displayedStatus.url, account.id, this.bookmarkStatePerAccountId[account.id]); this.bookmarkingIsLoading = false; }); // setTimeout(() => { // this.isBookmarked = !this.isBookmarked; // this.bookmarkingIsLoading = false; // }, 2000); 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; } } private checkIfBookmarked() { const selectedAccount = this.selectedAccounts[0]; if (selectedAccount) { this.isBookmarked = this.bookmarkStatePerAccountId[selectedAccount.id]; } else { this.isBookmarked = false; } } private checkIfBookmarksAreAvailable(account: AccountInfo) { this.toolsService.getInstanceInfo(account) .then((instance: InstanceInfo) => { if (instance.major >= 3 && instance.minor >= 1) { this.isBookmarksAvailable = true; } else { this.isBookmarksAvailable = false; } }) .catch(err => { this.isBookmarksAvailable = false; }); } browseThread(event: OpenThreadEvent) { this.browseThreadEvent.next(event); } }