Home Reference Source

js/template/NotificationItemTemplate.js

import Template from '~/template/Template';
import SwappingTemplate from '~/template/SwappingTemplate';
import MarkNotificationStatus from '~/models/Request/MarkNotificationStatus';
import NotificationButtonTemplate from '~/template/NotificationButtonTemplate';
import Notification from '~/models/Notification';
import SVG from '~/models/Request/SVG';
import LoadingIcon from '~/svg/LoadingIcon';
import { HandleUnhandledPromise } from '~/helpers/ErrorManager';

/**
 * Represents a single notification.
 */
export default class NotificationItemTemplate extends Template {
    /**
     * @param {NotificationGroup} notificationGroup
     */
    constructor(notificationGroup) {
        const root = (
            <li class="notification notification-group"/>
        );
        super(root);

        /**
         * @type {NotificationGroup}
         */
        this.notificationGroup = notificationGroup;

        return (async () => {

            const NotificationStatus = await Notification.getStatuses();
            const NotificationType = await Notification.getTypes();
            const status = await notificationGroup.getStatus();
            const description = NotificationType.descriptionForValue(notificationGroup.primaryNotification.type);

            const unread = status !== NotificationStatus.read;
            const unreadIndicator = await SVG.load('unread');

            const markReadButton = await new NotificationButtonTemplate('Mark as read', 'mark-read')
            const markRead = markReadButton.unique();

            const state = unread ? 'unread' : 'read';
            const link = notificationGroup.primaryNotification.responder;

            this._unreadIconWrapper = (
                <div class={`notification__detail notification__detail--style-state notification__detail--state-${state}`}>
                    { unreadIndicator }
                </div>
            );

            this._markReadIcon = new SwappingTemplate(markReadButton.unique());
            this._loadingMarkedRead = new Template(LoadingIcon.cloneNode(true));
            this._markReadWrapper = (
                <div class="notification__detail notification__detail--size-small notification__detail--style-button">
                    { this._markReadIcon.unique() }
                </div>
            );

            this._markingRead = false;

            const responder = (
                <a href={link} target="_blank" class="notification__detail notification__detail--size-wide">
                    <h3>{ notificationGroup.primaryNotification.getTitle() }</h3>
                    <h4>{ notificationGroup.primaryNotification.body }</h4>
                </a>
            );

            const icon = await notificationGroup.primaryNotification.getIconURL();
            const relativeDate = moment(notificationGroup.primaryNotification.dateCreated).fromNow();

            let otherNotifications = <DocumentFragment/>;
            if (notificationGroup.siblings.length > 0) {
                const uniqueUsers = [...notificationGroup.senders().take(4)];

                otherNotifications = (
                    <div class="notification__details notification__details--pad-top">
                        <div class="notification__detail notification__detail--size-noverrideimg avatar-list">
                            { uniqueUsers.map(user => (
                                <img src={user.avatar} />
                            )) }
                        </div>
                        <div class="notification__detail notification__detail--style-detail">
                            { notificationGroup.siblings.length } other { notificationGroup.primaryNotification.plural }
                        </div>
                    </div>
                );
            }

            root.appendChild(
                <DocumentFragment>
                    <div class="notification__details">
                        { this._unreadIconWrapper }
                        <div class="notification__detail notification__detail--size-wide notification__detail--style-type">{ description }</div>
                        <div class="notification__detail notification__detail--style-timestamp">{ relativeDate }</div>
                    </div>
                    <div class="notification__details notification__details--pad-top">
                        <div class="notification__detail notification__detail--style-state notification__detail--align-top">
                            <img src={icon} />
                        </div>
                        { responder }
                        { unread ? this._markReadWrapper : <DocumentFragment/> }
                    </div>
                    { otherNotifications }
                </DocumentFragment>
            );

            responder.addEventListener('click', () => {
                this.markRead()
                    .catch(HandleUnhandledPromise);
            });

            this._markReadWrapper.addEventListener('click', () => {
                this.markRead()
                    .catch(HandleUnhandledPromise);
            });

            return this;
        })();
    }

    /**
     * Marks a notification as read and runs a request. Calls
     * {@link NotificationItemTemplate#markedRead}
     */
    async markRead() {
        if (this._markingRead) return;

        this._markReadIcon.controller.displayAlternate(this._loadingMarkedRead);
        this._markingRead = true;
        const NotificationStatus = await Notification.getStatuses();
        const markNotificationStatus = new MarkNotificationStatus(this.notificationGroup, NotificationStatus.read);
        await markNotificationStatus.run();
        this._markingRead = false;

        // Switch the checkmark to a loading icon
        this.markedRead();
    }

    /**
     * Marks a notification item read. Does not run request
     */
    markedRead() {
        // Switch from blue -> gray icon
        this._unreadIconWrapper.classList.remove('notification__detail--state-unread');
        this._unreadIconWrapper.classList.add('notification__detail--state-read');

        // Remove the read button
        if (this._markReadWrapper.parentNode) {
            this._markReadWrapper.parentNode.removeChild(this._markReadWrapper);
        }
    }
}