|
@@ -1,18 +1,18 @@
|
|
|
-import { filesize } from 'filesize';
|
|
|
-import pick from 'lodash-es/pick';
|
|
|
-import debounce from 'lodash-es/debounce.js';
|
|
|
-import { getOpenPromise } from '@converse/openpromise';
|
|
|
-import { Model } from '@converse/skeletor';
|
|
|
+import { filesize } from "filesize";
|
|
|
+import pick from "lodash-es/pick";
|
|
|
+import debounce from "lodash-es/debounce.js";
|
|
|
+import { getOpenPromise } from "@converse/openpromise";
|
|
|
+import { Model } from "@converse/skeletor";
|
|
|
import log from "@converse/log";
|
|
|
-import { initStorage } from '../utils/storage.js';
|
|
|
-import * as constants from './constants.js';
|
|
|
-import converse from './api/public.js';
|
|
|
-import api from './api/index.js';
|
|
|
-import { isNewMessage } from '../plugins/chat/utils.js';
|
|
|
-import _converse from './_converse.js';
|
|
|
-import { MethodNotImplementedError } from './errors.js';
|
|
|
-import { sendMarker, sendReceiptStanza, sendRetractionMessage } from './actions.js';
|
|
|
-import { parseMessage } from '../plugins/chat/parsers';
|
|
|
+import { initStorage } from "../utils/storage.js";
|
|
|
+import * as constants from "./constants.js";
|
|
|
+import converse from "./api/public.js";
|
|
|
+import api from "./api/index.js";
|
|
|
+import { isNewMessage } from "../plugins/chat/utils.js";
|
|
|
+import _converse from "./_converse.js";
|
|
|
+import { MethodNotImplementedError } from "./errors.js";
|
|
|
+import { sendMarker, sendReceiptStanza, sendRetractionMessage } from "./actions.js";
|
|
|
+import { parseMessage } from "../plugins/chat/parsers";
|
|
|
|
|
|
const { Strophe, stx, u } = converse.env;
|
|
|
|
|
@@ -52,7 +52,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
this.initMessages();
|
|
|
this.initNotifications();
|
|
|
|
|
|
- this.ui.on('change:scrolled', () => this.onScrolledChanged());
|
|
|
+ this.ui.on("change:scrolled", () => this.onScrolledChanged());
|
|
|
}
|
|
|
|
|
|
initNotifications() {
|
|
@@ -67,7 +67,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @returns {string}
|
|
|
*/
|
|
|
getDisplayName() {
|
|
|
- return this.get('jid');
|
|
|
+ return this.get("jid");
|
|
|
}
|
|
|
|
|
|
canPostMessages() {
|
|
@@ -89,7 +89,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
}
|
|
|
|
|
|
getMessagesCacheKey() {
|
|
|
- return `converse.messages-${this.get('jid')}-${_converse.session.get('bare_jid')}`;
|
|
|
+ return `converse.messages-${this.get("jid")}-${_converse.session.get("bare_jid")}`;
|
|
|
}
|
|
|
|
|
|
getMessagesCollection() {
|
|
@@ -98,14 +98,14 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
|
|
|
getNotificationsText() {
|
|
|
const { __ } = _converse;
|
|
|
- if (this.notifications?.get('chat_state') === constants.COMPOSING) {
|
|
|
- return __('%1$s is typing', this.getDisplayName());
|
|
|
- } else if (this.notifications?.get('chat_state') === constants.PAUSED) {
|
|
|
- return __('%1$s has stopped typing', this.getDisplayName());
|
|
|
- } else if (this.notifications?.get('chat_state') === constants.GONE) {
|
|
|
- return __('%1$s has gone away', this.getDisplayName());
|
|
|
+ if (this.notifications?.get("chat_state") === constants.COMPOSING) {
|
|
|
+ return __("%1$s is typing", this.getDisplayName());
|
|
|
+ } else if (this.notifications?.get("chat_state") === constants.PAUSED) {
|
|
|
+ return __("%1$s has stopped typing", this.getDisplayName());
|
|
|
+ } else if (this.notifications?.get("chat_state") === constants.GONE) {
|
|
|
+ return __("%1$s has gone away", this.getDisplayName());
|
|
|
} else {
|
|
|
- return '';
|
|
|
+ return "";
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -115,13 +115,13 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
this.messages.chatbox = this;
|
|
|
initStorage(this.messages, this.getMessagesCacheKey());
|
|
|
|
|
|
- this.listenTo(this.messages, 'add', (m) => this.onMessageAdded(m));
|
|
|
- this.listenTo(this.messages, 'change:upload', (m) => this.onMessageUploadChanged(m));
|
|
|
+ this.listenTo(this.messages, "add", (m) => this.onMessageAdded(m));
|
|
|
+ this.listenTo(this.messages, "change:upload", (m) => this.onMessageUploadChanged(m));
|
|
|
}
|
|
|
|
|
|
fetchMessages() {
|
|
|
if (this.messages.fetched_flag) {
|
|
|
- log.info(`Not re-fetching messages for ${this.get('jid')}`);
|
|
|
+ log.info(`Not re-fetching messages for ${this.get("jid")}`);
|
|
|
return;
|
|
|
}
|
|
|
this.messages.fetched_flag = true;
|
|
@@ -149,14 +149,14 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @type {ModelWithMessages}
|
|
|
* @example _converse.api.listen.on('afterMessagesFetched', (model) => { ... });
|
|
|
*/
|
|
|
- api.trigger('afterMessagesFetched', this);
|
|
|
+ api.trigger("afterMessagesFetched", this);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @param {MessageAttributes|Error} _attrs_or_error
|
|
|
*/
|
|
|
async onMessage(_attrs_or_error) {
|
|
|
- throw new MethodNotImplementedError('onMessage is not implemented');
|
|
|
+ throw new MethodNotImplementedError("onMessage is not implemented");
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -165,7 +165,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @returns {object}
|
|
|
*/
|
|
|
getUpdatedMessageAttributes(message, attrs) {
|
|
|
- if (!attrs.error_type && message.get('error_type') === 'Decryption') {
|
|
|
+ if (!attrs.error_type && message.get("error_type") === "Decryption") {
|
|
|
// Looks like we have a failed decrypted message stored, and now
|
|
|
// we have a properly decrypted version of the same message.
|
|
|
// See issue: https://github.com/conversejs/converse.js/issues/2733#issuecomment-1035493594
|
|
@@ -209,7 +209,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
}
|
|
|
|
|
|
let query;
|
|
|
- if (attrs.type === 'groupchat') {
|
|
|
+ if (attrs.type === "groupchat") {
|
|
|
const { occupant_id, replace_id } = /** @type {MUCMessageAttributes} */ (attrs);
|
|
|
query = occupant_id
|
|
|
? ({ attributes: m }) => m.msgid === replace_id && m.occupant_id == occupant_id
|
|
@@ -222,26 +222,26 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
|
|
|
const message = this.messages.models.find(query);
|
|
|
if (!message) {
|
|
|
- attrs['older_versions'] = {};
|
|
|
+ attrs["older_versions"] = {};
|
|
|
return await this.createMessage(attrs); // eslint-disable-line no-return-await
|
|
|
}
|
|
|
|
|
|
- const older_versions = message.get('older_versions') || {};
|
|
|
- if (attrs.time < message.get('time') && message.get('edited')) {
|
|
|
+ const older_versions = message.get("older_versions") || {};
|
|
|
+ if (attrs.time < message.get("time") && message.get("edited")) {
|
|
|
// This is an older message which has been corrected afterwards
|
|
|
- older_versions[attrs.time] = attrs['message'];
|
|
|
- message.save({ 'older_versions': older_versions });
|
|
|
+ older_versions[attrs.time] = attrs["message"];
|
|
|
+ message.save({ "older_versions": older_versions });
|
|
|
} else {
|
|
|
// This is a correction of an earlier message we already received
|
|
|
if (Object.keys(older_versions).length) {
|
|
|
- older_versions[message.get('edited')] = message.getMessageText();
|
|
|
+ older_versions[message.get("edited")] = message.getMessageText();
|
|
|
} else {
|
|
|
- older_versions[message.get('time')] = message.getMessageText();
|
|
|
+ older_versions[message.get("time")] = message.getMessageText();
|
|
|
}
|
|
|
attrs = Object.assign(attrs, { older_versions });
|
|
|
- delete attrs['msgid']; // We want to keep the msgid of the original message
|
|
|
- delete attrs['id']; // Delete id, otherwise a new cache entry gets created
|
|
|
- attrs['time'] = message.get('time');
|
|
|
+ delete attrs["msgid"]; // We want to keep the msgid of the original message
|
|
|
+ delete attrs["id"]; // Delete id, otherwise a new cache entry gets created
|
|
|
+ attrs["time"] = message.get("time");
|
|
|
message.save(attrs);
|
|
|
}
|
|
|
return message;
|
|
@@ -263,7 +263,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @return {Promise<MessageAttributes>}
|
|
|
*/
|
|
|
async getOutgoingMessageAttributes(_attrs) {
|
|
|
- throw new MethodNotImplementedError('getOutgoingMessageAttributes is not implemented');
|
|
|
+ throw new MethodNotImplementedError("getOutgoingMessageAttributes is not implemented");
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -278,19 +278,19 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
await converse.emojis?.initialized_promise;
|
|
|
|
|
|
if (!this.canPostMessages()) {
|
|
|
- log.warn('sendMessage was called but canPostMessages is false');
|
|
|
+ log.warn("sendMessage was called but canPostMessages is false");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
attrs = await this.getOutgoingMessageAttributes(attrs);
|
|
|
- let message = this.messages.findWhere('correcting');
|
|
|
+ let message = this.messages.findWhere("correcting");
|
|
|
if (message) {
|
|
|
- const older_versions = message.get('older_versions') || {};
|
|
|
- const edited_time = message.get('edited') || message.get('time');
|
|
|
+ const older_versions = message.get("older_versions") || {};
|
|
|
+ const edited_time = message.get("edited") || message.get("time");
|
|
|
older_versions[edited_time] = message.getMessageText();
|
|
|
|
|
|
message.save({
|
|
|
- ...['body', 'is_only_emojis', 'media_urls', 'references', 'is_encrypted'].reduce((obj, k) => {
|
|
|
+ ...["body", "is_only_emojis", "media_urls", "references", "is_encrypted"].reduce((obj, k) => {
|
|
|
if (attrs.hasOwnProperty(k)) obj[k] = attrs[k];
|
|
|
return obj;
|
|
|
}, {}),
|
|
@@ -327,7 +327,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @property {(ChatBox|MUC)} data.chatbox
|
|
|
* @property {(BaseMessage)} data.message
|
|
|
*/
|
|
|
- api.trigger('sendMessage', { 'chatbox': this, message });
|
|
|
+ api.trigger("sendMessage", { "chatbox": this, message });
|
|
|
return message;
|
|
|
}
|
|
|
|
|
@@ -337,13 +337,13 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
*/
|
|
|
retractOwnMessage(message) {
|
|
|
const retraction_id = u.getUniqueId();
|
|
|
- sendRetractionMessage(this.get('jid'), message, retraction_id);
|
|
|
+ sendRetractionMessage(this.get("jid"), message, retraction_id);
|
|
|
message.save({
|
|
|
- 'retracted': new Date().toISOString(),
|
|
|
- 'retracted_id': message.get('origin_id'),
|
|
|
- 'retraction_id': retraction_id,
|
|
|
- 'is_ephemeral': true,
|
|
|
- 'editable': false,
|
|
|
+ "retracted": new Date().toISOString(),
|
|
|
+ "retracted_id": message.get("origin_id"),
|
|
|
+ "retraction_id": retraction_id,
|
|
|
+ "is_ephemeral": true,
|
|
|
+ "editable": false,
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -352,27 +352,27 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
*/
|
|
|
async sendFiles(files) {
|
|
|
const { __, session } = _converse;
|
|
|
- const result = await api.disco.features.get(Strophe.NS.HTTPUPLOAD, session.get('domain'));
|
|
|
+ const result = await api.disco.features.get(Strophe.NS.HTTPUPLOAD, session.get("domain"));
|
|
|
const item = result.pop();
|
|
|
if (!item) {
|
|
|
this.createMessage({
|
|
|
- 'message': __('Sorry, looks like file upload is not supported by your server.'),
|
|
|
- 'type': 'error',
|
|
|
- 'is_ephemeral': true,
|
|
|
+ "message": __("Sorry, looks like file upload is not supported by your server."),
|
|
|
+ "type": "error",
|
|
|
+ "is_ephemeral": true,
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
const data = item.dataforms
|
|
|
- .where({ 'FORM_TYPE': { 'value': Strophe.NS.HTTPUPLOAD, 'type': 'hidden' } })
|
|
|
+ .where({ "FORM_TYPE": { "value": Strophe.NS.HTTPUPLOAD, "type": "hidden" } })
|
|
|
.pop();
|
|
|
- const max_file_size = parseInt((data?.attributes || {})['max-file-size']?.value, 10);
|
|
|
+ const max_file_size = parseInt((data?.attributes || {})["max-file-size"]?.value, 10);
|
|
|
const slot_request_url = item?.id;
|
|
|
|
|
|
if (!slot_request_url) {
|
|
|
this.createMessage({
|
|
|
- 'message': __('Sorry, looks like file upload is not supported by your server.'),
|
|
|
- 'type': 'error',
|
|
|
- 'is_ephemeral': true,
|
|
|
+ "message": __("Sorry, looks like file upload is not supported by your server."),
|
|
|
+ "type": "error",
|
|
|
+ "is_ephemeral": true,
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
@@ -384,33 +384,33 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {ChatBox|MUC} chat - The chat from which this file will be uploaded.
|
|
|
* @param {File} file - The file that will be uploaded
|
|
|
*/
|
|
|
- file = await api.hook('beforeFileUpload', this, file);
|
|
|
+ file = await api.hook("beforeFileUpload", this, file);
|
|
|
|
|
|
if (!isNaN(max_file_size) && file.size > max_file_size) {
|
|
|
const size = filesize(max_file_size);
|
|
|
const message = Array.isArray(size)
|
|
|
- ? __('The size of your file, %1$s, exceeds the maximum allowed by your server.', file.name)
|
|
|
+ ? __("The size of your file, %1$s, exceeds the maximum allowed by your server.", file.name)
|
|
|
: __(
|
|
|
- 'The size of your file, %1$s, exceeds the maximum allowed by your server, which is %2$s.',
|
|
|
+ "The size of your file, %1$s, exceeds the maximum allowed by your server, which is %2$s.",
|
|
|
file.name,
|
|
|
size
|
|
|
);
|
|
|
return this.createMessage({
|
|
|
message,
|
|
|
- type: 'error',
|
|
|
+ type: "error",
|
|
|
is_ephemeral: true,
|
|
|
});
|
|
|
} else {
|
|
|
const initial_attrs = await this.getOutgoingMessageAttributes();
|
|
|
const attrs = Object.assign(initial_attrs, {
|
|
|
- 'file': true,
|
|
|
- 'progress': 0,
|
|
|
- 'slot_request_url': slot_request_url,
|
|
|
+ "file": true,
|
|
|
+ "progress": 0,
|
|
|
+ "slot_request_url": slot_request_url,
|
|
|
});
|
|
|
this.setEditable(attrs, new Date().toISOString());
|
|
|
- const message = await this.createMessage(attrs, { 'silent': true });
|
|
|
+ const message = await this.createMessage(attrs, { "silent": true });
|
|
|
message.file = file;
|
|
|
- this.messages.trigger('add', message);
|
|
|
+ this.messages.trigger("add", message);
|
|
|
message.getRequestSlotURL();
|
|
|
}
|
|
|
});
|
|
@@ -425,15 +425,15 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {String} send_time - time when the message was sent
|
|
|
*/
|
|
|
setEditable(attrs, send_time) {
|
|
|
- if (attrs.is_headline || u.isEmptyMessage(attrs) || attrs.sender !== 'me') {
|
|
|
+ if (attrs.is_headline || u.isEmptyMessage(attrs) || attrs.sender !== "me") {
|
|
|
return;
|
|
|
}
|
|
|
- if (api.settings.get('allow_message_corrections') === 'all') {
|
|
|
- attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
|
|
- } else if (api.settings.get('allow_message_corrections') === 'last' && send_time > this.get('time_sent')) {
|
|
|
- this.set({ 'time_sent': send_time });
|
|
|
- this.messages.findWhere({ 'editable': true })?.save({ 'editable': false });
|
|
|
- attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs);
|
|
|
+ if (api.settings.get("allow_message_corrections") === "all") {
|
|
|
+ attrs.editable = !(attrs.file || attrs.retracted || "oob_url" in attrs);
|
|
|
+ } else if (api.settings.get("allow_message_corrections") === "last" && send_time > this.get("time_sent")) {
|
|
|
+ this.set({ "time_sent": send_time });
|
|
|
+ this.messages.findWhere({ "editable": true })?.save({ "editable": false });
|
|
|
+ attrs.editable = !(attrs.file || attrs.retracted || "oob_url" in attrs);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -465,7 +465,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
constants.INACTIVE
|
|
|
);
|
|
|
}
|
|
|
- this.set('chat_state', state, options);
|
|
|
+ this.set("chat_state", state, options);
|
|
|
return this;
|
|
|
}
|
|
|
|
|
@@ -474,8 +474,8 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
*/
|
|
|
onMessageAdded(message) {
|
|
|
if (
|
|
|
- api.settings.get('prune_messages_above') &&
|
|
|
- (api.settings.get('pruning_behavior') === 'scrolled' || !this.ui.get('scrolled')) &&
|
|
|
+ api.settings.get("prune_messages_above") &&
|
|
|
+ (api.settings.get("pruning_behavior") === "scrolled" || !this.ui.get("scrolled")) &&
|
|
|
!u.isEmptyMessage(message)
|
|
|
) {
|
|
|
this.debouncedPruneHistory();
|
|
@@ -486,11 +486,11 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {BaseMessage} message
|
|
|
*/
|
|
|
async onMessageUploadChanged(message) {
|
|
|
- if (message.get('upload') === constants.SUCCESS) {
|
|
|
+ if (message.get("upload") === constants.SUCCESS) {
|
|
|
const attrs = {
|
|
|
- 'body': message.get('body'),
|
|
|
- 'spoiler_hint': message.get('spoiler_hint'),
|
|
|
- 'oob_url': message.get('oob_url'),
|
|
|
+ "body": message.get("body"),
|
|
|
+ "spoiler_hint": message.get("spoiler_hint"),
|
|
|
+ "oob_url": message.get("oob_url"),
|
|
|
};
|
|
|
await this.sendMessage(attrs);
|
|
|
message.destroy();
|
|
@@ -498,7 +498,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
}
|
|
|
|
|
|
onScrolledChanged() {
|
|
|
- if (!this.ui.get('scrolled')) {
|
|
|
+ if (!this.ui.get("scrolled")) {
|
|
|
this.clearUnreadMsgCounter();
|
|
|
this.pruneHistoryWhenScrolledDown();
|
|
|
}
|
|
@@ -506,9 +506,9 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
|
|
|
pruneHistoryWhenScrolledDown() {
|
|
|
if (
|
|
|
- api.settings.get('prune_messages_above') &&
|
|
|
- api.settings.get('pruning_behavior') === 'unscrolled' &&
|
|
|
- !this.ui.get('scrolled')
|
|
|
+ api.settings.get("prune_messages_above") &&
|
|
|
+ api.settings.get("pruning_behavior") === "unscrolled" &&
|
|
|
+ !this.ui.get("scrolled")
|
|
|
) {
|
|
|
this.debouncedPruneHistory();
|
|
|
}
|
|
@@ -536,7 +536,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
try {
|
|
|
await this.messages.clearStore();
|
|
|
} catch (e) {
|
|
|
- this.messages.trigger('reset');
|
|
|
+ this.messages.trigger("reset");
|
|
|
log.error(e);
|
|
|
} finally {
|
|
|
// No point in fetching messages from the cache if it's been cleared.
|
|
@@ -547,13 +547,13 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
|
|
|
editEarlierMessage() {
|
|
|
let message;
|
|
|
- let idx = this.messages.findLastIndex('correcting');
|
|
|
+ let idx = this.messages.findLastIndex("correcting");
|
|
|
if (idx >= 0) {
|
|
|
- this.messages.at(idx).save('correcting', false);
|
|
|
+ this.messages.at(idx).save("correcting", false);
|
|
|
while (idx > 0) {
|
|
|
idx -= 1;
|
|
|
const candidate = this.messages.at(idx);
|
|
|
- if (candidate.get('editable')) {
|
|
|
+ if (candidate.get("editable")) {
|
|
|
message = candidate;
|
|
|
break;
|
|
|
}
|
|
@@ -562,25 +562,25 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
message =
|
|
|
message ||
|
|
|
this.messages
|
|
|
- .filter({ 'sender': 'me' })
|
|
|
+ .filter({ "sender": "me" })
|
|
|
.reverse()
|
|
|
- .find((m) => m.get('editable'));
|
|
|
+ .find((m) => m.get("editable"));
|
|
|
if (message) {
|
|
|
- message.save('correcting', true);
|
|
|
+ message.save("correcting", true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
editLaterMessage() {
|
|
|
let message;
|
|
|
- let idx = this.messages.findLastIndex('correcting');
|
|
|
+ let idx = this.messages.findLastIndex("correcting");
|
|
|
if (idx >= 0) {
|
|
|
- this.messages.at(idx).save('correcting', false);
|
|
|
+ this.messages.at(idx).save("correcting", false);
|
|
|
while (idx < this.messages.length - 1) {
|
|
|
idx += 1;
|
|
|
const candidate = this.messages.at(idx);
|
|
|
- if (candidate.get('editable')) {
|
|
|
+ if (candidate.get("editable")) {
|
|
|
message = candidate;
|
|
|
- message.save('correcting', true);
|
|
|
+ message.save("correcting", true);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -591,7 +591,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
getOldestMessage() {
|
|
|
for (let i = 0; i < this.messages.length; i++) {
|
|
|
const message = this.messages.at(i);
|
|
|
- if (message.get('type') === this.get('message_type')) {
|
|
|
+ if (message.get("type") === this.get("message_type")) {
|
|
|
return message;
|
|
|
}
|
|
|
}
|
|
@@ -600,7 +600,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
getMostRecentMessage() {
|
|
|
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
|
const message = this.messages.at(i);
|
|
|
- if (message.get('type') === this.get('message_type')) {
|
|
|
+ if (message.get("type") === this.get("message_type")) {
|
|
|
return message;
|
|
|
}
|
|
|
}
|
|
@@ -613,7 +613,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
*/
|
|
|
getMessageReferencedByError(attrs) {
|
|
|
const id = attrs.msgid;
|
|
|
- return id && this.messages.models.find((m) => [m.get('msgid'), m.get('retraction_id')].includes(id));
|
|
|
+ return id && this.messages.models.find((m) => [m.get("msgid"), m.get("retraction_id")].includes(id));
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -631,7 +631,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
}
|
|
|
// Only look for dangling retractions if there are newer
|
|
|
// messages than this one, since retractions come after.
|
|
|
- if (this.messages.last().get('time') > attrs.time) {
|
|
|
+ if (this.messages.last().get("time") > attrs.time) {
|
|
|
// Search from latest backwards
|
|
|
const messages = Array.from(this.messages.models);
|
|
|
messages.reverse();
|
|
@@ -676,9 +676,9 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {object} attrs - Attributes representing a received
|
|
|
*/
|
|
|
getStanzaIdQueryAttrs(attrs) {
|
|
|
- const keys = Object.keys(attrs).filter((k) => k.startsWith('stanza_id '));
|
|
|
+ const keys = Object.keys(attrs).filter((k) => k.startsWith("stanza_id "));
|
|
|
return keys.map((key) => {
|
|
|
- const by_jid = key.replace(/^stanza_id /, '');
|
|
|
+ const by_jid = key.replace(/^stanza_id /, "");
|
|
|
const query = {};
|
|
|
query[`stanza_id ${by_jid}`] = attrs[key];
|
|
|
return query;
|
|
@@ -698,7 +698,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
if (!attrs.is_encrypted && attrs.body) {
|
|
|
// We can't match the message if it's a reflected
|
|
|
// encrypted message (e.g. via MAM or in a MUC)
|
|
|
- query['body'] = attrs.body;
|
|
|
+ query["body"] = attrs.body;
|
|
|
}
|
|
|
return query;
|
|
|
}
|
|
@@ -711,22 +711,22 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {boolean} [force=false] - Whether a marker should be sent for the
|
|
|
* message, even if it didn't include a `markable` element.
|
|
|
*/
|
|
|
- async sendMarkerForMessage(msg, type = 'displayed', force = false) {
|
|
|
- if (!msg || msg?.get('type') === 'groupchat' || !api.settings.get('send_chat_markers').includes(type)) {
|
|
|
+ async sendMarkerForMessage(msg, type = "displayed", force = false) {
|
|
|
+ if (!msg || msg?.get("type") === "groupchat" || !api.settings.get("send_chat_markers").includes(type)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Don't send chat markers to contacts that are not subscribed
|
|
|
// to our presence.
|
|
|
- const contact = await api.contacts.get(this.get('jid'));
|
|
|
- const subscription = contact?.get('subscription');
|
|
|
- if (!contact || subscription === 'none' || subscription === 'to') {
|
|
|
+ const contact = await api.contacts.get(this.get("jid"));
|
|
|
+ const subscription = contact?.get("subscription");
|
|
|
+ if (!contact || subscription === "none" || subscription === "to") {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (msg?.get('is_markable') || force) {
|
|
|
- const from_jid = Strophe.getBareJidFromJid(msg.get('from'));
|
|
|
- sendMarker(from_jid, msg.get('msgid'), type, msg.get('type'));
|
|
|
+ if (msg?.get("is_markable") || force) {
|
|
|
+ const from_jid = Strophe.getBareJidFromJid(msg.get("from"));
|
|
|
+ sendMarker(from_jid, msg.get("msgid"), type, msg.get("type"));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -736,17 +736,17 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {BaseMessage} message
|
|
|
*/
|
|
|
handleUnreadMessage(message) {
|
|
|
- if (!message?.get('body')) {
|
|
|
+ if (!message?.get("body")) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (isNewMessage(message)) {
|
|
|
- if (message.get('sender') === 'me') {
|
|
|
+ if (message.get("sender") === "me") {
|
|
|
// We remove the "scrolled" flag so that the chat area
|
|
|
// gets scrolled down. We always want to scroll down
|
|
|
// when the user writes a message as opposed to when a
|
|
|
// message is received.
|
|
|
- this.ui.set('scrolled', false);
|
|
|
+ this.ui.set("scrolled", false);
|
|
|
} else if (this.isHidden()) {
|
|
|
this.incrementUnreadMsgsCounter(message);
|
|
|
} else {
|
|
@@ -769,28 +769,28 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
error_type: attrs.error_type,
|
|
|
is_error: true,
|
|
|
};
|
|
|
- if (attrs.msgid === message.get('retraction_id')) {
|
|
|
+ if (attrs.msgid === message.get("retraction_id")) {
|
|
|
// The error message refers to a retraction
|
|
|
new_attrs.retraction_id = undefined;
|
|
|
if (!attrs.error) {
|
|
|
- if (attrs.error_condition === 'forbidden') {
|
|
|
+ if (attrs.error_condition === "forbidden") {
|
|
|
new_attrs.error = __("You're not allowed to retract your message.");
|
|
|
} else {
|
|
|
- new_attrs.error = __('Sorry, an error occurred while trying to retract your message.');
|
|
|
+ new_attrs.error = __("Sorry, an error occurred while trying to retract your message.");
|
|
|
}
|
|
|
}
|
|
|
} else if (!attrs.error) {
|
|
|
- if (attrs.error_condition === 'forbidden') {
|
|
|
+ if (attrs.error_condition === "forbidden") {
|
|
|
new_attrs.error = __("You're not allowed to send a message.");
|
|
|
} else {
|
|
|
- new_attrs.error = __('Sorry, an error occurred while trying to send your message.');
|
|
|
+ new_attrs.error = __("Sorry, an error occurred while trying to send your message.");
|
|
|
}
|
|
|
}
|
|
|
/**
|
|
|
* *Hook* which allows plugins to add application-specific attributes
|
|
|
* @event _converse#getErrorAttributesForMessage
|
|
|
*/
|
|
|
- return await api.hook('getErrorAttributesForMessage', attrs, new_attrs);
|
|
|
+ return await api.hook("getErrorAttributesForMessage", attrs, new_attrs);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -823,16 +823,16 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
*/
|
|
|
incrementUnreadMsgsCounter(message) {
|
|
|
const settings = {
|
|
|
- 'num_unread': this.get('num_unread') + 1,
|
|
|
+ "num_unread": this.get("num_unread") + 1,
|
|
|
};
|
|
|
- if (this.get('num_unread') === 0) {
|
|
|
- settings['first_unread_id'] = message.get('id');
|
|
|
+ if (this.get("num_unread") === 0) {
|
|
|
+ settings["first_unread_id"] = message.get("id");
|
|
|
}
|
|
|
this.save(settings);
|
|
|
}
|
|
|
|
|
|
clearUnreadMsgCounter() {
|
|
|
- if (this.get('num_unread') > 0) {
|
|
|
+ if (this.get("num_unread") > 0) {
|
|
|
this.sendMarkerForMessage(this.messages.last());
|
|
|
}
|
|
|
u.safeSave(this, { num_unread: 0 });
|
|
@@ -846,20 +846,19 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* whether a message was retracted or not.
|
|
|
*/
|
|
|
async handleRetraction(attrs) {
|
|
|
- const RETRACTION_ATTRIBUTES = ['retracted', 'retracted_id', 'editable'];
|
|
|
+ const RETRACTION_ATTRIBUTES = ["retracted", "retracted_id", "editable"];
|
|
|
if (attrs.retracted) {
|
|
|
if (attrs.is_tombstone) return false;
|
|
|
|
|
|
for (const m of this.messages.models) {
|
|
|
- if (m.get('from') !== attrs.from) continue;
|
|
|
- if (m.get('origin_id') === attrs.retracted_id ||
|
|
|
- m.get('msgid') === attrs.retracted_id) {
|
|
|
+ if (m.get("from") !== attrs.from) continue;
|
|
|
+ if (m.get("origin_id") === attrs.retracted_id || m.get("msgid") === attrs.retracted_id) {
|
|
|
m.save(pick(attrs, RETRACTION_ATTRIBUTES));
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- attrs['dangling_retraction'] = true;
|
|
|
+ attrs["dangling_retraction"] = true;
|
|
|
await this.createMessage(attrs);
|
|
|
return true;
|
|
|
} else {
|
|
@@ -868,7 +867,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
if (message) {
|
|
|
const retraction_attrs = pick(message.attributes, RETRACTION_ATTRIBUTES);
|
|
|
const new_attrs = Object.assign({ dangling_retraction: false }, attrs, retraction_attrs);
|
|
|
- delete new_attrs['id']; // Delete id, otherwise a new cache entry gets created
|
|
|
+ delete new_attrs["id"]; // Delete id, otherwise a new cache entry gets created
|
|
|
message.save(new_attrs);
|
|
|
return true;
|
|
|
}
|
|
@@ -880,13 +879,13 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @param {MessageAttributes} attrs
|
|
|
*/
|
|
|
handleReceipt(attrs) {
|
|
|
- if (attrs.sender === 'them') {
|
|
|
+ if (attrs.sender === "them") {
|
|
|
if (attrs.is_valid_receipt_request) {
|
|
|
sendReceiptStanza(attrs.from, attrs.msgid);
|
|
|
} else if (attrs.receipt_id) {
|
|
|
- const message = this.messages.findWhere({ 'msgid': attrs.receipt_id });
|
|
|
- if (message && !message.get('received')) {
|
|
|
- message.save({ 'received': new Date().toISOString() });
|
|
|
+ const message = this.messages.findWhere({ "msgid": attrs.receipt_id });
|
|
|
+ if (message && !message.get("received")) {
|
|
|
+ message.save({ "received": new Date().toISOString() });
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
@@ -915,15 +914,15 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
|
|
|
const stanza = stx`
|
|
|
<message xmlns="jabber:client"
|
|
|
- from="${message.get('type') === 'groupchat' ? api.connection.get().jid : message.get('from')}"
|
|
|
- to="${message.get('to') || this.get('jid')}"
|
|
|
- type="${this.get('message_type')}"
|
|
|
+ from="${message.get("type") === "groupchat" ? api.connection.get().jid : message.get("from")}"
|
|
|
+ to="${message.get("to") || this.get("jid")}"
|
|
|
+ type="${this.get("message_type")}"
|
|
|
id="${(edited && u.getUniqueId()) || msgid}">
|
|
|
- ${body ? stx`<body>${body}</body>` : ''}
|
|
|
+ ${body ? stx`<body>${body}</body>` : ""}
|
|
|
<active xmlns="${Strophe.NS.CHATSTATES}"/>
|
|
|
- ${type === 'chat' ? stx`<request xmlns="${Strophe.NS.RECEIPTS}"></request>` : ''}
|
|
|
- ${!is_encrypted && oob_url ? stx`<x xmlns="${Strophe.NS.OUTOFBAND}"><url>${oob_url}</url></x>` : ''}
|
|
|
- ${!is_encrypted && is_spoiler ? stx`<spoiler xmlns="${Strophe.NS.SPOILER}">${spoiler_hint ?? ''}</spoiler>` : ''}
|
|
|
+ ${type === "chat" ? stx`<request xmlns="${Strophe.NS.RECEIPTS}"></request>` : ""}
|
|
|
+ ${!is_encrypted && oob_url ? stx`<x xmlns="${Strophe.NS.OUTOFBAND}"><url>${oob_url}</url></x>` : ""}
|
|
|
+ ${!is_encrypted && is_spoiler ? stx`<spoiler xmlns="${Strophe.NS.SPOILER}">${spoiler_hint ?? ""}</spoiler>` : ""}
|
|
|
${
|
|
|
!is_encrypted
|
|
|
? references?.map(
|
|
@@ -933,10 +932,10 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
type="${ref.type}"
|
|
|
uri="${ref.uri}"></reference>`
|
|
|
)
|
|
|
- : ''
|
|
|
+ : ""
|
|
|
}
|
|
|
- ${edited ? stx`<replace xmlns="${Strophe.NS.MESSAGE_CORRECT}" id="${msgid}"></replace>` : ''}
|
|
|
- ${origin_id ? stx`<origin-id xmlns="${Strophe.NS.SID}" id="${origin_id}"></origin-id>` : ''}
|
|
|
+ ${edited ? stx`<replace xmlns="${Strophe.NS.MESSAGE_CORRECT}" id="${msgid}"></replace>` : ""}
|
|
|
+ ${origin_id ? stx`<origin-id xmlns="${Strophe.NS.SID}" id="${origin_id}"></origin-id>` : ""}
|
|
|
</message>`;
|
|
|
|
|
|
/**
|
|
@@ -952,7 +951,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* You can use the Strophe.Builder functions to extend the stanza.
|
|
|
* See http://strophe.im/strophejs/doc/1.4.3/files/strophe-umd-js.html#Strophe.Builder.Functions
|
|
|
*/
|
|
|
- const data = await api.hook('createMessageStanza', this, { message, stanza });
|
|
|
+ const data = await api.hook("createMessageStanza", this, { message, stanza });
|
|
|
return data.stanza;
|
|
|
}
|
|
|
|
|
@@ -961,8 +960,8 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* number of messages specified in the settings.
|
|
|
*/
|
|
|
pruneHistory() {
|
|
|
- const max_history = api.settings.get('prune_messages_above');
|
|
|
- if (max_history && typeof max_history === 'number') {
|
|
|
+ const max_history = api.settings.get("prune_messages_above");
|
|
|
+ if (max_history && typeof max_history === "number") {
|
|
|
if (this.messages.length > max_history) {
|
|
|
const non_empty_messages = this.messages.filter((m) => !u.isEmptyMessage(m));
|
|
|
if (non_empty_messages.length > max_history) {
|
|
@@ -974,7 +973,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* once older messages have been removed to keep the
|
|
|
* number of messages below the value set in `prune_messages_above`.
|
|
|
*/
|
|
|
- this.trigger('historyPruned');
|
|
|
+ this.trigger("historyPruned");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -982,7 +981,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
debouncedPruneHistory = debounce(() => this.pruneHistory(), 500, { maxWait: 2000 });
|
|
|
|
|
|
isScrolledUp() {
|
|
|
- return this.ui.get('scrolled');
|
|
|
+ return this.ui.get("scrolled");
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -991,7 +990,7 @@ export default function ModelWithMessages(BaseModel) {
|
|
|
* @returns {boolean}
|
|
|
*/
|
|
|
isHidden() {
|
|
|
- return this.get('hidden') || this.isScrolledUp() || document.hidden;
|
|
|
+ return this.get("hidden") || this.isScrolledUp() || document.hidden;
|
|
|
}
|
|
|
};
|
|
|
}
|