1
0
Fork 0
mirror of https://github.com/alangrainger/immich-public-proxy.git synced 2024-12-28 03:41:58 +00:00

Merge pull request #38 from tenekev/main

Improved layout with Isotope Masonry and the loading experience with the Immich loading stencil
This commit is contained in:
Alan Grainger 2024-12-19 09:26:46 +13:00 committed by GitHub
commit 48e1ef832d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 232 additions and 38 deletions

View file

@ -0,0 +1,12 @@
/*!
* imagesLoaded PACKAGED v5.0.0
* JavaScript is all like "You images are done yet or what?"
* MIT License
*/
!function(t,e){"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,(function(){function t(){}let e=t.prototype;return e.on=function(t,e){if(!t||!e)return this;let i=this._events=this._events||{},s=i[t]=i[t]||[];return s.includes(e)||s.push(e),this},e.once=function(t,e){if(!t||!e)return this;this.on(t,e);let i=this._onceEvents=this._onceEvents||{};return(i[t]=i[t]||{})[e]=!0,this},e.off=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;let s=i.indexOf(e);return-1!=s&&i.splice(s,1),this},e.emitEvent=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;i=i.slice(0),e=e||[];let s=this._onceEvents&&this._onceEvents[t];for(let n of i){s&&s[n]&&(this.off(t,n),delete s[n]),n.apply(this,e)}return this},e.allOff=function(){return delete this._events,delete this._onceEvents,this},t})),
/*!
* imagesLoaded v5.0.0
* JavaScript is all like "You images are done yet or what?"
* MIT License
*/
function(t,e){"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}("undefined"!=typeof window?window:this,(function(t,e){let i=t.jQuery,s=t.console;function n(t,e,o){if(!(this instanceof n))return new n(t,e,o);let r=t;var h;("string"==typeof t&&(r=document.querySelectorAll(t)),r)?(this.elements=(h=r,Array.isArray(h)?h:"object"==typeof h&&"number"==typeof h.length?[...h]:[h]),this.options={},"function"==typeof e?o=e:Object.assign(this.options,e),o&&this.on("always",o),this.getImages(),i&&(this.jqDeferred=new i.Deferred),setTimeout(this.check.bind(this))):s.error(`Bad element for imagesLoaded ${r||t}`)}n.prototype=Object.create(e.prototype),n.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)};const o=[1,9,11];n.prototype.addElementImages=function(t){"IMG"===t.nodeName&&this.addImage(t),!0===this.options.background&&this.addElementBackgroundImages(t);let{nodeType:e}=t;if(!e||!o.includes(e))return;let i=t.querySelectorAll("img");for(let t of i)this.addImage(t);if("string"==typeof this.options.background){let e=t.querySelectorAll(this.options.background);for(let t of e)this.addElementBackgroundImages(t)}};const r=/url\((['"])?(.*?)\1\)/gi;function h(t){this.img=t}function d(t,e){this.url=t,this.element=e,this.img=new Image}return n.prototype.addElementBackgroundImages=function(t){let e=getComputedStyle(t);if(!e)return;let i=r.exec(e.backgroundImage);for(;null!==i;){let s=i&&i[2];s&&this.addBackground(s,t),i=r.exec(e.backgroundImage)}},n.prototype.addImage=function(t){let e=new h(t);this.images.push(e)},n.prototype.addBackground=function(t,e){let i=new d(t,e);this.images.push(i)},n.prototype.check=function(){if(this.progressedCount=0,this.hasAnyBroken=!1,!this.images.length)return void this.complete();let t=(t,e,i)=>{setTimeout((()=>{this.progress(t,e,i)}))};this.images.forEach((function(e){e.once("progress",t),e.check()}))},n.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount===this.images.length&&this.complete(),this.options.debug&&s&&s.log(`progress: ${i}`,t,e)},n.prototype.complete=function(){let t=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){let t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},h.prototype=Object.create(e.prototype),h.prototype.check=function(){this.getIsImageComplete()?this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.img.crossOrigin&&(this.proxyImage.crossOrigin=this.img.crossOrigin),this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.proxyImage.src=this.img.currentSrc||this.img.src)},h.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},h.prototype.confirm=function(t,e){this.isLoaded=t;let{parentNode:i}=this.img,s="PICTURE"===i.nodeName?i:this.img;this.emitEvent("progress",[this,s,e])},h.prototype.handleEvent=function(t){let e="on"+t.type;this[e]&&this[e](t)},h.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},h.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},h.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype=Object.create(h.prototype),d.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url,this.getIsImageComplete()&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},d.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},n.makeJQueryPlugin=function(e){(e=e||t.jQuery)&&(i=e,i.fn.imagesLoaded=function(t,e){return new n(this,t,e).jqDeferred.promise(i(this))})},n.makeJQueryPlugin(),n}));

