Move converse-minimize into a folder
Also, use utility methods instead of adding a mixin to ChatBoxViews (since we're planning on removing it).
This commit is contained in:
parent
1a88aff179
commit
85bd192f3c
@ -81,7 +81,7 @@ describe("Chatboxes", function () {
|
|||||||
// openControlBox was called earlier, so the controlbox is
|
// openControlBox was called earlier, so the controlbox is
|
||||||
// visible, but no other chat boxes have been created.
|
// visible, but no other chat boxes have been created.
|
||||||
expect(_converse.chatboxes.length).toEqual(1);
|
expect(_converse.chatboxes.length).toEqual(1);
|
||||||
spyOn(_converse.chatboxviews, 'trimChats');
|
spyOn(_converse.minimize, 'trimChats');
|
||||||
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(1); // Controlbox is open
|
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(1); // Controlbox is open
|
||||||
|
|
||||||
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group li').length, 700);
|
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group li').length, 700);
|
||||||
@ -90,11 +90,11 @@ describe("Chatboxes", function () {
|
|||||||
let el = online_contacts[0];
|
let el = online_contacts[0];
|
||||||
el.click();
|
el.click();
|
||||||
await u.waitUntil(() => document.querySelectorAll("#conversejs .chatbox").length == 2);
|
await u.waitUntil(() => document.querySelectorAll("#conversejs .chatbox").length == 2);
|
||||||
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
|
expect(_converse.minimize.trimChats).toHaveBeenCalled();
|
||||||
online_contacts[1].click();
|
online_contacts[1].click();
|
||||||
await u.waitUntil(() => _converse.chatboxes.length == 3);
|
await u.waitUntil(() => _converse.chatboxes.length == 3);
|
||||||
el = online_contacts[1];
|
el = online_contacts[1];
|
||||||
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
|
expect(_converse.minimize.trimChats).toHaveBeenCalled();
|
||||||
// Check that new chat boxes are created to the left of the
|
// Check that new chat boxes are created to the left of the
|
||||||
// controlbox (but to the right of all existing chat boxes)
|
// controlbox (but to the right of all existing chat boxes)
|
||||||
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(3);
|
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(3);
|
||||||
@ -171,7 +171,7 @@ describe("Chatboxes", function () {
|
|||||||
['rosterGroupsFetched'], {},
|
['rosterGroupsFetched'], {},
|
||||||
async function (done, _converse) {
|
async function (done, _converse) {
|
||||||
|
|
||||||
spyOn(_converse.ChatBoxViews.prototype, 'trimChats');
|
spyOn(_converse.minimize, 'trimChats');
|
||||||
await mock.waitForRoster(_converse, 'current');
|
await mock.waitForRoster(_converse, 'current');
|
||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ describe("Chatboxes", function () {
|
|||||||
|
|
||||||
mock.openChatBoxes(_converse, 6);
|
mock.openChatBoxes(_converse, 6);
|
||||||
await u.waitUntil(() => _converse.chatboxes.length == 7);
|
await u.waitUntil(() => _converse.chatboxes.length == 7);
|
||||||
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
|
expect(_converse.minimize.trimChats).toHaveBeenCalled();
|
||||||
// We instantiate a new ChatBoxes collection, which by default
|
// We instantiate a new ChatBoxes collection, which by default
|
||||||
// will be empty.
|
// will be empty.
|
||||||
const newchatboxes = new _converse.ChatBoxes();
|
const newchatboxes = new _converse.ChatBoxes();
|
||||||
@ -228,7 +228,7 @@ describe("Chatboxes", function () {
|
|||||||
['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||||
async function (done, _converse) {
|
async function (done, _converse) {
|
||||||
|
|
||||||
spyOn(_converse.ChatBoxViews.prototype, 'trimChats');
|
spyOn(_converse.minimize, 'trimChats');
|
||||||
await mock.waitForRoster(_converse, 'current');
|
await mock.waitForRoster(_converse, 'current');
|
||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
|
await u.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length);
|
||||||
@ -241,7 +241,7 @@ describe("Chatboxes", function () {
|
|||||||
expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
|
expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
|
||||||
mock.openChatBoxes(_converse, 6);
|
mock.openChatBoxes(_converse, 6);
|
||||||
await u.waitUntil(() => _converse.chatboxes.length == 7)
|
await u.waitUntil(() => _converse.chatboxes.length == 7)
|
||||||
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
|
expect(_converse.minimize.trimChats).toHaveBeenCalled();
|
||||||
expect(_converse.chatboxes.length).toEqual(7);
|
expect(_converse.chatboxes.length).toEqual(7);
|
||||||
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxViewInitialized', jasmine.any(Object));
|
expect(_converse.api.trigger).toHaveBeenCalledWith('chatBoxViewInitialized', jasmine.any(Object));
|
||||||
await mock.closeAllChatBoxes(_converse);
|
await mock.closeAllChatBoxes(_converse);
|
||||||
|
@ -162,11 +162,11 @@ describe("A Chatbox", function () {
|
|||||||
mock.initConverse(['rosterGroupsFetched'], {},
|
mock.initConverse(['rosterGroupsFetched'], {},
|
||||||
async function (done, _converse) {
|
async function (done, _converse) {
|
||||||
|
|
||||||
spyOn(_converse.chatboxviews, 'trimChats');
|
spyOn(_converse.minimize, 'trimChats');
|
||||||
|
|
||||||
await mock.waitForRoster(_converse, 'current');
|
await mock.waitForRoster(_converse, 'current');
|
||||||
await mock.openControlBox(_converse);
|
await mock.openControlBox(_converse);
|
||||||
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(1);
|
expect(_converse.minimize.trimChats.calls.count()).toBe(1);
|
||||||
|
|
||||||
let jid, chatboxview;
|
let jid, chatboxview;
|
||||||
// openControlBox was called earlier, so the controlbox is
|
// openControlBox was called earlier, so the controlbox is
|
||||||
@ -185,7 +185,7 @@ describe("A Chatbox", function () {
|
|||||||
el.click();
|
el.click();
|
||||||
}
|
}
|
||||||
await u.waitUntil(() => _converse.chatboxes.length == 16);
|
await u.waitUntil(() => _converse.chatboxes.length == 16);
|
||||||
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(16);
|
expect(_converse.minimize.trimChats.calls.count()).toBe(16);
|
||||||
|
|
||||||
_converse.api.chatviews.get().forEach(v => spyOn(v, 'onMinimized').and.callThrough());
|
_converse.api.chatviews.get().forEach(v => spyOn(v, 'onMinimized').and.callThrough());
|
||||||
for (i=0; i<online_contacts.length; i++) {
|
for (i=0; i<online_contacts.length; i++) {
|
||||||
@ -202,7 +202,7 @@ describe("A Chatbox", function () {
|
|||||||
_converse.minimized_chats.el.querySelector("a.restore-chat").click();
|
_converse.minimized_chats.el.querySelector("a.restore-chat").click();
|
||||||
|
|
||||||
expect(chatbox.maximize).toHaveBeenCalled();
|
expect(chatbox.maximize).toHaveBeenCalled();
|
||||||
expect(_converse.chatboxviews.trimChats.calls.count()).toBe(17);
|
expect(_converse.minimize.trimChats.calls.count()).toBe(17);
|
||||||
done();
|
done();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,7 @@ import "./plugins/controlbox/index.js"; // The control box
|
|||||||
import "./plugins/dragresize/index.js"; // Allows chat boxes to be resized by dragging them
|
import "./plugins/dragresize/index.js"; // Allows chat boxes to be resized by dragging them
|
||||||
import "./plugins/fullscreen.js";
|
import "./plugins/fullscreen.js";
|
||||||
import "./plugins/mam-views.js";
|
import "./plugins/mam-views.js";
|
||||||
import "./plugins/minimize.js"; // Allows chat boxes to be minimized
|
import "./plugins/minimize/index.js"; // Allows chat boxes to be minimized
|
||||||
import "./plugins/muc-views/index.js"; // Views related to MUC
|
import "./plugins/muc-views/index.js"; // Views related to MUC
|
||||||
import "./plugins/headlines-view/index.js";
|
import "./plugins/headlines-view/index.js";
|
||||||
import "./plugins/notifications.js";
|
import "./plugins/notifications.js";
|
||||||
|
@ -1,437 +0,0 @@
|
|||||||
/**
|
|
||||||
* @module converse-minimize
|
|
||||||
* @copyright 2020, the Converse.js contributors
|
|
||||||
* @license Mozilla Public License (MPLv2)
|
|
||||||
*/
|
|
||||||
import '../components/minimized_chat.js';
|
|
||||||
import './chatview/index.js';
|
|
||||||
import tpl_chats_panel from '../templates/chats_panel.js';
|
|
||||||
import { Model } from '@converse/skeletor/src/model.js';
|
|
||||||
import { View } from '@converse/skeletor/src/view';
|
|
||||||
import { __ } from '../i18n';
|
|
||||||
import { _converse, api, converse } from '@converse/headless/core';
|
|
||||||
import { debounce } from 'lodash-es';
|
|
||||||
import { render } from 'lit-html';
|
|
||||||
|
|
||||||
const { dayjs } = converse.env;
|
|
||||||
const u = converse.env.utils;
|
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-minimize', {
|
|
||||||
/* Optional dependencies are other plugins which might be
|
|
||||||
* overridden or relied upon, and therefore need to be loaded before
|
|
||||||
* this plugin. They are called "optional" because they might not be
|
|
||||||
* available, in which case any overrides applicable to them will be
|
|
||||||
* ignored.
|
|
||||||
*
|
|
||||||
* It's possible however to make optional dependencies non-optional.
|
|
||||||
* If the setting "strict_plugin_dependencies" is set to true,
|
|
||||||
* an error will be raised if the plugin is not found.
|
|
||||||
*
|
|
||||||
* NB: These plugins need to have already been loaded via require.js.
|
|
||||||
*/
|
|
||||||
dependencies: [
|
|
||||||
"converse-chatview",
|
|
||||||
"converse-controlbox",
|
|
||||||
"converse-muc-views",
|
|
||||||
"converse-headlines-view",
|
|
||||||
"converse-dragresize"
|
|
||||||
],
|
|
||||||
|
|
||||||
enabled (_converse) {
|
|
||||||
return _converse.api.settings.get("view_mode") === 'overlayed';
|
|
||||||
},
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
ChatBox: {
|
|
||||||
initialize () {
|
|
||||||
this.__super__.initialize.apply(this, arguments);
|
|
||||||
this.on('show', this.maximize, this);
|
|
||||||
|
|
||||||
if (this.get('id') === 'controlbox') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.save({
|
|
||||||
'minimized': this.get('minimized') || false,
|
|
||||||
'time_minimized': this.get('time_minimized') || dayjs(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
maybeShow (force) {
|
|
||||||
if (!force && this.get('minimized')) {
|
|
||||||
// Must return the chatbox
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
return this.__super__.maybeShow.apply(this, arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
isHidden () {
|
|
||||||
return this.__super__.isHidden.call(this) || this.get('minimized');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ChatBoxView: {
|
|
||||||
show () {
|
|
||||||
const { _converse } = this.__super__;
|
|
||||||
if (_converse.api.settings.get("view_mode") === 'overlayed' && this.model.get('minimized')) {
|
|
||||||
this.model.minimize();
|
|
||||||
return this;
|
|
||||||
} else {
|
|
||||||
return this.__super__.show.apply(this, arguments);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
isNewMessageHidden () {
|
|
||||||
return this.model.get('minimized') ||
|
|
||||||
this.__super__.isNewMessageHidden.apply(this, arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
setChatBoxHeight (height) {
|
|
||||||
if (!this.model.get('minimized')) {
|
|
||||||
return this.__super__.setChatBoxHeight.call(this, height);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setChatBoxWidth (width) {
|
|
||||||
if (!this.model.get('minimized')) {
|
|
||||||
return this.__super__.setChatBoxWidth.call(this, width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
/* The initialize function gets called as soon as the plugin is
|
|
||||||
* loaded by Converse.js's plugin machinery.
|
|
||||||
*/
|
|
||||||
|
|
||||||
api.settings.extend({'no_trimming': false});
|
|
||||||
|
|
||||||
const minimizableChatBox = {
|
|
||||||
maximize () {
|
|
||||||
u.safeSave(this, {
|
|
||||||
'minimized': false,
|
|
||||||
'time_opened': (new Date()).getTime()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
minimize () {
|
|
||||||
u.safeSave(this, {
|
|
||||||
'minimized': true,
|
|
||||||
'time_minimized': (new Date()).toISOString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.assign(_converse.ChatBox.prototype, minimizableChatBox);
|
|
||||||
|
|
||||||
|
|
||||||
const minimizableChatBoxView = {
|
|
||||||
/**
|
|
||||||
* Handler which gets called when a {@link _converse#ChatBox} has it's
|
|
||||||
* `minimized` property set to false.
|
|
||||||
*
|
|
||||||
* Will trigger {@link _converse#chatBoxMaximized}
|
|
||||||
* @private
|
|
||||||
* @returns {_converse.ChatBoxView|_converse.ChatRoomView}
|
|
||||||
*/
|
|
||||||
onMaximized () {
|
|
||||||
const { _converse } = this.__super__;
|
|
||||||
this.insertIntoDOM();
|
|
||||||
|
|
||||||
if (!this.model.isScrolledUp()) {
|
|
||||||
this.model.clearUnreadMsgCounter();
|
|
||||||
}
|
|
||||||
this.model.setChatState(_converse.ACTIVE);
|
|
||||||
this.show();
|
|
||||||
/**
|
|
||||||
* Triggered when a previously minimized chat gets maximized
|
|
||||||
* @event _converse#chatBoxMaximized
|
|
||||||
* @type { _converse.ChatBoxView }
|
|
||||||
* @example _converse.api.listen.on('chatBoxMaximized', view => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('chatBoxMaximized', this);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler which gets called when a {@link _converse#ChatBox} has it's
|
|
||||||
* `minimized` property set to true.
|
|
||||||
*
|
|
||||||
* Will trigger {@link _converse#chatBoxMinimized}
|
|
||||||
* @private
|
|
||||||
* @returns {_converse.ChatBoxView|_converse.ChatRoomView}
|
|
||||||
*/
|
|
||||||
onMinimized (ev) {
|
|
||||||
const { _converse } = this.__super__;
|
|
||||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
|
||||||
// save the scroll position to restore it on maximize
|
|
||||||
if (this.model.collection && this.model.collection.browserStorage) {
|
|
||||||
this.model.save({'scroll': this.content.scrollTop});
|
|
||||||
} else {
|
|
||||||
this.model.set({'scroll': this.content.scrollTop});
|
|
||||||
}
|
|
||||||
this.model.setChatState(_converse.INACTIVE);
|
|
||||||
this.hide();
|
|
||||||
/**
|
|
||||||
* Triggered when a previously maximized chat gets Minimized
|
|
||||||
* @event _converse#chatBoxMinimized
|
|
||||||
* @type { _converse.ChatBoxView }
|
|
||||||
* @example _converse.api.listen.on('chatBoxMinimized', view => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('chatBoxMinimized', this);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimizes a chat box.
|
|
||||||
* @returns {_converse.ChatBoxView|_converse.ChatRoomView}
|
|
||||||
*/
|
|
||||||
minimize (ev) {
|
|
||||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
|
||||||
this.model.minimize();
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
onMinimizedChanged (item) {
|
|
||||||
if (item.get('minimized')) {
|
|
||||||
this.onMinimized();
|
|
||||||
} else {
|
|
||||||
this.onMaximized();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.assign(_converse.ChatBoxView.prototype, minimizableChatBoxView);
|
|
||||||
|
|
||||||
|
|
||||||
const chatTrimmer = {
|
|
||||||
getChatBoxWidth (view) {
|
|
||||||
if (view.model.get('id') === 'controlbox') {
|
|
||||||
const controlbox = this.get('controlbox');
|
|
||||||
// We return the width of the controlbox or its toggle,
|
|
||||||
// depending on which is visible.
|
|
||||||
if (!controlbox || !u.isVisible(controlbox.el)) {
|
|
||||||
return u.getOuterWidth(_converse.controlboxtoggle.el, true);
|
|
||||||
} else {
|
|
||||||
return u.getOuterWidth(controlbox.el, true);
|
|
||||||
}
|
|
||||||
} else if (!view.model.get('minimized') && u.isVisible(view.el)) {
|
|
||||||
return u.getOuterWidth(view.el, true);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
getShownChats () {
|
|
||||||
return this.filter((view) =>
|
|
||||||
// The controlbox can take a while to close,
|
|
||||||
// so we need to check its state. That's why we checked
|
|
||||||
// the 'closed' state.
|
|
||||||
!view.model.get('minimized') &&
|
|
||||||
!view.model.get('closed') &&
|
|
||||||
u.isVisible(view.el)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getMinimizedWidth () {
|
|
||||||
const minimized_el = _converse.minimized_chats?.el;
|
|
||||||
return this.model.pluck('minimized').includes(true) ? u.getOuterWidth(minimized_el, true) : 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
getBoxesWidth (newchat) {
|
|
||||||
const new_id = newchat ? newchat.model.get('id') : null;
|
|
||||||
const newchat_width = newchat ? u.getOuterWidth(newchat.el, true) : 0;
|
|
||||||
return Object.values(this.xget(new_id))
|
|
||||||
.reduce((memo, view) => memo + this.getChatBoxWidth(view), newchat_width);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when a newly created chat box will be shown.
|
|
||||||
* It checks whether there is enough space on the page to show
|
|
||||||
* another chat box. Otherwise it minimizes the oldest chat box
|
|
||||||
* to create space.
|
|
||||||
* @private
|
|
||||||
* @method _converse.ChatBoxViews#trimChats
|
|
||||||
* @param { _converse.ChatBoxView|_converse.ChatRoomView|_converse.ControlBoxView|_converse.HeadlinesBoxView } [newchat]
|
|
||||||
*/
|
|
||||||
async trimChats (newchat) {
|
|
||||||
if (api.settings.get('no_trimming') || !api.connection.connected() || api.settings.get("view_mode") !== 'overlayed') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const shown_chats = this.getShownChats();
|
|
||||||
if (shown_chats.length <= 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const body_width = u.getOuterWidth(document.querySelector('body'), true);
|
|
||||||
if (this.getChatBoxWidth(shown_chats[0]) === body_width) {
|
|
||||||
// If the chats shown are the same width as the body,
|
|
||||||
// then we're in responsive mode and the chats are
|
|
||||||
// fullscreen. In this case we don't trim.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await api.waitUntil('minimizedChatsInitialized');
|
|
||||||
const minimized_el = _converse.minimized_chats?.el;
|
|
||||||
if (minimized_el) {
|
|
||||||
while ((this.getMinimizedWidth() + this.getBoxesWidth(newchat)) > body_width) {
|
|
||||||
const new_id = newchat ? newchat.model.get('id') : null;
|
|
||||||
const oldest_chat = this.getOldestMaximizedChat([new_id]);
|
|
||||||
if (oldest_chat) {
|
|
||||||
// We hide the chat immediately, because waiting
|
|
||||||
// for the event to fire (and letting the
|
|
||||||
// ChatBoxView hide it then) causes race
|
|
||||||
// conditions.
|
|
||||||
const view = this.get(oldest_chat.get('id'));
|
|
||||||
if (view) {
|
|
||||||
view.hide();
|
|
||||||
}
|
|
||||||
oldest_chat.minimize();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getOldestMaximizedChat (exclude_ids) {
|
|
||||||
// Get oldest view (if its id is not excluded)
|
|
||||||
exclude_ids.push('controlbox');
|
|
||||||
let i = 0;
|
|
||||||
let model = this.model.sort().at(i);
|
|
||||||
while (exclude_ids.includes(model.get('id')) || model.get('minimized') === true) {
|
|
||||||
i++;
|
|
||||||
model = this.model.at(i);
|
|
||||||
if (!model) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.assign(_converse.ChatBoxViews.prototype, chatTrimmer);
|
|
||||||
|
|
||||||
|
|
||||||
api.promises.add('minimizedChatsInitialized');
|
|
||||||
|
|
||||||
|
|
||||||
_converse.MinimizedChatsToggle = Model.extend({
|
|
||||||
defaults: {
|
|
||||||
'collapsed': false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
_converse.MinimizedChats = View.extend({
|
|
||||||
tagName: 'span',
|
|
||||||
|
|
||||||
async initialize () {
|
|
||||||
await this.initToggle();
|
|
||||||
this.render();
|
|
||||||
this.listenTo(this.minchats, 'change:collapsed', this.render)
|
|
||||||
this.listenTo(this.model, 'add', this.render)
|
|
||||||
this.listenTo(this.model, 'change:fullname', this.render)
|
|
||||||
this.listenTo(this.model, 'change:jid', this.render)
|
|
||||||
this.listenTo(this.model, 'change:minimized', this.render)
|
|
||||||
this.listenTo(this.model, 'change:name', this.render)
|
|
||||||
this.listenTo(this.model, 'change:num_unread', this.render)
|
|
||||||
this.listenTo(this.model, 'remove', this.render)
|
|
||||||
},
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const chats = this.model.where({'minimized': true});
|
|
||||||
const num_unread = chats.reduce((acc, chat) => (acc + chat.get('num_unread')), 0);
|
|
||||||
const num_minimized = chats.reduce((acc, chat) => (acc + (chat.get('minimized') ? 1 : 0)), 0);
|
|
||||||
const collapsed = this.minchats.get('collapsed');
|
|
||||||
const data = { chats, num_unread, num_minimized, collapsed };
|
|
||||||
data.toggle = ev => this.toggle(ev);
|
|
||||||
render(tpl_chats_panel(data), this.el);
|
|
||||||
|
|
||||||
if (!this.el.parentElement) {
|
|
||||||
_converse.chatboxviews.insertRowColumn(this.el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async initToggle () {
|
|
||||||
const id = `converse.minchatstoggle-${_converse.bare_jid}`;
|
|
||||||
this.minchats = new _converse.MinimizedChatsToggle({id});
|
|
||||||
this.minchats.browserStorage = _converse.createStore(id);
|
|
||||||
await new Promise(resolve => this.minchats.fetch({'success': resolve, 'error': resolve}));
|
|
||||||
},
|
|
||||||
|
|
||||||
toggle (ev) {
|
|
||||||
ev?.preventDefault();
|
|
||||||
this.minchats.save({'collapsed': !this.minchats.get('collapsed')});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function initMinimizedChats () {
|
|
||||||
_converse.minimized_chats?.remove();
|
|
||||||
_converse.minimized_chats = new _converse.MinimizedChats({model: _converse.chatboxes});
|
|
||||||
/**
|
|
||||||
* Triggered once the _converse.MinimizedChats instance has been initialized
|
|
||||||
* @event _converse#minimizedChatsInitialized
|
|
||||||
* @example _converse.api.listen.on('minimizedChatsInitialized', () => { ... });
|
|
||||||
*/
|
|
||||||
api.trigger('minimizedChatsInitialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMinimizeButtonToChat (view, buttons) {
|
|
||||||
const data = {
|
|
||||||
'a_class': 'toggle-chatbox-button',
|
|
||||||
'handler': ev => view.minimize(ev),
|
|
||||||
'i18n_text': __('Minimize'),
|
|
||||||
'i18n_title': __('Minimize this chat'),
|
|
||||||
'icon_class': "fa-minus",
|
|
||||||
'name': 'minimize',
|
|
||||||
'standalone': _converse.api.settings.get("view_mode") === 'overlayed'
|
|
||||||
}
|
|
||||||
const names = buttons.map(t => t.name);
|
|
||||||
const idx = names.indexOf('close');
|
|
||||||
return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons];
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMinimizeButtonToMUC (view, buttons) {
|
|
||||||
const data = {
|
|
||||||
'a_class': 'toggle-chatbox-button',
|
|
||||||
'handler': ev => view.minimize(ev),
|
|
||||||
'i18n_text': __('Minimize'),
|
|
||||||
'i18n_title': __('Minimize this groupchat'),
|
|
||||||
'icon_class': "fa-minus",
|
|
||||||
'name': 'minimize',
|
|
||||||
'standalone': _converse.api.settings.get("view_mode") === 'overlayed'
|
|
||||||
}
|
|
||||||
const names = buttons.map(t => t.name);
|
|
||||||
const idx = names.indexOf('signout');
|
|
||||||
return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons];
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************ BEGIN Event Handlers ************************/
|
|
||||||
api.listen.on('chatBoxInsertedIntoDOM', view => _converse.chatboxviews.trimChats(view));
|
|
||||||
api.listen.on('connected', () => initMinimizedChats());
|
|
||||||
api.listen.on('controlBoxOpened', view => _converse.chatboxviews.trimChats(view));
|
|
||||||
api.listen.on('chatBoxViewInitialized', v => v.listenTo(v.model, 'change:minimized', v.onMinimizedChanged));
|
|
||||||
|
|
||||||
api.listen.on('chatRoomViewInitialized', view => {
|
|
||||||
view.listenTo(view.model, 'change:minimized', view.onMinimizedChanged)
|
|
||||||
view.model.get('minimized') && view.hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
api.listen.on('getHeadingButtons', (view, buttons) => {
|
|
||||||
if (view.model.get('type') === _converse.CHATROOMS_TYPE) {
|
|
||||||
return addMinimizeButtonToMUC(view, buttons);
|
|
||||||
} else {
|
|
||||||
return addMinimizeButtonToChat(view, buttons);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const debouncedTrimChats = debounce(() => _converse.chatboxviews.trimChats(), 250);
|
|
||||||
api.listen.on('registeredGlobalEventHandlers', () => window.addEventListener("resize", debouncedTrimChats));
|
|
||||||
api.listen.on('unregisteredGlobalEventHandlers', () => window.removeEventListener("resize", debouncedTrimChats));
|
|
||||||
/************************ END Event Handlers ************************/
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,5 +1,5 @@
|
|||||||
import { CustomElement } from './element.js';
|
import tpl_trimmed_chat from "../templates/trimmed_chat.js";
|
||||||
import tpl_trimmed_chat from "templates/trimmed_chat.js";
|
import { CustomElement } from 'components/element.js';
|
||||||
import { api, _converse } from "@converse/headless/core";
|
import { api, _converse } from "@converse/headless/core";
|
||||||
|
|
||||||
|
|
150
src/plugins/minimize/index.js
Normal file
150
src/plugins/minimize/index.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* @module converse-minimize
|
||||||
|
* @copyright 2020, the Converse.js contributors
|
||||||
|
* @license Mozilla Public License (MPLv2)
|
||||||
|
*/
|
||||||
|
import './components/minimized-chat.js';
|
||||||
|
import 'plugins/chatview/index.js';
|
||||||
|
import MinimizedChats from './view.js';
|
||||||
|
import MinimizedChatsToggle from './toggle.js';
|
||||||
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
|
import { addMinimizeButtonToChat, addMinimizeButtonToMUC, initMinimizedChats, trimChats } from './utils.js';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { minimizableChatBox, minimizableChatBoxView } from './mixins.js';
|
||||||
|
|
||||||
|
const { dayjs } = converse.env;
|
||||||
|
|
||||||
|
|
||||||
|
converse.plugins.add('converse-minimize', {
|
||||||
|
/* Optional dependencies are other plugins which might be
|
||||||
|
* overridden or relied upon, and therefore need to be loaded before
|
||||||
|
* this plugin. They are called "optional" because they might not be
|
||||||
|
* available, in which case any overrides applicable to them will be
|
||||||
|
* ignored.
|
||||||
|
*
|
||||||
|
* It's possible however to make optional dependencies non-optional.
|
||||||
|
* If the setting "strict_plugin_dependencies" is set to true,
|
||||||
|
* an error will be raised if the plugin is not found.
|
||||||
|
*
|
||||||
|
* NB: These plugins need to have already been loaded via require.js.
|
||||||
|
*/
|
||||||
|
dependencies: [
|
||||||
|
"converse-chatview",
|
||||||
|
"converse-controlbox",
|
||||||
|
"converse-muc-views",
|
||||||
|
"converse-headlines-view",
|
||||||
|
"converse-dragresize"
|
||||||
|
],
|
||||||
|
|
||||||
|
enabled (_converse) {
|
||||||
|
return _converse.api.settings.get("view_mode") === 'overlayed';
|
||||||
|
},
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
ChatBox: {
|
||||||
|
initialize () {
|
||||||
|
this.__super__.initialize.apply(this, arguments);
|
||||||
|
this.on('show', this.maximize, this);
|
||||||
|
|
||||||
|
if (this.get('id') === 'controlbox') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.save({
|
||||||
|
'minimized': this.get('minimized') || false,
|
||||||
|
'time_minimized': this.get('time_minimized') || dayjs(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
maybeShow (force) {
|
||||||
|
if (!force && this.get('minimized')) {
|
||||||
|
// Must return the chatbox
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.__super__.maybeShow.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
isHidden () {
|
||||||
|
return this.__super__.isHidden.call(this) || this.get('minimized');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ChatBoxView: {
|
||||||
|
show () {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
if (_converse.api.settings.get("view_mode") === 'overlayed' && this.model.get('minimized')) {
|
||||||
|
this.model.minimize();
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return this.__super__.show.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isNewMessageHidden () {
|
||||||
|
return this.model.get('minimized') ||
|
||||||
|
this.__super__.isNewMessageHidden.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
setChatBoxHeight (height) {
|
||||||
|
if (!this.model.get('minimized')) {
|
||||||
|
return this.__super__.setChatBoxHeight.call(this, height);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setChatBoxWidth (width) {
|
||||||
|
if (!this.model.get('minimized')) {
|
||||||
|
return this.__super__.setChatBoxWidth.call(this, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
/* The initialize function gets called as soon as the plugin is
|
||||||
|
* loaded by Converse.js's plugin machinery.
|
||||||
|
*/
|
||||||
|
|
||||||
|
api.settings.extend({'no_trimming': false});
|
||||||
|
|
||||||
|
Object.assign(_converse.ChatBox.prototype, minimizableChatBox);
|
||||||
|
Object.assign(_converse.ChatBoxView.prototype, minimizableChatBoxView);
|
||||||
|
|
||||||
|
api.promises.add('minimizedChatsInitialized');
|
||||||
|
|
||||||
|
_converse.MinimizedChatsToggle = MinimizedChatsToggle;
|
||||||
|
_converse.MinimizedChats = MinimizedChats;
|
||||||
|
|
||||||
|
_converse.minimize = {};
|
||||||
|
_converse.minimize.trimChats = trimChats;
|
||||||
|
|
||||||
|
|
||||||
|
/************************ BEGIN Event Handlers ************************/
|
||||||
|
api.listen.on('chatBoxInsertedIntoDOM', view => _converse.minimize.trimChats(view));
|
||||||
|
api.listen.on('connected', () => initMinimizedChats());
|
||||||
|
api.listen.on('controlBoxOpened', view => _converse.minimize.trimChats(view));
|
||||||
|
api.listen.on('chatBoxViewInitialized', v => v.listenTo(v.model, 'change:minimized', v.onMinimizedChanged));
|
||||||
|
|
||||||
|
api.listen.on('chatRoomViewInitialized', view => {
|
||||||
|
view.listenTo(view.model, 'change:minimized', view.onMinimizedChanged)
|
||||||
|
view.model.get('minimized') && view.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
api.listen.on('getHeadingButtons', (view, buttons) => {
|
||||||
|
if (view.model.get('type') === _converse.CHATROOMS_TYPE) {
|
||||||
|
return addMinimizeButtonToMUC(view, buttons);
|
||||||
|
} else {
|
||||||
|
return addMinimizeButtonToChat(view, buttons);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const debouncedTrimChats = debounce(() => _converse.minimize.trimChats(), 250);
|
||||||
|
api.listen.on('registeredGlobalEventHandlers', () => window.addEventListener("resize", debouncedTrimChats));
|
||||||
|
api.listen.on('unregisteredGlobalEventHandlers', () => window.removeEventListener("resize", debouncedTrimChats));
|
||||||
|
}
|
||||||
|
});
|
99
src/plugins/minimize/mixins.js
Normal file
99
src/plugins/minimize/mixins.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { api, converse } from '@converse/headless/core';
|
||||||
|
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
export const minimizableChatBox = {
|
||||||
|
maximize () {
|
||||||
|
u.safeSave(this, {
|
||||||
|
'minimized': false,
|
||||||
|
'time_opened': new Date().getTime()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
minimize () {
|
||||||
|
u.safeSave(this, {
|
||||||
|
'minimized': true,
|
||||||
|
'time_minimized': new Date().toISOString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const minimizableChatBoxView = {
|
||||||
|
/**
|
||||||
|
* Handler which gets called when a {@link _converse#ChatBox} has it's
|
||||||
|
* `minimized` property set to false.
|
||||||
|
*
|
||||||
|
* Will trigger {@link _converse#chatBoxMaximized}
|
||||||
|
* @private
|
||||||
|
* @returns {_converse.ChatBoxView|_converse.ChatRoomView}
|
||||||
|
*/
|
||||||
|
onMaximized () {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
this.insertIntoDOM();
|
||||||
|
|
||||||
|
if (!this.model.isScrolledUp()) {
|
||||||
|
this.model.clearUnreadMsgCounter();
|
||||||
|
}
|
||||||
|
this.model.setChatState(_converse.ACTIVE);
|
||||||
|
this.show();
|
||||||
|
/**
|
||||||
|
* Triggered when a previously minimized chat gets maximized
|
||||||
|
* @event _converse#chatBoxMaximized
|
||||||
|
* @type { _converse.ChatBoxView }
|
||||||
|
* @example _converse.api.listen.on('chatBoxMaximized', view => { ... });
|
||||||
|
*/
|
||||||
|
api.trigger('chatBoxMaximized', this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler which gets called when a {@link _converse#ChatBox} has it's
|
||||||
|
* `minimized` property set to true.
|
||||||
|
*
|
||||||
|
* Will trigger {@link _converse#chatBoxMinimized}
|
||||||
|
* @private
|
||||||
|
* @returns {_converse.ChatBoxView|_converse.ChatRoomView}
|
||||||
|
*/
|
||||||
|
onMinimized (ev) {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
if (ev && ev.preventDefault) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
// save the scroll position to restore it on maximize
|
||||||
|
if (this.model.collection && this.model.collection.browserStorage) {
|
||||||
|
this.model.save({ 'scroll': this.content.scrollTop });
|
||||||
|
} else {
|
||||||
|
this.model.set({ 'scroll': this.content.scrollTop });
|
||||||
|
}
|
||||||
|
this.model.setChatState(_converse.INACTIVE);
|
||||||
|
this.hide();
|
||||||
|
/**
|
||||||
|
* Triggered when a previously maximized chat gets Minimized
|
||||||
|
* @event _converse#chatBoxMinimized
|
||||||
|
* @type { _converse.ChatBoxView }
|
||||||
|
* @example _converse.api.listen.on('chatBoxMinimized', view => { ... });
|
||||||
|
*/
|
||||||
|
api.trigger('chatBoxMinimized', this);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimizes a chat box.
|
||||||
|
* @returns {_converse.ChatBoxView|_converse.ChatRoomView}
|
||||||
|
*/
|
||||||
|
minimize (ev) {
|
||||||
|
if (ev && ev.preventDefault) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
this.model.minimize();
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
onMinimizedChanged (item) {
|
||||||
|
if (item.get('minimized')) {
|
||||||
|
this.onMinimized();
|
||||||
|
} else {
|
||||||
|
this.onMaximized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from 'i18n';
|
||||||
|
|
||||||
export default (o) =>
|
export default (o) =>
|
||||||
html`<div id="minimized-chats" class="${o.chats.length ? '' : 'hidden'}">
|
html`<div id="minimized-chats" class="${o.chats.length ? '' : 'hidden'}">
|
@ -1,5 +1,5 @@
|
|||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import { __ } from '../i18n';
|
import { __ } from 'i18n';
|
||||||
|
|
||||||
|
|
||||||
export default (o) => {
|
export default (o) => {
|
9
src/plugins/minimize/toggle.js
Normal file
9
src/plugins/minimize/toggle.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Model } from '@converse/skeletor/src/model.js';
|
||||||
|
|
||||||
|
const MinimizedChatsToggle = Model.extend({
|
||||||
|
defaults: {
|
||||||
|
'collapsed': false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default MinimizedChatsToggle;
|
147
src/plugins/minimize/utils.js
Normal file
147
src/plugins/minimize/utils.js
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { _converse, api, converse } from '@converse/headless/core';
|
||||||
|
import { __ } from 'i18n';
|
||||||
|
|
||||||
|
const u = converse.env.utils;
|
||||||
|
|
||||||
|
|
||||||
|
function getChatBoxWidth (view) {
|
||||||
|
if (view.model.get('id') === 'controlbox') {
|
||||||
|
const controlbox = view.model;
|
||||||
|
// We return the width of the controlbox or its toggle,
|
||||||
|
// depending on which is visible.
|
||||||
|
if (u.isVisible(controlbox.el)) {
|
||||||
|
return u.getOuterWidth(controlbox.el, true);
|
||||||
|
} else {
|
||||||
|
return u.getOuterWidth(_converse.controlboxtoggle.el, true);
|
||||||
|
}
|
||||||
|
} else if (!view.model.get('minimized') && u.isVisible(view.el)) {
|
||||||
|
return u.getOuterWidth(view.el, true);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShownChats () {
|
||||||
|
return _converse.chatboxviews.filter((view) =>
|
||||||
|
// The controlbox can take a while to close,
|
||||||
|
// so we need to check its state. That's why we checked
|
||||||
|
// the 'closed' state.
|
||||||
|
!view.model.get('minimized') &&
|
||||||
|
!view.model.get('closed') &&
|
||||||
|
u.isVisible(view.el)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMinimizedWidth () {
|
||||||
|
const minimized_el = _converse.minimized_chats?.el;
|
||||||
|
return _converse.chatboxes.pluck('minimized').includes(true) ? u.getOuterWidth(minimized_el, true) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBoxesWidth (newchat) {
|
||||||
|
const new_id = newchat ? newchat.model.get('id') : null;
|
||||||
|
const newchat_width = newchat ? u.getOuterWidth(newchat.el, true) : 0;
|
||||||
|
return Object.values(_converse.chatboxviews.xget(new_id))
|
||||||
|
.reduce((memo, view) => memo + getChatBoxWidth(view), newchat_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a newly created chat box will be shown.
|
||||||
|
* It checks whether there is enough space on the page to show
|
||||||
|
* another chat box. Otherwise it minimizes the oldest chat box
|
||||||
|
* to create space.
|
||||||
|
* @private
|
||||||
|
* @method _converse.ChatBoxViews#trimChats
|
||||||
|
* @param { _converse.ChatBoxView|_converse.ChatRoomView|_converse.ControlBoxView|_converse.HeadlinesBoxView } [newchat]
|
||||||
|
*/
|
||||||
|
export async function trimChats (newchat) {
|
||||||
|
if (api.settings.get('no_trimming') || !api.connection.connected() || api.settings.get("view_mode") !== 'overlayed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const shown_chats = getShownChats();
|
||||||
|
if (shown_chats.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const body_width = u.getOuterWidth(document.querySelector('body'), true);
|
||||||
|
if (getChatBoxWidth(shown_chats[0]) === body_width) {
|
||||||
|
// If the chats shown are the same width as the body,
|
||||||
|
// then we're in responsive mode and the chats are
|
||||||
|
// fullscreen. In this case we don't trim.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await api.waitUntil('minimizedChatsInitialized');
|
||||||
|
const minimized_el = _converse.minimized_chats?.el;
|
||||||
|
if (minimized_el) {
|
||||||
|
while ((getMinimizedWidth() + getBoxesWidth(newchat)) > body_width) {
|
||||||
|
const new_id = newchat ? newchat.model.get('id') : null;
|
||||||
|
const oldest_chat = getOldestMaximizedChat([new_id]);
|
||||||
|
if (oldest_chat) {
|
||||||
|
// We hide the chat immediately, because waiting
|
||||||
|
// for the event to fire (and letting the
|
||||||
|
// ChatBoxView hide it then) causes race
|
||||||
|
// conditions.
|
||||||
|
const view = _converse.chatboxviews.get(oldest_chat.get('id'));
|
||||||
|
if (view) {
|
||||||
|
view.hide();
|
||||||
|
}
|
||||||
|
oldest_chat.minimize();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOldestMaximizedChat (exclude_ids) {
|
||||||
|
// Get oldest view (if its id is not excluded)
|
||||||
|
exclude_ids.push('controlbox');
|
||||||
|
let i = 0;
|
||||||
|
let model = _converse.chatboxes.sort().at(i);
|
||||||
|
while (exclude_ids.includes(model.get('id')) || model.get('minimized') === true) {
|
||||||
|
i++;
|
||||||
|
model = _converse.chatboxes.at(i);
|
||||||
|
if (!model) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initMinimizedChats () {
|
||||||
|
_converse.minimized_chats?.remove();
|
||||||
|
_converse.minimized_chats = new _converse.MinimizedChats({model: _converse.chatboxes});
|
||||||
|
/**
|
||||||
|
* Triggered once the _converse.MinimizedChats instance has been initialized
|
||||||
|
* @event _converse#minimizedChatsInitialized
|
||||||
|
* @example _converse.api.listen.on('minimizedChatsInitialized', () => { ... });
|
||||||
|
*/
|
||||||
|
api.trigger('minimizedChatsInitialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addMinimizeButtonToChat (view, buttons) {
|
||||||
|
const data = {
|
||||||
|
'a_class': 'toggle-chatbox-button',
|
||||||
|
'handler': ev => view.minimize(ev),
|
||||||
|
'i18n_text': __('Minimize'),
|
||||||
|
'i18n_title': __('Minimize this chat'),
|
||||||
|
'icon_class': "fa-minus",
|
||||||
|
'name': 'minimize',
|
||||||
|
'standalone': _converse.api.settings.get("view_mode") === 'overlayed'
|
||||||
|
}
|
||||||
|
const names = buttons.map(t => t.name);
|
||||||
|
const idx = names.indexOf('close');
|
||||||
|
return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addMinimizeButtonToMUC (view, buttons) {
|
||||||
|
const data = {
|
||||||
|
'a_class': 'toggle-chatbox-button',
|
||||||
|
'handler': ev => view.minimize(ev),
|
||||||
|
'i18n_text': __('Minimize'),
|
||||||
|
'i18n_title': __('Minimize this groupchat'),
|
||||||
|
'icon_class': "fa-minus",
|
||||||
|
'name': 'minimize',
|
||||||
|
'standalone': _converse.api.settings.get("view_mode") === 'overlayed'
|
||||||
|
}
|
||||||
|
const names = buttons.map(t => t.name);
|
||||||
|
const idx = names.indexOf('signout');
|
||||||
|
return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons];
|
||||||
|
}
|
50
src/plugins/minimize/view.js
Normal file
50
src/plugins/minimize/view.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import tpl_chats_panel from './templates/chats-panel.js';
|
||||||
|
import { View } from '@converse/skeletor/src/view';
|
||||||
|
import { render } from 'lit-html';
|
||||||
|
import { _converse } from '@converse/headless/core';
|
||||||
|
|
||||||
|
|
||||||
|
const MinimizedChats = View.extend({
|
||||||
|
tagName: 'span',
|
||||||
|
|
||||||
|
async initialize () {
|
||||||
|
await this.initToggle();
|
||||||
|
this.render();
|
||||||
|
this.listenTo(this.minchats, 'change:collapsed', this.render)
|
||||||
|
this.listenTo(this.model, 'add', this.render)
|
||||||
|
this.listenTo(this.model, 'change:fullname', this.render)
|
||||||
|
this.listenTo(this.model, 'change:jid', this.render)
|
||||||
|
this.listenTo(this.model, 'change:minimized', this.render)
|
||||||
|
this.listenTo(this.model, 'change:name', this.render)
|
||||||
|
this.listenTo(this.model, 'change:num_unread', this.render)
|
||||||
|
this.listenTo(this.model, 'remove', this.render)
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const chats = this.model.where({'minimized': true});
|
||||||
|
const num_unread = chats.reduce((acc, chat) => (acc + chat.get('num_unread')), 0);
|
||||||
|
const num_minimized = chats.reduce((acc, chat) => (acc + (chat.get('minimized') ? 1 : 0)), 0);
|
||||||
|
const collapsed = this.minchats.get('collapsed');
|
||||||
|
const data = { chats, num_unread, num_minimized, collapsed };
|
||||||
|
data.toggle = ev => this.toggle(ev);
|
||||||
|
render(tpl_chats_panel(data), this.el);
|
||||||
|
|
||||||
|
if (!this.el.parentElement) {
|
||||||
|
_converse.chatboxviews.insertRowColumn(this.el);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async initToggle () {
|
||||||
|
const id = `converse.minchatstoggle-${_converse.bare_jid}`;
|
||||||
|
this.minchats = new _converse.MinimizedChatsToggle({id});
|
||||||
|
this.minchats.browserStorage = _converse.createStore(id);
|
||||||
|
await new Promise(resolve => this.minchats.fetch({'success': resolve, 'error': resolve}));
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle (ev) {
|
||||||
|
ev?.preventDefault();
|
||||||
|
this.minchats.save({'collapsed': !this.minchats.get('collapsed')});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default MinimizedChats;
|
Loading…
Reference in New Issue
Block a user