Web: Client: added error media component
This commit is contained in:
parent
321298d809
commit
fa650bad45
@ -679,6 +679,7 @@ class MediaViewer extends React.Component {
|
||||
audioIcon={audioIcon}
|
||||
onDownloadClick={this.onDownload}
|
||||
archiveRoom={archiveRoom}
|
||||
errorTitle={t("Files:MediaError")}
|
||||
// isFavoritesFolder={isFavoritesFolder}
|
||||
/>
|
||||
)}
|
||||
|
@ -178,6 +178,7 @@ class ImageViewer extends React.Component {
|
||||
onClose,
|
||||
userAccess,
|
||||
title,
|
||||
errorTitle,
|
||||
onPrevClick,
|
||||
onNextClick,
|
||||
playlist,
|
||||
@ -374,6 +375,7 @@ class ImageViewer extends React.Component {
|
||||
visible={visible}
|
||||
zoomSpeed={0.25}
|
||||
title={title}
|
||||
errorTitle={errorTitle}
|
||||
contextModel={contextModel}
|
||||
generateContextMenu={generateContextMenu}
|
||||
isImage={isImage}
|
||||
|
@ -49,6 +49,7 @@ export const Viewer = (props) => {
|
||||
const [container, setContainer] = React.useState(props.container);
|
||||
const [panelVisible, setPanelVisible] = React.useState(true);
|
||||
const [isOpenContextMenu, setIsOpenContextMenu] = React.useState(false);
|
||||
const [isError, setIsError] = React.useState(false);
|
||||
const [isPlay, setIsPlay] = React.useState(null);
|
||||
const [globalTimer, setGlobalTimer] = React.useState(null);
|
||||
const [init, setInit] = React.useState(false);
|
||||
@ -138,7 +139,7 @@ export const Viewer = (props) => {
|
||||
<Text fontSize="14px" color={"#fff"} className="title">
|
||||
{title}
|
||||
</Text>
|
||||
{!props.isPreviewFile && (
|
||||
{!props.isPreviewFile && !isError && (
|
||||
<div className="details-context">
|
||||
<MediaContextMenu
|
||||
className="mobile-context"
|
||||
@ -196,6 +197,7 @@ export const Viewer = (props) => {
|
||||
setPanelVisible={setPanelVisible}
|
||||
generateContextMenu={generateContextMenu}
|
||||
setIsFullScreen={setIsFullScreen}
|
||||
setIsError={setIsError}
|
||||
videoRef={videoElement}
|
||||
video={playlist[playlistPos]}
|
||||
activeIndex={playlistPos}
|
||||
|
93
packages/components/viewer/sub-components/media-error.js
Normal file
93
packages/components/viewer/sub-components/media-error.js
Normal file
@ -0,0 +1,93 @@
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import Text from "@docspace/components/text";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
const StyledMediaError = styled.div`
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
width: ${(props) => props.width + "px"};
|
||||
height: 56px;
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
z-index: 1006;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 20px;
|
||||
`;
|
||||
|
||||
const StyledErrorToolbar = styled.div`
|
||||
padding: 10px 24px;
|
||||
bottom: 24px;
|
||||
z-index: 1006;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 18px;
|
||||
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.toolbar-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MediaError = ({
|
||||
width,
|
||||
height,
|
||||
onMaskClick,
|
||||
model,
|
||||
errorTitle,
|
||||
}) => {
|
||||
let errorLeft = (window.innerWidth - width) / 2 + "px";
|
||||
let errorTop = (window.innerHeight - height) / 2 + "px";
|
||||
|
||||
const items = !isMobileOnly
|
||||
? model.filter((el) => el.key !== "rename")
|
||||
: model.filter((el) => el.key === "delete" || el.key === "download");
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledMediaError
|
||||
width={width}
|
||||
height={height}
|
||||
style={{
|
||||
left: `${errorLeft}`,
|
||||
top: `${errorTop}`,
|
||||
}}
|
||||
>
|
||||
<Text fontSize="15px" color={"#fff"} className="title">
|
||||
{errorTitle}
|
||||
</Text>
|
||||
</StyledMediaError>
|
||||
|
||||
<StyledErrorToolbar>
|
||||
{items.map((item) => {
|
||||
if (item.disabled) return;
|
||||
|
||||
const onClick = () => {
|
||||
onMaskClick();
|
||||
item.onClick && item.onClick();
|
||||
};
|
||||
return (
|
||||
<div className="toolbar-item" key={item.key} onClick={onClick}>
|
||||
<ReactSVG src={item.icon} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</StyledErrorToolbar>
|
||||
</>
|
||||
);
|
||||
};
|
@ -22,6 +22,7 @@ import Icon2x from "PUBLIC_DIR/images/media.viewer2x.react.svg";
|
||||
|
||||
import BigIconPlay from "PUBLIC_DIR/images/media.bgplay.react.svg";
|
||||
import { useSwipeable } from "../../react-swipeable";
|
||||
import { MediaError } from "./media-error";
|
||||
|
||||
let iconWidth = 80;
|
||||
let iconHeight = 60;
|
||||
@ -39,6 +40,10 @@ function createAction(type, payload) {
|
||||
}
|
||||
|
||||
const StyledVideoPlayer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:focus-visible,
|
||||
#videoPlayer:focus-visible {
|
||||
outline: none;
|
||||
@ -357,6 +362,8 @@ export default function ViewerPlayer(props) {
|
||||
setIsOpenContextMenu,
|
||||
contextModel,
|
||||
onDownloadClick,
|
||||
errorTitle,
|
||||
setIsError,
|
||||
} = props;
|
||||
|
||||
const localStorageVolume = localStorage.getItem("player-volume");
|
||||
@ -387,6 +394,7 @@ export default function ViewerPlayer(props) {
|
||||
isFirstTap: false,
|
||||
isSecondTap: false,
|
||||
isFirstStart: true,
|
||||
loadingError: false,
|
||||
};
|
||||
function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
@ -409,7 +417,6 @@ export default function ViewerPlayer(props) {
|
||||
|
||||
const inputRef = React.useRef(null);
|
||||
const volumeRef = React.useRef(null);
|
||||
// const videoWrapperRef = React.useRef(null);
|
||||
const actionRef = React.useRef(null);
|
||||
const mobileProgressRef = React.useRef(null);
|
||||
|
||||
@ -728,10 +735,6 @@ export default function ViewerPlayer(props) {
|
||||
|
||||
const lasting = `${currentTime} / ${duration}`;
|
||||
|
||||
// if (progress === 100) {
|
||||
// props.setIsPlay(false);
|
||||
// }
|
||||
|
||||
dispatch(
|
||||
createAction(ACTION_TYPES.update, {
|
||||
duration: lasting,
|
||||
@ -829,6 +832,19 @@ export default function ViewerPlayer(props) {
|
||||
state.isPlaying ? videoRef.current.play() : videoRef.current.pause();
|
||||
}, [state.isPlaying, videoRef.current]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (videoRef && videoRef.current) {
|
||||
videoRef.current.addEventListener("error", (event) => {
|
||||
setIsError(true);
|
||||
return dispatch(
|
||||
createAction(ACTION_TYPES.update, {
|
||||
loadingError: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}, [videoRef.current]);
|
||||
|
||||
function loadVideo(video) {
|
||||
const currentTime = getDuration(video.currentTime);
|
||||
const duration = getDuration(video.duration);
|
||||
@ -862,6 +878,7 @@ export default function ViewerPlayer(props) {
|
||||
isFirstTap: false,
|
||||
isSecondTap: false,
|
||||
isFirstStart: true,
|
||||
loadingError: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -986,7 +1003,7 @@ translateX(${state.left !== null ? state.left + "px" : "auto"}) translateY(${
|
||||
playsInline
|
||||
></video>
|
||||
</div>
|
||||
{!state.isPlaying && displayUI && !isAudio && (
|
||||
{!state.isPlaying && displayUI && !isAudio && !state.loadingError && (
|
||||
<div
|
||||
className="bg-play"
|
||||
style={{
|
||||
@ -1027,118 +1044,128 @@ translateX(${state.left !== null ? state.left + "px" : "auto"}) translateY(${
|
||||
)}
|
||||
</div>
|
||||
|
||||
<StyledVideoControls
|
||||
ref={videoControls}
|
||||
style={{ opacity: `${displayUI ? "1" : "0"}` }}
|
||||
>
|
||||
<div className="mobile-video-progress" ref={mobileProgressRef}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={state.progress ? state.progress : "0"}
|
||||
onChange={(e) => handleVideoProgress(e)}
|
||||
/>
|
||||
</div>
|
||||
<StyledVideoActions>
|
||||
<div
|
||||
className={
|
||||
isMobileOnly
|
||||
? "actions-container mobile-actions"
|
||||
: "actions-container"
|
||||
}
|
||||
ref={actionRef}
|
||||
>
|
||||
<div className="controll-box">
|
||||
<div className="controller video-play" onClick={togglePlay}>
|
||||
{!state.isPlaying ? (
|
||||
<IconPlay
|
||||
className={isAudio ? "icon-play is-audio" : "icon-play"}
|
||||
/>
|
||||
) : (
|
||||
<IconStop className="icon-stop" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<StyledDuration>
|
||||
{state.duration && state.duration}
|
||||
</StyledDuration>
|
||||
|
||||
{!isMobileOnly && (
|
||||
<div
|
||||
className={
|
||||
isAudio
|
||||
? "volume-container volume-container-audio"
|
||||
: "volume-container"
|
||||
}
|
||||
>
|
||||
{state.isMuted ? (
|
||||
<IconVolumeMuted onClick={toggleVolumeMute} />
|
||||
) : state.volume >= 50 ? (
|
||||
<IconVolumeMax onClick={toggleVolumeMute} />
|
||||
) : (
|
||||
<IconVolumeMin onClick={toggleVolumeMute} />
|
||||
)}
|
||||
|
||||
<div className="volume-wrapper">
|
||||
<input
|
||||
ref={volumeRef}
|
||||
className="volume-toolbar"
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={state.volume}
|
||||
onChange={(e) => handleVolumeUpdate(e)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="controll-box">
|
||||
<div
|
||||
className="controller dropdown-speed"
|
||||
onClick={toggleSpeedSelectionMenu}
|
||||
>
|
||||
{speedIcons[state.speedState]}
|
||||
{state.speedSelection && (
|
||||
<div className="dropdown-content">
|
||||
{SpeedButtonComponent()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!isAudio && (
|
||||
<div
|
||||
className="controller fullscreen-button"
|
||||
onClick={toggleScreen}
|
||||
>
|
||||
{!state.isFullScreen ? (
|
||||
<IconFullScreen />
|
||||
) : (
|
||||
<IconExitFullScreen />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobileOnly && !props.isPreviewFile && !hideContextMenu && (
|
||||
<div
|
||||
className="controller context-menu-wrapper"
|
||||
onClick={toggleContext}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<MediaContextMenu className="context-menu-icon" />
|
||||
{contextMenu}
|
||||
</div>
|
||||
)}
|
||||
{hideContextMenu && (
|
||||
<div className="controller" onClick={onDownloadClick}>
|
||||
<DownloadReactSvgUrl />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!state.loadingError ? (
|
||||
<StyledVideoControls
|
||||
ref={videoControls}
|
||||
style={{ opacity: `${displayUI ? "1" : "0"}` }}
|
||||
>
|
||||
<div className="mobile-video-progress" ref={mobileProgressRef}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={state.progress ? state.progress : "0"}
|
||||
onChange={(e) => handleVideoProgress(e)}
|
||||
/>
|
||||
</div>
|
||||
</StyledVideoActions>
|
||||
</StyledVideoControls>
|
||||
<StyledVideoActions>
|
||||
<div
|
||||
className={
|
||||
isMobileOnly
|
||||
? "actions-container mobile-actions"
|
||||
: "actions-container"
|
||||
}
|
||||
ref={actionRef}
|
||||
>
|
||||
<div className="controll-box">
|
||||
<div className="controller video-play" onClick={togglePlay}>
|
||||
{!state.isPlaying ? (
|
||||
<IconPlay
|
||||
className={isAudio ? "icon-play is-audio" : "icon-play"}
|
||||
/>
|
||||
) : (
|
||||
<IconStop className="icon-stop" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<StyledDuration>
|
||||
{state.duration && state.duration}
|
||||
</StyledDuration>
|
||||
|
||||
{!isMobileOnly && (
|
||||
<div
|
||||
className={
|
||||
isAudio
|
||||
? "volume-container volume-container-audio"
|
||||
: "volume-container"
|
||||
}
|
||||
>
|
||||
{state.isMuted ? (
|
||||
<IconVolumeMuted onClick={toggleVolumeMute} />
|
||||
) : state.volume >= 50 ? (
|
||||
<IconVolumeMax onClick={toggleVolumeMute} />
|
||||
) : (
|
||||
<IconVolumeMin onClick={toggleVolumeMute} />
|
||||
)}
|
||||
|
||||
<div className="volume-wrapper">
|
||||
<input
|
||||
ref={volumeRef}
|
||||
className="volume-toolbar"
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={state.volume}
|
||||
onChange={(e) => handleVolumeUpdate(e)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="controll-box">
|
||||
<div
|
||||
className="controller dropdown-speed"
|
||||
onClick={toggleSpeedSelectionMenu}
|
||||
>
|
||||
{speedIcons[state.speedState]}
|
||||
{state.speedSelection && (
|
||||
<div className="dropdown-content">
|
||||
{SpeedButtonComponent()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!isAudio && (
|
||||
<div
|
||||
className="controller fullscreen-button"
|
||||
onClick={toggleScreen}
|
||||
>
|
||||
{!state.isFullScreen ? (
|
||||
<IconFullScreen />
|
||||
) : (
|
||||
<IconExitFullScreen />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isMobileOnly && !props.isPreviewFile && !hideContextMenu && (
|
||||
<div
|
||||
className="controller context-menu-wrapper"
|
||||
onClick={toggleContext}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<MediaContextMenu className="context-menu-icon" />
|
||||
{contextMenu}
|
||||
</div>
|
||||
)}
|
||||
{hideContextMenu && (
|
||||
<div className="controller" onClick={onDownloadClick}>
|
||||
<DownloadReactSvgUrl />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</StyledVideoActions>
|
||||
</StyledVideoControls>
|
||||
) : (
|
||||
<MediaError
|
||||
width={267}
|
||||
height={56}
|
||||
onMaskClick={onMaskClick}
|
||||
model={model}
|
||||
errorTitle={errorTitle}
|
||||
/>
|
||||
)}
|
||||
</StyledVideoPlayer>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user