12
app/public/metafizzy/isotope.min.js vendored Normal file

File diff suppressed because one or more lines are too long

9
app/public/metafizzy/masonry.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,36 +1,71 @@
:root {
--gallery-width-ultrawide: 1900px;
--gallery-width-desktop: 90%;
--gallery-width-tablet: 90%;
--gallery-width-mobile: 98%;
--color-1: #191919;
--stencil-width: 150px;
}
html {
background: #191919;
background: var(--color-1);
}
#lightgallery {
max-width: var(--gallery-width-mobile);
margin: 0 auto;
}
@media screen and (min-width: 600px) {
#lightgallery {
max-width: var(--gallery-width-tablet);
}
}
@media screen and (min-width: 900px) {
#lightgallery {
max-width: var(--gallery-width-desktop);
}
}
@media screen and (min-width: 1950px) {
#lightgallery {
max-width: var(--gallery-width-ultrawide);
}
}
#lightgallery a {
position: relative;
display: inline-block;
text-decoration: none;
margin: 0;
padding: 0;
cursor: pointer;
width: 400px;
max-width: calc(50% - 6px);
margin: 3px;
overflow: hidden;
}
#lightgallery img {
max-height: 250px;
margin: 4px;
}
@media (max-width: 800px) {
@media screen and (min-width: 600px) {
#lightgallery a {
width: calc(100vw / 3 - 10px);
height: calc(100vw / 3 - 10px);
overflow: hidden;
max-width: calc(33.33333% - 10px);
margin: 5px;
}
}
#lightgallery img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
@media screen and (min-width: 900px) {
#lightgallery a {
max-width: calc(20% - 10px);
}
}
#lightgallery a img {
width: 100%;
position: relative;
display: block;
transition: transform .15s ease-in, filter .15s ease-in;
}
#lightgallery a:hover img {
transform: scale(1.1);
filter: brightness(0.7);
}
.play-icon {
position: absolute;
top: 50%;
@ -68,3 +103,41 @@ html {
#download-all {
margin: auto 4px auto auto;
}
#page-loader {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
flex-wrap: wrap;
z-index: 999999;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--color-1)
}
#stencil {
display: flex;
width: var(--stencil-width);
margin-left: auto;
margin-right: auto;
margin-top: calc(50vh - var(--stencil-width) / 2);
margin-bottom: 100vh;
place-items: center;
justify-content: center;
overflow: hidden;
visibility: hidden;
animation: 0s linear 0.3s forwards delayedVisibility, loadspin 8s linear infinite;
}
@keyframes delayedVisibility {
to {
visibility: visible;
}
}
@keyframes loadspin {
100% {
transform: rotate(360deg);
}
}

View file

@ -1,19 +1,64 @@
function initLightGallery (config = {}) {
lightGallery(document.getElementById('lightgallery'), Object.assign({
plugins: [lgZoom, lgThumbnail, lgVideo, lgFullscreen],
/*
This license key was graciously provided by LightGallery under their
GPLv3 open-source project license:
*/
licenseKey: '8FFA6495-676C4D30-8BFC54B6-4D0A6CEC'
/*
Please do not take it and use it for other projects, as it was provided
specifically for Immich Public Proxy.
/* Preloader */
const preloader = document.getElementById('page-loader');
if (preloader) {
const fadeEffect = () => {
setInterval(() => {
if (!preloader.style.opacity) { preloader.style.opacity = 1; }
if (preloader.style.opacity > 0) { preloader.style.opacity -= 0.1;}
else {
clearInterval(fadeEffect);
preloader.remove();
}
}, 20)
}
window.addEventListener('load', fadeEffect);
}
For your own projects you can use the default license key of
0000-0000-000-0000 as per their docs:
/* Init Gallery */
masonryElMixed = document.getElementById('lightgallery'),
masonryElMixed && imagesLoaded( document.getElementById('lightgallery'), function() {
https://www.lightgalleryjs.com/docs/settings/#licenseKey
*/
}, config))
masonry = new Masonry(masonryElMixed,{
itemSelector: '.grid-item',
percentPosition: true,
gutter: 0,
columnWidth: '.lg-item:first-of-type' //Taken from first item. Can put .Class as selector
})
masonry.layout()
lgallery = window.lightGallery(masonryElMixed, Object.assign({
selector: '.lg-item',
animateThumb: true,
/*
This license key was graciously provided by LightGallery under their
GPLv3 open-source project license:
*/
licenseKey: '8FFA6495-676C4D30-8BFC54B6-4D0A6CEC',
/*
Please do not take it and use it for other projects, as it was provided
specifically for Immich Public Proxy.
For your own projects you can use the default license key of
0000-0000-000-0000 as per their docs:
https://www.lightgalleryjs.com/docs/settings/#licenseKey
*/
plugins: [
lgFullscreen,
lgThumbnail,
lgVideo,
lgZoom
],
hash: true,
toggleThumb: true,
allowMediaOverlap: true,
subHtmlSelectorRelative: true,
zoomFromOrigin: true,
}), config)
})
}

