From 56317b00afc5a06ba02e961bac80b365864104fb Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 8 Nov 2024 02:12:28 +0000 Subject: [PATCH] filecopy: ui for resolving name conflicts --- copyparty/web/browser.css | 9 +- copyparty/web/browser.js | 182 ++++++++++++++++++++++++++++---------- 2 files changed, 138 insertions(+), 53 deletions(-) diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index a3ad0ef5..102d010a 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -1943,11 +1943,10 @@ html.y #tree.nowrap .ntree a+a:hover { #rn_f.m td+td { width: 50%; } -#rn_f .err td { - background: var(--err-bg); - color: var(--fg-max); -} -#rn_f .err input[readonly] { +#rn_f .err td, +#rn_f .err input[readonly], +#rui .ng input[readonly] { + color: var(--err-fg); background: var(--err-bg); } #rui input[readonly] { diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 15818625..5d3aa96a 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -385,9 +385,11 @@ var Ls = { "fcc_ok": "copied {0} items to clipboard", "fcc_warn": 'copied {0} items to clipboard\n\nbut: only this browser-tab can paste them\n(since the selection is so absolutely massive)', + "fp_apply": "use these names", "fp_ecut": "first cut or copy some files / folders to paste / move\n\nnote: you can cut / paste across different browser tabs", - "fp_ename": "these {0} items cannot be moved here (names already exist):", - "fcp_ename": "these {0} items cannot be copied here (names already exist):", + "fp_ename": "{0} items cannot be moved here because the names are already taken. Give them new names below to continue, or blank the name to skip them:", + "fcp_ename": "{0} items cannot be copied here because the names are already taken. Give them new names below to continue, or blank the name to skip them:", + "fp_emore": "there are still some filename collisions left to fix", "fp_ok": "move OK", "fcp_ok": "copy OK", "fp_busy": "moving {0} items...\n\n{1}", @@ -967,9 +969,11 @@ var Ls = { "fcc_ok": "kopierte {0} filer til utklippstavlen", "fcc_warn": 'kopierte {0} filer til utklippstavlen\n\nmen: kun denne nettleserfanen har mulighet til å lime dem inn et annet sted, siden antallet filer er helt hinsides', + "fp_apply": "bekreft og lim inn nå", "fp_ecut": "du må klippe ut eller kopiere noen filer / mapper først\n\nmerk: du kan gjerne jobbe på kryss av nettleserfaner; klippe ut i én fane, lime inn i en annen", - "fp_ename": "disse {0} filene kan ikke flyttes til målmappen fordi det allerede finnes filer med samme navn:", - "fcp_ename": "disse {0} filene kan ikke kopieres til målmappen fordi det allerede finnes filer med samme navn:", + "fp_ename": "{0} filer kan ikke flyttes til målmappen fordi det allerede finnes filer med samme navn. Gi dem nye navn nedenfor, eller gi dem et blankt navn for å hoppe over dem:", + "fcp_ename": "{0} filer kan ikke kopieres til målmappen fordi det allerede finnes filer med samme navn. Gi dem nye navn nedenfor, eller gi dem et blankt navn for å hoppe over dem:", + "fp_emore": "det er fortsatt flere navn som må endres", "fp_ok": "flytting OK", "fcp_ok": "kopiering OK", "fp_busy": "flytter {0} filer...\n\n{1}", @@ -1549,9 +1553,11 @@ var Ls = { "fcc_ok": "已将 {0} 项复制到剪贴板", //m "fcc_warn": '已将 {0} 项复制到剪贴板\n\n但:只有 这个 浏览器标签页可以粘贴它们\n(因为选择非常庞大)', //m + "fp_apply": "确认并立即粘贴", //m "fp_ecut": "首先剪切或复制一些文件/文件夹以粘贴/移动\n\n注意:你可以在不同的浏览器标签页之间剪切/粘贴", //m - "fp_ename": "这些 {0} 项不能移动到这里(名称已存在):", - "fcp_ename": "这些 {0} 项不能复制到这里(名称已存在):", //m + "fp_ename": "{0} 项不能移动到这里,因为名称已被占用。请在下方输入新名称以继续,或将名称留空以跳过这些项:", //m + "fcp_ename": "{0} 项不能复制到这里,因为名称已被占用。请在下方输入新名称以继续,或将名称留空以跳过这些项:", //m + "fp_emore": "还有一些文件名冲突需要解决", //m "fp_ok": "移动成功", "fcp_ok": "复制成功", //m "fp_busy": "正在移动 {0} 项...\n\n{1}", @@ -4751,9 +4757,9 @@ var fileman = (function () { var html = sel.length > 1 ? ['
'] : [ '
', - '', + '   src: ' + esc(r.clip[0].replace(/[^/]+$/, '')), + '
', + '

