Sengi-Windows-MacOS-Linux/src/app/services/user-notification.service.ts

279 lines
11 KiB
TypeScript

import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject, Observable, Subscription } from 'rxjs';
import { Store } from '@ngxs/store';
import { Howl } from 'howler';
import { Status, Notification } from './models/mastodon.interfaces';
import { MastodonWrapperService } from './mastodon-wrapper.service';
import { AccountInfo } from '../states/accounts.state';
import { NotificationService } from './notification.service';
import { StreamingService, StatusUpdate, EventEnum } from './streaming.service';
import { StreamElement, StreamTypeEnum } from '../states/streams.state';
import { SettingsService } from './settings.service';
@Injectable({
providedIn: 'root'
})
export class UserNotificationService {
userNotifications = new BehaviorSubject<UserNotification[]>([]);
// private mentionsSinceIds: { [id: string]: string } = {};
// private notificationsSinceIds: { [id: string]: string } = {};
private sound: Howl;
private soundJustPlayed = false;
private soundFileId: string;
private accountSub: Subscription;
private loadedAccounts: AccountInfo[] = [];
constructor(
private readonly settingsService: SettingsService,
private readonly streamingService: StreamingService,
private readonly notificationService: NotificationService,
private readonly mastodonService: MastodonWrapperService,
private readonly store: Store) {
this.fetchNotifications();
}
private fetchNotifications() {
let accounts = this.store.snapshot().registeredaccounts.accounts;
accounts.forEach((account: AccountInfo) => {
this.loadedAccounts.push(account);
this.startFetchingNotifications(account);
});
this.accountSub = this.store.select(state => state.registeredaccounts.accounts)
.subscribe((accounts: AccountInfo[]) => {
accounts.forEach(a => {
if(!this.loadedAccounts.find(x => x.id === a.id)){
this.loadedAccounts.push(a);
this.startFetchingNotifications(a);
}
});
});
}
private startFetchingNotifications(account: AccountInfo) {
let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll', 'follow_request', 'move', 'update'], null, null, 10)
.then((notifications: Notification[]) => {
notifications = notifications.filter(x => x.status !== null);
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
})
.catch(err => {
this.notificationService.notifyHttpError(err, account);
});
let getNotificationPromise = this.mastodonService.getNotifications(account, ['mention'], null, null, 10)
.then((notifications: Notification[]) => {
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserNotification);
})
.catch(err => {
this.notificationService.notifyHttpError(err, account);
});
Promise.all([getMentionsPromise, getNotificationPromise])
.then(() => {
let streamElement = new StreamElement(StreamTypeEnum.personnal, 'activity', account.id, null, null, null, account.instance);
let streaming = this.streamingService.getStreaming(account, streamElement);
streaming.statusUpdateSubjet.subscribe((notification: StatusUpdate) => {
if (notification && notification.type === EventEnum.notification) {
this.processNewUpdate(account, notification);
}
});
})
.catch(err => { });
}
private playSoundNotification() {
const settings = this.settingsService.getSettings();
if (settings.disableSounds) return;
if (this.soundJustPlayed) return;
this.soundJustPlayed = true;
this.setNotificationSound();
this.sound.play();
setTimeout(() => {
this.soundJustPlayed = false;
}, 2000);
}
private setNotificationSound() {
let settings = this.settingsService.getSettings();
let soundId = settings.notificationSoundFileId;
if (!soundId) {
soundId = '0';
settings.notificationSoundFileId = '0';
this.settingsService.saveSettings(settings);
}
if (this.soundFileId === soundId) return;
var sound = this.getAllNotificationSounds().find(x => x.id === soundId);
this.sound = new Howl({
src: [sound.path]
});
this.soundFileId = soundId;
}
private processNewUpdate(account: AccountInfo, notification: StatusUpdate) {
if (!notification && !notification.notification) return;
if (!notification.muteSound) {
this.playSoundNotification();
}
if (notification.notification.type === 'mention') {
this.processMentionsAndNotifications(account, [notification.notification], NotificationTypeEnum.UserMention);
} else {
this.processMentionsAndNotifications(account, [notification.notification], NotificationTypeEnum.UserNotification);
}
}
private processMentionsAndNotifications(account: AccountInfo, notifications: Notification[], type: NotificationTypeEnum) {
if (notifications.length === 0) {
return;
}
let currentNotifications = this.userNotifications.value;
let currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
if (currentAccountNotifications) {
currentAccountNotifications = this.analyseNotifications(account, currentAccountNotifications, notifications, type);
//if (currentAccountNotifications.hasNewMentions || currentAccountNotifications.hasNewNotifications) {
currentNotifications = currentNotifications.filter(x => x.account.id !== account.id);
currentNotifications.push(currentAccountNotifications);
this.userNotifications.next(currentNotifications);
//}
} else {
let newNotifications = new UserNotification();
newNotifications.account = account;
newNotifications.mentions = [];
newNotifications.notifications = [];
newNotifications = this.analyseNotifications(account, newNotifications, notifications, type);
currentNotifications.push(newNotifications);
this.userNotifications.next(currentNotifications);
}
}
private hasNewNotifications(lastNotification: Notification, lastCreationDate: string): boolean {
if (!lastNotification) return false;
if (!lastCreationDate) return false;
return new Date(lastNotification.created_at) > new Date(lastCreationDate);
}
private analyseNotifications(account: AccountInfo, userNotification: UserNotification, newNotifications: Notification[], type: NotificationTypeEnum): UserNotification {
let lastNotificationId = newNotifications[newNotifications.length - 1].id;
const accountSettings = this.settingsService.getAccountSettings(account);
if (type === NotificationTypeEnum.UserMention) {
userNotification.lastMentionsId = lastNotificationId;
// let status = newNotifications.map(x => x.status);
userNotification.mentions = [...newNotifications, ...userNotification.mentions];
userNotification.hasNewMentions = this.hasNewNotifications(userNotification.mentions[0], accountSettings.lastMentionCreationDate);
} else {
userNotification.lastNotificationsId = lastNotificationId;
userNotification.notifications = [...newNotifications, ...userNotification.notifications];
userNotification.hasNewNotifications = this.hasNewNotifications(userNotification.notifications[0], accountSettings.lastNotificationCreationDate);
}
// Set settings if needed
if (type === NotificationTypeEnum.UserMention && !accountSettings.lastMentionCreationDate && newNotifications.length > 0) {
accountSettings.lastMentionCreationDate = newNotifications[0].created_at;
this.settingsService.saveAccountSettings(accountSettings);
}
if (type === NotificationTypeEnum.UserNotification && !accountSettings.lastNotificationCreationDate && newNotifications.length > 0) {
accountSettings.lastNotificationCreationDate = newNotifications[0].created_at;
this.settingsService.saveAccountSettings(accountSettings);
}
return userNotification;
}
markMentionsAsRead(account: AccountInfo) {
let currentNotifications = this.userNotifications.value;
const currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
const lastMention = currentAccountNotifications.mentions[0];
if (lastMention) {
const settings = this.settingsService.getAccountSettings(account);
// const lastMentionNotification = currentAccountNotifications.mentions[0];
settings.lastMentionCreationDate = lastMention.created_at;
this.settingsService.saveAccountSettings(settings);
}
if (currentAccountNotifications.hasNewMentions === true) {
currentAccountNotifications.hasNewMentions = false;
this.userNotifications.next(currentNotifications);
}
}
markNotificationAsRead(account: AccountInfo) {
let currentNotifications = this.userNotifications.value;
const currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
const lastNotification = currentAccountNotifications.notifications[0];
if (lastNotification) {
const settings = this.settingsService.getAccountSettings(account);
settings.lastNotificationCreationDate = lastNotification.created_at;
this.settingsService.saveAccountSettings(settings);
}
if (currentAccountNotifications.hasNewNotifications === true) {
currentAccountNotifications.hasNewNotifications = false;
this.userNotifications.next(currentNotifications);
}
}
getAllNotificationSounds(): NotificationSoundDefinition[] {
let defs: NotificationSoundDefinition[] = [
new NotificationSoundDefinition('0', 'assets/audio/all-eyes-on-me.mp3', 'All eyes on me'),
new NotificationSoundDefinition('1', 'assets/audio/exquisite.mp3', 'Exquisite'),
new NotificationSoundDefinition('2', 'assets/audio/appointed.mp3', 'Appointed'),
new NotificationSoundDefinition('3', 'assets/audio/boop.mp3', 'Mastodon boop'),
];
return defs;
}
}
export class UserNotification {
account: AccountInfo;
//allNotifications: Notification[] = [];
hasNewNotifications: boolean;
hasNewMentions: boolean;
notifications: Notification[] = [];
mentions: Notification[] = [];
lastMentionsId: string;
lastNotificationsId: string;
}
enum NotificationTypeEnum {
UserNotification,
UserMention
}
export class NotificationSoundDefinition {
constructor(
public readonly id: string,
public readonly path: string,
public readonly name: string) { }
}