Merge pull request #638 from NicolasConstant/topic_enhance-filters

Topic enhance filters
This commit is contained in:
Nicolas Constant 2024-03-09 02:36:17 -05:00 committed by GitHub
commit 6a8d85f40c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 132 additions and 18 deletions

View File

@ -70,17 +70,29 @@
</div> </div>
<app-status *ngIf="notification.status && notification.type === 'update'" class="stream__status" <app-status *ngIf="notification.status && notification.type === 'update'" class="stream__status"
[statusWrapper]="notification.status" [notificationAccount]="notification.account" [statusWrapper]="notification.status"
[notificationType]="notification.type" (browseAccountEvent)="browseAccount($event)" [notificationAccount]="notification.account"
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status> [notificationType]="notification.type"
[context]="'notifications'"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-status>
<app-status *ngIf="notification.status && notification.type === 'mention'" class="stream__status" <app-status *ngIf="notification.status && notification.type === 'mention'" class="stream__status"
[statusWrapper]="notification.status" (browseAccountEvent)="browseAccount($event)" [statusWrapper]="notification.status"
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status> [context]="'notifications'"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-status>
<app-status *ngIf="notification.status && notification.type !== 'mention' && notification.type !== 'update'" <app-status *ngIf="notification.status && notification.type !== 'mention' && notification.type !== 'update'"
class="stream__status" [statusWrapper]="notification.status" [notificationAccount]="notification.account" class="stream__status"
[notificationType]="notification.type" (browseAccountEvent)="browseAccount($event)" [statusWrapper]="notification.status"
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status> [notificationAccount]="notification.account"
[notificationType]="notification.type"
[context]="'notifications'"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-status>
</div> </div>

View File