View file

@ -8,6 +8,46 @@
<link type="text/css" rel="stylesheet" href="/share/static/lg/lightgallery-bundle.min.css"/>
</head>
<body>
<div id="page-loader">
<div id="stencil">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 792 792">
<style type="text/css">
.st0 {
fill: #fa2921;
}
.st1 {
fill: #ed79b5;
}
.st2 {
fill: #ffb400;
}
.st3 {
fill: #1e83f7;
}
.st4 {
fill: #18c249;
}
</style>
<g>
<path class="st0" d="M375.48,267.63c38.64,34.21,69.78,70.87,89.82,105.42c34.42-61.56,57.42-134.71,57.71-181.3
c0-0.33,0-0.63,0-0.91c0-68.94-68.77-95.77-128.01-95.77s-128.01,26.83-128.01,95.77c0,0.94,0,2.2,0,3.72
C300.01,209.24,339.15,235.47,375.48,267.63z"></path>
<path class="st1" d="M164.7,455.63c24.15-26.87,61.2-55.99,103.01-80.61c44.48-26.18,88.97-44.47,128.02-52.84
c-47.91-51.76-110.37-96.24-154.6-110.91c-0.31-0.1-0.6-0.19-0.86-0.28c-65.57-21.3-112.34,35.81-130.64,92.15
c-18.3,56.34-14.04,130.04,51.53,151.34C162.05,454.77,163.25,455.16,164.7,455.63z"></path>
<path class="st2" d="M681.07,302.19c-18.3-56.34-65.07-113.45-130.64-92.15c-0.9,0.29-2.1,0.68-3.54,1.15
c-3.75,35.93-16.6,81.27-35.96,125.76c-20.59,47.32-45.84,88.27-72.51,118c69.18,13.72,145.86,12.98,190.26-1.14
c0.31-0.1,0.6-0.2,0.86-0.28C695.11,432.22,699.37,358.52,681.07,302.19z"></path>
<path class="st3" d="M336.54,510.71c-11.15-50.39-14.8-98.36-10.7-138.08c-64.03,29.57-125.63,75.23-153.26,112.76
c-0.19,0.26-0.37,0.51-0.53,0.73c-40.52,55.78-0.66,117.91,47.27,152.72c47.92,34.82,119.33,53.54,159.86-2.24
c0.56-0.76,1.3-1.78,2.19-3.01C363.28,602.32,347.02,558.08,336.54,510.71z"></path>
<path class="st4" d="M617.57,482.52c-35.33,7.54-82.42,9.33-130.72,4.66c-51.37-4.96-98.11-16.32-134.63-32.5
c8.33,70.03,32.73,142.73,59.88,180.6c0.19,0.26,0.37,0.51,0.53,0.73c40.52,55.78,111.93,37.06,159.86,2.24
c47.92-34.82,87.79-96.95,47.27-152.72C619.2,484.77,618.46,483.75,617.57,482.52z"></path>
</g>
</svg>
</div>
</div>
<div id="header">
<% if (showTitle) { %>
<h1><%- title || 'Gallery' %></h1>
@ -27,7 +67,7 @@
<div id="lightgallery">
<% items.forEach(item => {
if (item.video) { %>
<a data-video='<%- item.video %>'
<a data-video='<%- item.video %>' class="lg-item grid-item"
<% if (item.downloadUrl) { %>
data-download-url="<%- item.downloadUrl %>"
<% } %>
@ -36,7 +76,7 @@
<div class="play-icon"></div>
</a>
<% } else { %>
<a href="<%- item.previewUrl %>"
<a href="<%- item.previewUrl %>" class="lg-item grid-item"
<% if (item.downloadUrl) { %>
data-download-url="<%- item.downloadUrl %>"
<% } %>
@ -52,6 +92,9 @@
<script src="/share/static/lg/lg-thumbnail.min.js"></script>
<script src="/share/static/lg/lg-video.min.js"></script>
<script src="/share/static/lg/lg-zoom.min.js"></script>
<script src="/share/static/metafizzy/imagesloaded.min.js"></script>
<script src="/share/static/metafizzy/isotope.min.js"></script>
<script src="/share/static/metafizzy/masonry.min.js"></script>
<script type="text/javascript">
initLightGallery(<%- JSON.stringify(lgConfig) %>) // initLightGallery imported from web.js
<% if (openItem) { %>