diff --git a/install/package.json b/install/package.json index 41c7a9367b..2ed05dc31d 100644 --- a/install/package.json +++ b/install/package.json @@ -35,6 +35,9 @@ "@isaacs/ttlcache": "1.4.1", "@nodebb/spider-detector": "2.0.3", "@popperjs/core": "2.11.8", + "@textcomplete/contenteditable": "0.1.13", + "@textcomplete/core": "0.1.13", + "@textcomplete/textarea": "0.1.13", "ace-builds": "1.36.2", "archiver": "7.0.1", "async": "3.2.6", @@ -96,7 +99,7 @@ "multiparty": "4.2.3", "nconf": "0.12.1", "nodebb-plugin-2factor": "7.5.4", - "nodebb-plugin-composer-default": "10.2.38", + "nodebb-plugin-composer-default": "10.2.39", "nodebb-plugin-dbsearch": "6.2.5", "nodebb-plugin-emoji": "5.1.15", "nodebb-plugin-emoji-android": "4.0.0", diff --git a/public/language/bg/admin/extend/widgets.json b/public/language/bg/admin/extend/widgets.json index 42c01a18b2..b0f6a79f3c 100644 --- a/public/language/bg/admin/extend/widgets.json +++ b/public/language/bg/admin/extend/widgets.json @@ -12,8 +12,8 @@ "container.card": "Карта", "container.card-header": "Заглавна част на карта", "container.card-body": "Основна част на карта", - "container.title": "Title", - "container.body": "Body", + "container.title": "Заглавие", + "container.body": "Съдържание", "container.alert": "Предупреждение", "alert.confirm-delete": "Наистина ли искате да изтриете джаджата?", diff --git a/public/language/bg/users.json b/public/language/bg/users.json index 63ac09bea6..31e48cdd91 100644 --- a/public/language/bg/users.json +++ b/public/language/bg/users.json @@ -1,6 +1,6 @@ { "all-users": "Всички потребители", - "followed-users": "Followed Users", + "followed-users": "Следвани потребители", "latest-users": "Последни потребители", "top-posters": "С най-много публикации", "most-reputation": "С най-много репутация", diff --git a/public/language/it/admin/extend/widgets.json b/public/language/it/admin/extend/widgets.json index ab6c54d517..d5ebc44952 100644 --- a/public/language/it/admin/extend/widgets.json +++ b/public/language/it/admin/extend/widgets.json @@ -12,8 +12,8 @@ "container.card": "Scheda", "container.card-header": "Intestazione della scheda", "container.card-body": "Corpo della scheda", - "container.title": "Title", - "container.body": "Body", + "container.title": "Titolo", + "container.body": "Corpo", "container.alert": "Avviso", "alert.confirm-delete": "Sei sicuro di voler eliminare questo widget?", diff --git a/public/language/it/users.json b/public/language/it/users.json index 19a5574cd8..bf4e3b0aca 100644 --- a/public/language/it/users.json +++ b/public/language/it/users.json @@ -1,6 +1,6 @@ { "all-users": "Tutti gli utenti", - "followed-users": "Followed Users", + "followed-users": "Utenti seguiti", "latest-users": "Ultimi Utenti", "top-posters": "Utenti più attivi", "most-reputation": "Reputazione più alta", diff --git a/public/language/th/admin/extend/widgets.json b/public/language/th/admin/extend/widgets.json index ee86bc24a0..b9309eaaa1 100644 --- a/public/language/th/admin/extend/widgets.json +++ b/public/language/th/admin/extend/widgets.json @@ -12,8 +12,8 @@ "container.card": "การ์ด", "container.card-header": "ส่วนหัวของการ์ด", "container.card-body": "ข้างในการ์ด", - "container.title": "Title", - "container.body": "Body", + "container.title": "ชื่อ", + "container.body": "เนื้อหา", "container.alert": "แจ้งเตือน", "alert.confirm-delete": "คุณต้องการจะลบวิดเจ็ตนี้หรือไม่?", diff --git a/public/language/th/users.json b/public/language/th/users.json index 353ab6b1da..91872ee541 100644 --- a/public/language/th/users.json +++ b/public/language/th/users.json @@ -1,6 +1,6 @@ { "all-users": "ผู้ใช้งานทั้งหมด", - "followed-users": "Followed Users", + "followed-users": "ผู้ใช้งานที่ติดตาม", "latest-users": "ผู้ใช้งานล่าสุด", "top-posters": "ผู้ที่โพสต์มากที่สุด", "most-reputation": "ผู้ที่มีชื่อเสียงมากที่สุด", diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 1570872d62..4181bd79cb 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -11,7 +11,7 @@ define('forum/chats', [ 'forum/chats/user-list', 'forum/chats/message-search', 'forum/chats/pinned-messages', - 'composer/autocomplete', + 'autocomplete', 'hooks', 'bootbox', 'alerts', diff --git a/public/src/modules/autocomplete.js b/public/src/modules/autocomplete.js index 277859ce3d..13e4e56427 100644 --- a/public/src/modules/autocomplete.js +++ b/public/src/modules/autocomplete.js @@ -1,6 +1,8 @@ 'use strict'; -define('autocomplete', ['api', 'alerts'], function (api, alerts) { +define('autocomplete', [ + 'api', 'alerts', '@textcomplete/core', '@textcomplete/textarea', '@textcomplete/contenteditable', +], function (api, alerts, { Textcomplete }, { TextareaEditor }, { ContenteditableEditor }) { const autocomplete = {}; const _default = { delay: 200, @@ -129,5 +131,37 @@ define('autocomplete', ['api', 'alerts'], function (api, alerts) { onselect(event, ui); } + // This is a generic method that is also used by the chat + autocomplete.setup = function ({ element, strategies, options }) { + const targetEl = element.get(0); + if (!targetEl) { + return; + } + var editor; + if (targetEl.nodeName === 'TEXTAREA' || targetEl.nodeName === 'INPUT') { + editor = new TextareaEditor(targetEl); + } else if (targetEl.nodeName === 'DIV' && targetEl.getAttribute('contenteditable') === 'true') { + editor = new ContenteditableEditor(targetEl); + } + if (!editor) { + throw new Error('unknown target element type'); + } + // yuku-t/textcomplete inherits directionality from target element itself + targetEl.setAttribute('dir', document.querySelector('html').getAttribute('data-dir')); + + var textcomplete = new Textcomplete(editor, strategies, { + dropdown: options, + }); + textcomplete.on('rendered', function () { + if (textcomplete.dropdown.items.length) { + // Activate the first item by default. + textcomplete.dropdown.items[0].activate(); + } + }); + + return textcomplete; + }; + + return autocomplete; }); diff --git a/public/src/modules/quickreply.js b/public/src/modules/quickreply.js index 55aea0b769..563ee486f8 100644 --- a/public/src/modules/quickreply.js +++ b/public/src/modules/quickreply.js @@ -1,13 +1,15 @@ 'use strict'; define('quickreply', [ - 'components', 'composer', 'composer/autocomplete', 'api', + 'components', 'autocomplete', 'api', 'alerts', 'uploadHelpers', 'mousetrap', 'storage', 'hooks', ], function ( - components, composer, autocomplete, api, + components, autocomplete, api, alerts, uploadHelpers, mousetrap, storage, hooks ) { - const QuickReply = {}; + const QuickReply = { + _active: {}, + }; QuickReply.init = function () { const element = components.get('topic/quickreply/text'); @@ -27,7 +29,7 @@ define('quickreply', [ destroyAutoComplete(); }); $(window).trigger('composer:autocomplete:init', data); - autocomplete._active.core_qr = autocomplete.setup(data); + QuickReply._active.core_qr = autocomplete.setup(data); mousetrap.bind('ctrl+return', (e) => { if (e.target === element.get(0)) { @@ -113,7 +115,7 @@ define('quickreply', [ e.preventDefault(); storage.removeItem(qrDraftId); const textEl = components.get('topic/quickreply/text'); - composer.newReply({ + hooks.fire('action:composer.post.new', { tid: ajaxify.data.tid, title: ajaxify.data.titleRaw, body: textEl.val(), @@ -123,9 +125,9 @@ define('quickreply', [ }; function destroyAutoComplete() { - if (autocomplete._active.core_qr) { - autocomplete._active.core_qr.destroy(); - autocomplete._active.core_qr = null; + if (QuickReply._active.core_qr) { + QuickReply._active.core_qr.destroy(); + QuickReply._active.core_qr = null; } }