@ -59,6 +59,8 @@ export class StatusComponent implements OnInit {
@Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll' | 'update'; @Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll' | 'update';
@Input() notificationAccount: Account; @Input() notificationAccount: Account;
@Input() context: 'home' | 'notifications' | 'public' | 'thread' | 'account';
private _statusWrapper: StatusWrapper; private _statusWrapper: StatusWrapper;
status: Status; status: Status;
@ -98,6 +100,8 @@ export class StatusComponent implements OnInit {
// this.statusAccountName = this.emojiConverter.applyEmojis(this.displayedStatus.account.emojis, this.displayedStatus.account.display_name, EmojiTypeEnum.small); // this.statusAccountName = this.emojiConverter.applyEmojis(this.displayedStatus.account.emojis, this.displayedStatus.account.display_name, EmojiTypeEnum.small);
let statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, this.displayedStatus.content, EmojiTypeEnum.medium); let statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, this.displayedStatus.content, EmojiTypeEnum.medium);
this.statusContent = this.ensureMentionAreDisplayed(statusContent); this.statusContent = this.ensureMentionAreDisplayed(statusContent);
this.validateFilteringStatus();
} }
get statusWrapper(): StatusWrapper { get statusWrapper(): StatusWrapper {
return this._statusWrapper; return this._statusWrapper;
@ -123,6 +127,36 @@ export class StatusComponent implements OnInit {
if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe(); if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
} }
private validateFilteringStatus(){
const filterStatus = this.displayedStatus.filtered;
if(!filterStatus || filterStatus.length === 0) return;
// if(!this.context){
// console.warn('this.context not found');
// console.warn(this.context);
// }
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'){
this.isContentWarned = true;
let filterTxt = `FILTERED:`;
for(let w of filter.keyword_matches){
filterTxt += ` ${w}`;
}
this.contentWarningText = filterTxt;
} else if (filter.filter.filter_action === 'hide'){
this.hideStatus = true;
}
}
}
getAvatar(acc: Account): string { getAvatar(acc: Account): string {
if(this.freezeAvatarEnabled){ if(this.freezeAvatarEnabled){
return acc.avatar_static; return acc.avatar_static;

View File

@ -122,7 +122,7 @@ export class StreamNotificationsComponent extends BrowseBase {
loadNotifications(): any { loadNotifications(): any {
this.account = this.toolsService.getAccountById(this.streamElement.accountId); this.account = this.toolsService.getAccountById(this.streamElement.accountId);
this.mentionsSubscription = this.userNotificationService.userNotifications.subscribe((userNotifications: UserNotification[]) => { this.mentionsSubscription = this.userNotificationService.userNotifications.subscribe((userNotifications: UserNotification[]) => {
this.loadMentions(userNotifications); this.loadMentions(userNotifications);
}); });
@ -130,10 +130,13 @@ export class StreamNotificationsComponent extends BrowseBase {
.then((notifications: Notification[]) => { .then((notifications: Notification[]) => {
this.isNotificationsLoading = false; this.isNotificationsLoading = false;
this.notifications = notifications.map(x => { let wrappedNotification= notifications.map(x => {
let cwPolicy = this.toolsService.checkContentWarning(x.status); let cwPolicy = this.toolsService.checkContentWarning(x.status);
return new NotificationWrapper(x, this.account, cwPolicy.applyCw, cwPolicy.hide); return new NotificationWrapper(x, this.account, cwPolicy.applyCw, cwPolicy.hide);
}); });
this.notifications = wrappedNotification.filter(x => x.type !== 'mention' || (x.type === 'mention' && x.status.status !== null));
this.lastNotificationId = this.notifications[this.notifications.length - 1].notification.id; this.lastNotificationId = this.notifications[this.notifications.length - 1].notification.id;
}) })
.catch(err => { .catch(err => {

View File

@ -17,8 +17,11 @@
<div class="stream-toots__status" *ngFor="let statusWrapper of statuses" #status> <div class="stream-toots__status" *ngFor="let statusWrapper of statuses" #status>
<app-status <app-status
[statusWrapper]="statusWrapper" [isThreadDisplay]="isThread" [statusWrapper]="statusWrapper"
(browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)" [isThreadDisplay]="isThread"
[context]="context"
(browseAccountEvent)="browseAccount($event)"
(browseHashtagEvent)="browseHashtag($event)"
(browseThreadEvent)="browseThread($event)"></app-status> (browseThreadEvent)="browseThread($event)"></app-status>
</div> </div>

View File

@ -3,7 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { Store } from '@ngxs/store'; import { Store } from '@ngxs/store';
import { StreamElement } from '../../../states/streams.state'; import { StreamElement, StreamTypeEnum } from '../../../states/streams.state';
import { AccountInfo } from '../../../states/accounts.state'; import { AccountInfo } from '../../../states/accounts.state';
import { StreamingService, EventEnum, StatusUpdate } from '../../../services/streaming.service'; import { StreamingService, EventEnum, StatusUpdate } from '../../../services/streaming.service';
import { Status } from '../../../services/models/mastodon.interfaces'; import { Status } from '../../../services/models/mastodon.interfaces';
@ -20,9 +20,11 @@ import { SettingsService } from '../../../services/settings.service';
templateUrl: './stream-statuses.component.html', templateUrl: './stream-statuses.component.html',
styleUrls: ['./stream-statuses.component.scss'] styleUrls: ['./stream-statuses.component.scss']
}) })
export class StreamStatusesComponent extends TimelineBase { export class StreamStatusesComponent extends TimelineBase {
protected _streamElement: StreamElement; protected _streamElement: StreamElement;
context: 'home' | 'notifications' | 'public' | 'thread' | 'account';
@Input() @Input()
set streamElement(streamElement: StreamElement) { set streamElement(streamElement: StreamElement) {
this._streamElement = streamElement; this._streamElement = streamElement;
@ -32,6 +34,8 @@ export class StreamStatusesComponent extends TimelineBase {
this.hideReplies = streamElement.hideReplies; this.hideReplies = streamElement.hideReplies;
this.load(this._streamElement); this.load(this._streamElement);
this.setContext(this._streamElement);
} }
get streamElement(): StreamElement { get streamElement(): StreamElement {
return this._streamElement; return this._streamElement;
@ -112,6 +116,24 @@ export class StreamStatusesComponent extends TimelineBase {
if (this.deleteStatusSubscription) this.deleteStatusSubscription.unsubscribe(); if (this.deleteStatusSubscription) this.deleteStatusSubscription.unsubscribe();
} }
private setContext(streamElement: StreamElement) {
switch(streamElement.type){
case StreamTypeEnum.global:
case StreamTypeEnum.local:
case StreamTypeEnum.tag:
this.context = 'public';
break;
case StreamTypeEnum.personnal:
case StreamTypeEnum.list:
this.context = 'home';
break;
case StreamTypeEnum.activity:
case StreamTypeEnum.directmessages:
this.context = 'notifications';
break;
}
}
refresh(): any { refresh(): any {
this.load(this._streamElement); this.load(this._streamElement);
} }

View File

@ -28,6 +28,8 @@ export class ThreadComponent extends BrowseBase {
hasContentWarnings = false; hasContentWarnings = false;
private remoteStatusFetchingDisabled = false; private remoteStatusFetchingDisabled = false;
context = 'thread';
numNewItems: number; //html compatibility only numNewItems: number; //html compatibility only
bufferStream: Status[] = []; //html compatibility only bufferStream: Status[] = []; //html compatibility only
streamPositionnedAtTop: boolean = true; //html compatibility only streamPositionnedAtTop: boolean = true; //html compatibility only

View File

@ -196,16 +196,24 @@
<div *ngIf="statusSection === 'status' && !statusLoading"> <div *ngIf="statusSection === 'status' && !statusLoading">
<div *ngFor="let statusWrapper of pinnedStatuses"> <div *ngFor="let statusWrapper of pinnedStatuses">
<app-status [statusWrapper]="statusWrapper" (browseHashtagEvent)="browseHashtag($event)" <app-status
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)"> [statusWrapper]="statusWrapper"
[context]="'account'"
(browseHashtagEvent)="browseHashtag($event)"
(browseAccountEvent)="browseAccount($event)"
(browseThreadEvent)="browseThread($event)">
</app-status> </app-status>
</div> </div>
</div> </div>
<div *ngFor="let statusWrapper of statuses"> <div *ngFor="let statusWrapper of statuses">
<div *ngIf="statusSection !== 'media'"> <div *ngIf="statusSection !== 'media'">
<app-status [statusWrapper]="statusWrapper" (browseHashtagEvent)="browseHashtag($event)" <app-status
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)"> [statusWrapper]="statusWrapper"
[context]="'account'"
(browseHashtagEvent)="browseHashtag($event)"
(browseAccountEvent)="browseAccount($event)"
(browseThreadEvent)="browseThread($event)">
</app-status> </app-status>
</div> </div>
<div *ngIf="statusSection === 'media'" class="status-media"> <div *ngIf="statusSection === 'media'" class="status-media">

View File

@ -194,6 +194,33 @@ export interface Results {
hashtags: string[]; hashtags: string[];
} }
export interface FilterKeyword {
id: string;
keyword: string;
whole_word: boolean;
}
export interface FilterStatus {
id: string;
status_id: string;
}
export interface Filter {
id: string;
title: string;
context: string[]; //home notifications public thread account
expires_at: string;
filter_action: string; //warn hide
keywords: FilterKeyword[];
statuses: FilterStatus[];
}
export interface FilterResult {
filter: Filter;
keyword_matches: string[];
status_matches: string[];
}
export interface Status { export interface Status {
id: string; id: string;
uri: string; uri: string;
@ -224,6 +251,7 @@ export interface Status {
bookmarked: boolean; bookmarked: boolean;
card: Card; card: Card;
poll: Poll; poll: Poll;
filtered: FilterResult[];
pleroma: PleromaStatusInfo; pleroma: PleromaStatusInfo;
} }

View File

@ -60,6 +60,8 @@ export class UserNotificationService {
private startFetchingNotifications(account: AccountInfo) { private startFetchingNotifications(account: AccountInfo) {
let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll', 'follow_request', 'move', 'update'], null, null, 10) let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll', 'follow_request', 'move', 'update'], null, null, 10)
.then((notifications: Notification[]) => { .then((notifications: Notification[]) => {
notifications = notifications.filter(x => x.status !== null);
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention); this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
}) })
.catch(err => { .catch(err => {