', + '
', + '', + ], + ui = false, + f = [], indir = [], srcdir = vsplit(r.clip[0])[0], links = QSA('#files tbody td:nth-child(2) a'); for (var a = 0, aa = links.length; a < aa; a++) - indir.push(vsplit(noq_href(links[a]))[1]); + indir.push(uricom_dec(vsplit(noq_href(links[a]))[1])); for (var a = 0; a < r.clip.length; a++) { - var found = false; - for (var b = 0; b < indir.length; b++) { - if (r.clip[a].endsWith('/' + indir[b])) { - exists.push(r.clip[a]); - found = true; + var t = { + 'ok': true, + 'src': r.clip[a], + 'dst': uricom_dec(r.clip[a].split('/').pop()), + }; + f.push(t); + + for (var b = 0; b < indir.length; b++) + if (t.dst == indir[b]) { + t.ok = false; + ui = true; } - } - if (!found) - req.push(r.clip[a]); + + html.push(''); } - if (exists.length) - toast.warn(30, (r.ccp ? L.fcp_ename : L.fp_ename).format(exists.length) + '
    ' + uricom_adec(exists, true).join('') + '
'); - - if (!req.length) - return; - function paster() { - var xhr = new XHR(), - vp = req.shift(); - - if (!vp) { + var t = f.shift(); + if (!t) { toast.ok(2, r.ccp ? L.fcp_ok : L.fp_ok); treectl.goto(); r.tx(srcdir); return; } - toast.show('inf r', 0, esc((r.ccp ? L.fcp_busy : L.fp_busy).format(req.length + 1, uricom_dec(vp)))); + if (!t.dst) + return paster(); - var act = r.ccp ? '?copy=' : '?move=', - dst = get_evpath() + vp.split('/').pop(); + toast.show('inf r', 0, esc((r.ccp ? L.fcp_busy : L.fp_busy).format(f.length + 1, uricom_dec(f.src)))); - xhr.open('POST', vp + act + dst, true); + var xhr = new XHR(), + act = r.ccp ? '?copy=' : '?move=', + dst = get_evpath() + uricom_enc(t.dst); + + xhr.open('POST', t.src + act + dst, true); xhr.onload = xhr.onerror = paste_cb; xhr.send(); } @@ -5292,12 +5308,82 @@ var fileman = (function () { } paster(); } - - modal.confirm((r.ccp ? L.fcp_confirm : L.fp_confirm).format(req.length) + '
    ' + uricom_adec(req, true).join('') + '
', function () { + function okgo() { paster(); jwrite('fman_clip', [Date.now()]); - }, null); - }; + } + + if (!ui) { + var src = []; + for (var a = 0; a < f.length; a++) + src.push(f[a].src); + + return modal.confirm((r.ccp ? L.fcp_confirm : L.fp_confirm).format(f.length) + '
    ' + uricom_adec(src, true).join('') + '
', okgo, null); + } + + var rui = ebi('rui'); + if (!rui) { + rui = mknod('div', 'rui'); + document.body.appendChild(rui); + } + html.push('
' + L.fr_lnew + '' + L.fr_lold + '
'); + rui.innerHTML = html.join('\n'); + tt.att(rui); + + function rn_apply(e) { + for (var a = 0; a < f.length; a++) + if (!f[a].ok) { + toast.err(30, L.fp_emore); + return setcnmt(true); + } + rn_cancel(e); + okgo(); + } + function rn_cancel(e) { + ev(e); + rui.parentNode.removeChild(rui); + } + ebi('rn_cancel').onclick = rn_cancel; + ebi('rn_apply').onclick = rn_apply; + + var first_bad = 0; + function setcnmt(sel) { + var nbad = 0; + for (var a = 0; a < f.length; a++) { + if (f[a].ok) + continue; + if (!nbad) + first_bad = a; + nbad += 1; + } + ebi('cnmt').innerHTML = (r.ccp ? L.fcp_ename : L.fp_ename).format(nbad); + if (sel && nbad) { + var el = ebi('rn_new_' + first_bad); + el.focus(); + el.setSelectionRange(0, el.value.lastIndexOf('.'), "forward"); + } + } + setcnmt(true); + + for (var a = 0; a < f.length; a++) + (function (a) { + var inew = ebi('rn_new_' + a); + inew.onkeydown = function (e) { + if (((e.code || e.key) + '').endsWith('Enter')) + return rn_apply(); + }; + inew.oninput = function (e) { + f[a].dst = this.value; + f[a].ok = true; + if (f[a].dst) + for (var b = 0; b < indir.length; b++) + if (indir[b] == this.value) + f[a].ok = false; + clmod(this.closest('tr'), 'ng', !f[a].ok); + setcnmt(); + }; + })(a); + } function onmsg(msg) { r.clip = null;