Converse converse.js

Source: converse-headlines-view.js

/**
 * @module converse-headlines-view
 * @copyright 2020, the Converse.js contributors
 * @license Mozilla Public License (MPLv2)
 */
import "converse-chatview";
import tpl_chatbox from "templates/chatbox.js";
import tpl_headline_panel from "templates/headline_panel.js";
import { ChatBoxView } from "./converse-chatview";
import { View } from '@converse/skeletor/src/view.js';
import { __ } from './i18n';
import { _converse, api, converse } from "@converse/headless/converse-core";
import { render } from "lit-html";

const u = converse.env.utils;


const HeadlinesBoxView = ChatBoxView.extend({
    className: 'chatbox headlines hidden',

    events: {
        'click .close-chatbox-button': 'close',
        'click .toggle-chatbox-button': 'minimize',
        'keypress textarea.chat-textarea': 'onKeyDown'
    },

    async initialize () {
        this.initDebounced();

        this.model.disable_mam = true; // Don't do MAM queries for this box
        this.listenTo(this.model, 'change:hidden', m => m.get('hidden') ? this.hide() : this.show());
        this.listenTo(this.model, 'destroy', this.remove);
        this.listenTo(this.model, 'show', this.show);

        this.render();

        // Need to be registered after render has been called.
        this.listenTo(this.model.messages, 'add', this.onMessageAdded);
        this.listenTo(this.model.messages, 'remove', this.renderChatHistory);
        this.listenTo(this.model.messages, 'rendered', this.maybeScrollDown);
        this.listenTo(this.model.messages, 'reset', this.renderChatHistory);

        await this.model.messages.fetched;
        this.insertIntoDOM();
        this.model.maybeShow();
        this.scrollDown();
        /**
         * Triggered once the {@link _converse.HeadlinesBoxView} has been initialized
         * @event _converse#headlinesBoxViewInitialized
         * @type { _converse.HeadlinesBoxView }
         * @example _converse.api.listen.on('headlinesBoxViewInitialized', view => { ... });
         */
        api.trigger('headlinesBoxViewInitialized', this);
    },

    render () {
        this.el.setAttribute('id', this.model.get('box_id'))
        const result = tpl_chatbox(
            Object.assign(this.model.toJSON(), {
                    info_close: '',
                    label_personal_message: '',
                    show_send_button: false,
                    show_toolbar: false,
                    unread_msgs: ''
                }
            ));
        render(result, this.el);
        this.content = this.el.querySelector('.chat-content');
        this.msgs_container = this.el.querySelector('.chat-content__messages');
        this.renderChatContent();
        this.renderHeading();
        return this;
    },

    getNotifications () {
        // Override method in ChatBox. We don't show notifications for
        // headlines boxes.
        return [];
    },

    /**
     * Returns a list of objects which represent buttons for the headlines header.
     * @async
     * @emits _converse#getHeadingButtons
     * @private
     * @method _converse.HeadlinesBoxView#getHeadingButtons
     */
    getHeadingButtons () {
        const buttons = [];
        if (!api.settings.get("singleton")) {
            buttons.push({
                'a_class': 'close-chatbox-button',
                'handler': ev => this.close(ev),
                'i18n_text': __('Close'),
                'i18n_title': __('Close these announcements'),
                'icon_class': 'fa-times',
                'name': 'close',
                'standalone': api.settings.get("view_mode") === 'overlayed',
            });
        }
        return _converse.api.hook('getHeadingButtons', this, buttons);
    },

    // Override to avoid the methods in converse-chatview.js
    'renderMessageForm': function renderMessageForm () {},
    'afterShown': function afterShown () {}
});


/**
 * View which renders headlines section of the control box.
 * @class
 * @namespace _converse.HeadlinesPanel
 * @memberOf _converse
 */
export const HeadlinesPanel = View.extend({
    tagName: 'div',
    className: 'controlbox-section',
    id: 'headline',

    events: {
        'click .open-headline': 'openHeadline'
    },

    initialize () {
        this.listenTo(this.model, 'add', this.renderIfHeadline)
        this.listenTo(this.model, 'remove', this.renderIfHeadline)
        this.listenTo(this.model, 'destroy', this.renderIfHeadline)
        this.render();
        this.insertIntoDOM();
    },

    toHTML () {
        return tpl_headline_panel({
            'heading_headline': __('Announcements'),
            'headlineboxes': this.model.filter(m => m.get('type') === _converse.HEADLINES_TYPE),
            'open_title': __('Click to open this server message'),
        });
    },

    renderIfHeadline (model) {
        return (model && model.get('type') === _converse.HEADLINES_TYPE) && this.render();
    },

    openHeadline (ev) {
        ev.preventDefault();
        const jid = ev.target.getAttribute('data-headline-jid');
        const chat = _converse.chatboxes.get(jid);
        chat.maybeShow(true);
    },

    insertIntoDOM () {
        const view = _converse.chatboxviews.get('controlbox');
        view && view.el.querySelector('.controlbox-pane').insertAdjacentElement('beforeEnd', this.el);
    }
});


converse.plugins.add('converse-headlines-view', {
    /* Plugin dependencies are other plugins which might be
     * overridden or relied upon, and therefore need to be loaded before
     * this plugin.
     *
     * If the setting "strict_plugin_dependencies" is set to true,
     * an error will be raised if the plugin is not found. By default it's
     * false, which means these plugins are only loaded opportunistically.
     *
     * NB: These plugins need to have already been loaded via require.js.
     */
    dependencies: ["converse-headlines", "converse-chatview"],

    overrides: {
        // Overrides mentioned here will be picked up by converse.js's
        // plugin architecture they will replace existing methods on the
        // relevant objects or classes.
        //
        // New functions which don't exist yet can also be added.

        ControlBoxView: {
            renderControlBoxPane () {
                this.__super__.renderControlBoxPane.apply(this, arguments);
                this.renderHeadlinesPanel();
            },
        },
    },


    initialize () {
        /* The initialize function gets called as soon as the plugin is
         * loaded by converse.js's plugin machinery.
         */

        const viewWithHeadlinesPanel = {
            renderHeadlinesPanel () {
                if (this.headlinepanel && u.isInDOM(this.headlinepanel.el)) {
                    return this.headlinepanel;
                }
                this.headlinepanel = new _converse.HeadlinesPanel({'model': _converse.chatboxes});
                /**
                 * Triggered once the section of the { @link _converse.ControlBoxView }
                 * which shows announcements has been rendered.
                 * @event _converse#headlinesPanelRendered
                 * @example _converse.api.listen.on('headlinesPanelRendered', () => { ... });
                 */
                api.trigger('headlinesPanelRendered');
                return this.headlinepanel;
            }
        }

        if (_converse.ControlBoxView) {
            Object.assign(_converse.ControlBoxView.prototype, viewWithHeadlinesPanel);
        }

        _converse.HeadlinesBoxView = HeadlinesBoxView;
        _converse.HeadlinesPanel = HeadlinesPanel;


        /************************ BEGIN Event Handlers ************************/
        api.listen.on('chatBoxViewsInitialized', () => {
            const views = _converse.chatboxviews;
            _converse.chatboxes.on('add', item => {
                if (!views.get(item.get('id')) && item.get('type') === _converse.HEADLINES_TYPE) {
                    views.add(item.get('id'), new _converse.HeadlinesBoxView({model: item}));
                }
            });
        });
    }
});