[FIX] PhotoSwipe crashing on show (#23499)

Co-authored-by: dougfabris <devfabris@gmail.com>
pull/23725/head
Tasso Evangelista 4 years ago committed by GitHub
parent c320e377a5
commit 7ed68465e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/ui-master/client/main.html
  2. 3
      app/ui/client/index.ts
  3. 103
      app/ui/client/views/app/photoswipe.js
  4. 4
      app/ui/client/views/app/photoswipeContent.html
  5. 158
      app/ui/client/views/app/photoswipeContent.ts
  6. 0
      app/ui/index.ts
  7. 6
      package-lock.json
  8. 1
      package.json

@ -35,7 +35,6 @@
{{/if}}
{{/unless}}
{{ CustomScriptLoggedIn }}
{{> photoswipe}}
{{/unless}}
{{else}}
{{> loading}}

@ -17,7 +17,6 @@ import './views/app/pageCustomContainer.html';
import './views/app/roomSearch.html';
import './views/app/secretURL.html';
import './views/app/userSearch.html';
import './views/app/photoswipe.html';
import './views/cmsPage';
import './views/404/roomNotFound';
import './views/app/burger';
@ -25,7 +24,7 @@ import './views/app/home';
import './views/app/roomSearch';
import './views/app/secretURL';
import './views/app/invite';
import './views/app/photoswipe';
import './views/app/photoswipeContent.ts'; // without the *.ts extension, *.html gets loaded first
import './components/icon';
import './components/table.html';
import './components/table';

@ -1,103 +0,0 @@
import { Meteor } from 'meteor/meteor';
import { Blaze } from 'meteor/blaze';
import { Template } from 'meteor/templating';
import { escapeHTML } from '@rocket.chat/string-helpers';
Meteor.startup(() => {
let currentGallery = null;
const initGallery = async (items, options) => {
Blaze.render(Template.photoswipeContent, document.body);
const [PhotoSwipeImport, PhotoSwipeUI_DefaultImport] = await Promise.all([import('photoswipe'), import('photoswipe/dist/photoswipe-ui-default'), import('photoswipe/dist/photoswipe.css')]);
if (!currentGallery) {
const PhotoSwipe = PhotoSwipeImport.default;
const PhotoSwipeUI_Default = PhotoSwipeUI_DefaultImport.default;
currentGallery = new PhotoSwipe(document.getElementById('pswp'), PhotoSwipeUI_Default, items, options);
currentGallery.listen('destroy', () => {
currentGallery = null;
});
currentGallery.init();
}
};
const defaultGalleryOptions = {
bgOpacity: 0.7,
showHideOpacity: true,
counterEl: false,
shareEl: false,
clickToCloseNonZoomable: false,
};
const createEventListenerFor = (className) => (event) => {
event.preventDefault();
event.stopPropagation();
const galleryOptions = {
...defaultGalleryOptions,
index: 0,
addCaptionHTMLFn(item, captionEl) {
captionEl.children[0].innerHTML = `${ escapeHTML(item.title) }<br/><small>${ escapeHTML(item.description) }</small>`;
return true;
},
};
const items = Array.from(document.querySelectorAll(className))
.map((element, i) => {
if (element === event.currentTarget) {
galleryOptions.index = i;
}
const item = {
src: element.src,
w: element.naturalWidth,
h: element.naturalHeight,
title: element.dataset.title || element.title,
description: element.dataset.description,
};
if (element.dataset.src || element.href) {
// use stored sizes if available
if (element.dataset.width && element.dataset.height) {
return {
...item,
h: element.dataset.height,
w: element.dataset.width,
src: element.dataset.src || element.href,
};
}
const img = new Image();
img.addEventListener('load', () => {
if (!currentGallery) {
return;
}
// stores loaded sizes on original image element
element.dataset.width = img.naturalWidth;
element.dataset.height = img.naturalHeight;
delete currentGallery.items[i].html;
currentGallery.items[i].src = img.src;
currentGallery.items[i].w = img.naturalWidth;
currentGallery.items[i].h = img.naturalHeight;
currentGallery.invalidateCurrItems();
currentGallery.updateSize(true);
});
img.src = element.dataset.src || element.href;
return {
...item,
msrc: element.src,
src: element.dataset.src || element.href,
};
}
return item;
});
initGallery(items, galleryOptions);
};
$(document).on('click', '.gallery-item', createEventListenerFor('.gallery-item'));
});

@ -1,5 +1,3 @@
<template name="photoswipe"></template>
<template name="photoswipeContent">
<div class="pswp" id="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<div class="pswp__bg"></div>
@ -36,4 +34,4 @@
</div>
</div>
</div>
</template>
</template>

@ -0,0 +1,158 @@
import { Meteor } from 'meteor/meteor';
import { Blaze } from 'meteor/blaze';
import { Template } from 'meteor/templating';
import { escapeHTML } from '@rocket.chat/string-helpers';
import type PhotoSwipe from 'photoswipe';
import type PhotoSwipeUiDefault from 'photoswipe/dist/photoswipe-ui-default';
const parseLength = (x: unknown): number | undefined => {
const length = typeof x === 'string' ? parseInt(x, 10) : undefined;
return Number.isFinite(length) ? length : undefined;
};
const getImageSize = (src: string): Promise<[w: number, h: number]> => new Promise((resolve, reject) => {
const img = new Image();
img.addEventListener('load', () => {
resolve([img.naturalWidth, img.naturalHeight]);
});
img.addEventListener('error', (error) => {
reject(error.error);
});
img.src = src;
});
type Slide = PhotoSwipeUiDefault.Item & { description?: string };
const fromElementToSlide = async (element: Element): Promise<Slide | null> => {
if (!(element instanceof HTMLElement)) {
return null;
}
const title = element.dataset.title || element.title;
const { description } = element.dataset;
if (element instanceof HTMLAnchorElement) {
const src = element.dataset.src || element.href;
let w = parseLength(element.dataset.width);
let h = parseLength(element.dataset.height);
if (w === undefined || h === undefined) {
[w, h] = await getImageSize(src);
}
return { src, w, h, title, description };
}
if (element instanceof HTMLImageElement) {
let msrc: string | undefined;
let { src } = element;
let w: number | undefined = element.naturalWidth;
let h: number | undefined = element.naturalHeight;
if (element.dataset.src) {
msrc = element.src;
src = element.dataset.src;
w = parseLength(element.dataset.width);
h = parseLength(element.dataset.height);
if (w === undefined || h === undefined) {
[w, h] = await getImageSize(src);
}
}
return { msrc, src, w, h, title, description };
}
return null;
};
let currentGallery: PhotoSwipe<PhotoSwipeUiDefault.Options> | null = null;
const initGallery = async (items: Slide[], options: PhotoSwipeUiDefault.Options): Promise<void> => {
const [
{ default: PhotoSwipe },
{ default: PhotoSwipeUiDefault }, // eslint-disable-line @typescript-eslint/camelcase
] = await Promise.all([
import('photoswipe'),
import('photoswipe/dist/photoswipe-ui-default'),
// @ts-ignore
import('photoswipe/dist/photoswipe.css'),
// @ts-ignore
import('./photoswipeContent.html'),
]);
Blaze.render(Template.photoswipeContent, document.body);
if (!currentGallery) {
const container = document.getElementById('pswp');
if (!container) {
throw new Error('Photoswipe container element not found');
}
currentGallery = new PhotoSwipe(container, PhotoSwipeUiDefault, items, options);
currentGallery.listen('destroy', () => {
currentGallery = null;
});
currentGallery.init();
}
};
const defaultGalleryOptions: PhotoSwipeUiDefault.Options = {
bgOpacity: 0.7,
showHideOpacity: true,
counterEl: false,
shareEl: false,
clickToCloseNonZoomable: false,
index: 0,
addCaptionHTMLFn(item: Slide, captionEl: HTMLElement): boolean {
captionEl.children[0].innerHTML = `
${ escapeHTML(item.title ?? '') }<br/>
<small>${ escapeHTML(item.description ?? '') }</small>
`;
return true;
},
};
const createEventListenerFor = (className: string) => (event: JQuery.ClickEvent): void => {
event.preventDefault();
event.stopPropagation();
const { currentTarget } = event;
Array.from(document.querySelectorAll(className))
.sort((a, b) => {
if (a === currentTarget) {
return -1;
}
if (b === currentTarget) {
return 1;
}
return 0;
})
.map((element) => fromElementToSlide(element))
.reduce((p, curr) => p.then(() => curr).then(async (slide) => {
if (!slide) {
return;
}
if (!currentGallery) {
return initGallery([slide], defaultGalleryOptions);
}
currentGallery.items.push(slide);
currentGallery.invalidateCurrItems();
currentGallery.updateSize(true);
}), Promise.resolve());
};
Meteor.startup(() => {
$(document).on('click', '.gallery-item', createEventListenerFor('.gallery-item'));
});

6
package-lock.json generated

@ -10161,6 +10161,12 @@
"@types/node": "*"
}
},
"@types/photoswipe": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@types/photoswipe/-/photoswipe-4.1.2.tgz",
"integrity": "sha512-HA9TtCAQKToldgxRiyJ1DbsElg/cQV/SQ8COVjqIqghjy60Zxfh78E1WiFotthquqkS86nz13Za9wEbToe0svQ==",
"dev": true
},
"@types/pretty-hrtime": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz",

@ -100,6 +100,7 @@
"@types/node-rsa": "^1.1.1",
"@types/nodemailer": "^6.4.2",
"@types/parseurl": "^1.3.1",
"@types/photoswipe": "^4.1.2",
"@types/psl": "^1.1.0",
"@types/react": "^17.0.32",
"@types/react-dom": "^17.0.10",

Loading…
Cancel
Save