2019-03-24 22:52:34 +01:00
|
|
|
import { Injectable } from '@angular/core';
|
|
|
|
import { BehaviorSubject, Subject, Observable, Subscription } from 'rxjs';
|
|
|
|
import { Store } from '@ngxs/store';
|
2019-11-08 06:04:41 +01:00
|
|
|
import { Howl } from 'howler';
|
2019-03-24 22:52:34 +01:00
|
|
|
|
|
|
|
import { Status, Notification } from './models/mastodon.interfaces';
|
2019-10-02 06:14:40 +02:00
|
|
|
import { MastodonWrapperService } from './mastodon-wrapper.service';
|
2019-03-24 22:52:34 +01:00
|
|
|
import { AccountInfo } from '../states/accounts.state';
|
|
|
|
import { NotificationService } from './notification.service';
|
2019-11-08 05:05:48 +01:00
|
|
|
import { StreamingService, StatusUpdate, EventEnum } from './streaming.service';
|
|
|
|
import { StreamElement, StreamTypeEnum } from '../states/streams.state';
|
2021-02-28 08:38:42 +01:00
|
|
|
import { SettingsService } from './settings.service';
|
2019-03-24 22:52:34 +01:00
|
|
|
|
2019-11-08 06:04:41 +01:00
|
|
|
|
2019-03-24 22:52:34 +01:00
|
|
|
@Injectable({
|
|
|
|
providedIn: 'root'
|
|
|
|
})
|
2019-03-25 06:09:53 +01:00
|
|
|
export class UserNotificationService {
|
2019-03-24 22:52:34 +01:00
|
|
|
userNotifications = new BehaviorSubject<UserNotification[]>([]);
|
|
|
|
|
2019-11-14 04:03:56 +01:00
|
|
|
// private mentionsSinceIds: { [id: string]: string } = {};
|
|
|
|
// private notificationsSinceIds: { [id: string]: string } = {};
|
2019-03-25 06:22:55 +01:00
|
|
|
|
2019-11-08 06:35:48 +01:00
|
|
|
private sound: Howl;
|
2019-11-15 04:06:16 +01:00
|
|
|
private soundJustPlayed = false;
|
|
|
|
private soundFileId: string;
|
2019-11-08 06:35:48 +01:00
|
|
|
|
2020-05-01 00:46:15 +02:00
|
|
|
private accountSub: Subscription;
|
|
|
|
private loadedAccounts: AccountInfo[] = [];
|
|
|
|
|
2019-03-24 22:52:34 +01:00
|
|
|
constructor(
|
2021-02-28 08:38:42 +01:00
|
|
|
private readonly settingsService: SettingsService,
|
2019-11-07 06:19:06 +01:00
|
|
|
private readonly streamingService: StreamingService,
|
2019-03-24 22:52:34 +01:00
|
|
|
private readonly notificationService: NotificationService,
|
2019-10-02 06:14:40 +02:00
|
|
|
private readonly mastodonService: MastodonWrapperService,
|
2019-03-24 22:52:34 +01:00
|
|
|
private readonly store: Store) {
|
2020-05-01 00:46:15 +02:00
|
|
|
|
2019-11-15 04:06:16 +01:00
|
|
|
this.fetchNotifications();
|
2020-05-01 00:46:15 +02:00
|
|
|
}
|
2019-11-08 06:35:48 +01:00
|
|
|
|
2019-11-14 04:32:42 +01:00
|
|
|
private fetchNotifications() {
|
2019-03-24 22:52:34 +01:00
|
|
|
let accounts = this.store.snapshot().registeredaccounts.accounts;
|
|
|
|
|
2019-03-25 06:22:55 +01:00
|
|
|
accounts.forEach((account: AccountInfo) => {
|
2020-05-01 00:46:15 +02:00
|
|
|
this.loadedAccounts.push(account);
|
|
|
|
this.startFetchingNotifications(account);
|
|
|
|
});
|
2019-03-25 06:22:55 +01:00
|
|
|
|
2020-05-01 00:46:15 +02:00
|
|
|
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);
|
|
|
|
}
|
2019-11-14 04:03:56 +01:00
|
|
|
});
|
2020-05-01 00:46:15 +02:00
|
|
|
});
|
|
|
|
}
|
2019-11-07 06:19:06 +01:00
|
|
|
|
2020-05-01 00:46:15 +02:00
|
|
|
private startFetchingNotifications(account: AccountInfo) {
|
2023-04-24 07:04:44 +02:00
|
|
|
let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll', 'follow_request', 'move', 'update'], null, null, 10)
|
2020-05-01 00:46:15 +02:00
|
|
|
.then((notifications: Notification[]) => {
|
|
|
|
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
this.notificationService.notifyHttpError(err, account);
|
|
|
|
});
|
|
|
|
|
2023-04-24 07:04:44 +02:00
|
|
|
let getNotificationPromise = this.mastodonService.getNotifications(account, ['mention', 'update'], null, null, 10)
|
2020-05-01 00:46:15 +02:00
|
|
|
.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 => { });
|
2019-03-24 22:52:34 +01:00
|
|
|
}
|
|
|
|
|
2019-11-08 06:04:41 +01:00
|
|
|
private playSoundNotification() {
|
2021-02-28 08:38:42 +01:00
|
|
|
const settings = this.settingsService.getSettings();
|
2020-05-01 00:46:15 +02:00
|
|
|
if (settings.disableSounds) return;
|
|
|
|
if (this.soundJustPlayed) return;
|
2019-11-14 04:03:56 +01:00
|
|
|
this.soundJustPlayed = true;
|
2020-05-01 00:46:15 +02:00
|
|
|
|
2019-11-16 23:52:21 +01:00
|
|
|
this.setNotificationSound();
|
2019-11-08 06:35:48 +01:00
|
|
|
this.sound.play();
|
2019-11-14 04:03:56 +01:00
|
|
|
|
2020-05-01 00:46:15 +02:00
|
|
|
setTimeout(() => {
|
2019-11-14 04:03:56 +01:00
|
|
|
this.soundJustPlayed = false;
|
|
|
|
}, 2000);
|
2019-11-08 06:04:41 +01:00
|
|
|
}
|
|
|
|
|
2019-11-15 04:06:16 +01:00
|
|
|
private setNotificationSound() {
|
2021-02-28 08:38:42 +01:00
|
|
|
let settings = this.settingsService.getSettings();
|
2020-05-01 00:46:15 +02:00
|
|
|
let soundId = settings.notificationSoundFileId;
|
|
|
|
|
|
|
|
if (!soundId) {
|
2019-11-15 04:06:16 +01:00
|
|
|
soundId = '0';
|
|
|
|
settings.notificationSoundFileId = '0';
|
2021-02-28 08:38:42 +01:00
|
|
|
this.settingsService.saveSettings(settings);
|
2019-11-15 04:06:16 +01:00
|
|
|
}
|
|
|
|
|
2020-05-01 00:46:15 +02:00
|
|
|
if (this.soundFileId === soundId) return;
|
2019-11-24 00:24:53 +01:00
|
|
|
|
2019-11-15 04:06:16 +01:00
|
|
|
var sound = this.getAllNotificationSounds().find(x => x.id === soundId);
|
|
|
|
this.sound = new Howl({
|
|
|
|
src: [sound.path]
|
|
|
|
});
|
|
|
|
this.soundFileId = soundId;
|
|
|
|
}
|
|
|
|
|
2019-11-08 05:05:48 +01:00
|
|
|
private processNewUpdate(account: AccountInfo, notification: StatusUpdate) {
|
2020-05-01 00:46:15 +02:00
|
|
|
if (!notification && !notification.notification) return;
|
2019-11-08 06:56:44 +01:00
|
|
|
|
2020-05-01 00:46:15 +02:00
|
|
|
if (!notification.muteSound) {
|
2019-11-14 04:03:56 +01:00
|
|
|
this.playSoundNotification();
|
|
|
|
}
|
2019-11-08 06:04:41 +01:00
|
|
|
|
2019-11-08 05:05:48 +01:00
|
|
|
if (notification.notification.type === 'mention') {
|
|
|
|
this.processMentionsAndNotifications(account, [notification.notification], NotificationTypeEnum.UserMention);
|
|
|
|
} else {
|
|
|
|
this.processMentionsAndNotifications(account, [notification.notification], NotificationTypeEnum.UserNotification);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
private processMentionsAndNotifications(account: AccountInfo, notifications: Notification[], type: NotificationTypeEnum) {
|
2019-03-28 05:46:37 +01:00
|
|
|
if (notifications.length === 0) {
|
2019-03-25 06:22:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-24 22:52:34 +01:00
|
|
|
let currentNotifications = this.userNotifications.value;
|
2020-05-01 00:46:15 +02:00
|
|
|
let currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
|
2019-03-25 05:52:30 +01:00
|
|
|
|
2019-03-24 22:52:34 +01:00
|
|
|
if (currentAccountNotifications) {
|
2019-11-07 06:19:06 +01:00
|
|
|
currentAccountNotifications = this.analyseNotifications(account, currentAccountNotifications, notifications, type);
|
2019-03-24 22:52:34 +01:00
|
|
|
|
2020-04-22 03:45:21 +02:00
|
|
|
//if (currentAccountNotifications.hasNewMentions || currentAccountNotifications.hasNewNotifications) {
|
2020-05-01 00:46:15 +02:00
|
|
|
currentNotifications = currentNotifications.filter(x => x.account.id !== account.id);
|
|
|
|
currentNotifications.push(currentAccountNotifications);
|
|
|
|
this.userNotifications.next(currentNotifications);
|
2020-04-22 03:45:21 +02:00
|
|
|
//}
|
2019-03-24 22:52:34 +01:00
|
|
|
} else {
|
2019-03-28 05:46:37 +01:00
|
|
|
let newNotifications = new UserNotification();
|
2019-03-24 22:52:34 +01:00
|
|
|
newNotifications.account = account;
|
2019-11-07 06:19:06 +01:00
|
|
|
newNotifications.mentions = [];
|
|
|
|
newNotifications.notifications = [];
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
newNotifications = this.analyseNotifications(account, newNotifications, notifications, type);
|
2019-03-24 22:52:34 +01:00
|
|
|
|
|
|
|
currentNotifications.push(newNotifications);
|
|
|
|
this.userNotifications.next(currentNotifications);
|
|
|
|
}
|
|
|
|
}
|
2019-03-25 05:52:30 +01:00
|
|
|
|
2019-11-08 05:05:48 +01:00
|
|
|
private hasNewNotifications(lastNotification: Notification, lastCreationDate: string): boolean {
|
|
|
|
if (!lastNotification) return false;
|
|
|
|
if (!lastCreationDate) return false;
|
2019-11-07 06:19:06 +01:00
|
|
|
return new Date(lastNotification.created_at) > new Date(lastCreationDate);
|
|
|
|
}
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
private analyseNotifications(account: AccountInfo, userNotification: UserNotification, newNotifications: Notification[], type: NotificationTypeEnum): UserNotification {
|
2019-11-08 05:05:48 +01:00
|
|
|
|
|
|
|
let lastNotificationId = newNotifications[newNotifications.length - 1].id;
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2021-02-28 08:38:42 +01:00
|
|
|
const accountSettings = this.settingsService.getAccountSettings(account);
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
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);
|
2019-04-03 02:39:44 +02:00
|
|
|
}
|
|
|
|
|
2019-11-14 04:03:56 +01:00
|
|
|
// Set settings if needed
|
2019-11-07 06:19:06 +01:00
|
|
|
if (type === NotificationTypeEnum.UserMention && !accountSettings.lastMentionCreationDate && newNotifications.length > 0) {
|
|
|
|
accountSettings.lastMentionCreationDate = newNotifications[0].created_at;
|
2021-02-28 08:38:42 +01:00
|
|
|
this.settingsService.saveAccountSettings(accountSettings);
|
2019-04-01 00:53:11 +02:00
|
|
|
}
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
if (type === NotificationTypeEnum.UserNotification && !accountSettings.lastNotificationCreationDate && newNotifications.length > 0) {
|
|
|
|
accountSettings.lastNotificationCreationDate = newNotifications[0].created_at;
|
2021-02-28 08:38:42 +01:00
|
|
|
this.settingsService.saveAccountSettings(accountSettings);
|
2019-11-07 06:19:06 +01:00
|
|
|
}
|
|
|
|
|
2019-03-28 05:46:37 +01:00
|
|
|
return userNotification;
|
|
|
|
}
|
|
|
|
|
2019-03-25 05:52:30 +01:00
|
|
|
markMentionsAsRead(account: AccountInfo) {
|
|
|
|
let currentNotifications = this.userNotifications.value;
|
|
|
|
const currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
|
2019-03-28 05:57:09 +01:00
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
const lastMention = currentAccountNotifications.mentions[0];
|
2019-04-03 03:42:38 +02:00
|
|
|
|
2019-11-07 06:19:06 +01:00
|
|
|
if (lastMention) {
|
2021-02-28 08:38:42 +01:00
|
|
|
const settings = this.settingsService.getAccountSettings(account);
|
2019-11-07 06:19:06 +01:00
|
|
|
// const lastMentionNotification = currentAccountNotifications.mentions[0];
|
|
|
|
settings.lastMentionCreationDate = lastMention.created_at;
|
2021-02-28 08:38:42 +01:00
|
|
|
this.settingsService.saveAccountSettings(settings);
|
2019-04-01 00:53:11 +02:00
|
|
|
}
|
|
|
|
|
2019-03-28 05:57:09 +01:00
|
|
|
if (currentAccountNotifications.hasNewMentions === true) {
|
|
|
|
currentAccountNotifications.hasNewMentions = false;
|
|
|
|
this.userNotifications.next(currentNotifications);
|
|
|
|
}
|
2019-03-25 05:52:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
markNotificationAsRead(account: AccountInfo) {
|
|
|
|
let currentNotifications = this.userNotifications.value;
|
|
|
|
const currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
|
2019-03-28 05:57:09 +01:00
|
|
|
|
2019-04-01 00:53:11 +02:00
|
|
|
const lastNotification = currentAccountNotifications.notifications[0];
|
2019-04-03 02:39:44 +02:00
|
|
|
if (lastNotification) {
|
2021-02-28 08:38:42 +01:00
|
|
|
const settings = this.settingsService.getAccountSettings(account);
|
2019-04-03 03:42:38 +02:00
|
|
|
settings.lastNotificationCreationDate = lastNotification.created_at;
|
2021-02-28 08:38:42 +01:00
|
|
|
this.settingsService.saveAccountSettings(settings);
|
2019-04-01 00:53:11 +02:00
|
|
|
}
|
|
|
|
|
2019-03-28 05:57:09 +01:00
|
|
|
if (currentAccountNotifications.hasNewNotifications === true) {
|
|
|
|
currentAccountNotifications.hasNewNotifications = false;
|
|
|
|
this.userNotifications.next(currentNotifications);
|
|
|
|
}
|
2019-03-25 05:52:30 +01:00
|
|
|
}
|
2019-11-14 04:32:42 +01:00
|
|
|
|
|
|
|
getAllNotificationSounds(): NotificationSoundDefinition[] {
|
|
|
|
let defs: NotificationSoundDefinition[] = [
|
2019-11-14 05:12:00 +01:00
|
|
|
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'),
|
2020-05-01 00:46:15 +02:00
|
|
|
new NotificationSoundDefinition('3', 'assets/audio/boop.mp3', 'Mastodon boop'),
|
2019-11-14 04:32:42 +01:00
|
|
|
];
|
|
|
|
return defs;
|
|
|
|
}
|
|
|
|
|
2019-03-24 22:52:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class UserNotification {
|
|
|
|
account: AccountInfo;
|
2019-11-07 06:19:06 +01:00
|
|
|
//allNotifications: Notification[] = [];
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2019-03-24 22:52:34 +01:00
|
|
|
hasNewNotifications: boolean;
|
|
|
|
hasNewMentions: boolean;
|
2019-03-28 05:46:37 +01:00
|
|
|
|
2019-04-01 02:32:13 +02:00
|
|
|
notifications: Notification[] = [];
|
2019-11-07 06:19:06 +01:00
|
|
|
mentions: Notification[] = [];
|
|
|
|
|
|
|
|
lastMentionsId: string;
|
|
|
|
lastNotificationsId: string;
|
2019-03-24 22:52:34 +01:00
|
|
|
}
|
2019-11-07 06:19:06 +01:00
|
|
|
|
|
|
|
enum NotificationTypeEnum {
|
|
|
|
UserNotification,
|
|
|
|
UserMention
|
2019-11-14 04:32:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class NotificationSoundDefinition {
|
|
|
|
|
|
|
|
constructor(
|
2019-11-14 05:12:00 +01:00
|
|
|
public readonly id: string,
|
2019-11-14 04:32:42 +01:00
|
|
|
public readonly path: string,
|
2020-05-01 00:46:15 +02:00
|
|
|
public readonly name: string) { }
|
2019-11-07 06:19:06 +01:00
|
|
|
}
|