Source

classes/notification.ts

import Collection from "@discordjs/collection";
import { NotificationType } from "../types/notification";
import { SnowflakeString } from "../types/snowflake";
import Animation, { BasicAnimation } from "./animation";
import Comment from "./comment";
import Message from "./message";
import Rating from "./rating";
import { RequestContext } from "./requestContext";
import Snowflake from "./snowflake";
import User from "./user";
import { DBNotification, DBNotificationAny, prisma } from "../../prisma";

/**
 * Handles notification data
 */
class Notification {
    id: Snowflake;
    type: NotificationType;

    user?: User;
    animation?: BasicAnimation;
    comment?: Comment;
    rating?: Rating;
    message?: Message;

    constructor(data: {
        id?: Snowflake,
        type: NotificationType.USER_REGISTRATION,
        user: User
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.USER_UPDATE,
        user: User
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.ANIMATION_CREATE,
        animation: Animation
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.ANIMATION_UPDATE,
        animation: Animation
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.COMMENT_CREATE,
        comment: Comment
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.COMMENT_EDIT,
        comment: Comment
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.RATING_ADD,
        rating: Rating
    });
    constructor(data: {
        id?: Snowflake,
        type: NotificationType.MESSAGE_RECEIVED,
        message: Message
    });

    /**
     * Creates new notification
     * @param {any} data
     */
    constructor(data: {
        id?: Snowflake,
        type: NotificationType,
        user?: User,
        animation?: BasicAnimation,
        comment?: Comment,
        rating?: Rating,
        message?: Message
    }) {
        this.id = data.id || Snowflake.newSnowflake();
        this.type = data.type;
        this.user = data.user;
        this.animation = data.animation;
        this.comment = data.comment;
        this.rating = data.rating;
        this.message = data.message;
        if(!this.user) {
            if(this.animation) {
                this.user = this.animation.author;
            } else if(this.comment) {
                this.user = this.comment.user;
            } else if(this.rating) {
                this.user = this.rating.user;
            } else if(this.message) {
                this.user = this.message.author;
            }
        }
        if(!this.animation) {
            if(this.comment) {
                this.animation = this.comment.animation;
            } else if(this.rating) {
                this.animation = this.rating.animation;
            }
        }
    }

    /**
     * @param {DBNotification | DBNotificationAny} data
     * @param {RequestContext} ctx
     */
    static async fromDatabase(data: DBNotification | DBNotificationAny, ctx: RequestContext) {
        return new Notification({
            id: new Snowflake(data.snowflake),
            type: data.type,
            user: "user" in data ? await User.fromDatabase(data.user, ctx) :
                data.userId && await User.getUser(new Snowflake(data.userId), ctx),
            animation: "animation" in data && data.animation ?
                await Animation.fromDatabase(data.animation, ctx) :
                data.animationId && await Animation.getAnimation(new Snowflake(data.animationId),
                    ctx),
            comment: "comment" in data && data.comment ?
                await Comment.fromDatabase(data.comment, ctx) :
                data.commentId && await Comment.getComment(new Snowflake(data.commentId), ctx),
            rating: "rating" in data && data.rating ?
                await Rating.fromDatabase(data.rating, ctx) :
                data.ratingId && await Rating.getRating(new Snowflake(data.ratingId), ctx),
            // @ts-ignore overload thingies
            message: !ctx.hasClient() ? undefined:
                ("message" in data && data.message ?
                    await Message.fromDatabase(data.message, ctx) :
                    data.messageId && await Message.getMessage(data.messageId.toString(), ctx))
        });
    }

    /**
     * Saves the notification into database
     * @return {Promise<void>}
     */
    async create(): Promise<void> {
        await prisma.notification.create({
            data: {
                snowflake: this.id.toBigInt(),
                userId: this.user?.id.toBigInt(),
                animationId: this.animation?.id.toBigInt(),
                commentId: this.comment?.id.toBigInt(),
                ratingId: this.rating?.id.toBigInt(),
                messageId: this.message?.id.toBigInt(),
                type: this.type
            }
        });
    }

    /**
     * Fetches data
     * @param {any} data
     * @param {RequestContext} ctx
     */
    static async fetchData(data: any, ctx: RequestContext): Promise<{
        user?: User,
        animation?: BasicAnimation,
        comment?: Comment,
        rating?: Rating,
        message?: Message
    }> {
        if(data.message && !ctx.hasClient()) {
            throw new Error("User client is required for fetching messages");
        }
        return {
            user: data.user && await User.getUser(data.user, ctx),
            animation: data.animation && await Animation.getAnimation(data.animation, ctx),
            comment: data.comment && await Comment.getComment(data.comment, ctx),
            rating: data.rating && await Rating.getRating(data.rating, ctx),
            // @ts-ignore checked by previous if
            message: data.message && await Message.getMessage(data.message, ctx)
        };
    }

    /**
     * Returns public notifications for the home page
     * @param {RequestContext} ctx
     * @return {Promise<Collection<SnowflakeString, Notification>>}
     */
    static async getPublicNotifications(ctx: RequestContext):
    Promise<Collection<SnowflakeString, Notification>> {
        const res = await prisma.notification.findMany({
            where: {
                type: {
                    not: NotificationType.MESSAGE_RECEIVED
                }
            },
            orderBy: {
                snowflake: "desc"
            },
            include: {
                animation: true,
                comment: true,
                rating: true,
                user: true
            },
            take: 10
        });

        const col = new Collection<SnowflakeString, Notification>();
        for(var notif of res) {
            col.set(notif.snowflake.toString(), await Notification.fromDatabase(notif, ctx));
        }
        return col;
    }

    /**
     * Returns user specific notifications. If no user in context, returns public notifications
     * @param {RequestContext} ctx
     * @param {number} page
     * @param {number} max
     * @return {Promise<Collection<SnowflakeString, Notification>>}
     */
    static async getUserNotifications(ctx: RequestContext, page?: number, max?: number):
    Promise<Collection<SnowflakeString, Notification>> {
        if(!ctx.hasClient()) {
            return Notification.getPublicNotifications(ctx);
        }

        const res = await prisma.notification.findMany({
            where: {
                OR: [{
                    type: {
                        not: NotificationType.MESSAGE_RECEIVED
                    }
                }, {
                    message: {
                        targetId: ctx.client.user.id.toBigInt()
                    }
                }]
            },
            orderBy: {
                snowflake: "desc"
            },
            include: {
                animation: true,
                comment: true,
                message: true,
                rating: true,
                user: true
            },
            skip: page && max ? page * max : undefined,
            take: max || 10
        });

        const col = new Collection<SnowflakeString, Notification>();
        for(var notif of res) {
            col.set(notif.snowflake.toString(), await Notification.fromDatabase(notif, ctx));
        }
        return col;
    }

    /**
     * Returns JSON compatible representation
     * @return {any}
     */
    toJSON(): any {
        return {
            id: this.id.toString(),
            date: this.id.time,
            type: this.type,
            user: this.user?.toJSON(),
            animation: this.animation?.toJSON(),
            comment: this.comment?.toJSON(),
            rating: this.rating?.toJSON(),
            message: this.message?.toJSON()
        };
    }
}

export default Notification;