Просмотр исходного кода

More work on cleaning up src/headless/utils/index.js

JC Brand 1 год назад
Родитель
Сommit
45a18900f5

+ 2 - 1
src/headless/plugins/chat/model.js

@@ -15,6 +15,7 @@ import { isEmptyMessage } from '../../utils/index.js';
 import { isUniView } from '../../utils/session.js';
 import { parseMessage } from './parsers.js';
 import { sendMarker } from '../../shared/actions.js';
+import { isNewMessage } from './utils.js';
 
 const { Strophe, $msg } = converse.env;
 
@@ -1094,7 +1095,7 @@ const ChatBox = ModelWithContact.extend({
         if (!message?.get('body')) {
             return
         }
-        if (u.isNewMessage(message)) {
+        if (isNewMessage(message)) {
             if (message.get('sender') === 'me') {
                 // We remove the "scrolled" flag so that the chat area
                 // gets scrolled down. We always want to scroll down

+ 18 - 0
src/headless/plugins/chat/utils.js

@@ -1,3 +1,5 @@
+import sizzle from "sizzle";
+import { Model } from '@converse/skeletor/src/model.js';
 import _converse from '../../shared/_converse.js';
 import api, { converse } from '../../shared/api/index.js';
 import log from '../../log.js';
@@ -24,6 +26,22 @@ export async function onClearSession () {
     }
 }
 
+export function isNewMessage (message) {
+    /* Given a stanza, determine whether it's a new
+     * message, i.e. not a MAM archived one.
+     */
+    if (message instanceof Element) {
+        return !(
+            sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, message).length &&
+            sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, message).length
+        );
+    } else if (message instanceof Model) {
+        message = message.attributes;
+    }
+    return !(message['is_delayed'] && message['is_archived']);
+}
+
+
 async function handleErrorMessage (stanza) {
     const from_jid = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
     if (u.isSameBareJID(from_jid, _converse.bare_jid)) {

+ 2 - 1
src/headless/plugins/muc/muc.js

@@ -18,6 +18,7 @@ import { getUniqueId, safeSave } from '../../utils/index.js';
 import { isUniView } from '../../utils/session.js';
 import { parseMUCMessage, parseMUCPresence } from './parsers.js';
 import { sendMarker } from '../../shared/actions.js';
+import { shouldCreateGroupchatMessage } from './utils.js';
 
 const { u } = converse.env;
 
@@ -2296,7 +2297,7 @@ const ChatRoomMixin = {
         if (attrs['chat_state']) {
             this.updateNotifications(attrs.nick, attrs.chat_state);
         }
-        if (u.shouldCreateGroupchatMessage(attrs)) {
+        if (shouldCreateGroupchatMessage(attrs)) {
             const msg = await handleCorrection(this, attrs) || (await this.createMessage(attrs));
             this.removeNotification(attrs.nick, ['composing', 'paused']);
             this.handleUnreadMessage(msg);

+ 9 - 0
src/headless/plugins/muc/utils.js

@@ -6,6 +6,15 @@ import { safeSave } from '../../utils/index.js';
 
 const { Strophe, sizzle, u } = converse.env;
 
+export function isChatRoom (model) {
+    return model?.get('type') === 'chatroom';
+}
+
+export function shouldCreateGroupchatMessage (attrs) {
+    return attrs.nick && (u.shouldCreateMessage(attrs) || attrs.is_tombstone);
+}
+
+
 export function getAutoFetchedAffiliationLists () {
     const affs = api.settings.get('muc_fetch_members');
     return Array.isArray(affs) ? affs : affs ? ['member', 'admin', 'owner'] : [];

+ 1 - 1
src/headless/shared/parsers.js

@@ -6,7 +6,7 @@ import log from '../log.js';
 import sizzle from 'sizzle';
 import { Strophe } from 'strophe.js';
 import { URL_PARSE_OPTIONS } from './constants.js';
-import { decodeHTMLEntities } from '../utils/index.js';
+import { decodeHTMLEntities } from '../utils/html.js';
 import { rejectMessage } from './actions';
 import {
     isAudioURL,

+ 26 - 0
src/headless/utils/html.js

@@ -1,3 +1,4 @@
+import DOMPurify from 'dompurify';
 import { Strophe } from 'strophe.js';
 
 /**
@@ -62,3 +63,28 @@ export function stringToElement (s) {
 export function queryChildren (el, selector) {
     return Array.from(el.childNodes).filter(el => (el instanceof Element) && el.matches(selector));
 }
+
+/**
+ * @param {Element} el - the DOM element
+ * @return {number}
+ */
+export function siblingIndex (el) {
+    /* eslint-disable no-cond-assign */
+    for (var i = 0; el = el.previousElementSibling; i++);
+    return i;
+}
+
+const element = document.createElement('div');
+
+/**
+ * @param {string} str
+ * @return {string}
+ */
+export function decodeHTMLEntities (str) {
+    if (str && typeof str === 'string') {
+        element.innerHTML = DOMPurify.sanitize(str);
+        str = element.textContent;
+        element.textContent = '';
+    }
+    return str;
+}

+ 34 - 104
src/headless/utils/index.js

@@ -3,16 +3,15 @@
  * @license Mozilla Public License (MPLv2)
  * @description This is the core utilities module.
  */
-import DOMPurify from 'dompurify';
-import sizzle from "sizzle";
 import { Model } from '@converse/skeletor/src/model.js';
-import { Strophe, toStanza } from 'strophe.js';
+import { toStanza } from 'strophe.js';
 import { getOpenPromise } from '@converse/openpromise';
 import { saveWindowState, shouldClearCache } from './session.js';
 import { merge, isError, isFunction } from './object.js';
 import { createStore, getDefaultStore } from './storage.js';
 import { waitUntil } from './promise.js';
 import { isValidJID, isValidMUCJID, isSameBareJID } from './jid.js';
+import { isErrorStanza } from './stanza.js';
 import {
     getCurrentWord,
     getSelectValues,
@@ -52,26 +51,7 @@ import {
  * The utils object
  * @namespace u
  */
-const u = {
-    arrayBufferToBase64,
-    arrayBufferToHex,
-    arrayBufferToString,
-    base64ToArrayBuffer,
-    checkFileTypes,
-    getSelectValues,
-    getURI,
-    isAllowedProtocolForMedia,
-    isAudioURL,
-    isError,
-    isFunction,
-    isGIFURL,
-    isImageURL,
-    isURLWithImageExtension,
-    isVideoURL,
-    shouldRenderMediaFromURL,
-    stringToArrayBuffer,
-    webForm2xForm,
-};
+const u = {};
 
 
 export function isEmptyMessage (attrs) {
@@ -99,7 +79,7 @@ export function prefixMentions (message) {
     return text;
 }
 
-u.getLongestSubstring = function (string, candidates) {
+function getLongestSubstring (string, candidates) {
     function reducer (accumulator, current_value) {
         if (string.startsWith(current_value)) {
             if (current_value.length > accumulator.length) {
@@ -114,80 +94,23 @@ u.getLongestSubstring = function (string, candidates) {
     return candidates.reduce(reducer, '');
 }
 
-u.isNewMessage = function (message) {
-    /* Given a stanza, determine whether it's a new
-     * message, i.e. not a MAM archived one.
-     */
-    if (message instanceof Element) {
-        return !(
-            sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, message).length &&
-            sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, message).length
-        );
-    } else if (message instanceof Model) {
-        message = message.attributes;
-    }
-    return !(message['is_delayed'] && message['is_archived']);
-};
-
-u.shouldCreateMessage = function (attrs) {
+function shouldCreateMessage (attrs) {
     return attrs['retracted'] || // Retraction received *before* the message
         !isEmptyMessage(attrs);
 }
 
-u.shouldCreateGroupchatMessage = function (attrs) {
-    return attrs.nick && (u.shouldCreateMessage(attrs) || attrs.is_tombstone);
-}
-
-u.isChatRoom = function (model) {
-    return model && (model.get('type') === 'chatroom');
-}
-
 export function isErrorObject (o) {
     return o instanceof Error;
 }
 
-u.isErrorStanza = function (stanza) {
-    if (!isElement(stanza)) {
-        return false;
-    }
-    return stanza.getAttribute('type') === 'error';
-}
-
-u.isForbiddenError = function (stanza) {
-    if (!isElement(stanza)) {
-        return false;
-    }
-    return sizzle(`error[type="auth"] forbidden[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length > 0;
-}
-
-u.isServiceUnavailableError = function (stanza) {
-    if (!isElement(stanza)) {
-        return false;
-    }
-    return sizzle(`error[type="cancel"] service-unavailable[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length > 0;
-}
-
-u.getAttribute = function (key, item) {
-    return item.get(key);
-};
-
-u.isPersistableModel = function (model) {
-    return model.collection && model.collection.browserStorage;
-};
-
-u.getResolveablePromise = getOpenPromise;
-u.getOpenPromise = getOpenPromise;
-
 /**
  * Call the callback once all the events have been triggered
- * @private
- * @method u#onMultipleEvents
  * @param { Array } events: An array of objects, with keys `object` and
  *   `event`, representing the event name and the object it's triggered upon.
  * @param { Function } callback: The function to call once all events have
  *    been triggered.
  */
-u.onMultipleEvents = function (events=[], callback) {
+function onMultipleEvents (events=[], callback) {
     let triggered = [];
 
     function handler (result) {
@@ -198,24 +121,20 @@ u.onMultipleEvents = function (events=[], callback) {
         }
     }
     events.forEach(e => e.object.on(e.event, handler));
-};
+}
 
+function isPersistableModel (model) {
+    return model.collection && model.collection.browserStorage;
+}
 
 export function safeSave (model, attributes, options) {
-    if (u.isPersistableModel(model)) {
+    if (isPersistableModel(model)) {
         model.save(attributes, options);
     } else {
         model.set(attributes, options);
     }
 }
 
-
-u.siblingIndex = function (el) {
-    /* eslint-disable no-cond-assign */
-    for (var i = 0; el = el.previousElementSibling; i++);
-    return i;
-};
-
 /**
  * @param {Element} el
  * @param {string} name
@@ -251,34 +170,41 @@ export function getUniqueId (suffix) {
     }
 }
 
-
-const element = document.createElement('div');
-
-export function decodeHTMLEntities (str) {
-    if (str && typeof str === 'string') {
-        element.innerHTML = DOMPurify.sanitize(str);
-        str = element.textContent;
-        element.textContent = '';
-    }
-    return str;
-}
-
 export default Object.assign({
+    arrayBufferToBase64,
+    arrayBufferToHex,
+    arrayBufferToString,
+    base64ToArrayBuffer,
+    checkFileTypes,
     createStore,
     getCurrentWord,
     getDefaultStore,
+    getLongestSubstring,
+    getOpenPromise,
     getOuterWidth,
     getRandomInt,
+    getSelectValues,
+    getURI,
     getUniqueId,
+    isAllowedProtocolForMedia,
+    isAudioURL,
     isElement,
     isEmptyMessage,
+    isError,
     isErrorObject,
+    isErrorStanza,
+    isFunction,
+    isGIFURL,
+    isImageURL,
     isMentionBoundary,
     isSameBareJID,
     isTagEqual,
+    isURLWithImageExtension,
     isValidJID,
     isValidMUCJID,
+    isVideoURL,
     merge,
+    onMultipleEvents,
     placeCaretAtEnd,
     prefixMentions,
     queryChildren,
@@ -286,8 +212,12 @@ export default Object.assign({
     safeSave,
     saveWindowState,
     shouldClearCache,
+    shouldCreateMessage,
+    shouldRenderMediaFromURL,
+    stringToArrayBuffer,
     stringToElement,
     toStanza,
     triggerEvent,
+    webForm2xForm,
     waitUntil, // TODO: remove. Only the API should be used
 }, u);

+ 24 - 0
src/headless/utils/stanza.js

@@ -0,0 +1,24 @@
+import sizzle from "sizzle";
+import { Strophe } from 'strophe.js';
+import { isElement } from './html.js';
+
+export function isErrorStanza (stanza) {
+    if (!isElement(stanza)) {
+        return false;
+    }
+    return stanza.getAttribute('type') === 'error';
+}
+
+export function isForbiddenError (stanza) {
+    if (!isElement(stanza)) {
+        return false;
+    }
+    return sizzle(`error[type="auth"] forbidden[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length > 0;
+}
+
+export function isServiceUnavailableError (stanza) {
+    if (!isElement(stanza)) {
+        return false;
+    }
+    return sizzle(`error[type="cancel"] service-unavailable[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length > 0;
+}

+ 0 - 1
src/plugins/adhoc-views/tests/adhoc.js

@@ -226,7 +226,6 @@ describe("Ad-hoc commands consisting of multiple steps", function () {
         `));
 
         let button = await u.waitUntil(() => modal.querySelector('input[data-action="next"]'));
-        debugger;
         button.click();
 
         sel = `iq[to="${entity_jid}"] command[sessionid="${sessionid}"]`;

+ 3 - 2
src/plugins/roomslist/view.js

@@ -5,6 +5,7 @@ import { CustomElement } from 'shared/components/element.js';
 import { __ } from 'i18n';
 import { _converse, api, converse } from "@converse/headless";
 import { initStorage } from '@converse/headless/utils/storage.js';
+import { isChatRoom } from '@converse/headless/plugins/muc/utils.js';
 
 const { Strophe, u } = converse.env;
 
@@ -30,13 +31,13 @@ export class RoomsList extends CustomElement {
     }
 
     renderIfChatRoom (model) {
-        u.isChatRoom(model) && this.requestUpdate();
+        isChatRoom(model) && this.requestUpdate();
     }
 
     renderIfRelevantChange (model) {
         const attrs = ['bookmarked', 'hidden', 'name', 'num_unread', 'num_unread_general', 'has_activity'];
         const changed = model.changed || {};
-        if (u.isChatRoom(model) && Object.keys(changed).filter(m => attrs.includes(m)).length) {
+        if (isChatRoom(model) && Object.keys(changed).filter(m => attrs.includes(m)).length) {
             this.requestUpdate();
         }
     }

+ 4 - 3
src/shared/autocomplete/autocomplete.js

@@ -6,10 +6,11 @@
  * @license Mozilla Public License (MPLv2)
  */
 
-import { Events } from '@converse/skeletor/src/events.js';
-import { helpers, FILTER_CONTAINS, ITEM, SORT_BY_QUERY_POSITION } from './utils.js';
 import Suggestion from './suggestion.js';
+import { Events } from '@converse/skeletor/src/events.js';
 import { converse } from "@converse/headless";
+import { helpers, FILTER_CONTAINS, ITEM, SORT_BY_QUERY_POSITION } from './utils.js';
+import { siblingIndex } from '@converse/headless/utils/html.js';
 
 
 const u = converse.env.utils;
@@ -185,7 +186,7 @@ export class AutoComplete {
 
     select (selected) {
         if (selected) {
-            this.index = u.siblingIndex(selected);
+            this.index = siblingIndex(selected);
         } else {
             selected = this.ul.children[this.index];
         }