Home Reference Source

js/template/FollowModalTemplate.js

import AnimationController, { Animation } from '~/controllers/AnimationController';
import FollowedUser, { FollowedType } from '~/models/Request/FollowedUser';
import ProgressButtonTemplate from '~/template/ProgressButtonTemplate';
import { ButtonColor } from '~/template/ButtonTemplate';
import { HandleUnhandledPromise } from '~/helpers/ErrorManager';
import ModalViewTemplate from '~/template/ModalViewTemplate';
import SwappingTemplate from '~/template/SwappingTemplate';
import LoadingTemplate from '~/template/LoadingTemplate';
import UserTemplate from '~/template/UserTemplate';
import Template from '~/template/Template';
import SVG from '~/models/Request/SVG';

/**
 * @typedef {Object} FollowType
 * @property {Object} followers
 * @property {Object} following
 */
export const FollowType = {
    followers: {
        title: 'Followers',
        description: 'View {}\'s public followers',
        negative: 'No followers',
        endpoint: 'followers',
        requestType: FollowedType.followers
    },
    following: {
        title: 'Following',
        description: 'View who {} is following',
        endpoint: 'following',
        negative: 'Not following anyone',
        requestType: FollowedType.following
    }
};

/**
 * The modal showing followers or following.
 */
export default class FollowModalTemplate extends ModalViewTemplate {
    /**
     * Creates the modal which can be re-used
     * @param {User} user - The user to get modal for
     * @param {FollowType} followType - What to show, followers or following?
     */
    constructor(user, followType) {
        super({
            title: `${followType.title}.`,
            subtitle: followType.description.replace(/{}/g, user.name),
            requestedWidth: 500
        });

        /**
         * The loading sign (i.e. body of this)
         * @protected
         * @type {LoadingTemplate}
         */
        this.loadingSign = new LoadingTemplate();

        this.body.appendChild(
            <DocumentFragment>
                { this.loadingSign.unique() }
            </DocumentFragment>
        );

        /** @private */
        this.userList = <ul class="user-item__list"/>;

        /** @type {User} */
        this.user = user;

        /** @type {FollowType} */
        this.followType = followType;

        /**
         * The request, don't call since it'll
         * mess up pagination.
         * @type {FollowedUser}
         */
        this.request = new FollowedUser(user, followType.requestType);
    }

    /** @override */
    async didInitialLoad() {
        const areUsers = await this.nextPage(false);

        if (areUsers) {
            // Already populated
            this.loadingSign.controller.displayAlternate(new Template(this.userList));
        } else {
            this.loadingSign.controller.displayAlternate(new Template(
                <h3 class="user-item__empty">{ this.followType.negative } 😞</h3>
            ));
        }
    }

    /**
     * Loads the next page
     * @param {boolean} animated - if the addition of new nodes should be animated
     * @return {boolean} True if was able to load anything
     */
    async nextPage(animated = true) {
        const users = await this.request.nextPage();
        if (users.length === 0) return false;

        const subList = <ul class="user-item__sublist" />;

        // Load all items into a temporary list
        await Promise.all(
            users.map(async user => {
                const template = await new UserTemplate(user);
                template.loadInContext(subList);
            })
        );

        const newSublist = <li class="user-item__sublist_wrap">{ subList }</li>;
        this.userList.appendChild(newSublist);

        if (animated) {
            const animationController = new AnimationController(
                newSublist,
                [ Animation.expand.height ]
            );
            animationController.triggerAnimation();
        }


        // Create & style the load more button
        if (this.request.areMore) {
            const loadMore = new ProgressButtonTemplate({
                icon: await SVG.load('down'),
                color: ButtonColor.plain,
                text: 'Load More'
            });

            loadMore.isWide = true;
            loadMore.hasPaddedTop = true;

            loadMore.loadInContext(this.userList);
            loadMore.delegate.didSetStateTo = async (button, state) => {
                await this.nextPage(true);
                button.removeFromContext();
            }
        }

        return true;
    }
}