Sengi-Windows-MacOS-Linux/src/app/components/stream/status/status.component.ts

316 lines
11 KiB
TypeScript
Raw Normal View History

2019-08-14 05:56:47 +02:00
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from "@angular/core";
2023-08-06 10:41:10 +02:00
import { faStar, faRetweet, faList, faThumbtack, faEdit } from "@fortawesome/free-solid-svg-icons";
2022-12-11 00:31:54 +01:00
import { Subscription } from "rxjs";
2023-08-05 05:36:21 +02:00
import { Status, Account, Translation } from "../../../services/models/mastodon.interfaces";
2019-04-14 05:04:19 +02:00
import { OpenThreadEvent, ToolsService } from "../../../services/tools.service";
import { ActionBarComponent } from "./action-bar/action-bar.component";
import { StatusWrapper } from '../../../models/common.model';
2019-04-14 05:04:19 +02:00
import { EmojiConverter, EmojiTypeEnum } from '../../../tools/emoji.tools';
2020-03-28 22:43:31 +01:00
import { ContentWarningPolicyEnum } from '../../../states/settings.state';
2022-12-11 00:31:54 +01:00
import { StatusesStateService, StatusState } from "../../../services/statuses-state.service";
2023-08-08 01:47:54 +02:00
import { DatabindedTextComponent } from "./databinded-text/databinded-text.component";
import { SettingsService } from "../../../services/settings.service";
@Component({
2018-09-19 07:03:07 +02:00
selector: "app-status",
templateUrl: "./status.component.html",
styleUrls: ["./status.component.scss"]
})
export class StatusComponent implements OnInit {
2019-04-14 05:04:19 +02:00
private emojiConverter = new EmojiConverter();
faStar = faStar;
faRetweet = faRetweet;
2019-06-14 02:57:05 +02:00
faList = faList;
2019-07-07 23:22:48 +02:00
faThumbtack = faThumbtack;
2023-08-06 10:41:10 +02:00
faEdit = faEdit;
2018-09-21 04:51:18 +02:00
displayedStatus: Status;
displayedStatusWrapper: StatusWrapper;
2019-04-14 05:04:19 +02:00
2019-04-25 22:52:14 +02:00
// statusAccountName: string;
2019-04-14 05:04:19 +02:00
statusContent: string;
2018-09-21 04:51:18 +02:00
reblog: boolean;
2018-09-21 05:27:04 +02:00
hasAttachments: boolean;
2018-10-13 07:10:43 +02:00
replyingToStatus: boolean;
isCrossPoster: boolean;
2019-02-24 20:49:02 +01:00
isThread: boolean;
2019-06-18 03:49:18 +02:00
isOld: boolean;
2019-02-24 20:34:15 +01:00
isContentWarned: boolean;
2019-03-01 06:59:12 +01:00
hasReply: boolean;
2019-02-24 20:34:15 +01:00
contentWarningText: string;
2019-07-04 06:03:12 +02:00
isDirectMessage: boolean;
2019-08-14 05:56:47 +02:00
isSelected: boolean;
2020-04-07 03:54:31 +02:00
isRemote: boolean;
2018-09-21 04:51:18 +02:00
private freezeAvatarEnabled: boolean;
2020-04-01 00:53:56 +02:00
hideStatus: boolean = false;
@Output() browseAccountEvent = new EventEmitter<string>();
@Output() browseHashtagEvent = new EventEmitter<string>();
2019-02-19 04:44:21 +01:00
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
@ViewChild('appActionBar') appActionBar: ActionBarComponent;
@Input() isThreadDisplay: boolean;
2023-08-06 10:41:10 +02:00
@Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll' | 'update';
@Input() notificationAccount: Account;
2024-03-08 09:13:58 +01:00
@Input() context: 'home' | 'notifications' | 'public' | 'thread' | 'account';
private _statusWrapper: StatusWrapper;
status: Status;
2020-03-28 22:43:31 +01:00
2022-12-11 00:31:54 +01:00
private statusesStateServiceSub: Subscription;
@Input('statusWrapper')
set statusWrapper(value: StatusWrapper) {
this._statusWrapper = value;
this.status = value.status;
2020-02-15 01:59:45 +01:00
this.isSelected = value.isSelected;
2020-04-07 03:54:31 +02:00
this.isRemote = value.isRemote;
2018-10-27 02:20:01 +02:00
if (this.status.reblog) {
2018-09-21 04:51:18 +02:00
this.reblog = true;
this.displayedStatus = this.status.reblog;
2018-09-21 04:51:18 +02:00
} else {
this.displayedStatus = this.status;
2018-09-21 04:51:18 +02:00
}
2019-07-04 06:03:12 +02:00
this.isDirectMessage = this.displayedStatus.visibility === 'direct';
2020-04-01 08:29:51 +02:00
let cwPolicy = this.toolsService.checkContentWarning(this.displayedStatus);
this.displayedStatusWrapper = new StatusWrapper(cwPolicy.status, value.provider, cwPolicy.applyCw, cwPolicy.hide);
2020-04-07 03:54:31 +02:00
this.displayedStatusWrapper.isRemote = value.isRemote;
2019-03-01 06:59:12 +01:00
this.checkLabels(this.displayedStatus);
2020-04-01 08:29:51 +02:00
this.setContentWarning(this.displayedStatusWrapper);
2018-10-27 02:20:01 +02:00
if (!this.displayedStatus.account.display_name) {
2018-09-21 04:51:18 +02:00
this.displayedStatus.account.display_name = this.displayedStatus.account.username;
}
2018-10-27 02:20:01 +02:00
if (this.displayedStatus.media_attachments && this.displayedStatus.media_attachments.length > 0) {
2018-09-21 05:27:04 +02:00
this.hasAttachments = true;
}
2019-04-14 05:04:19 +02:00
// const instanceUrl = 'https://' + this.status.uri.split('https://')[1].split('/')[0];
2019-04-25 22:52:14 +02:00
// this.statusAccountName = this.emojiConverter.applyEmojis(this.displayedStatus.account.emojis, this.displayedStatus.account.display_name, EmojiTypeEnum.small);
2022-01-20 04:08:19 +01:00
let statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, this.displayedStatus.content, EmojiTypeEnum.medium);
this.statusContent = this.ensureMentionAreDisplayed(statusContent);
2024-03-08 09:13:58 +01:00
this.validateFilteringStatus();
2018-10-27 02:20:01 +02:00
}
get statusWrapper(): StatusWrapper {
return this._statusWrapper;
2018-09-21 04:51:18 +02:00
}
2018-10-27 02:20:01 +02:00
2019-07-12 01:02:15 +02:00
constructor(
2019-08-14 05:56:47 +02:00
public elem: ElementRef,
2022-12-11 00:31:54 +01:00
private readonly toolsService: ToolsService,
private readonly settingsService: SettingsService,
2022-12-11 00:31:54 +01:00
private readonly statusesStateService: StatusesStateService) { }
2018-10-27 02:20:01 +02:00
ngOnInit() {
2022-12-11 00:31:54 +01:00
this.statusesStateServiceSub = this.statusesStateService.stateNotification.subscribe(notification => {
2023-08-08 01:47:54 +02:00
if (this._statusWrapper.status.url === notification.statusId && notification.isEdited) {
2022-12-11 00:31:54 +01:00
this.statusWrapper = notification.editedStatus;
}
});
this.freezeAvatarEnabled = this.settingsService.getSettings().enableFreezeAvatar;
2022-12-11 00:31:54 +01:00
}
2023-08-08 01:47:54 +02:00
ngOnDestroy() {
if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
2018-10-27 02:20:01 +02:00
}
2023-08-08 01:47:54 +02:00
2024-03-08 09:13:58 +01:00
private validateFilteringStatus(){
const filterStatus = this.displayedStatus.filtered;
2024-03-09 08:00:05 +01:00
2024-03-08 09:13:58 +01:00
if(!filterStatus || filterStatus.length === 0) return;
2024-03-09 06:40:37 +01:00
// if(!this.context){
// console.warn('this.context not found');
// console.warn(this.context);
// }
2024-03-08 09:13:58 +01:00
for (let filter of filterStatus) {
if(this.context && filter.filter.context && filter.filter.context.length > 0){
if(!filter.filter.context.includes(this.context)) continue;
}
if(filter.filter.filter_action === 'warn'){
2024-03-09 06:40:37 +01:00
this.isContentWarned = true;
let filterTxt = `FILTERED:`;
2024-03-09 08:00:05 +01:00
for(let w of filter.keyword_matches){
2024-03-09 06:40:37 +01:00
filterTxt += ` ${w}`;
}
2024-03-08 09:13:58 +01:00
2024-03-09 06:40:37 +01:00
this.contentWarningText = filterTxt;
2024-03-08 09:13:58 +01:00
} else if (filter.filter.filter_action === 'hide'){
this.hideStatus = true;
}
}
}
getAvatar(acc: Account): string {
if(this.freezeAvatarEnabled){
return acc.avatar_static;
} else {
return acc.avatar;
}
}
2022-01-20 04:08:19 +01:00
private ensureMentionAreDisplayed(data: string): string {
const mentions = this.displayedStatus.mentions;
2023-08-08 01:47:54 +02:00
if (!mentions || mentions.length === 0) return data;
2022-01-20 04:08:19 +01:00
let textMentions = '';
for (const m of mentions) {
2023-08-08 01:47:54 +02:00
if (!data.includes(m.url)) {
2022-01-20 04:08:19 +01:00
textMentions += `<span class="h-card"><a class="u-url mention" data-user="${m.id}" href="${m.url}" rel="ugc">@<span>${m.username}</span></a></span> `
}
}
2023-08-08 01:47:54 +02:00
if (textMentions !== '') {
2022-01-20 04:08:19 +01:00
data = textMentions + data;
}
return data;
}
2020-04-01 08:29:51 +02:00
private setContentWarning(status: StatusWrapper) {
this.hideStatus = status.hide;
this.isContentWarned = status.applyCw;
2021-01-21 03:59:14 +01:00
let spoiler = this.htmlEncode(status.status.spoiler_text);
this.contentWarningText = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, spoiler, EmojiTypeEnum.medium);
}
private htmlEncode(str: string): string {
var encodedStr = str.replace(/[\u00A0-\u9999<>\&]/gim, function (i) {
return '&#' + i.charCodeAt(0) + ';';
});
return encodedStr;
2020-03-28 22:43:31 +01:00
}
2019-02-24 20:34:15 +01:00
removeContentWarning(): boolean {
this.isContentWarned = false;
this.appActionBar.showContent();
2019-02-24 20:34:15 +01:00
return false;
}
2019-03-01 06:59:12 +01:00
changeCw(cwIsActive: boolean) {
this.isContentWarned = cwIsActive;
}
2023-08-08 01:47:54 +02:00
@ViewChild('databindedtext') public databindedText: DatabindedTextComponent;
2023-08-05 05:36:21 +02:00
onTranslation(translation: Translation) {
2023-08-08 01:47:54 +02:00
let statusContent = translation.content;
// clean up a bit some issues (not reliable)
while (statusContent.includes('<span>@')) {
statusContent = statusContent.replace('<span>@', '@<span>');
}
while (statusContent.includes('h<span class="invisible">')){
statusContent = statusContent.replace('h<span class="invisible">', '<span class="invisible">h');
}
while (statusContent.includes('<span>#')){
statusContent = statusContent.replace('<span>#', '#<span>');
}
statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, statusContent, EmojiTypeEnum.medium);
this.statusContent = this.ensureMentionAreDisplayed(statusContent);
setTimeout(x => {
this.databindedText.processEventBindings();
}, 500);
2023-08-05 05:36:21 +02:00
}
2019-02-24 20:49:02 +01:00
private checkLabels(status: Status) {
//since API is limited with federated status...
if (!status.account.bot) {
if (status.uri.includes('birdsite.link')) {
this.isCrossPoster = true;
}
else if (status.application) {
const usedApp = status.application.name.toLowerCase();
if (usedApp && (usedApp.includes('moa') || usedApp.includes('birdsite') || usedApp.includes('twitter'))) {
this.isCrossPoster = true;
}
}
}
2019-02-24 20:49:02 +01:00
if (this.isThreadDisplay) return;
2019-03-01 06:59:12 +01:00
if (status.in_reply_to_account_id && status.in_reply_to_account_id === status.account.id) {
2019-02-24 20:49:02 +01:00
this.isThread = true;
}
2019-03-01 06:59:12 +01:00
this.hasReply = status.replies_count > 0;
2019-06-18 03:49:18 +02:00
let createdAt = new Date(status.created_at);
let now = new Date();
now.setMonth(now.getMonth() - 3);
if (now > createdAt) {
this.isOld = true;
}
}
2018-10-27 02:20:01 +02:00
openAccount(account: Account): boolean {
2019-07-12 01:02:15 +02:00
let accountName = this.toolsService.getAccountFullHandle(account);
this.browseAccountEvent.next(accountName);
return false;
}
2018-10-27 02:20:01 +02:00
openReply(): boolean {
2018-10-13 07:10:43 +02:00
this.replyingToStatus = !this.replyingToStatus;
2018-10-27 02:20:01 +02:00
2018-10-13 07:10:43 +02:00
return false;
}
2018-10-13 17:38:23 +02:00
closeReply(): boolean {
this.replyingToStatus = false;
return false;
}
2018-10-27 02:20:01 +02:00
accountSelected(accountName: string): void {
this.browseAccountEvent.next(accountName);
}
hashtagSelected(hashtag: string): void {
this.browseHashtagEvent.next(hashtag);
}
2019-05-16 06:41:17 +02:00
textSelected(): boolean {
2020-03-28 22:43:31 +01:00
if (this.isSelected) return false;
2019-01-28 05:08:43 +01:00
const status = this._statusWrapper.status;
2019-02-19 04:44:21 +01:00
const accountInfo = this._statusWrapper.provider;
2019-01-28 05:08:43 +01:00
2019-02-19 04:44:21 +01:00
let openThread: OpenThreadEvent;
if (status.reblog) {
openThread = new OpenThreadEvent(status.reblog, accountInfo);
2019-01-28 05:08:43 +01:00
} else {
2019-02-19 04:44:21 +01:00
openThread = new OpenThreadEvent(status, accountInfo);
2019-01-28 05:08:43 +01:00
}
2019-02-19 04:44:21 +01:00
this.browseThreadEvent.next(openThread);
2019-05-16 06:41:17 +02:00
return false;
}
2019-07-04 02:23:23 +02:00
browseThread(event: OpenThreadEvent): void {
this.browseThreadEvent.next(event);
}
2019-06-16 02:41:21 +02:00
openUrl(url: string): boolean {
2019-05-16 06:41:17 +02:00
event.preventDefault();
2019-06-16 02:41:21 +02:00
window.open(url, "_blank");
2019-05-16 06:41:17 +02:00
return false;
2018-10-27 02:20:01 +02:00
}
}