diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf9dc8c..887103ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## ??? (Not Released Yet) + +### Minor changes and fixes + +* Fix #329: auto focus message field after anonymous user has entered nickname. +* Fix #392: add draggable items touch screen handling +* Fix #506: hide offline users by default in occupant list +* Fix #547: add button to go to the end of the chat +* Fix #503: set custom emojis max height to text height + bigger when posted alone +* Fix: Converse bottom panel messages not visible on new Peertube v7 theme (for example for muted users) +* Fix #75: New short video urls makes it difficult to use the settings «Activate chat for these videos». + ## 12.0.4 ### Minor changes and fixes diff --git a/client/tsconfig.json.license b/client/tsconfig.json.license index b253ad42..0549bba9 100644 --- a/client/tsconfig.json.license +++ b/client/tsconfig.json.license @@ -1,3 +1,4 @@ SPDX-FileCopyrightText: 2024 John Livingston +SPDX-FileCopyrightText: 2025 Mehdi Benadel SPDX-License-Identifier: AGPL-3.0-only diff --git a/conversejs/custom/shared/components/draggables/index.js b/conversejs/custom/shared/components/draggables/index.js index 2147a367..068cb50e 100644 --- a/conversejs/custom/shared/components/draggables/index.js +++ b/conversejs/custom/shared/components/draggables/index.js @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2024 John Livingston +// SPDX-FileCopyrightText: 2025 Nicolas Chesnais // // SPDX-License-Identifier: AGPL-3.0-only @@ -11,6 +12,7 @@ import './styles/draggables.scss' */ export class DraggablesCustomElement extends CustomElement { currentDragged = null + droppableEl = null /** * The tag name for draggable elements. @@ -37,6 +39,9 @@ export class DraggablesCustomElement extends CustomElement { this._handleDragLeaveBinded = this._handleDragLeave.bind(this) this._handleDragEndBinded = this._handleDragEnd.bind(this) this._handleDropBinded = this._handleDrop.bind(this) + this._handleTouchStartBinded = this._handleTouchStart.bind(this) + this._handleTouchMoveBinded = this._handleTouchMove.bind(this) + this._handleTouchEndBinded = this._handleTouchEnd.bind(this) return super.initialize() } @@ -44,21 +49,29 @@ export class DraggablesCustomElement extends CustomElement { connectedCallback () { super.connectedCallback() this.currentDragged = null + this.droppableEl = null this.addEventListener('dragstart', this._handleDragStartBinded) this.addEventListener('dragover', this._handleDragOverBinded) this.addEventListener('dragleave', this._handleDragLeaveBinded) this.addEventListener('dragend', this._handleDragEndBinded) this.addEventListener('drop', this._handleDropBinded) + this.addEventListener('touchstart', this._handleTouchStartBinded) + this.addEventListener('touchmove', this._handleTouchMoveBinded) + this.addEventListener('touchend', this._handleTouchEndBinded) } disconnectedCallback () { super.disconnectedCallback() this.currentDragged = null + this.droppableEl = null this.removeEventListener('dragstart', this._handleDragStartBinded) this.removeEventListener('dragover', this._handleDragOverBinded) this.removeEventListener('dragleave', this._handleDragLeaveBinded) this.removeEventListener('dragend', this._handleDragEndBinded) this.removeEventListener('drop', this._handleDropBinded) + this.removeEventListener('touchstart', this._handleTouchStartBinded) + this.removeEventListener('touchmove', this._handleTouchMoveBinded) + this.removeEventListener('touchend', this._handleTouchEndBinded) } _isADraggableEl (target) { @@ -69,8 +82,7 @@ export class DraggablesCustomElement extends CustomElement { return target.closest?.(this.droppableTagNames.join(',')) } - _isOnTopHalf (ev, el) { - const y = ev.clientY + _isOnTopHalf (y, el) { const bounding = el.getBoundingClientRect() return (y <= bounding.y + (bounding.height / 2)) } @@ -81,13 +93,39 @@ export class DraggablesCustomElement extends CustomElement { ) } + _setCurrentDragged (draggedEl) { + console.log('[livechat drag&drop] Starting to drag a ' + this.draggableTagName + '...') + this.currentDragged = draggedEl + this._resetDropOver() + } + + _setDroppableClasses (droppableEl, y) { + // Are we on the top or bottom part of the droppableEl? + let topHalf = false + if (!this.droppableAlwaysBottomTagNames.includes(droppableEl.nodeName.toLowerCase())) { + topHalf = this._isOnTopHalf(y, droppableEl) + } + droppableEl.classList.add(topHalf ? 'livechat-drag-top-half' : 'livechat-drag-bottom-half') + droppableEl.classList.remove(topHalf ? 'livechat-drag-bottom-half' : 'livechat-drag-top-half') + } + + _tryToDrop (droppedOnEl) { + if (!droppedOnEl) return + + console.log('[livechat drag&drop] ' + this.draggableTagName + ' dropped...') + try { + this._dropDone(this.currentDragged, droppedOnEl, droppedOnEl.classList.contains('livechat-drag-top-half')) + } catch (err) { + console.error(err) + } + this._resetDropOver() + } + _handleDragStart (ev) { // The draggable=true is on a child bode const possibleEl = ev.target.parentElement if (!this._isADraggableEl(possibleEl)) { return } - console.log('[livechat drag&drop] Starting to drag a ' + this.draggableTagName + '...') - this.currentDragged = possibleEl - this._resetDropOver() + this._setCurrentDragged(possibleEl) } _handleDragOver (ev) { @@ -97,14 +135,7 @@ export class DraggablesCustomElement extends CustomElement { // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event says we should preventDefault ev.preventDefault() - - // Are we on the top or bottom part of the droppableEl? - let topHalf = false - if (!this.droppableAlwaysBottomTagNames.includes(droppableEl.nodeName.toLowerCase())) { - topHalf = this._isOnTopHalf(ev, droppableEl) - } - droppableEl.classList.add(topHalf ? 'livechat-drag-top-half' : 'livechat-drag-bottom-half') - droppableEl.classList.remove(topHalf ? 'livechat-drag-bottom-half' : 'livechat-drag-top-half') + this._setDroppableClasses(droppableEl, ev.clientY) } _handleDragLeave (ev) { @@ -124,16 +155,33 @@ export class DraggablesCustomElement extends CustomElement { let droppedOnEl = document.querySelector('.livechat-drag-bottom-half, .livechat-drag-top-half') droppedOnEl = this._getParentDroppableEl(droppedOnEl) - if (!droppedOnEl) { return } + this._tryToDrop(droppedOnEl) + } - console.log('[livechat drag&drop] ' + this.draggableTagName + ' dropped...') + _handleTouchStart (ev) { + const possibleEl = this._getParentDroppableEl(ev.target) + if (!possibleEl) return + this._setCurrentDragged(possibleEl) + } - try { - this._dropDone(this.currentDragged, droppedOnEl, droppedOnEl.classList.contains('livechat-drag-top-half')) - } catch (err) { - console.error(err) - } + _handleTouchMove (ev) { + if (!this.currentDragged) return + + const { clientX, clientY } = ev.touches[0] + const droppableEl = this._getParentDroppableEl(document.elementFromPoint(clientX, clientY)) + if (!droppableEl || droppableEl === this.droppableEl) return + + this.droppableEl = droppableEl this._resetDropOver() + this._setDroppableClasses(droppableEl, clientY) + } + + _handleTouchEnd (_ev) { + if (!this.currentDragged || !this.droppableEl) return + + this._tryToDrop(this.droppableEl) + this.currentDragged = null + this.droppableEl = null } /** diff --git a/conversejs/custom/shared/components/draggables/styles/draggables.scss b/conversejs/custom/shared/components/draggables/styles/draggables.scss index 93279389..240dec95 100644 --- a/conversejs/custom/shared/components/draggables/styles/draggables.scss +++ b/conversejs/custom/shared/components/draggables/styles/draggables.scss @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2024 John Livingston + * SPDX-FileCopyrightText: 2025 Nicolas Chesnais * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -16,4 +17,8 @@ .livechat-drag-top-half > .draggables-line { border-top: 4px solid blue; } + + [draggable="true"] { + touch-action: none; + } } diff --git a/conversejs/custom/shared/styles/_peertubetheme.scss b/conversejs/custom/shared/styles/_peertubetheme.scss index 7b936587..6a8dc18c 100644 --- a/conversejs/custom/shared/styles/_peertubetheme.scss +++ b/conversejs/custom/shared/styles/_peertubetheme.scss @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2024 John Livingston + * SPDX-FileCopyrightText: 2025 Nicolas Chesnais * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -149,11 +150,17 @@ background-color: var(--peertube-grey-background) !important; } - // Changing size for emojis, to have bigger custom emojis + // Resize custom emojis to text height img.emoji { width: unset !important; height: unset !important; - max-height: 3em !important; // and no max-width + max-height: 1.5em !important; // and no max-width + vertical-align: -0.45em !important; + } + + // Trick to enlarge a single custom emoji with no text in message + &[text^=":"][text$=":"] img.emoji:only-child { + max-height: 2.5em !important; } // underline links in chat messages diff --git a/conversejs/custom/shared/styles/livechat.scss b/conversejs/custom/shared/styles/livechat.scss index d023e5c1..1123b40e 100644 --- a/conversejs/custom/shared/styles/livechat.scss +++ b/conversejs/custom/shared/styles/livechat.scss @@ -227,6 +227,21 @@ body.converse-embedded { .occupants { width: 100%; + + // Put occupants filters items on a single line + converse-list-filter form > div { + display: flex; + align-items: center; + + .filter-by { + margin-right: 4px; + } + + // Let search input take the whole width when displayed + .btn-group:has(+ select.hidden) { + width: 100%; + } + } } } } @@ -264,3 +279,13 @@ body.converse-embedded { justify-content: normal !important; } } + +/* stylelint-disable-next-line no-duplicate-selectors */ +.conversejs { + converse-muc { + .muc-bottom-panel, converse-muc-bottom-panel { + // Fixing a color (Converse use a hardcoded "white", which does not work with Peertube v7 new theme) + color: var(--peertube-menu-foreground) !important; + } + } +} diff --git a/conversejs/custom/templates/muc-bottom-panel.js b/conversejs/custom/templates/muc-bottom-panel.js index 87c07f98..c761598c 100644 --- a/conversejs/custom/templates/muc-bottom-panel.js +++ b/conversejs/custom/templates/muc-bottom-panel.js @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2024 John Livingston +// SPDX-FileCopyrightText: 2025 Nicolas Chesnais // // SPDX-License-Identifier: AGPL-3.0-only @@ -12,7 +13,7 @@ import tplMucBottomPanel from '../../src/plugins/muc-views/templates/muc-bottom- import { CustomElement } from 'shared/components/element.js' import 'shared/modals/livechat-external-login.js' -async function setNickname (ev, model) { +async function setNicknameAndFocus (ev, model) { ev.preventDefault() const nick = ev.target.nick.value.trim() if (!nick) { @@ -22,6 +23,7 @@ async function setNickname (ev, model) { _converse.api.trigger('livechatViewerModeSetNickname', model, nick, { synchronous: true }) + document.querySelector('.chat-textarea')?.focus() } class SlowMode extends CustomElement { @@ -100,6 +102,54 @@ const tplEmojiOnly = (o) => { ` } +class BackToLastMsg extends CustomElement { + static get properties () { + return { + jid: { type: String } + } + } + + show = false + + async connectedCallback () { + super.connectedCallback() + this.model = _converse.chatboxes.get(this.jid) + await this.model.initialized + + let scrolled = this.model.ui.get('scrolled') + let hasUnreadMsg = this.model.get('num_unread_general') > 0 + this.listenTo(this.model.ui, 'change:scrolled', () => { + scrolled = this.model.ui.get('scrolled') + this.show = scrolled && !hasUnreadMsg + this.requestUpdate() + }) + this.listenTo(this.model, 'change:num_unread_general', () => { + hasUnreadMsg = this.model.get('num_unread_general') > 0 + // Do not show the element if there is new messages since there is another element for that + this.show = scrolled && !hasUnreadMsg + this.requestUpdate() + }) + } + + onClick (ev) { + ev?.preventDefault() + const chatContainer = document.querySelector('converse-chat-content') + chatContainer?.scrollTo({ top: chatContainer.scrollHeight }) + } + + render () { + return this.show + ? html`
+ ▼ ${ + // eslint-disable-next-line no-undef + __(LOC_back_to_last_msg) + } ▼ +
` + : '' + } +} +api.elements.define('livechat-back-to-last-msg', BackToLastMsg) + const tplViewerMode = (o) => { if (!api.settings.get('livechat_enable_viewer_mode')) { return html`` @@ -112,7 +162,7 @@ const tplViewerMode = (o) => { const i18nExternalLogin = __(LOC_login_using_external_account) return html`
-
setNickname(ev, model)}> + setNicknameAndFocus(ev, model)}>
{ ${tplViewerMode(o)} ${tplSlowMode(o)} ${tplEmojiOnly(o)} + ${ mutedAnonymousMessage ? html`${mutedAnonymousMessage}` diff --git a/conversejs/lib/plugins/livechat-specific/toolbar.ts b/conversejs/lib/plugins/livechat-specific/toolbar.ts index a832fa3d..32bfeff5 100644 --- a/conversejs/lib/plugins/livechat-specific/toolbar.ts +++ b/conversejs/lib/plugins/livechat-specific/toolbar.ts @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2024 John Livingston +// SPDX-FileCopyrightText: 2025 Nicolas Chesnais // // SPDX-License-Identifier: AGPL-3.0-only @@ -53,6 +54,11 @@ function getToolbarButtons (this: any, toolbarEl: any, buttons: any[]): any { toolbarEl.model.save({ hidden_occupants: !toolbarEl.model.get('hidden_occupants') }) + + // Hide offline occupants by default + const sideBarEl = document.querySelector('converse-muc-sidebar') as unknown as any + sideBarEl?.model.set('filter_visible', true) + sideBarEl?.filter.set('type', 'state') }}> ${icon} ` diff --git a/conversejs/loc.keys.js b/conversejs/loc.keys.js index 019c63bd..e4b72e89 100644 --- a/conversejs/loc.keys.js +++ b/conversejs/loc.keys.js @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2024 John Livingston +// SPDX-FileCopyrightText: 2025 Nicolas Chesnais // // SPDX-License-Identifier: AGPL-3.0-only @@ -68,7 +69,8 @@ const locKeys = [ 'announcements_message_type_standard', 'announcements_message_type_announcement', 'announcements_message_type_highlight', - 'announcements_message_type_warning' + 'announcements_message_type_warning', + 'back_to_last_msg' ] module.exports = locKeys diff --git a/languages/en.yml b/languages/en.yml index 154e9447..12057dcf 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -149,7 +149,7 @@ all_non_lives_description: "If checked, the chat will be enabled for all video t videos_list_label: "Activate chat for these videos" videos_list_description: | - Videos UUIDs for which we want a web chat. + Videos UUIDs for which we want a web chat (short UUID or UUIDv4). Can be non-live videos. One per line.
You can add comments: everything after the # character will be stripped off, and empty lines ignored.
Don't add private videos, the UUIDs will be sent to the frontend. @@ -682,3 +682,4 @@ converse_theme_warning_description: | It is strongly recommanded to keep the "Peertube theme", in combinaison with the "Automatic color detection" feature. Otherwise some user may experience issues depending on the Peertube theme they use. +back_to_last_msg: Go back to last message diff --git a/languages/es.yml b/languages/es.yml index bde4980f..c92e50fd 100644 --- a/languages/es.yml +++ b/languages/es.yml @@ -152,7 +152,7 @@ prosody_muc_expiration_description: "Aquí puede elegir cuánto tiempo el servid durante 1 año. Puede reemplazar 1 por cualquier valor entero.\n \ \
  • nunca: el contenido nunca caducará y se mantendrá para siempre.
  • \n\ \n" -videos_list_description: "UUIDs de los videos para los cuales se desea un chat web.\n +videos_list_description: "UUIDs de los videos para los cuales se desea un chat web (corto UUID o UUIDv4).\n Pueden ser videos no en vivo. Una por línea.
    \nPuede agregar comentarios: todo lo que este detras del caracter # no sera interpretado y las líneas vacías ignoradas.
    \nNo agregue videos privados, los UUID se enviarán al frontend.\n" diff --git a/languages/fr.yml b/languages/fr.yml index d9964fa7..3bf9e776 100644 --- a/languages/fr.yml +++ b/languages/fr.yml @@ -111,7 +111,7 @@ all_non_lives_description: "Si coché, il y aura un tchat pour toutes les vidéo videos_list_label: "Activer le tchat pour ces vidéos" videos_list_description: | - Mettez ici les UUIDs des vidéos pour lesquelles vous voulez forcer l'activation du tchat. + Mettez ici les UUIDs des vidéos pour lesquelles vous voulez forcer l'activation du tchat (UUID court ou UUIDv4). Cela peut être des directs, ou non. Un UUID par ligne.
    Vous pouvez ajouter des commentaires : tout ce qui se trouve après le caractère # sera retiré, et les lignes vides ignorées.
    N'ajoutez pas de vidéos privées, les UUIDs fuiteraient. @@ -664,3 +664,4 @@ converse_theme_warning_description: "\n" +back_to_last_msg: Retourner au dernier message diff --git a/package-lock.json b/package-lock.json index 9161229f..8ef897d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "http-proxy": "^1.18.1", "log-rotate": "^0.2.8", "openid-client": "^5.7.1", + "short-uuid": "^5.2.0", "validate-color": "^2.2.4", "xmppjs-chat-bot": "^0.5.0" }, @@ -3250,6 +3251,20 @@ "node": "14 || >=16.14" } }, + "node_modules/@peertube/peertube-types/node_modules/short-uuid": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.2.tgz", + "integrity": "sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-base": "^1.1.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -5138,8 +5153,7 @@ "node_modules/any-base": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", - "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "dev": true + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" }, "node_modules/anymatch": { "version": "3.1.2", @@ -11637,16 +11651,29 @@ } }, "node_modules/short-uuid": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.0.tgz", - "integrity": "sha512-r3cxuPPZSuF0QkKsK9bBR7u+7cwuCRzWzgjPh07F5N2iIUNgblnMHepBY16xgj5t1lG9iOP9k/TEafY1qhRzaw==", - "dev": true, + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-5.2.0.tgz", + "integrity": "sha512-296/Nzi4DmANh93iYBwT4NoYRJuHnKEzefrkSagQbTH/A6NTaB68hSPDjm5IlbI5dx9FXdmtqPcj6N5H+CPm6w==", + "license": "MIT", "dependencies": { "any-base": "^1.1.0", - "uuid": "^8.3.2" + "uuid": "^9.0.1" }, "engines": { - "node": ">=8" + "node": ">=14" + } + }, + "node_modules/short-uuid/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/side-channel": { @@ -15504,6 +15531,16 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", "dev": true + }, + "short-uuid": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.2.tgz", + "integrity": "sha512-IE7hDSGV2U/VZoCsjctKX6l5t5ak2jE0+aeGJi3KtvjIUNuZVmHVYUjNBhmo369FIWGDtaieRaO8A83Lvwfpqw==", + "dev": true, + "requires": { + "any-base": "^1.1.0", + "uuid": "^8.3.2" + } } } }, @@ -16987,8 +17024,7 @@ "any-base": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", - "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "dev": true + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" }, "anymatch": { "version": "3.1.2", @@ -21663,13 +21699,19 @@ "dev": true }, "short-uuid": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-4.2.0.tgz", - "integrity": "sha512-r3cxuPPZSuF0QkKsK9bBR7u+7cwuCRzWzgjPh07F5N2iIUNgblnMHepBY16xgj5t1lG9iOP9k/TEafY1qhRzaw==", - "dev": true, + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/short-uuid/-/short-uuid-5.2.0.tgz", + "integrity": "sha512-296/Nzi4DmANh93iYBwT4NoYRJuHnKEzefrkSagQbTH/A6NTaB68hSPDjm5IlbI5dx9FXdmtqPcj6N5H+CPm6w==", "requires": { "any-base": "^1.1.0", - "uuid": "^8.3.2" + "uuid": "^9.0.1" + }, + "dependencies": { + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + } } }, "side-channel": { diff --git a/package-lock.json.license b/package-lock.json.license index b253ad42..0549bba9 100644 --- a/package-lock.json.license +++ b/package-lock.json.license @@ -1,3 +1,4 @@ SPDX-FileCopyrightText: 2024 John Livingston +SPDX-FileCopyrightText: 2025 Mehdi Benadel SPDX-License-Identifier: AGPL-3.0-only diff --git a/package.json b/package.json index 853c1800..c0abe4ba 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "http-proxy": "^1.18.1", "log-rotate": "^0.2.8", "openid-client": "^5.7.1", + "short-uuid": "^5.2.0", "validate-color": "^2.2.4", "xmppjs-chat-bot": "^0.5.0" }, diff --git a/package.json.license b/package.json.license index b253ad42..0549bba9 100644 --- a/package.json.license +++ b/package.json.license @@ -1,3 +1,4 @@ SPDX-FileCopyrightText: 2024 John Livingston +SPDX-FileCopyrightText: 2025 Mehdi Benadel SPDX-License-Identifier: AGPL-3.0-only diff --git a/server/tsconfig.json b/server/tsconfig.json index 483b0165..d33f1785 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -2,7 +2,7 @@ "extends": "@tsconfig/node16/tsconfig.json", "compilerOptions": { "noImplicitReturns": true, - "noImplicitOverride": true, + "noImplicitOverride": true, "noUnusedLocals": true, "removeComments": true, "sourceMap": true, diff --git a/shared/lib/config.ts b/shared/lib/config.ts index b20f9fbc..87d63c1a 100644 --- a/shared/lib/config.ts +++ b/shared/lib/config.ts @@ -1,16 +1,35 @@ // SPDX-FileCopyrightText: 2024 John Livingston +// SPDX-FileCopyrightText: 2025 Mehdi Benadel // // SPDX-License-Identifier: AGPL-3.0-only +const short = require('short-uuid') + +const translator = short() + +function shortToUUID (shortUUID: string): string { + if (!shortUUID) return shortUUID + + return translator.toUUID(shortUUID) +} + +function isShortUUID (value: string): boolean { + if (!value) return false + + return value.length === translator.maxLength +} + function parseConfigUUIDs (s: string): string[] { if (!s) { return [] } let a = s.split('\n') a = a.map(line => { - return line.replace(/#.*$/, '') + line = line.replace(/#.*$/, '') .replace(/^\s+/, '') .replace(/\s+$/, '') + + return isShortUUID(line) ? shortToUUID(line) : line }) return a.filter(line => line !== '') }