diff --git a/templates/upload.html b/templates/upload.html index 2d5fcc8..1446f84 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -94,8 +94,33 @@ refresh(); }); + // Mirror of the server's sanitiseSlug, so the client can predict the slug a + // filename yields and keep two files in one batch from colliding. + function slugify(name) { + let out = ''; + for (const ch of name.toLowerCase()) { + if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch === '-') out += ch; + else if (ch === ' ' || ch === '_') out += '-'; + } + return out; + } + + // A slug unique among the current cards. The server derives the slug from + // the uploaded filename, so two photos named the same would otherwise + // overwrite each other; the second (third, …) gets -2, -3, … + function uniqueSlug(filename) { + const stem = filename.replace(/\.[^.]+$/, ''); + const base = slugify(stem) || 'photo'; + let candidate = base; + let n = 2; + while (items.some((i) => i.slug === candidate)) candidate = base + '-' + (n++); + return candidate; + } + function addItem(file) { - const item = { id: ++seq, file, status: 'pending', el: null }; + const ext = (file.name.match(/\.[^.]+$/)?.[0] || '.jpg').toLowerCase(); + const item = { id: ++seq, file, ext, slug: '', status: 'pending', el: null }; + item.slug = uniqueSlug(file.name); // before push, so it compares against the others items.push(item); item.el = buildCard(item); list.appendChild(item.el); @@ -177,7 +202,9 @@ setStatus(item, 'up', 'Uploading…'); const form = new FormData(); - form.append('image', item.file); + // Send under the deduped slug so the server (which slugs from the + // filename) can't overwrite another photo in the same batch. + form.append('image', item.file, item.slug + item.ext); form.append('caption', item.el.querySelector('.c-caption').value); form.append('date', item.el.querySelector('.c-date').value); form.append('tags', item.el.querySelector('.c-tags').value);