282 lines
11 KiB
TypeScript
282 lines
11 KiB
TypeScript
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef, OnDestroy } from '@angular/core';
|
|
import { Observable, Subscription } from 'rxjs';
|
|
|
|
import { Notification } from '../../../services/models/mastodon.interfaces';
|
|
import { StreamElement, StreamTypeEnum } from '../../../states/streams.state';
|
|
import { OpenThreadEvent, ToolsService } from '../../../services/tools.service';
|
|
import { MastodonService } from '../../../services/mastodon.service';
|
|
import { UserNotificationService, UserNotification } from '../../../services/user-notification.service';
|
|
import { NotificationWrapper } from '../../floating-column/manage-account/notifications/notifications.component';
|
|
import { AccountInfo } from '../../../states/accounts.state';
|
|
import { NotificationService } from '../../../services/notification.service';
|
|
import { StreamingService, StatusUpdate, EventEnum } from '../../../services/streaming.service';
|
|
import { BrowseBase } from '../../common/browse-base';
|
|
|
|
@Component({
|
|
selector: 'app-stream-notifications',
|
|
templateUrl: './stream-notifications.component.html',
|
|
styleUrls: ['./stream-notifications.component.scss']
|
|
})
|
|
export class StreamNotificationsComponent extends BrowseBase {
|
|
displayingNotifications = true;
|
|
displayingMentions = false;
|
|
|
|
notifications: NotificationWrapper[] = [];
|
|
mentions: NotificationWrapper[] = [];
|
|
|
|
@Input() streamElement: StreamElement;
|
|
@Input() goToTop: Observable<void>;
|
|
|
|
@ViewChild('notificationstream') public notificationstream: ElementRef;
|
|
@ViewChild('mentionstream') public mentionstream: ElementRef;
|
|
|
|
private nbStatusPerIteration: number = 20;
|
|
private account: AccountInfo;
|
|
|
|
private goToTopSubscription: Subscription;
|
|
private mentionsSubscription: Subscription;
|
|
private notificationSubscription: Subscription;
|
|
|
|
isMentionsLoading: boolean;
|
|
mentionsMaxReached: boolean;
|
|
lastMentionId: string;
|
|
|
|
isNotificationsLoading: boolean = true;
|
|
notificationsMaxReached: boolean;
|
|
lastNotificationId: string;
|
|
|
|
constructor(
|
|
private readonly streamingService: StreamingService,
|
|
private readonly notificationService: NotificationService,
|
|
private readonly userNotificationService: UserNotificationService,
|
|
private readonly mastodonService: MastodonService,
|
|
private readonly toolsService: ToolsService) {
|
|
super();
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.goToTopSubscription = this.goToTop.subscribe(() => {
|
|
this.applyGoToTop();
|
|
});
|
|
|
|
this.loadNotifications();
|
|
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
if (this.goToTopSubscription) this.goToTopSubscription.unsubscribe();
|
|
if (this.mentionsSubscription) this.mentionsSubscription.unsubscribe();
|
|
if (this.notificationSubscription) this.notificationSubscription.unsubscribe();
|
|
}
|
|
|
|
private reduceSize(elements: NotificationWrapper[]) {
|
|
if (elements.length > 2 * this.nbStatusPerIteration) {
|
|
elements.length = 2 * this.nbStatusPerIteration;
|
|
}
|
|
}
|
|
|
|
private applyGoToTop(): boolean {
|
|
let stream: HTMLElement;
|
|
if (this.displayingNotifications) {
|
|
this.reduceSize(this.notifications);
|
|
stream = this.notificationstream.nativeElement as HTMLElement;
|
|
} else {
|
|
this.reduceSize(this.mentions);
|
|
stream = this.mentionstream.nativeElement as HTMLElement;
|
|
}
|
|
|
|
setTimeout(() => {
|
|
stream.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
}, 0);
|
|
|
|
return false;
|
|
}
|
|
|
|
private loadMentions(userNotifications: UserNotification[]) {
|
|
if (!userNotifications) return;
|
|
|
|
let userNotification = userNotifications.find(x => x.account.id === this.account.id);
|
|
|
|
if (!userNotification) return;
|
|
|
|
let mentions = userNotification.mentions
|
|
.map(x => {
|
|
let cwPolicy = this.toolsService.checkContentWarning(x.status);
|
|
return new NotificationWrapper(x, this.account, cwPolicy.applyCw, cwPolicy.hide);
|
|
})
|
|
.reverse();
|
|
this.lastMentionId = userNotification.lastMentionsId;
|
|
|
|
if (!mentions) return;
|
|
|
|
mentions.forEach(mention => {
|
|
if (!this.mentions.find(x => x.wrapperId === mention.wrapperId)) {
|
|
this.mentions.unshift(mention);
|
|
}
|
|
});
|
|
}
|
|
|
|
loadNotifications(): any {
|
|
this.account = this.toolsService.getAccountById(this.streamElement.accountId);
|
|
|
|
this.mentionsSubscription = this.userNotificationService.userNotifications.subscribe((userNotifications: UserNotification[]) => {
|
|
this.loadMentions(userNotifications);
|
|
});
|
|
|
|
this.mastodonService.getNotifications(this.account, [], null, null, 10)
|
|
.then((notifications: Notification[]) => {
|
|
this.isNotificationsLoading = false;
|
|
|
|
let wrappedNotification= notifications.map(x => {
|
|
let cwPolicy = this.toolsService.checkContentWarning(x.status);
|
|
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;
|
|
})
|
|
.catch(err => {
|
|
this.isNotificationsLoading = false;
|
|
})
|
|
.then(() => {
|
|
let streamElement = new StreamElement(StreamTypeEnum.personnal, 'activity', this.account.id, null, null, null, this.account.instance);
|
|
|
|
let streaming = this.streamingService.getStreaming(this.account, streamElement);
|
|
this.notificationSubscription = streaming.statusUpdateSubjet.subscribe((notification: StatusUpdate) => {
|
|
if (notification && notification.type === EventEnum.notification) {
|
|
let cwPolicy = this.toolsService.checkContentWarning(notification.status);
|
|
const n = new NotificationWrapper(notification.notification, this.account, cwPolicy.applyCw, cwPolicy.hide);
|
|
this.notifications.unshift(n);
|
|
}
|
|
});
|
|
})
|
|
.catch(err => { });
|
|
}
|
|
|
|
select(value: 'all' | 'mentions'): boolean {
|
|
if (value === 'all') {
|
|
if (this.displayingNotifications === true) {
|
|
this.applyGoToTop();
|
|
} else {
|
|
this.displayingMentions = false;
|
|
setTimeout(() => {
|
|
this.displayingNotifications = true;
|
|
}, 150);
|
|
}
|
|
} else {
|
|
if (this.displayingMentions === true) {
|
|
this.applyGoToTop();
|
|
} else {
|
|
this.displayingNotifications = false;
|
|
setTimeout(() => {
|
|
this.displayingMentions = true;
|
|
}, 150);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
onScroll() {
|
|
var element = this.notificationstream.nativeElement as HTMLElement;
|
|
if (this.displayingMentions) {
|
|
element = this.mentionstream.nativeElement as HTMLElement;
|
|
}
|
|
|
|
const atBottom = element.scrollHeight <= element.clientHeight + element.scrollTop + 1000;
|
|
|
|
if (atBottom) {
|
|
if (this.displayingMentions) {
|
|
this.mentionsScrolledToBottom();
|
|
} else {
|
|
this.notificationsScrolledToBottom();
|
|
}
|
|
}
|
|
}
|
|
|
|
private scrolledErrorOccured = false;
|
|
notificationsScrolledToBottom(): any {
|
|
if (this.isNotificationsLoading || this.notificationsMaxReached || this.notifications.length === 0 || this.scrolledErrorOccured)
|
|
return;
|
|
|
|
this.isNotificationsLoading = true;
|
|
|
|
this.mastodonService.getNotifications(this.account, ['update'], this.lastNotificationId)
|
|
.then((result: Notification[]) => {
|
|
if (result.length === 0) {
|
|
this.notificationsMaxReached = true;
|
|
return;
|
|
}
|
|
|
|
for (const s of result) {
|
|
let cwPolicy = this.toolsService.checkContentWarning(s.status);
|
|
const wrapper = new NotificationWrapper(s, this.account, cwPolicy.applyCw, cwPolicy.hide);
|
|
this.notifications.push(wrapper);
|
|
}
|
|
|
|
this.lastNotificationId = result[result.length - 1].id;
|
|
})
|
|
.catch(err => {
|
|
this.scrolledErrorOccured = true;
|
|
setTimeout(() => {
|
|
this.scrolledErrorOccured = false;
|
|
}, 5000);
|
|
|
|
this.notificationService.notifyHttpError(err, this.account);
|
|
})
|
|
.then(() => {
|
|
this.isNotificationsLoading = false;
|
|
});
|
|
}
|
|
|
|
mentionsScrolledToBottom(): any {
|
|
if (this.isMentionsLoading || this.mentionsMaxReached || this.mentions.length === 0 || this.scrolledErrorOccured)
|
|
return;
|
|
|
|
this.isMentionsLoading = true;
|
|
|
|
this.mastodonService.getNotifications(this.account, ['follow', 'favourite', 'reblog', 'poll', 'follow_request', 'move', 'update'], this.lastMentionId)
|
|
.then((result: Notification[]) => {
|
|
if (result.length === 0) {
|
|
this.mentionsMaxReached = true;
|
|
return;
|
|
}
|
|
|
|
for (const s of result) {
|
|
let cwPolicy = this.toolsService.checkContentWarning(s.status);
|
|
const wrapper = new NotificationWrapper(s, this.account, cwPolicy.applyCw, cwPolicy.hide);
|
|
this.mentions.push(wrapper);
|
|
}
|
|
|
|
this.lastMentionId = result[result.length - 1].id;
|
|
})
|
|
.catch(err => {
|
|
this.scrolledErrorOccured = true;
|
|
setTimeout(() => {
|
|
this.scrolledErrorOccured = false;
|
|
}, 5000);
|
|
|
|
this.notificationService.notifyHttpError(err, this.account);
|
|
})
|
|
.then(() => {
|
|
this.isMentionsLoading = false;
|
|
});
|
|
}
|
|
|
|
focus(): boolean {
|
|
setTimeout(() => {
|
|
let element: HTMLElement;
|
|
if (this.displayingMentions) {
|
|
element = this.mentionstream.nativeElement as HTMLElement;
|
|
} else {
|
|
element = this.notificationstream.nativeElement as HTMLElement;
|
|
}
|
|
element.focus({preventScroll:true});
|
|
}, 0);
|
|
return false;
|
|
}
|
|
}
|