diff --git a/src/Application/exportService.js b/src/Application/exportService.js index bb724dd..90a3f49 100644 --- a/src/Application/exportService.js +++ b/src/Application/exportService.js @@ -24,10 +24,16 @@ `${fd.orderNr.replace('/', '-')}_inspectie_${new Date().toISOString().slice(0, 10)}.xml`); let pc = 0; for (const [nr, photos] of Object.entries(fd.photos)) { - photos.forEach((p, i) => { pc++; I.downloadBlob(I.dataUrlToBlob(p.dataUrl), `${fd.orderNr.replace('/', '-')}_loc${nr}_foto${i + 1}.jpg`); }); + photos.forEach((p, i) => { + pc++; + I.downloadBlob(p.blob, `${fd.orderNr.replace('/', '-')}_loc${nr}_foto${i + 1}.jpg`); + }); } for (const [pos, photos] of Object.entries(fd.overviewPhotos)) { - photos.forEach((p, i) => { pc++; I.downloadBlob(I.dataUrlToBlob(p.dataUrl), `${fd.orderNr.replace('/', '-')}_${pos}_foto${i + 1}.jpg`); }); + photos.forEach((p, i) => { + pc++; + I.downloadBlob(p.blob, `${fd.orderNr.replace('/', '-')}_${pos}_foto${i + 1}.jpg`); + }); } alert(`Opgeslagen!\n- 1 XML-bestand\n- ${pc} foto('s)`); }; diff --git a/src/Application/photoService.js b/src/Application/photoService.js index d660afa..1af0c60 100644 --- a/src/Application/photoService.js +++ b/src/Application/photoService.js @@ -1,4 +1,8 @@ (function (A, D, I) { + function photoSrc(p) { + return URL.createObjectURL(p.blob); + } + A.openPhotoModal = function (nr, loc) { A.state.currentPhotoRow = nr; document.getElementById('photoModalTitle').textContent = `Foto's - Nr ${nr}: ${loc}`; @@ -17,7 +21,7 @@ for (const file of e.target.files) { if (!A.state.formData.photos[row]) A.state.formData.photos[row] = []; A.state.formData.photos[row].push({ - dataUrl: await I.readFileAsDataUrl(file), + blob: file, timestamp: new Date().toISOString(), gps: await I.getGPS(), filename: file.name @@ -39,14 +43,19 @@ } grid.innerHTML = photos.map((p, i) => `
- +
${new Date(p.timestamp).toLocaleTimeString('nl-NL', { hour: '2-digit', minute: '2-digit' })}${p.gps ? ' \u2316' : ''}
`).join(''); + photos.forEach((p, i) => { + const img = grid.querySelector(`img[data-r="${row}"][data-i="${i}"]`); + if (img) img.src = photoSrc(p); + }); + grid.querySelectorAll('img[data-r]').forEach(img => img.addEventListener('click', () => { const ph = A.state.formData.photos[img.dataset.r]?.[+img.dataset.i]; - if (ph) A.openLightbox(ph.dataUrl); + if (ph) A.openLightbox(photoSrc(ph)); })); grid.querySelectorAll('button[data-action="delete-photo"]').forEach(btn => btn.addEventListener('click', (ev) => { ev.stopPropagation(); @@ -84,7 +93,7 @@ if (!A.state.formData.overviewPhotos[pos]) A.state.formData.overviewPhotos[pos] = []; for (const file of e.target.files) { A.state.formData.overviewPhotos[pos].push({ - dataUrl: await I.readFileAsDataUrl(file), + blob: file, timestamp: new Date().toISOString(), gps: await I.getGPS(), filename: file.name @@ -121,15 +130,20 @@ } grid.innerHTML = all.map(it => `
- +
${it.label}
`).join(''); + all.forEach(it => { + const img = grid.querySelector(`img[data-pos="${it.pos}"][data-idx="${it.index}"]`); + if (img) img.src = photoSrc(it.photo); + }); + grid.querySelectorAll('img[data-pos]').forEach(img => img.addEventListener('click', ev => { ev.stopPropagation(); const ph = A.state.formData.overviewPhotos[img.dataset.pos]?.[+img.dataset.idx]; - if (ph) A.openLightbox(ph.dataUrl); + if (ph) A.openLightbox(photoSrc(ph)); })); grid.querySelectorAll('button[data-action="delete-ov-photo"]').forEach(btn => btn.addEventListener('click', ev => { ev.stopPropagation(); @@ -152,12 +166,18 @@ }; A.openLightbox = function (src) { - document.getElementById('lightboxImg').src = src; + const img = document.getElementById('lightboxImg'); + const prev = img.src; + img.src = src; document.getElementById('lightbox').classList.add('active'); + if (prev && prev.startsWith('blob:')) URL.revokeObjectURL(prev); }; A.closeLightbox = function () { + const img = document.getElementById('lightboxImg'); + const src = img.src; document.getElementById('lightbox').classList.remove('active'); - document.getElementById('lightboxImg').src = ''; + img.src = ''; + if (src && src.startsWith('blob:')) URL.revokeObjectURL(src); }; })(window.App.Application, window.App.Domain, window.App.Infrastructure); diff --git a/src/Infrastructure/db.js b/src/Infrastructure/db.js index ab859f2..6408a23 100644 --- a/src/Infrastructure/db.js +++ b/src/Infrastructure/db.js @@ -27,7 +27,7 @@ const d = await ensureDb(); return new Promise((resolve, reject) => { const tx = d.transaction('inspections', 'readwrite'); - tx.objectStore('inspections').put(JSON.parse(JSON.stringify(formData))); + tx.objectStore('inspections').put(formData); tx.oncomplete = () => resolve(); tx.onerror = reject; }); diff --git a/src/Infrastructure/utils.js b/src/Infrastructure/utils.js index 74b83b1..c9d5ea6 100644 --- a/src/Infrastructure/utils.js +++ b/src/Infrastructure/utils.js @@ -11,23 +11,6 @@ URL.revokeObjectURL(a.href); }; - I.dataUrlToBlob = function (dataUrl) { - const parts = dataUrl.split(','); - const mime = parts[0].match(/:(.*?);/)[1]; - const bin = atob(parts[1]); - const arr = new Uint8Array(bin.length); - for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i); - return new Blob([arr], { type: mime }); - }; - - I.readFileAsDataUrl = function (file) { - return new Promise(resolve => { - const fr = new FileReader(); - fr.onload = () => resolve(fr.result); - fr.readAsDataURL(file); - }); - }; - I.formatDate = function (d) { if (!d || d.length !== 8) return d || '-'; return d.substring(6, 8) + '.' + d.substring(4, 6) + '.' + d.substring(0, 4);