diff --git a/cmd/fibr/static/scripts/async-image.js b/cmd/fibr/static/scripts/async-image.js index 70c5adc6..a37f9b33 100644 --- a/cmd/fibr/static/scripts/async-image.js +++ b/cmd/fibr/static/scripts/async-image.js @@ -101,7 +101,7 @@ document.addEventListener( return; } - if (typeof fetchThumbnail === 'undefined' || !fetchThumbnail) { + if (typeof hasThumbnail === 'undefined' || !hasThumbnail) { return; } diff --git a/cmd/fibr/static/scripts/index.min.js b/cmd/fibr/static/scripts/index.min.js index 9943f30e..fef62250 100644 --- a/cmd/fibr/static/scripts/index.min.js +++ b/cmd/fibr/static/scripts/index.min.js @@ -1 +1 @@ -function isWebPCompatible(){const e="UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA";return new Promise((t,n)=>{const s=new Image;s.onload=()=>{s.width>0&&s.height>0?t():n()},s.onerror=n.bind(null,!0),s.src=`data:image/webp;base64,${e}`})}async function*readLineByLine(e){const o=new TextDecoder("utf-8"),i=e.body.getReader();let{value:t,done:a}=await i.read();t=t?o.decode(t,{stream:!0}):"";let s=/\r\n|\n|\r/gm,n=0;for(;;){const e=s.exec(t);if(!e){if(a)break;const e=t.substr(n);({value:t,done:a}=await i.read(),t=e+(t?o.decode(t,{stream:!0}):""),n=s.lastIndex=0);continue}yield t.substring(n,e.index),n=s.lastIndex}n=400)throw new Error("unable to load thumbnails");for await(let o of readLineByLine(t)){const s=o.split(",");if(s.length!=2){console.error("invalid line for thumbnail:",o);continue}const n=document.getElementById(`picture-${s[0]}`);if(!n)continue;const e=new Image;e.src=`data:image/webp;base64,${s[1]}`,e.alt=n.dataset.alt,e.dataset.src=n.dataset.src,e.classList.add("thumbnail","full","block"),replaceContent(n,e)}}document.addEventListener("readystatechange",async e=>{if(e.target.readyState!=="complete")return;if(typeof fetchThumbnail=="undefined"||!fetchThumbnail)return;let n=new Intl.DateTimeFormat(navigator.language,{dateStyle:"full",timeStyle:"long"});document.querySelectorAll(".date").forEach(e=>{e.innerHTML=n.format(new Date(e.innerHTML))});const t=document.querySelectorAll("[data-thumbnail]");if(!t)return;t.forEach(e=>{replaceContent(e,generateThrobber(["throbber-white"]))});try{await fetchThumbnail()}catch(e){console.error(e)}try{await isWebPCompatible()}catch{await resolveScript("https://unpkg.com/webp-hero@0.0.2/dist-cjs/webp-hero.bundle.js","sha512-DA6h9H5Sqn55/uVn4JI4aSPFnAWoCQYYDXUnvjOAMNVx11///hX4QaFbQt5yWsrIm9hSI5fLJYfRWt3KXneSXQ==","anonymous");const e=new webpHero.WebpMachine;e.polyfillDocument(),e.clearCache()}window.dispatchEvent(new Event("thumbnail-done"))},!1),window.addEventListener("thumbnail-done",()=>{if(typeof lazyLoadThumbnail=="undefined"||!lazyLoadThumbnail)return;const e=new IntersectionObserver(async(t)=>{for(const s of t){if(!s.isIntersecting)continue;const n=s.target;if(window.webpHero){const t=await fetch(n.dataset.src,{credentials:"same-origin"}),s=await t.arrayBuffer(),e=new webpHero.WebpMachine;n.src=await e.decode(new Uint8Array(s)),e.clearCache()}else n.src=n.dataset.src;e.unobserve(n)}});document.querySelectorAll("img.thumbnail").forEach(t=>e.observe(t))});async function fetchGeoJSON(e){const t=await fetch(e,{credentials:"same-origin"});if(t.status>=400)throw new Error("unable to load geojson");return t.status===204?null:t.json()}async function addStyle(e,t,n){return new Promise(s=>{const o=document.createElement("link");o.rel="stylesheet",o.href=e,o.onload=s,t&&(o.integrity=t,o.crossOrigin=n),document.querySelector("head").appendChild(o)})}async function loadLeaflet(){const e="1.9.2";await addStyle(`https://unpkg.com/leaflet@${e}/dist/leaflet.css`,"sha512-UkezATkM8unVC0R/Z9Kmq4gorjNoFwLMAWR/1yZpINW08I79jEKx/c8NlLSvvimcu7SL8pgeOnynxfRpe+5QpA==","anonymous"),await addStyle("https://unpkg.com/leaflet.markercluster@1.5.1/dist/MarkerCluster.Default.css","sha512-6ZCLMiYwTeli2rVh3XAPxy3YoR5fVxGdH/pz+KMCzRY2M65Emgkw00Yqmhh8qLGeYQ3LbVZGdmOX9KUjSKr0TA==","anonymous"),await resolveScript(`https://unpkg.com/leaflet@${e}/dist/leaflet.js`,"sha512-KMraOVM0qMVE0U1OULTpYO4gg5MZgazwPAPyMQWfOkEshpwlLQFCHZ/0lBXyviDNVL+pBGwmeXQnuvGK8Fscvg==","anonymous"),await resolveScript("https://unpkg.com/leaflet.markercluster@1.5.1/dist/leaflet.markercluster.js","sha512-+Zr0llcuE/Ho6wXRYtlWypMyWSEMxrWJxrYgeAMDRSf1FF46gQ3PAVOVp5RHdxdzikZXuHZ0soHpqRkkPkI3KA==","anonymous")}let map;async function renderMap(e){if(map){map.invalidateSize();return}const t=document.getElementById("map-container"),n=generateThrobber(["map-throbber","throbber-white"]);t&&t.appendChild(n),await loadLeaflet(),map=L.map("map-container",{center:[46.227638,2.213749],zoom:5}),L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{maxZoom:19,attribution:'© OpenStreetMap contributors'}).addTo(map);const s=await fetchGeoJSON(e);if(!s||!s.features)return;const o=L.markerClusterGroup({zoomToBoundsOnClick:!1}),i=[];s.features.map(e=>{const t=L.GeoJSON.coordsToLatLng(e.geometry.coordinates);i.push(t),o.addLayer(L.circleMarker(t).bindPopup(`Image thumbnail
${e.properties.date}`,{maxWidth:"auto",closeButton:!1,className:"thumbnail-popup"}))}),o.on("clusterclick",e=>{map.fitBounds(e.layer.getAllChildMarkers().map(e=>e.getLatLng()))}),i.length?(map.once("zoomend",()=>{t.removeChild(n)}),map.fitBounds(i)):t.removeChild(n),map.addLayer(o)}document.addEventListener("readystatechange",async e=>{if(e.target.readyState!=="complete")return;document.location.hash==="#map"?await renderMap(geoURL):window.addEventListener("popstate",async()=>{document.location.hash==="#map"&&await renderMap(geoURL)})});function resolveScript(e,t,n){return new Promise((s,o)=>{const i=document.createElement("script");i.type="text/javascript",i.src=e,i.async=!0,i.onload=s.bind(null,!0),i.onerror=o.bind(null,!0),t&&(i.integrity=t,i.crossOrigin=n),document.querySelector("head").appendChild(i)})}window.onkeyup=e=>{switch(e.key){case"ArrowLeft":goToPrevious();break;case"ArrowRight":goToNext();break;case"Escape":typeof abort=="function"?abort(e):goBack();break}};function replaceContent(e,t){for(;e.firstChild;)e.removeChild(e.firstChild);t&&e.appendChild(t)}function generateThrobber(e=[]){const t=document.createElement("div");t.classList.add("throbber"),e.forEach(e=>t.classList.add(e));for(let e=1;e<4;e++){const n=document.createElement("div");n.classList.add("throbber-dot",`throbber-dot-${e}`),t.appendChild(n)}return t}let fileInput,uploadList,cancelButton;function eventNoop(e){e.preventDefault(),e.stopPropagation()}document.addEventListener("readystatechange",async e=>{if(e.target.readyState!=="complete")return;const t=document.getElementsByTagName("body")[0];t.addEventListener("dragover",eventNoop),t.addEventListener("dragleave",eventNoop),t.addEventListener("drop",e=>{eventNoop(e),window.location.hash="#upload-modal",fileInput&&(fileInput.files=e.dataTransfer.files,fileInput.dispatchEvent(new Event("change")))})});function bufferToHex(e){return Array.prototype.map.call(new Uint8Array(e),e=>`00${e.toString(16)}`.slice(-2)).join("")}async function sha(e){const t=await crypto.subtle.digest("SHA-256",new TextEncoder("utf-8").encode(e));return bufferToHex(t)}async function fileMessageId(e){const t=await sha(JSON.stringify({name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}));return`upload-file-${t}`}function humanFileSize(e){return e<1024?e+"bytes":e<1048576?(e/1024).toFixed(0)+" KB":(e/1048576).toFixed(0)+" MB"}async function addUploadItem(e,t){const c=await fileMessageId(t),n=document.createElement("div");n.id=c,n.classList.add("flex","flex-center","margin");const o=document.createElement("div");o.classList.add("upload-item"),n.appendChild(o);const s=document.createElement("input");s.id=`${c}-filename`,s.classList.add("upload-name","full"),s.type="text",s.value=t.name,o.appendChild(s);const i=document.createElement("div");i.classList.add("full","flex","flex-center");const r=document.createElement("em");r.innerHTML=humanFileSize(t.size),r.style.width="8rem",i.appendChild(r);const a=document.createElement("progress");a.classList.add("flex-grow","margin-left"),a.max=100,a.value=0,i.appendChild(a),o.appendChild(i);const l=document.createElement("span");l.classList.add("upload-status"),n.appendChild(l),e.appendChild(n)}function getFiles(e){return[].filter.call(e.target,e=>e.nodeName.toLowerCase()==="input").reduce((e,t)=>(t.type==="file"?e.files=t.files:e[t.name]=t.value,e),{})}function getFilename(e,t){const n=document.getElementById(`${e}-filename`);return n&&n.value?n.value:t.name}function clearUploadStatus(e){if(!e)return;const t=e.querySelector(".upload-status");if(!t)return;t.classList.remove("danger"),t.classList.remove("success")}async function setUploadStatus(e,t,n,s){if(!e)return;const o=e.querySelector(".upload-status");if(!o)return;o.innerHTML=t,o.classList.add(n),s&&(o.title=s)}let aborter;const chunkSize=1024*1024;let currentUpload={};async function uploadFileByChunks(e,t,n,s){let i;if(e&&(i=e.querySelector("progress"),clearUploadStatus(e)),s.name!==currentUpload.filename){currentUpload.filename=s.name,currentUpload.chunks=[];for(let e=0;e=400)return Promise.reject(await a.text());currentUpload.chunks[e].done=!0,i&&(i.value=chunkSize*(e+1)/s.size*100)}const o=new FormData;o.append("method",t),o.append("filename",n),o.append("size",s.size);const a=await fetch("",{method:"POST",credentials:"same-origin",headers:{"X-Chunk-Upload":!0,Accept:"text/plain"},body:o}),r=await a.text();return a.status>=400?Promise.reject(r):(currentUpload={},Promise.resolve(r))}async function uploadFileByXHR(e,t,n,s){let o;e&&(o=e.querySelector("progress"),clearUploadStatus(e));const i=new FormData;return i.append("method",t),i.append("filename",n),i.append("size",s.size),i.append("file",s),new Promise((e,t)=>{let n=new XMLHttpRequest;aborter=n,o&&n.upload.addEventListener("progress",e=>o.value=parseInt(e.loaded/e.total*100,10),!1),n.addEventListener("readystatechange",s=>{n.readyState===XMLHttpRequest.DONE?n.status>=200&&n.status<400?(o&&(o.value=100),e(n.responseText),n=void 0):(t(s),n=void 0):n.readyState===XMLHttpRequest.UNSENT&&(t(new Error("request aborted")),n=void 0)},!1),n.open("POST","",!0),n.setRequestHeader("Accept","text/plain"),n.send(i)})}function sliceFileList(e,t){const n=document.getElementById(e),s=new DataTransfer;for(;t{if(e.target.readyState!=="complete")return;if(fileInput=document.getElementById("file"),uploadList=document.getElementById("upload-list"),cancelButton=document.getElementById("upload-cancel"),fileInput){fileInput.classList.add("opacity"),fileInput.multiple=!0,fileInput.addEventListener("change",()=>{window.location.hash="#upload-modal",replaceContent(uploadList);for(const e of fileInput.files)addUploadItem(uploadList,e)});const e=document.getElementById("upload-button-link");e&&e.addEventListener("click",e=>{eventNoop(e),fileInput.click()})}const t=document.getElementById("file-label");t&&(t.classList.remove("hidden"),t.innerHTML="Choose files..."),uploadList&&uploadList.classList.remove("hidden"),cancelButton&&cancelButton.addEventListener("click",abort);const n=document.getElementById("upload-form");n&&n.addEventListener("submit",upload)}) \ No newline at end of file +function isWebPCompatible(){const e="UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA";return new Promise((t,n)=>{const s=new Image;s.onload=()=>{s.width>0&&s.height>0?t():n()},s.onerror=n.bind(null,!0),s.src=`data:image/webp;base64,${e}`})}async function*readLineByLine(e){const o=new TextDecoder("utf-8"),i=e.body.getReader();let{value:t,done:a}=await i.read();t=t?o.decode(t,{stream:!0}):"";let s=/\r\n|\n|\r/gm,n=0;for(;;){const e=s.exec(t);if(!e){if(a)break;const e=t.substr(n);({value:t,done:a}=await i.read(),t=e+(t?o.decode(t,{stream:!0}):""),n=s.lastIndex=0);continue}yield t.substring(n,e.index),n=s.lastIndex}n=400)throw new Error("unable to load thumbnails");for await(let o of readLineByLine(t)){const s=o.split(",");if(s.length!=2){console.error("invalid line for thumbnail:",o);continue}const n=document.getElementById(`picture-${s[0]}`);if(!n)continue;const e=new Image;e.src=`data:image/webp;base64,${s[1]}`,e.alt=n.dataset.alt,e.dataset.src=n.dataset.src,e.classList.add("thumbnail","full","block"),replaceContent(n,e)}}document.addEventListener("readystatechange",async e=>{if(e.target.readyState!=="complete")return;if(typeof hasThumbnail=="undefined"||!hasThumbnail)return;let n=new Intl.DateTimeFormat(navigator.language,{dateStyle:"full",timeStyle:"long"});document.querySelectorAll(".date").forEach(e=>{e.innerHTML=n.format(new Date(e.innerHTML))});const t=document.querySelectorAll("[data-thumbnail]");if(!t)return;t.forEach(e=>{replaceContent(e,generateThrobber(["throbber-white"]))});try{await fetchThumbnail()}catch(e){console.error(e)}try{await isWebPCompatible()}catch{await resolveScript("https://unpkg.com/webp-hero@0.0.2/dist-cjs/webp-hero.bundle.js","sha512-DA6h9H5Sqn55/uVn4JI4aSPFnAWoCQYYDXUnvjOAMNVx11///hX4QaFbQt5yWsrIm9hSI5fLJYfRWt3KXneSXQ==","anonymous");const e=new webpHero.WebpMachine;e.polyfillDocument(),e.clearCache()}window.dispatchEvent(new Event("thumbnail-done"))},!1),window.addEventListener("thumbnail-done",()=>{if(typeof lazyLoadThumbnail=="undefined"||!lazyLoadThumbnail)return;const e=new IntersectionObserver(async(t)=>{for(const s of t){if(!s.isIntersecting)continue;const n=s.target;if(window.webpHero){const t=await fetch(n.dataset.src,{credentials:"same-origin"}),s=await t.arrayBuffer(),e=new webpHero.WebpMachine;n.src=await e.decode(new Uint8Array(s)),e.clearCache()}else n.src=n.dataset.src;e.unobserve(n)}});document.querySelectorAll("img.thumbnail").forEach(t=>e.observe(t))});async function fetchGeoJSON(e){const t=await fetch(e,{credentials:"same-origin"});if(t.status>=400)throw new Error("unable to load geojson");return t.status===204?null:t.json()}async function addStyle(e,t,n){return new Promise(s=>{const o=document.createElement("link");o.rel="stylesheet",o.href=e,o.onload=s,t&&(o.integrity=t,o.crossOrigin=n),document.querySelector("head").appendChild(o)})}async function loadLeaflet(){const e="1.9.2";await addStyle(`https://unpkg.com/leaflet@${e}/dist/leaflet.css`,"sha512-UkezATkM8unVC0R/Z9Kmq4gorjNoFwLMAWR/1yZpINW08I79jEKx/c8NlLSvvimcu7SL8pgeOnynxfRpe+5QpA==","anonymous"),await addStyle("https://unpkg.com/leaflet.markercluster@1.5.1/dist/MarkerCluster.Default.css","sha512-6ZCLMiYwTeli2rVh3XAPxy3YoR5fVxGdH/pz+KMCzRY2M65Emgkw00Yqmhh8qLGeYQ3LbVZGdmOX9KUjSKr0TA==","anonymous"),await resolveScript(`https://unpkg.com/leaflet@${e}/dist/leaflet.js`,"sha512-KMraOVM0qMVE0U1OULTpYO4gg5MZgazwPAPyMQWfOkEshpwlLQFCHZ/0lBXyviDNVL+pBGwmeXQnuvGK8Fscvg==","anonymous"),await resolveScript("https://unpkg.com/leaflet.markercluster@1.5.1/dist/leaflet.markercluster.js","sha512-+Zr0llcuE/Ho6wXRYtlWypMyWSEMxrWJxrYgeAMDRSf1FF46gQ3PAVOVp5RHdxdzikZXuHZ0soHpqRkkPkI3KA==","anonymous")}let map;async function renderMap(e){if(map){map.invalidateSize();return}const t=document.getElementById("map-container"),n=generateThrobber(["map-throbber","throbber-white"]);t&&t.appendChild(n),await loadLeaflet(),map=L.map("map-container",{center:[46.227638,2.213749],zoom:5}),L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{maxZoom:19,attribution:'© OpenStreetMap contributors'}).addTo(map);const s=await fetchGeoJSON(e);if(!s||!s.features)return;const o=L.markerClusterGroup({zoomToBoundsOnClick:!1}),i=[];s.features.map(e=>{const t=L.GeoJSON.coordsToLatLng(e.geometry.coordinates);i.push(t),o.addLayer(L.circleMarker(t).bindPopup(`Image thumbnail
${e.properties.date}`,{maxWidth:"auto",closeButton:!1,className:"thumbnail-popup"}))}),o.on("clusterclick",e=>{map.fitBounds(e.layer.getAllChildMarkers().map(e=>e.getLatLng()))}),i.length?(map.once("zoomend",()=>{t.removeChild(n)}),map.fitBounds(i)):t.removeChild(n),map.addLayer(o)}document.addEventListener("readystatechange",async e=>{if(e.target.readyState!=="complete")return;document.location.hash==="#map"?await renderMap(geoURL):window.addEventListener("popstate",async()=>{document.location.hash==="#map"&&await renderMap(geoURL)})});function resolveScript(e,t,n){return new Promise((s,o)=>{const i=document.createElement("script");i.type="text/javascript",i.src=e,i.async=!0,i.onload=s.bind(null,!0),i.onerror=o.bind(null,!0),t&&(i.integrity=t,i.crossOrigin=n),document.querySelector("head").appendChild(i)})}window.onkeyup=e=>{switch(e.key){case"ArrowLeft":goToPrevious();break;case"ArrowRight":goToNext();break;case"Escape":typeof abort=="function"?abort(e):goBack();break}};function replaceContent(e,t){for(;e.firstChild;)e.removeChild(e.firstChild);t&&e.appendChild(t)}function generateThrobber(e=[]){const t=document.createElement("div");t.classList.add("throbber"),e.forEach(e=>t.classList.add(e));for(let e=1;e<4;e++){const n=document.createElement("div");n.classList.add("throbber-dot",`throbber-dot-${e}`),t.appendChild(n)}return t}let fileInput,uploadList,cancelButton;function eventNoop(e){e.preventDefault(),e.stopPropagation()}document.addEventListener("readystatechange",async e=>{if(e.target.readyState!=="complete")return;const t=document.getElementsByTagName("body")[0];t.addEventListener("dragover",eventNoop),t.addEventListener("dragleave",eventNoop),t.addEventListener("drop",e=>{eventNoop(e),window.location.hash="#upload-modal",fileInput&&(fileInput.files=e.dataTransfer.files,fileInput.dispatchEvent(new Event("change")))})});function bufferToHex(e){return Array.prototype.map.call(new Uint8Array(e),e=>`00${e.toString(16)}`.slice(-2)).join("")}async function sha(e){const t=await crypto.subtle.digest("SHA-256",new TextEncoder("utf-8").encode(e));return bufferToHex(t)}async function fileMessageId(e){const t=await sha(JSON.stringify({name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}));return`upload-file-${t}`}function humanFileSize(e){return e<1024?e+"bytes":e<1048576?(e/1024).toFixed(0)+" KB":(e/1048576).toFixed(0)+" MB"}async function addUploadItem(e,t){const c=await fileMessageId(t),n=document.createElement("div");n.id=c,n.classList.add("flex","flex-center","margin");const o=document.createElement("div");o.classList.add("upload-item"),n.appendChild(o);const s=document.createElement("input");s.id=`${c}-filename`,s.classList.add("upload-name","full"),s.type="text",s.value=t.name,o.appendChild(s);const i=document.createElement("div");i.classList.add("full","flex","flex-center");const r=document.createElement("em");r.innerHTML=humanFileSize(t.size),r.style.width="8rem",i.appendChild(r);const a=document.createElement("progress");a.classList.add("flex-grow","margin-left"),a.max=100,a.value=0,i.appendChild(a),o.appendChild(i);const l=document.createElement("span");l.classList.add("upload-status"),n.appendChild(l),e.appendChild(n)}function getFiles(e){return[].filter.call(e.target,e=>e.nodeName.toLowerCase()==="input").reduce((e,t)=>(t.type==="file"?e.files=t.files:e[t.name]=t.value,e),{})}function getFilename(e,t){const n=document.getElementById(`${e}-filename`);return n&&n.value?n.value:t.name}function clearUploadStatus(e){if(!e)return;const t=e.querySelector(".upload-status");if(!t)return;t.classList.remove("danger"),t.classList.remove("success")}async function setUploadStatus(e,t,n,s){if(!e)return;const o=e.querySelector(".upload-status");if(!o)return;o.innerHTML=t,o.classList.add(n),s&&(o.title=s)}let aborter;const chunkSize=1024*1024;let currentUpload={};async function uploadFileByChunks(e,t,n,s){let i;if(e&&(i=e.querySelector("progress"),clearUploadStatus(e)),s.name!==currentUpload.filename){currentUpload.filename=s.name,currentUpload.chunks=[];for(let e=0;e=400)return Promise.reject(await a.text());currentUpload.chunks[e].done=!0,i&&(i.value=chunkSize*(e+1)/s.size*100)}const o=new FormData;o.append("method",t),o.append("filename",n),o.append("size",s.size);const a=await fetch("",{method:"POST",credentials:"same-origin",headers:{"X-Chunk-Upload":!0,Accept:"text/plain"},body:o}),r=await a.text();return a.status>=400?Promise.reject(r):(currentUpload={},Promise.resolve(r))}async function uploadFileByXHR(e,t,n,s){let o;e&&(o=e.querySelector("progress"),clearUploadStatus(e));const i=new FormData;return i.append("method",t),i.append("filename",n),i.append("size",s.size),i.append("file",s),new Promise((e,t)=>{let n=new XMLHttpRequest;aborter=n,o&&n.upload.addEventListener("progress",e=>o.value=parseInt(e.loaded/e.total*100,10),!1),n.addEventListener("readystatechange",s=>{n.readyState===XMLHttpRequest.DONE?n.status>=200&&n.status<400?(o&&(o.value=100),e(n.responseText),n=void 0):(t(s),n=void 0):n.readyState===XMLHttpRequest.UNSENT&&(t(new Error("request aborted")),n=void 0)},!1),n.open("POST","",!0),n.setRequestHeader("Accept","text/plain"),n.send(i)})}function sliceFileList(e,t){const n=document.getElementById(e),s=new DataTransfer;for(;t{if(e.target.readyState!=="complete")return;if(fileInput=document.getElementById("file"),uploadList=document.getElementById("upload-list"),cancelButton=document.getElementById("upload-cancel"),fileInput){fileInput.classList.add("opacity"),fileInput.multiple=!0,fileInput.addEventListener("change",()=>{window.location.hash="#upload-modal",replaceContent(uploadList);for(const e of fileInput.files)addUploadItem(uploadList,e)});const e=document.getElementById("upload-button-link");e&&e.addEventListener("click",e=>{eventNoop(e),fileInput.click()})}const t=document.getElementById("file-label");t&&(t.classList.remove("hidden"),t.innerHTML="Choose files..."),uploadList&&uploadList.classList.remove("hidden"),cancelButton&&cancelButton.addEventListener("click",abort);const n=document.getElementById("upload-form");n&&n.addEventListener("submit",upload)}) \ No newline at end of file diff --git a/cmd/fibr/templates/files.html b/cmd/fibr/templates/files.html index d9a02a0c..8a391773 100644 --- a/cmd/fibr/templates/files.html +++ b/cmd/fibr/templates/files.html @@ -326,7 +326,7 @@ {{ if and (eq .Request.Display "grid") .HasThumbnail }} {{ end }} diff --git a/cmd/fibr/templates/story.html b/cmd/fibr/templates/story.html index c234eda7..19652234 100644 --- a/cmd/fibr/templates/story.html +++ b/cmd/fibr/templates/story.html @@ -5,7 +5,7 @@ {{ $root := . }} diff --git a/go.mod b/go.mod index f618c85c..c586f901 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ViBiOh/auth/v2 v2.14.20 github.com/ViBiOh/exas v0.5.11 github.com/ViBiOh/flags v1.2.0 - github.com/ViBiOh/httputils/v4 v4.50.0 + github.com/ViBiOh/httputils/v4 v4.50.1 github.com/ViBiOh/vith v0.5.10 github.com/golang/mock v1.6.0 github.com/prometheus/client_golang v1.13.1 diff --git a/go.sum b/go.sum index 802d7268..a428238c 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/ViBiOh/exas v0.5.11 h1:KpW9mmt+Fe3I2l11UXovye8wWZ+zoV8LVVyCcvH6j+w= github.com/ViBiOh/exas v0.5.11/go.mod h1:A1dbq+ZR5yBo4RkDWhg4M2hze9MLTBUDMk5gRliWEEk= github.com/ViBiOh/flags v1.2.0 h1:DaujjNXzD29KxKyp4eZdn7c9+uBN5DokWgDAe7DcUmc= github.com/ViBiOh/flags v1.2.0/go.mod h1:UyMB5zeD/aId7Xw3x7577ZNU298JmukzOcV8p/H2W1s= -github.com/ViBiOh/httputils/v4 v4.50.0 h1:87oPPf4hvE8SetNpUOxxNUazhn8XCESz0glvJcj+KgU= -github.com/ViBiOh/httputils/v4 v4.50.0/go.mod h1:yp/iCcK7SBdQfxCqPNwvF4J8WAVZ3M1o95gfFCJIsbE= +github.com/ViBiOh/httputils/v4 v4.50.1 h1:NHtJvM8Vf05DXAyQ9jgmLMBrjTERagD+bCBBTCvOIGc= +github.com/ViBiOh/httputils/v4 v4.50.1/go.mod h1:yp/iCcK7SBdQfxCqPNwvF4J8WAVZ3M1o95gfFCJIsbE= github.com/ViBiOh/vith v0.5.10 h1:BriyDGDFmBRpJpbQhsFxIlq2bT9HmuJ/q903Y+0w0S0= github.com/ViBiOh/vith v0.5.10/go.mod h1:JcGIWVjvXA0zYHz/picn+xMIdk7ljbYjDp7d16BufzM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/pkg/mocks/cache.go b/pkg/mocks/cache.go new file mode 100644 index 00000000..a49bb187 --- /dev/null +++ b/pkg/mocks/cache.go @@ -0,0 +1,118 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ViBiOh/httputils/v4/pkg/cache (interfaces: RedisClient) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + time "time" + + gomock "github.com/golang/mock/gomock" +) + +// RedisClient is a mock of RedisClient interface. +type RedisClient struct { + ctrl *gomock.Controller + recorder *RedisClientMockRecorder +} + +// RedisClientMockRecorder is the mock recorder for RedisClient. +type RedisClientMockRecorder struct { + mock *RedisClient +} + +// NewRedisClient creates a new mock instance. +func NewRedisClient(ctrl *gomock.Controller) *RedisClient { + mock := &RedisClient{ctrl: ctrl} + mock.recorder = &RedisClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *RedisClient) EXPECT() *RedisClientMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *RedisClient) Delete(arg0 context.Context, arg1 ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *RedisClientMockRecorder) Delete(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*RedisClient)(nil).Delete), varargs...) +} + +// Enabled mocks base method. +func (m *RedisClient) Enabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Enabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Enabled indicates an expected call of Enabled. +func (mr *RedisClientMockRecorder) Enabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enabled", reflect.TypeOf((*RedisClient)(nil).Enabled)) +} + +// Load mocks base method. +func (m *RedisClient) Load(arg0 context.Context, arg1 string) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Load", arg0, arg1) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Load indicates an expected call of Load. +func (mr *RedisClientMockRecorder) Load(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Load", reflect.TypeOf((*RedisClient)(nil).Load), arg0, arg1) +} + +// LoadMany mocks base method. +func (m *RedisClient) LoadMany(arg0 context.Context, arg1 ...string) ([]string, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "LoadMany", varargs...) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LoadMany indicates an expected call of LoadMany. +func (mr *RedisClientMockRecorder) LoadMany(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadMany", reflect.TypeOf((*RedisClient)(nil).LoadMany), varargs...) +} + +// Store mocks base method. +func (m *RedisClient) Store(arg0 context.Context, arg1 string, arg2 interface{}, arg3 time.Duration) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Store", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// Store indicates an expected call of Store. +func (mr *RedisClientMockRecorder) Store(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*RedisClient)(nil).Store), arg0, arg1, arg2, arg3) +} diff --git a/pkg/thumbnail/utils.go b/pkg/thumbnail/utils.go index c4c10d2f..f1c90a0d 100644 --- a/pkg/thumbnail/utils.go +++ b/pkg/thumbnail/utils.go @@ -13,6 +13,8 @@ import ( "github.com/ViBiOh/vith/pkg/model" ) +//go:generate mockgen -destination ../mocks/cache.go -package mocks -mock_names RedisClient=RedisClient github.com/ViBiOh/httputils/v4/pkg/cache RedisClient + var redisCacheDuration = time.Hour * 96 func (a App) CanHaveThumbnail(item absto.Item) bool { diff --git a/pkg/thumbnail/utils_test.go b/pkg/thumbnail/utils_test.go index 285f4705..ea84cabe 100644 --- a/pkg/thumbnail/utils_test.go +++ b/pkg/thumbnail/utils_test.go @@ -84,13 +84,15 @@ func TestHasThumbnail(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - storageMock := mocks.NewStorage(ctrl) + mockStorage := mocks.NewStorage(ctrl) + mockRedisClient := mocks.NewRedisClient(ctrl) - tc.instance.storageApp = storageMock - tc.instance.cacheApp = cache.New(nil, nil, storageMock.Info, 0, 0, nil) + tc.instance.storageApp = mockStorage + tc.instance.cacheApp = cache.New(mockRedisClient, nil, mockStorage.Info, 0, 0, nil) if intention == "found" { - storageMock.EXPECT().Info(gomock.Any(), gomock.Any()).Return(absto.Item{}, nil) + mockRedisClient.EXPECT().Enabled().Return(false) + mockStorage.EXPECT().Info(gomock.Any(), gomock.Any()).Return(absto.Item{}, nil) } if result := tc.instance.HasThumbnail(context.TODO(), tc.input, SmallSize); result != tc.want {