Home Reference Source

js/controllers/AnswerViewController.js

import ViewController from '~/controllers/ViewController';
import AnswerVoteViewController from '~/controllers/AnswerVoteViewController';
import CommentListViewController from '~/controllers/CommentListViewController';
import DeleteItemViewController from '~/controllers/DeleteItemViewController';
import EditAnswerViewController from '~/controllers/EditAnswerViewController';
import ActionControllerDelegate from '~/delegate/ActionControllerDelegate';
import NukeItemViewController from '~/controllers/NukeItemViewController';

import Data from '~/models/Data';
import Answer from '~/models/Answer';

/**
 * Manages an answer of a given id.
 */
export default class AnswerViewController extends ViewController {
    /**
     * @param {HTMLElement} answer
     * @param {number} answerId
     */
    constructor(answer, answerId) {
        super(answer);

        this._body = answer;
        this._answerId = answerId;

        this._answer = Answer.fromJSON(Data.shared.encodedJSONForKey(`a${answerId}`));
        this._answer.code = this._body.getElementsByTagName('code')[0].textContent;

        this._bodyEl = this._body.getElementsByClassName('body')[0];
        this._byteCount = this._body.getElementsByClassName('answer-metric__value')[0];

        AnswerVoteViewController.forClass(
            'vote-button',
            (btn) => [btn, {
                voteType: btn.dataset.type,
                answerId: answerId
            }],
            answer
        );

        /** @type {DeleteItemViewController} */
        this.deletionController = DeleteItemViewController.forClass(
            'delete-button',
            (btn) => [{
                trigger: btn,
                item: this._answer
            }],
            answer
        )[0];

        if (this.deletionController)
            this.deletionController.delegate.didSetStateTo = async (controller, state) =>  {
            await this.setAnswer(state);
        };

        /** @type {NukeItemViewController} */
        this.nukeController = NukeItemViewController.forClass(
            'nuke-button',
            (btn) => [{
                trigger: btn,
                item: this._answer
            }],
            answer
        )[0];

        if (this.nukeController)
            this.nukeController.delegate.didSetStateTo = async (controller) =>  {
            this.isDeleted = true;
        };

        /** @type {EditAnswerViewController} */
        this.editAnswerController = EditAnswerViewController.forClass(
            'golf-button',
            (btn) => [{
                trigger: btn,
                answerController: this
            }],
            answer
        )[0];


        const answerComments = new CommentListViewController(
            answer.querySelector('.comment-list'),
            this.answer
        );
        answerComments.setupSublists();

        // Deletion Fields
        this.isDeleted = false;
        this._deletionOverlay = null;

    }

    /**
     * Gets if deleted or no.
     * @type {boolean}
     */
    get isDeleted() {
        return this._deleted;
    }

    /**
     * Sets if deleted or no. This does NOT affect the model use a Request.
     * @type {boolean}
     */
    set isDeleted(isDeleted) {
        if (this._deleted === isDeleted) return;
        this._deleted = isDeleted;

        // TODO: improve + add undo
        if (isDeleted) {
            this._body.parentNode.removeChild(this._body);
        }
    }

    /**
     * Gets the node where the body is
     * @type {HTMLElement}
     */
    get body() {
        return this._bodyEl;
    }

    /**
     * Sets the code
     * @param {string} code
     * @param {Language} language
     */
    async setBody(code, language) {
        const { default: highlight } = await import('#/hljs-renderer');
        this.body.innerHTML = highlight(code, language.hljsId, language.id);
    }


    /**
     * Returns the byte count element. For the value use .answer.length
     * @type {HTMLElement}
     */
    get byteCount() {
        return +this._byteCount.textContent;
    }

    /**
     * Sets the byte count.
     * @type {number}
     */
    set byteCount(byteCount) {
        const byteCountElement = this._byteCount;
        while (byteCountElement.firstChild) {
            byteCountElement.removeChild(byteCountElement.firstChild);
        }
        byteCountElement.appendChild(document.createTextNode(byteCount+""));
    }

    /** @type {Answer} */
    get answer() { return this._answer; }

    /**
     * Sets the answer. Does NOT update model
     * @param {Answer} newAnswer
     */
    async setAnswer(newAnswer) {
        this._answer = newAnswer;
        await this.setBody(newAnswer.code, newAnswer.language);
        this.byteCount = newAnswer.length;
        this.isDeleted = newAnswer.isDeleted;
    }
}