mirror of
https://gitlab.com/comunic/comunicapiv2
synced 2025-02-16 22:22:39 +00:00
554 lines
14 KiB
TypeScript
554 lines
14 KiB
TypeScript
import { DatabaseHelper } from "./DatabaseHelper";
|
|
import { Notif, NotifElemType, NotifEventVisibility, NotifEventType } from "../entities/Notification";
|
|
import { time } from "../utils/DateUtils";
|
|
import { PostsHelper } from "./PostsHelper";
|
|
import { PostPageKind, PostVisibilityLevel } from "../entities/Post";
|
|
import { FriendsHelper } from "./FriendsHelper";
|
|
import { GroupsHelper } from "./GroupsHelper";
|
|
import { GroupMembershipLevels } from "../entities/GroupMember";
|
|
import { EventsHelper } from "./EventsHelper";
|
|
|
|
/**
|
|
* Notifications helper
|
|
*
|
|
* @author Pierre HUBERT
|
|
*/
|
|
|
|
const NOTIFICATIONS_TABLE = "comunic_notifications";
|
|
|
|
export class NotificationsHelper {
|
|
|
|
/**
|
|
* Create a notification about a post
|
|
*
|
|
* @param fromUser Source user ID
|
|
* @param postID Target post ID
|
|
* @param action Action to notify
|
|
*/
|
|
public static async CreatePostNotification(fromUser: number, postID: number, action: NotifEventType) {
|
|
await this.Push(new Notif({
|
|
timeCreate: time(),
|
|
fromUserID: fromUser,
|
|
onElemID: postID,
|
|
onElemType: NotifElemType.POST,
|
|
type: action
|
|
}));
|
|
}
|
|
|
|
|
|
/**
|
|
* Create & push friendship request notification
|
|
*
|
|
* @param fromUser Source user ID
|
|
* @param destUser Destination user ID
|
|
* @param action The kind of action
|
|
*/
|
|
public static async CreateFriendsNotifications(fromUser: number, destUser: number, action: NotifEventType) {
|
|
await this.DeleteNotificationsFrienshipRequest(fromUser, destUser);
|
|
|
|
// Push the notification
|
|
await this.Push(new Notif({
|
|
fromUserID: fromUser,
|
|
destUserID: destUser,
|
|
onElemID: fromUser, // Same as fromUser
|
|
onElemType: NotifElemType.FRIENDSHIP_REQUEST,
|
|
type: action
|
|
}));
|
|
}
|
|
|
|
|
|
/**
|
|
* Create & push a group membership notification
|
|
*
|
|
* @param userID The ID of the target user for the membership
|
|
* @param moderatorID The ID of the moderator creating the notification (0 if it is the user)
|
|
* @param groupID The ID of the target group
|
|
* @param kind The kind of notification to create
|
|
*/
|
|
public static async CreateGroupMembershipNotification(userID: number, moderatorID: number, groupID: number, kind: NotifEventType) {
|
|
await this.DeleteNotificationsGroupsMembership(userID, groupID);
|
|
|
|
const n = new Notif({
|
|
onElemID: groupID,
|
|
onElemType: NotifElemType.GROUP_MEMBERSHIP,
|
|
type: kind
|
|
});
|
|
|
|
|
|
// The notification must be sent to all the moderators of the group
|
|
if(moderatorID < 1) {
|
|
n.fromUserID = userID;
|
|
n.destUserID = -1;
|
|
}
|
|
|
|
// We specify both the source and the destination of the notification
|
|
// not to broadcast the notification to all the group members
|
|
else {
|
|
n.fromUserID = moderatorID;
|
|
n.destUserID = userID;
|
|
}
|
|
|
|
await this.Push(n);
|
|
}
|
|
|
|
/**
|
|
* Push a new notification
|
|
*
|
|
* @param n Notification to push
|
|
*/
|
|
public static async Push(n: Notif) {
|
|
|
|
if(!n.hasTimeCreate)
|
|
n.timeCreate = time();
|
|
|
|
// Determine the visibility level of the notification
|
|
if(n.onElemType == NotifElemType.POST) {
|
|
const post = await PostsHelper.GetSingle(n.onElemID);
|
|
|
|
// Determine post container
|
|
switch(post.kindPage) {
|
|
|
|
case PostPageKind.PAGE_KIND_USER:
|
|
n.fromContainerType = NotifElemType.USER_PAGE
|
|
n.fromContainerID = post.userPageID
|
|
break;
|
|
|
|
case PostPageKind.PAGE_KIND_GROUP:
|
|
n.fromContainerType = NotifElemType.GROUP_PAGE
|
|
n.fromContainerID = post.groupID
|
|
break;
|
|
|
|
default:
|
|
throw new Error("Unsupported page kind: " + post.kindPage)
|
|
}
|
|
|
|
|
|
// Check the scope of the notification
|
|
// Private post (on user pages)
|
|
if(post.visibilityLevel == PostVisibilityLevel.VISIBILITY_USER) {
|
|
|
|
// Check if the user does not own the post
|
|
if(post.userID == post.userPageID)
|
|
return;
|
|
|
|
// If the personn who posted that is not the owner of the page
|
|
else if(post.userPageID != n.fromUserID)
|
|
n.destUserID = post.userPageID
|
|
|
|
// If the user is the one who own the page
|
|
else
|
|
n.destUserID = post.userID
|
|
|
|
await this.PushPrivate(n)
|
|
}
|
|
|
|
// Posts on users page
|
|
else if(n.fromContainerType == NotifElemType.USER_PAGE) {
|
|
|
|
// Process friends list of the user creating the notification
|
|
const friendsList = await FriendsHelper.GetList(n.fromUserID);
|
|
const targetUsers = [];
|
|
|
|
for(const friend of friendsList) {
|
|
if(friend.friendID != n.fromContainerID &&
|
|
post.userPageID != n.fromUserID && // Skip friendship check if user
|
|
// creating the notification
|
|
// & target page are the same
|
|
!await FriendsHelper.AreFriend(friend.friendID, post.userPageID))
|
|
continue;
|
|
|
|
// Check if the user is following his friend
|
|
if(!await FriendsHelper.IsFollowing(friend.friendID, n.fromUserID))
|
|
continue;
|
|
|
|
targetUsers.push(friend.friendID)
|
|
}
|
|
|
|
await this.PushPublic(n, targetUsers);
|
|
|
|
}
|
|
|
|
// For posts on grou pages
|
|
else if(n.fromContainerType == NotifElemType.GROUP_PAGE) {
|
|
await this.PushGroupMembers(n, n.fromContainerID)
|
|
}
|
|
|
|
// Mark the scenario as unsupported
|
|
else {
|
|
console.error(n)
|
|
throw new Error("Post notification scenario not supported!")
|
|
}
|
|
}
|
|
|
|
// Frienship notifications
|
|
else if(n.onElemType == NotifElemType.FRIENDSHIP_REQUEST) {
|
|
n.fromContainerID = 0
|
|
n.fromContainerType = NotifElemType.EMPTY;
|
|
await this.PushPrivate(n);
|
|
}
|
|
|
|
|
|
// Groups membership notifications
|
|
else if (n.onElemType == NotifElemType.GROUP_MEMBERSHIP) {
|
|
|
|
// Complete the notification
|
|
n.fromContainerType = NotifElemType.EMPTY;
|
|
n.fromContainerID = 0;
|
|
|
|
// Check whether the notification has to be pushed to a single user
|
|
// or to all the moderators of the group
|
|
if(n.hasDestUserID) {
|
|
|
|
//Push the notification in private way (if it has a destination,
|
|
//generally the target user of the membership request)
|
|
await this.PushPrivate(n);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Push the notification to all the moderators of the group
|
|
await this.PushGroupModerators(n, n.onElemID);
|
|
|
|
}
|
|
}
|
|
|
|
// Unsupported notification type
|
|
else {
|
|
console.error(n)
|
|
throw new Error("Type of notification not supported!")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Push a notification to all the members of a group
|
|
*
|
|
* @param n The notification to push
|
|
* @param groupID Target group ID
|
|
*/
|
|
private static async PushGroupMembers(n: Notif, groupID: number) {
|
|
let list = await GroupsHelper.GetListFollowers(groupID);
|
|
|
|
// Remove the user at the source of the notification
|
|
list = list.filter((v) => v != n.fromUserID);
|
|
|
|
await this.PushPublic(n, list);
|
|
}
|
|
|
|
/**
|
|
* Push a notification to all the moderators & administrators of a group
|
|
*
|
|
* @param n The notification to push
|
|
* @param groupID Target group ID
|
|
*/
|
|
private static async PushGroupModerators(n: Notif, groupID: number) {
|
|
let list = await GroupsHelper.GetListMembers(groupID);
|
|
|
|
// Remove all the users who are not moderators or administrators
|
|
list = list.filter((v) => v.level <= GroupMembershipLevels.MODERATOR);
|
|
const ids = list.map((v) => v.userID);
|
|
|
|
await this.PushPublic(n, ids);
|
|
}
|
|
|
|
/**
|
|
* Push a notification to multiple users
|
|
*
|
|
* @param n The notification to push
|
|
* @param users The list of target users
|
|
*/
|
|
private static async PushPublic(n: Notif, users: Array<number>) {
|
|
n.eventVisibility = NotifEventVisibility.EVENT_PUBLIC
|
|
|
|
for(const userID of users) {
|
|
|
|
n.destUserID = userID;
|
|
|
|
if(await this.SimilarExists(n))
|
|
continue
|
|
|
|
await this.Create(n);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Push a private notification
|
|
*
|
|
* @param n The notification
|
|
*/
|
|
private static async PushPrivate(n: Notif) {
|
|
n.eventVisibility = NotifEventVisibility.EVENT_PRIVATE
|
|
|
|
if(await this.SimilarExists(n))
|
|
return;
|
|
|
|
await this.Create(n)
|
|
}
|
|
|
|
/**
|
|
* Check out whether a similar notification exists
|
|
* for a given notification
|
|
*
|
|
* @param n The notification
|
|
*/
|
|
public static async SimilarExists(n: Notif) : Promise<boolean> {
|
|
return await DatabaseHelper.Count({
|
|
table: NOTIFICATIONS_TABLE,
|
|
where: this.NotifToDB(n, false)
|
|
}) > 0;
|
|
}
|
|
|
|
/**
|
|
* Create the notification
|
|
*
|
|
* @param n The notification
|
|
*/
|
|
private static async Create(n: Notif) {
|
|
await DatabaseHelper.InsertRow(
|
|
NOTIFICATIONS_TABLE,
|
|
this.NotifToDB(n, true)
|
|
)
|
|
|
|
// Trigger notify system
|
|
await EventsHelper.Emit("updated_number_notifications", {usersID: [n.destUserID]});
|
|
}
|
|
|
|
/**
|
|
* Delete notifications
|
|
*
|
|
* @param n Notification
|
|
*/
|
|
public static async Delete(n: Notif) {
|
|
// Delete a specific notification
|
|
const cond = n.hasId ? {id: n.id} : this.NotifToDB(n, false);
|
|
|
|
// Check for affected users
|
|
const users = new Set<number>();
|
|
(await DatabaseHelper.Query({
|
|
table: NOTIFICATIONS_TABLE,
|
|
where: cond,
|
|
fields: ["dest_user_id"]
|
|
})).forEach((row) => users.add(row.dest_user_id))
|
|
|
|
// Delete notifications
|
|
await DatabaseHelper.DeleteRows(NOTIFICATIONS_TABLE, cond);
|
|
|
|
// Trigger notifications system
|
|
await EventsHelper.Emit("updated_number_notifications", {usersID: [...users]});
|
|
}
|
|
|
|
/**
|
|
* Delete all the notifications of a given user
|
|
*
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async DeleteAllUser(userID: number) {
|
|
await this.Delete(new Notif({
|
|
destUserID: userID
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Delete all the notifications related with a group
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
public static async DeleteAllRelatedWithGroup(groupID: number) {
|
|
const n = new Notif({
|
|
onElemType: NotifElemType.GROUP_MEMBERSHIP,
|
|
onElemID: groupID
|
|
});
|
|
await this.Delete(n);
|
|
|
|
n.onElemType = NotifElemType.GROUP_PAGE;
|
|
await this.Delete(n);
|
|
}
|
|
|
|
/**
|
|
* Delete all the notifications related with a specified user
|
|
*
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async DeleteAllRelatedWithUser(userID: number) {
|
|
// Delete all the notifications targetting the user
|
|
await this.DeleteAllUser(userID);
|
|
|
|
// Delete all the notifications created by the user
|
|
await this.Delete(new Notif({
|
|
fromUserID: userID
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Delete all the notifications about a post targetting a specified
|
|
* user
|
|
*
|
|
* @param userID Target user ID
|
|
* @param postID Target post ID
|
|
*/
|
|
public static async DeleteAllPostsNotificationsTargetingUser(userID: number, postID: number) {
|
|
await this.Delete(new Notif({
|
|
destUserID: userID,
|
|
onElemType: NotifElemType.POST,
|
|
onElemID: postID
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Delete all the notification related with a post
|
|
*
|
|
* @param postID Target post id
|
|
*/
|
|
public static async DeleteAllRelatedWithPost(postID: number) {
|
|
await this.Delete(new Notif({
|
|
onElemType: NotifElemType.POST,
|
|
onElemID: postID
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Delete all the notifications related to a friendship request between two users
|
|
*
|
|
* @param userOne First user
|
|
* @param userTwo Second user
|
|
*/
|
|
public static async DeleteNotificationsFrienshipRequest(userOne: number, userTwo: number) {
|
|
|
|
// Delete notifications in two ways
|
|
await this.Delete(new Notif({
|
|
onElemType: NotifElemType.FRIENDSHIP_REQUEST,
|
|
destUserID: userOne,
|
|
fromUserID: userTwo,
|
|
}));
|
|
|
|
await this.Delete(new Notif({
|
|
onElemType: NotifElemType.FRIENDSHIP_REQUEST,
|
|
destUserID: userTwo,
|
|
fromUserID: userOne,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Delete all the notifications related to a specific group membership
|
|
*
|
|
* @param userID Target user
|
|
* @param groupID The ID of the target group
|
|
*/
|
|
public static async DeleteNotificationsGroupsMembership(userID: number, groupID: number) {
|
|
|
|
const n = new Notif({
|
|
onElemType: NotifElemType.GROUP_MEMBERSHIP,
|
|
onElemID: groupID
|
|
});
|
|
|
|
n.destUserID = userID;
|
|
n.fromUserID = -1;
|
|
await this.Delete(n);
|
|
|
|
n.destUserID = -1;
|
|
n.fromUserID = userID;
|
|
await this.Delete(n);
|
|
|
|
}
|
|
|
|
/**
|
|
* Count the number of unread notifications of a user
|
|
*
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async CountUnread(userID: number) : Promise<number> {
|
|
return await DatabaseHelper.Count({
|
|
table: NOTIFICATIONS_TABLE,
|
|
where: {
|
|
dest_user_id: userID,
|
|
seen: 0
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the list of unread notifications of a user
|
|
*
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async GetListUnread(userID: number) : Promise<Array<Notif>> {
|
|
const list = await DatabaseHelper.Query({
|
|
table: NOTIFICATIONS_TABLE,
|
|
where: {
|
|
dest_user_id: userID,
|
|
seen: 0
|
|
},
|
|
order: "id DESC"
|
|
});
|
|
|
|
return list.map((e) => this.DBToNotif(e));
|
|
}
|
|
|
|
/**
|
|
* Get information about a single notification
|
|
*
|
|
* @param notifID Target notification id
|
|
*/
|
|
public static async GetSingle(notifID: number) : Promise<Notif> {
|
|
const row = await DatabaseHelper.QueryRow({
|
|
table: NOTIFICATIONS_TABLE,
|
|
where: {
|
|
id: notifID
|
|
}
|
|
});
|
|
|
|
if(row == null)
|
|
throw new Error("Could not find notification !");
|
|
|
|
return this.DBToNotif(row);
|
|
}
|
|
|
|
/**
|
|
* Turn a notification into a database entry
|
|
*
|
|
* @param n The notification
|
|
* @param allInfo True to return a full notification entry
|
|
*/
|
|
private static NotifToDB(n: Notif, allInfo = true) : any {
|
|
let data: any = {}
|
|
|
|
if(n.hasId) data.id = n.id
|
|
if(n.hasSeen) data.seen = n.seen ? 1 : 0
|
|
if(n.hasFromUserID) data.from_user_id = n.fromUserID
|
|
if(n.hasDestUserID) data.dest_user_id = n.destUserID
|
|
if(n.hasType) data.type = n.type
|
|
if(n.hasOnElemID) data.on_elem_id = n.onElemID
|
|
if(n.hasOnElemType) data.on_elem_type = n.onElemType
|
|
|
|
if(allInfo) {
|
|
data.from_container_id = n.fromContainerID
|
|
data.from_container_type = n.fromContainerType
|
|
data.time_create = n.timeCreate
|
|
data.visibility = n.eventVisibility
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Database entry to notification
|
|
*
|
|
* @param row Database entry
|
|
*/
|
|
private static DBToNotif(row: any) : Notif {
|
|
return new Notif({
|
|
id: row.id,
|
|
seen: row.seen == 1,
|
|
timeCreate: row.time_create,
|
|
fromUserID: row.from_user_id,
|
|
destUserID: row.dest_user_id,
|
|
onElemID: row.on_elem_id,
|
|
onElemType: row.on_elem_type,
|
|
type: row.type,
|
|
eventVisibility: row.visibility,
|
|
fromContainerID: row.from_container_id,
|
|
fromContainerType: row.from_container_type
|
|
});
|
|
}
|
|
} |