2023-02-18 18:30:20 +00:00
|
|
|
import ReactDOM from "react-dom";
|
2023-03-02 06:09:39 +00:00
|
|
|
import { isMobileOnly, isMobile } from "react-device-detect";
|
2023-02-18 18:30:20 +00:00
|
|
|
import React, { useRef, useState, useEffect, useCallback } from "react";
|
|
|
|
|
|
|
|
import ContextMenu from "@docspace/components/context-menu";
|
|
|
|
import ViewerPlayer from "@docspace/components/viewer/sub-components/viewer-player";
|
|
|
|
|
2023-03-02 06:09:39 +00:00
|
|
|
import { StyledViewerContainer } from "../../StyledComponents";
|
2023-02-18 18:30:20 +00:00
|
|
|
|
|
|
|
import NextButton from "../NextButton";
|
2023-03-02 06:09:39 +00:00
|
|
|
import PrevButton from "../PrevButton";
|
|
|
|
import ImageViewer from "../ImageViewer";
|
|
|
|
import MobileDetails from "../MobileDetails";
|
|
|
|
import DesktopDetails from "../DesktopDetails";
|
2023-02-18 18:30:20 +00:00
|
|
|
|
|
|
|
import type ViewerProps from "./Viewer.props";
|
|
|
|
|
|
|
|
function Viewer(props: ViewerProps) {
|
|
|
|
const timerIDRef = useRef<NodeJS.Timeout>();
|
|
|
|
|
|
|
|
const containerRef = React.useRef(document.createElement("div"));
|
|
|
|
|
|
|
|
const [panelVisible, setPanelVisible] = useState<boolean>(true);
|
|
|
|
const [isOpenContextMenu, setIsOpenContextMenu] = useState<boolean>(false);
|
|
|
|
const [isError, setIsError] = useState<boolean>(false);
|
|
|
|
const [isPlay, setIsPlay] = useState<boolean | null>(null);
|
|
|
|
|
|
|
|
const [imageTimer, setImageTimer] = useState<NodeJS.Timeout>();
|
|
|
|
|
2023-03-02 06:19:43 +00:00
|
|
|
const panelVisibleRef = useRef<boolean>(false);
|
|
|
|
|
2023-02-18 18:30:20 +00:00
|
|
|
const contextMenuRef = useRef<ContextMenu>(null);
|
|
|
|
const videoElementRef = useRef<HTMLVideoElement>(null);
|
|
|
|
|
|
|
|
const [isFullscreen, setIsFullScreen] = useState<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
|
|
document.body.appendChild(containerRef.current);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
document.body.removeChild(containerRef.current);
|
|
|
|
timerIDRef.current && clearTimeout(timerIDRef.current);
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if ((!isPlay || isOpenContextMenu) && (!props.isImage || isOpenContextMenu))
|
|
|
|
return clearTimeout(timerIDRef.current);
|
|
|
|
}, [isPlay, isOpenContextMenu, props.isImage]);
|
|
|
|
|
2023-03-02 06:09:39 +00:00
|
|
|
const resetToolbarVisibleTimer = () => {
|
2023-03-02 06:19:43 +00:00
|
|
|
if (panelVisibleRef.current) {
|
|
|
|
clearTimeout(timerIDRef.current);
|
|
|
|
timerIDRef.current = setTimeout(() => {
|
|
|
|
panelVisibleRef.current = false;
|
|
|
|
setPanelVisible(false);
|
|
|
|
}, 2500);
|
|
|
|
} else {
|
|
|
|
setPanelVisible(true);
|
|
|
|
panelVisibleRef.current = true;
|
|
|
|
|
|
|
|
timerIDRef.current = setTimeout(() => {
|
|
|
|
panelVisibleRef.current = false;
|
|
|
|
setPanelVisible(false);
|
|
|
|
}, 2500);
|
|
|
|
}
|
2023-03-02 06:09:39 +00:00
|
|
|
};
|
2023-02-20 06:50:18 +00:00
|
|
|
|
2023-03-02 06:09:39 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (isMobile) return;
|
2023-02-20 06:50:18 +00:00
|
|
|
|
2023-03-02 06:09:39 +00:00
|
|
|
document.addEventListener("mousemove", resetToolbarVisibleTimer, {
|
|
|
|
passive: true,
|
|
|
|
});
|
2023-02-20 06:50:18 +00:00
|
|
|
|
|
|
|
return () => {
|
2023-03-02 06:09:39 +00:00
|
|
|
document.removeEventListener("mousemove", resetToolbarVisibleTimer);
|
2023-02-20 06:50:18 +00:00
|
|
|
clearTimeout(timerIDRef.current);
|
|
|
|
setPanelVisible(true);
|
|
|
|
};
|
2023-02-18 18:30:20 +00:00
|
|
|
}, [setImageTimer, setPanelVisible]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
document.addEventListener("touchstart", onTouch);
|
|
|
|
|
|
|
|
return () => document.removeEventListener("touchstart", onTouch);
|
|
|
|
}, [setPanelVisible]);
|
|
|
|
|
|
|
|
const onTouch = useCallback(
|
|
|
|
(e: TouchEvent, canTouch?: boolean) => {
|
|
|
|
if (e.target === videoElementRef.current || canTouch) {
|
|
|
|
setPanelVisible((visible) => !visible);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[setPanelVisible]
|
|
|
|
);
|
|
|
|
|
|
|
|
const nextClick = () => {
|
|
|
|
clearTimeout(imageTimer);
|
|
|
|
props.onNextClick();
|
|
|
|
};
|
|
|
|
|
|
|
|
const prevClick = () => {
|
|
|
|
clearTimeout(imageTimer);
|
|
|
|
props.onPrevClick();
|
|
|
|
};
|
|
|
|
|
|
|
|
const onMobileContextMenu = useCallback(
|
|
|
|
(e: TouchEvent) => {
|
|
|
|
setIsOpenContextMenu((open) => !open);
|
|
|
|
props.onSetSelectionFile();
|
|
|
|
contextMenuRef.current?.show(e);
|
|
|
|
},
|
|
|
|
[props.onSetSelectionFile, setIsOpenContextMenu]
|
|
|
|
);
|
|
|
|
|
|
|
|
const onHide = useCallback(() => {
|
|
|
|
setIsOpenContextMenu(false);
|
|
|
|
}, [setIsOpenContextMenu]);
|
|
|
|
|
|
|
|
const mobileDetails = (
|
|
|
|
<MobileDetails
|
2023-03-02 06:09:39 +00:00
|
|
|
onHide={onHide}
|
2023-02-18 18:30:20 +00:00
|
|
|
isError={isError}
|
|
|
|
title={props.title}
|
2023-03-02 06:09:39 +00:00
|
|
|
ref={contextMenuRef}
|
2023-02-18 18:30:20 +00:00
|
|
|
icon={props.headerIcon}
|
2023-03-02 06:09:39 +00:00
|
|
|
onMaskClick={props.onMaskClick}
|
2023-02-18 18:30:20 +00:00
|
|
|
contextModel={props.contextModel}
|
|
|
|
onContextMenu={onMobileContextMenu}
|
2023-03-02 06:09:39 +00:00
|
|
|
isPreviewFile={props.isPreviewFile}
|
2023-02-18 18:30:20 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
|
|
|
const displayUI = (isMobileOnly && props.isAudio) || panelVisible;
|
|
|
|
|
|
|
|
const isNotFirstElement = props.playlistPos !== 0;
|
|
|
|
const isNotLastElement = props.playlistPos < props.playlist.length - 1;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<StyledViewerContainer visible={props.visible}>
|
2023-03-02 06:09:39 +00:00
|
|
|
{!isFullscreen && !isMobile && panelVisible && (
|
|
|
|
<DesktopDetails title={props.title} onMaskClick={props.onMaskClick} />
|
2023-02-18 18:30:20 +00:00
|
|
|
)}
|
|
|
|
|
2023-03-02 06:09:39 +00:00
|
|
|
{props.playlist.length > 1 && !isFullscreen && !isMobile && (
|
2023-02-18 18:30:20 +00:00
|
|
|
<>
|
|
|
|
{isNotFirstElement && <PrevButton prevClick={prevClick} />}
|
|
|
|
{isNotLastElement && <NextButton nextClick={nextClick} />}
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
|
|
|
|
{props.isImage
|
|
|
|
? ReactDOM.createPortal(
|
2023-03-02 06:09:39 +00:00
|
|
|
<ImageViewer
|
|
|
|
panelVisible={panelVisible}
|
|
|
|
toolbar={props.toolbar}
|
2023-03-02 14:08:58 +00:00
|
|
|
src={props.fileUrl}
|
2023-02-18 18:30:20 +00:00
|
|
|
mobileDetails={mobileDetails}
|
2023-03-02 06:09:39 +00:00
|
|
|
onMask={props.onMaskClick}
|
|
|
|
onPrev={props.onPrevClick}
|
|
|
|
onNext={props.onNextClick}
|
|
|
|
isLastImage={!isNotLastElement}
|
|
|
|
isFistImage={!isNotFirstElement}
|
2023-02-18 18:30:20 +00:00
|
|
|
setPanelVisible={setPanelVisible}
|
|
|
|
generateContextMenu={props.generateContextMenu}
|
2023-03-02 06:09:39 +00:00
|
|
|
setIsOpenContextMenu={setIsOpenContextMenu}
|
|
|
|
resetToolbarVisibleTimer={resetToolbarVisibleTimer}
|
2023-02-18 18:30:20 +00:00
|
|
|
/>,
|
|
|
|
containerRef.current
|
|
|
|
)
|
|
|
|
: (props.isVideo || props.isAudio) &&
|
|
|
|
ReactDOM.createPortal(
|
|
|
|
<ViewerPlayer
|
|
|
|
{...props}
|
|
|
|
onNextClick={nextClick}
|
|
|
|
onPrevClick={prevClick}
|
|
|
|
isAudio={props.isAudio}
|
|
|
|
audioIcon={props.audioIcon}
|
|
|
|
contextModel={props.contextModel}
|
|
|
|
mobileDetails={mobileDetails}
|
|
|
|
displayUI={displayUI}
|
|
|
|
isOpenContextMenu={isOpenContextMenu}
|
|
|
|
onTouch={onTouch}
|
|
|
|
title={props.title}
|
|
|
|
setIsPlay={setIsPlay}
|
|
|
|
setIsOpenContextMenu={setIsOpenContextMenu}
|
|
|
|
isPlay={isPlay}
|
|
|
|
onMaskClick={props.onMaskClick}
|
|
|
|
setPanelVisible={setPanelVisible}
|
|
|
|
generateContextMenu={props.generateContextMenu}
|
|
|
|
setIsFullScreen={setIsFullScreen}
|
|
|
|
setIsError={setIsError}
|
|
|
|
videoRef={videoElementRef}
|
|
|
|
video={props.playlist[props.playlistPos]}
|
|
|
|
activeIndex={props.playlistPos}
|
|
|
|
/>,
|
|
|
|
containerRef.current
|
|
|
|
)}
|
|
|
|
</StyledViewerContainer>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Viewer;
|