// tslint:disable:react-a11y-anchors import React, { useEffect, useRef } from 'react'; import is from '@sindresorhus/is'; import * as GoogleChrome from '../util/GoogleChrome'; import * as MIME from '../types/MIME'; import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon'; import { Flex } from './basic/Flex'; import { DefaultTheme } from 'styled-components'; // useCss has some issues on our setup. so import it directly // tslint:disable-next-line: no-submodule-imports import useUnmount from 'react-use/lib/useUnmount'; import { useEncryptedFileFetch } from '../hooks/useEncryptedFileFetch'; import { darkTheme } from '../state/ducks/SessionTheme'; const Colors = { TEXT_SECONDARY: '#bbb', ICON_SECONDARY: '#ccc', }; const colorSVG = (url: string, color: string) => { return { WebkitMask: `url(${url}) no-repeat center`, WebkitMaskSize: '100%', backgroundColor: color, }; }; type Props = { close: () => void; contentType: MIME.MIMEType | undefined; objectURL: string; caption?: string; onNext?: () => void; onPrevious?: () => void; onSave?: () => void; }; const CONTROLS_WIDTH = 50; const CONTROLS_SPACING = 10; const styles = { container: { display: 'flex', flexDirection: 'column', position: 'fixed', width: '100vw', height: '100vh', left: 0, zIndex: 5, right: 0, top: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.8)', } as React.CSSProperties, mainContainer: { display: 'flex', flexDirection: 'row', flexGrow: 1, paddingTop: 40, paddingLeft: 40, paddingRight: 40, paddingBottom: 0, minHeight: 0, overflow: 'hidden', minWidth: 0, } as React.CSSProperties, objectContainer: { position: 'relative', flexGrow: 1, display: 'inline-flex', justifyContent: 'center', } as React.CSSProperties, objectParentContainer: { flexGrow: 1, textAlign: 'center' as 'center', margin: 'auto', }, object: { flexGrow: 1, flexShrink: 0, maxWidth: '80vw', maxHeight: '80vh', objectFit: 'contain', } as React.CSSProperties, caption: { position: 'absolute', bottom: 0, left: 0, right: 0, textAlign: 'center', color: 'black', padding: '1em', paddingLeft: '3em', paddingRight: '3em', backgroundColor: 'rgba(192, 192, 192, .40)', } as React.CSSProperties, controlsOffsetPlaceholder: { width: CONTROLS_WIDTH, marginRight: CONTROLS_SPACING, flexShrink: 0, }, controls: { width: CONTROLS_WIDTH, flexShrink: 0, display: 'flex', flexDirection: 'column', marginLeft: CONTROLS_SPACING, } as React.CSSProperties, navigationContainer: { flexShrink: 0, display: 'flex', flexDirection: 'row', justifyContent: 'center', padding: 10, } as React.CSSProperties, saveButton: { marginTop: 10, }, iconButtonPlaceholder: { // Dimensions match `.iconButton`: display: 'inline-block', width: 50, height: 50, }, }; interface IconButtonProps { onClick?: () => void; style?: React.CSSProperties; type: 'save' | 'close' | 'previous' | 'next'; theme: DefaultTheme; } const IconButton = ({ onClick, type, theme }: IconButtonProps) => { const clickHandler = (event: React.MouseEvent): void => { if (!onClick) { return; } onClick(); }; let iconRotation = 0; let iconType = SessionIconType.Chevron; switch (type) { case 'next': iconRotation = 270; break; case 'previous': iconRotation = 90; break; case 'close': iconType = SessionIconType.Exit; break; case 'save': iconType = SessionIconType.Upload; iconRotation = 180; break; default: throw new TypeError(`Invalid button type: ${type}`); } return ( ); }; const IconButtonPlaceholder = () =>
; const Icon = ({ onClick, url, }: { onClick?: (event: React.MouseEvent) => void; url: string; }) => (
); export const LightboxObject = ({ objectURL, contentType, videoRef, onObjectClick, }: { objectURL: string; contentType: MIME.MIMEType; videoRef: React.MutableRefObject; onObjectClick: (event: any) => any; }) => { const { urlToLoad } = useEncryptedFileFetch(objectURL, contentType); const isImageTypeSupported = GoogleChrome.isImageTypeSupported(contentType); const playVideo = () => { if (!videoRef) { return; } const { current } = videoRef; if (!current) { return; } if (current.paused) { void current.play(); } else { current.pause(); } }; const pauseVideo = () => { if (!videoRef) { return; } const { current } = videoRef; if (current) { current.pause(); } }; // auto play video on showing a video attachment useUnmount(() => { pauseVideo(); }); if (isImageTypeSupported) { return {window.i18n('lightboxImageAlt')}; } const isVideoTypeSupported = GoogleChrome.isVideoTypeSupported(contentType); if (isVideoTypeSupported) { if (urlToLoad) { playVideo(); } return ( ); } const isUnsupportedImageType = !isImageTypeSupported && MIME.isImage(contentType); const isUnsupportedVideoType = !isVideoTypeSupported && MIME.isVideo(contentType); if (isUnsupportedImageType || isUnsupportedVideoType) { const iconUrl = isUnsupportedVideoType ? 'images/video.svg' : 'images/image.svg'; return ; } // tslint:disable-next-line no-console console.log('Lightbox: Unexpected content type', { contentType }); return ; }; export const Lightbox = (props: Props) => { const videoRef = useRef(null); const containerRef = useRef(null); // there is no theme in use on the lightbox const theme = darkTheme; const { caption, contentType, objectURL, onNext, onPrevious, onSave } = props; const onObjectClick = (event: any) => { event.stopPropagation(); props.close?.(); }; const onContainerClick = (event: React.MouseEvent) => { if (containerRef && event.target !== containerRef.current) { return; } props.close?.(); }; return (
{!is.undefined(contentType) ? ( ) : null} {caption ?
{caption}
: null}
{ props.close?.(); }} theme={theme} /> {onSave ? ( ) : null}
{onPrevious ? ( ) : ( )} {onNext ? ( ) : ( )}
); };