diff --git a/public/scripts/extensions/quick-reply/contextMenuEditor.html b/public/scripts/extensions/quick-reply/contextMenuEditor.html new file mode 100644 index 000000000..00e77c0ac --- /dev/null +++ b/public/scripts/extensions/quick-reply/contextMenuEditor.html @@ -0,0 +1,21 @@ +
+
+

Quick Reply Context Menu Editor

+
+ +
+
+ +
+
+
diff --git a/public/scripts/extensions/quick-reply/index.js b/public/scripts/extensions/quick-reply/index.js index ed0c0b0f4..6b2f4f3ad 100644 --- a/public/scripts/extensions/quick-reply/index.js +++ b/public/scripts/extensions/quick-reply/index.js @@ -103,14 +103,69 @@ function onQuickReplyInput(id) { function onQuickReplyLabelInput(id) { extension_settings.quickReply.quickReplySlots[id - 1].label = $(`#quickReply${id}Label`).val(); let quickReplyLabel = extension_settings.quickReply.quickReplySlots[id - 1]?.label || ''; - const parts = quickReplyLabel.split('...'); - if (parts.length > 1) { - quickReplyLabel = `${parts.shift()}…`; - } - $(`#quickReply${id}`).text(quickReplyLabel); + $(`#quickReply${id}`).text(quickReplyLabel + (extension_settings.quickReply.quickReplySlots[id - 1]?.contextMenu?.length?'…':'')); saveSettingsDebounced(); } +async function onQuickReplyContextMenuChange(id) { + extension_settings.quickReply.quickReplySlots[id - 1].contextMenu = JSON.parse($(`#quickReplyContainer > [data-order="${id}"]`).attr('data-contextMenu')) + saveSettingsDebounced(); +} + +async function onQuickReplyCtxButtonClick(id) { + const editorHtml = $(await $.get('scripts/extensions/quick-reply/contextMenuEditor.html')); + const popupResult = callPopup(editorHtml, "confirm", undefined, { okButton: "Save", wide:false, large:false, rows: 1 }); + const qr = extension_settings.quickReply.quickReplySlots[id - 1]; + if (!qr.contextMenu) { + qr.contextMenu = []; + } + /**@type {HTMLTemplateElement}*/ + const tpl = document.querySelector('#quickReply_contextMenuEditor_itemTemplate'); + const fillPresetSelect = (select, item) => { + [{name:'Select a preset', value:''}, ...presets].forEach(preset=>{ + const opt = document.createElement('option'); { + opt.value = preset.value ?? preset.name; + opt.textContent = preset.name; + opt.selected = preset.name == item.preset; + select.append(opt); + } + }); + }; + const addCtxItem = (item, idx) => { + const dom = tpl.content.cloneNode(true); + const ctxItem = dom.querySelector('.quickReplyContextMenuEditor_item'); + ctxItem.setAttribute('data-order', idx); + const select = ctxItem.querySelector('.quickReply_contextMenuEditor_preset'); + fillPresetSelect(select, item); + dom.querySelector('.quickReply_contextMenuEditor_chaining').checked = item.chain; + $('.quickReply_contextMenuEditor_remove', ctxItem).on('click', ()=>ctxItem.remove()); + document.querySelector('#quickReply_contextMenuEditor_content').append(ctxItem); + } + [...qr.contextMenu, {}].forEach((item,idx)=>{ + addCtxItem(item, idx) + }); + $('#quickReply_contextMenuEditor_addPreset').on('click', ()=>{ + addCtxItem({}, document.querySelector('#quickReply_contextMenuEditor_content').children.length); + }); + + $('#quickReply_contextMenuEditor_content').sortable({ + delay: getSortableDelay(), + stop: ()=>{}, + }); + + if (await popupResult) { + qr.contextMenu = Array.from(document.querySelectorAll('#quickReply_contextMenuEditor_content > .quickReplyContextMenuEditor_item')) + .map(item=>({ + preset: item.querySelector('.quickReply_contextMenuEditor_preset').value, + chain: item.querySelector('.quickReply_contextMenuEditor_chaining').checked, + })) + .filter(item=>item.preset); + $(`#quickReplyContainer[data-order="${id}"]`).attr('data-contextMenu', JSON.stringify(qr.contextMenu)); + updateQuickReplyPreset(); + onQuickReplyLabelInput(id); + } +} + async function onQuickReplyEnabledInput() { let isEnabled = $(this).prop('checked') extension_settings.quickReply.quickReplyEnabled = !!isEnabled; @@ -186,40 +241,33 @@ function buildContextMenu(qr, chainMes=null, hierarchy=[], labelHierarchy=[]) { mes: (chainMes&&qr.mes ? `${chainMes} | ` : '') + qr.mes, children: [], }; - const parts = qr.label.split('...'); - if (parts.length > 1) { - tree.label = parts.shift(); - parts.forEach(subName=>{ - let chain = false; - if (subName[0] == '!') { - chain = true; - subName = subName.substring(1); + qr.contextMenu?.forEach(ctxItem=>{ + let chain = ctxItem.chain; + let subName = ctxItem.preset; + const sub = presets.find(it=>it.name == subName); + if (sub) { + // prevent circular references + if (hierarchy.indexOf(sub.name) == -1) { + const nextHierarchy = [...hierarchy, sub.name]; + const nextLabelHierarchy = [...labelHierarchy, tree.label]; + tree.children.push(new MenuHeader(sub.name)); + sub.quickReplySlots.forEach(subQr=>{ + const subInfo = buildContextMenu(subQr, chain?tree.mes:null, nextHierarchy, nextLabelHierarchy); + tree.children.push(new MenuItem( + subInfo.label, + subInfo.mes, + (evt)=>{ + evt.stopPropagation(); + performQuickReply(subInfo.mes.replace(/%%parent(-\d+)?%%/g, (_, index)=>{ + return nextLabelHierarchy.slice(parseInt(index ?? '-1'))[0]; + })); + }, + subInfo.children, + )); + }); } - const sub = presets.find(it=>it.name == subName); - if (sub) { - // prevent circular references - if (hierarchy.indexOf(sub.name) == -1) { - const nextHierarchy = [...hierarchy, sub.name]; - const nextLabelHierarchy = [...labelHierarchy, tree.label]; - tree.children.push(new MenuHeader(sub.name)); - sub.quickReplySlots.forEach(subQr=>{ - const subInfo = buildContextMenu(subQr, chain?tree.mes:null, nextHierarchy, nextLabelHierarchy); - tree.children.push(new MenuItem( - subInfo.label, - subInfo.mes, - (evt)=>{ - evt.stopPropagation(); - performQuickReply(subInfo.mes.replace(/%%parent(-\d+)?%%/g, (_, index)=>{ - return nextLabelHierarchy.slice(parseInt(index ?? '-1'))[0]; - })); - }, - subInfo.children, - )); - }); - } - } - }); - } + } + }); return tree; } function addQuickReplyBar() { @@ -229,9 +277,8 @@ function addQuickReplyBar() { for (let i = 0; i < extension_settings.quickReply.numberOfSlots; i++) { let quickReplyMes = extension_settings.quickReply.quickReplySlots[i]?.mes || ''; let quickReplyLabel = extension_settings.quickReply.quickReplySlots[i]?.label || ''; - const parts = quickReplyLabel.split('...'); - if (parts.length > 1) { - quickReplyLabel = `${parts.shift()}…`; + if (extension_settings.quickReply.quickReplySlots[i]?.contextMenu?.length) { + quickReplyLabel = `${quickReplyLabel}…`; } quickReplyButtonHtml += `
${quickReplyLabel}
`; } @@ -251,12 +298,14 @@ function addQuickReplyBar() { sendQuickReply(index); }); $('.quickReplyButton').on('contextmenu', function (evt) { - evt.preventDefault(); let index = $(this).data('index'); const qr = extension_settings.quickReply.quickReplySlots[index]; - const tree = buildContextMenu(qr); - const menu = new ContextMenu(tree.children); - menu.show(evt); + if (qr.contextMenu?.length) { + evt.preventDefault(); + const tree = buildContextMenu(qr); + const menu = new ContextMenu(tree.children); + menu.show(evt); + } }); } @@ -401,9 +450,10 @@ function generateQuickReplyElements() { for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) { let itemNumber = i + 1 quickReplyHtml += ` -
+
+
`; @@ -414,6 +464,8 @@ function generateQuickReplyElements() { for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) { $(`#quickReply${i}Mes`).on('input', function () { onQuickReplyInput(i); }); $(`#quickReply${i}Label`).on('input', function () { onQuickReplyLabelInput(i); }); + $(`#quickReply${i}CtxButton`).on('click', function () { onQuickReplyCtxButtonClick(i); }); + $(`#quickReplyContainer > [data-order="${i}"]`).attr('data-contextMenu', JSON.stringify(extension_settings.quickReply.quickReplySlots[i-1]?.contextMenu??[])); } $('.quickReplySettings .inline-drawer-toggle').off('click').on('click', function () { @@ -475,6 +527,7 @@ function saveQROrder() { //rebuild the extension_Settings array based on new order i = 1 $('#quickReplyContainer').children().each(function () { + onQuickReplyContextMenuChange(i) onQuickReplyLabelInput(i) onQuickReplyInput(i) i++