Merge branch 'develop' into bugfix/spam-media-removal
This commit is contained in:
commit
44126aaf54
@ -27,6 +27,7 @@ const FilesMediaViewer = (props) => {
|
||||
setToPreviewFile,
|
||||
setScrollToItem,
|
||||
setCurrentId,
|
||||
setAlreadyFetchingRooms,
|
||||
setBufferSelection,
|
||||
isFavoritesFolder,
|
||||
archiveRoomsId,
|
||||
@ -72,6 +73,7 @@ const FilesMediaViewer = (props) => {
|
||||
fetchFiles(previewFile.folderId).finally(() => {
|
||||
setIsLoading(false);
|
||||
setFirstLoad(false);
|
||||
setAlreadyFetchingRooms(false);
|
||||
});
|
||||
}
|
||||
}, [previewFile]);
|
||||
@ -230,6 +232,7 @@ export default inject(
|
||||
isPreview,
|
||||
resetUrl,
|
||||
setSelection,
|
||||
setAlreadyFetchingRooms,
|
||||
} = filesStore;
|
||||
const {
|
||||
visible,
|
||||
@ -286,6 +289,7 @@ export default inject(
|
||||
setScrollToItem,
|
||||
setCurrentId,
|
||||
setBufferSelection,
|
||||
setAlreadyFetchingRooms,
|
||||
isFavoritesFolder,
|
||||
onClickFavorite,
|
||||
onClickDownloadAs,
|
||||
|
@ -55,6 +55,7 @@ let timeout = null,
|
||||
CancelToken,
|
||||
source;
|
||||
|
||||
const backUrl = window.location.origin;
|
||||
const PriceCalculation = ({
|
||||
t,
|
||||
user,
|
||||
@ -71,13 +72,19 @@ const PriceCalculation = ({
|
||||
currencySymbol,
|
||||
isAlreadyPaid,
|
||||
isFreeAfterPaidPeriod,
|
||||
setStartPaymentLink,
|
||||
managersCount,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
useEffect(async () => {
|
||||
initializeInfo();
|
||||
|
||||
!isAlreadyPaid && setStartPaymentLink(t);
|
||||
if (isAlreadyPaid) return;
|
||||
|
||||
try {
|
||||
const link = await getPaymentLink(managersCount, source?.token, backUrl);
|
||||
setPaymentLink(link);
|
||||
} catch (e) {
|
||||
toastr.error(t("ErrorNotification"));
|
||||
}
|
||||
|
||||
return () => {
|
||||
timeout && clearTimeout(timeout);
|
||||
@ -103,7 +110,7 @@ const PriceCalculation = ({
|
||||
CancelToken = axios.CancelToken;
|
||||
source = CancelToken.source();
|
||||
|
||||
await getPaymentLink(value, source.token)
|
||||
await getPaymentLink(value, source.token, backUrl)
|
||||
.then((link) => {
|
||||
setPaymentLink(link);
|
||||
setIsLoading(false);
|
||||
@ -207,7 +214,6 @@ export default inject(({ auth, payments }) => {
|
||||
setManagersCount,
|
||||
maxAvailableManagersCount,
|
||||
initializeInfo,
|
||||
setStartPaymentLink,
|
||||
managersCount,
|
||||
} = payments;
|
||||
const { theme } = auth.settingsStore;
|
||||
@ -224,7 +230,7 @@ export default inject(({ auth, payments }) => {
|
||||
|
||||
return {
|
||||
managersCount,
|
||||
setStartPaymentLink,
|
||||
|
||||
isFreeTariff,
|
||||
setManagersCount,
|
||||
tariffsInfo,
|
||||
|
@ -140,15 +140,6 @@ class PaymentStore {
|
||||
}
|
||||
};
|
||||
|
||||
setStartPaymentLink = async (t) => {
|
||||
try {
|
||||
const link = await api.portal.getPaymentLink(this.managersCount);
|
||||
this.setPaymentLink(link);
|
||||
} catch (e) {
|
||||
toastr.error(t("ErrorNotification"));
|
||||
}
|
||||
};
|
||||
|
||||
setTotalPrice = (value) => {
|
||||
const price = this.getTotalCostByFormula(value);
|
||||
if (price !== this.totalPrice) this.totalPrice = price;
|
||||
|
@ -239,12 +239,13 @@ export function getPaymentAccount() {
|
||||
return request({ method: "get", url: "/portal/payment/account" });
|
||||
}
|
||||
|
||||
export function getPaymentLink(adminCount, cancelToken) {
|
||||
export function getPaymentLink(adminCount, cancelToken, backUrl) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/portal/payment/url`,
|
||||
data: {
|
||||
quantity: { admin: adminCount },
|
||||
backUrl,
|
||||
},
|
||||
cancelToken,
|
||||
});
|
||||
|
@ -23,7 +23,7 @@ export interface MediaViewerProps {
|
||||
|
||||
playlistPos: number;
|
||||
|
||||
getIcon: (size: number, ext: string, ...arg: any) => any;
|
||||
getIcon: (size: number, ext: string, ...arg: any) => string;
|
||||
|
||||
onClose: VoidFunction;
|
||||
onError?: VoidFunction;
|
||||
|
@ -0,0 +1,112 @@
|
||||
import React from "react";
|
||||
|
||||
import MediaZoomInIcon from "PUBLIC_DIR/images/media.zoomin.react.svg";
|
||||
import MediaZoomOutIcon from "PUBLIC_DIR/images/media.zoomout.react.svg";
|
||||
import MediaRotateLeftIcon from "PUBLIC_DIR/images/media.rotateleft.react.svg";
|
||||
import MediaRotateRightIcon from "PUBLIC_DIR/images/media.rotateright.react.svg";
|
||||
import MediaDeleteIcon from "PUBLIC_DIR/images/media.delete.react.svg";
|
||||
import MediaDownloadIcon from "PUBLIC_DIR/images/download.react.svg";
|
||||
import MediaFavoriteIcon from "PUBLIC_DIR/images/favorite.react.svg";
|
||||
import ViewerSeparator from "PUBLIC_DIR/images/viewer.separator.react.svg";
|
||||
|
||||
export const getCustomToolbar = (
|
||||
onDeleteClick: VoidFunction,
|
||||
onDownloadClick: VoidFunction
|
||||
) => {
|
||||
return [
|
||||
{
|
||||
key: "zoomOut",
|
||||
percent: true,
|
||||
actionType: 2,
|
||||
render: (
|
||||
<div className="iconContainer zoomOut">
|
||||
<MediaZoomOutIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "percent",
|
||||
actionType: 999,
|
||||
},
|
||||
{
|
||||
key: "zoomIn",
|
||||
actionType: 1,
|
||||
render: (
|
||||
<div className="iconContainer zoomIn">
|
||||
<MediaZoomInIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "rotateLeft",
|
||||
actionType: 5,
|
||||
render: (
|
||||
<div className="iconContainer rotateLeft">
|
||||
<MediaRotateLeftIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "rotateRight",
|
||||
actionType: 6,
|
||||
render: (
|
||||
<div className="iconContainer rotateRight">
|
||||
<MediaRotateRightIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "separator download-separator",
|
||||
actionType: -1,
|
||||
noHover: true,
|
||||
render: (
|
||||
<div className="separator" style={{ height: "16px" }}>
|
||||
<ViewerSeparator size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "download",
|
||||
actionType: 102,
|
||||
render: (
|
||||
<div className="iconContainer download" style={{ height: "16px" }}>
|
||||
<MediaDownloadIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
onClick: onDownloadClick,
|
||||
},
|
||||
{
|
||||
key: "context-separator",
|
||||
actionType: -1,
|
||||
noHover: true,
|
||||
render: (
|
||||
<div className="separator" style={{ height: "16px" }}>
|
||||
<ViewerSeparator size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "context-menu",
|
||||
actionType: -1,
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
actionType: 103,
|
||||
render: (
|
||||
<div className="iconContainer viewer-delete">
|
||||
<MediaDeleteIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
onClick: onDeleteClick,
|
||||
},
|
||||
{
|
||||
key: "favorite",
|
||||
actionType: 104,
|
||||
render: (
|
||||
<div className="iconContainer viewer-favorite">
|
||||
<MediaFavoriteIcon size="scale" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
@ -1,4 +1,9 @@
|
||||
import { NullOrUndefined, PlaylistType } from "../types";
|
||||
import {
|
||||
ContextMenuModel,
|
||||
NullOrUndefined,
|
||||
PlaylistType,
|
||||
SeparatorType,
|
||||
} from "../types";
|
||||
|
||||
export const mediaTypes = Object.freeze({
|
||||
audio: 1,
|
||||
@ -57,3 +62,7 @@ export const findNearestIndex = (
|
||||
}
|
||||
return found;
|
||||
};
|
||||
|
||||
export const isSeparator = (arg: ContextMenuModel): arg is SeparatorType => {
|
||||
return arg?.isSeparator;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import React, { useState, useCallback, useMemo, useEffect } from "react";
|
||||
|
||||
import ImageViewer from "./sub-components/image-viewer";
|
||||
import ViewerWrapper from "./sub-components/ViewerWrapper";
|
||||
|
||||
import { MediaViewerProps } from "./MediaViewer.props";
|
||||
import { FileStatus } from "@docspace/common/constants";
|
||||
@ -432,7 +432,7 @@ function MediaViewer({
|
||||
return (
|
||||
<>
|
||||
{canOpen && (
|
||||
<ImageViewer
|
||||
<ViewerWrapper
|
||||
userAccess={props.userAccess}
|
||||
visible={props.visible}
|
||||
title={title}
|
||||
|
@ -0,0 +1,6 @@
|
||||
import styled from "styled-components";
|
||||
import DropDown from "@docspace/components/drop-down";
|
||||
|
||||
export const StyledDropDown = styled(DropDown)`
|
||||
background: #333;
|
||||
`;
|
@ -0,0 +1,16 @@
|
||||
import styled from "styled-components";
|
||||
import DropDownItem from "@docspace/components/drop-down-item";
|
||||
|
||||
export const StyledDropDownItem = styled(DropDownItem)`
|
||||
color: #fff;
|
||||
|
||||
.drop-down-item_icon svg {
|
||||
path {
|
||||
fill: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #444;
|
||||
}
|
||||
`;
|
@ -0,0 +1,33 @@
|
||||
import { ContextMenuModel, PlaylistType } from "../../types";
|
||||
|
||||
interface ViewerWrapperProps {
|
||||
userAccess: boolean;
|
||||
visible: boolean;
|
||||
title: string;
|
||||
images: { src: string; alt: string }[];
|
||||
inactive: boolean;
|
||||
playlist: PlaylistType[];
|
||||
playlistPos: number;
|
||||
|
||||
isFavorite: boolean;
|
||||
isImage: boolean;
|
||||
isAudio: boolean;
|
||||
isVideo: boolean;
|
||||
isPreviewFile: boolean;
|
||||
|
||||
archiveRoom: boolean;
|
||||
|
||||
errorTitle: string;
|
||||
headerIcon: string;
|
||||
audioIcon: string;
|
||||
|
||||
onClose: VoidFunction;
|
||||
onPrevClick: VoidFunction;
|
||||
onNextClick: VoidFunction;
|
||||
onDeleteClick: VoidFunction;
|
||||
onDownloadClick: VoidFunction;
|
||||
onSetSelectionFile: VoidFunction;
|
||||
contextModel: () => ContextMenuModel[];
|
||||
}
|
||||
|
||||
export default ViewerWrapperProps;
|
@ -0,0 +1,120 @@
|
||||
import React, { useMemo, memo, useCallback } from "react";
|
||||
import equal from "fast-deep-equal/react";
|
||||
|
||||
import { Viewer } from "@docspace/components/viewer";
|
||||
import { isSeparator } from "../../helpers";
|
||||
import { getCustomToolbar } from "../../helpers/getCustomToolbar";
|
||||
import { ContextMenuModel } from "../../types";
|
||||
|
||||
import { StyledDropDown } from "../StyledDropDown";
|
||||
import { StyledDropDownItem } from "../StyledDropDownItem";
|
||||
import ViewerWrapperProps from "./ViewerWrapper.props";
|
||||
|
||||
const DefaultSpeedZoom = 0.25;
|
||||
|
||||
function ViewerWrapper(props: ViewerWrapperProps) {
|
||||
const onClickContextItem = useCallback(
|
||||
(item: ContextMenuModel) => {
|
||||
if (isSeparator(item)) return;
|
||||
item.onClick();
|
||||
props.onClose();
|
||||
},
|
||||
[props.onClose]
|
||||
);
|
||||
|
||||
const generateContextMenu = (
|
||||
isOpen: boolean,
|
||||
right: string,
|
||||
bottom: string
|
||||
) => {
|
||||
const model = props.contextModel();
|
||||
|
||||
return (
|
||||
<StyledDropDown
|
||||
open={isOpen}
|
||||
isDefaultMode={false}
|
||||
directionY="top"
|
||||
directionX="right"
|
||||
fixedDirection={true}
|
||||
withBackdrop={false}
|
||||
manualY={(bottom || "63") + "px"}
|
||||
manualX={(right || "-31") + "px"}
|
||||
>
|
||||
{model.map((item) => {
|
||||
if (item.disabled) return;
|
||||
const isItemSeparator = isSeparator(item);
|
||||
|
||||
return (
|
||||
<StyledDropDownItem
|
||||
className={`${item.isSeparator ? "is-separator" : ""}`}
|
||||
key={item.key}
|
||||
label={isItemSeparator ? undefined : item.label}
|
||||
icon={!isItemSeparator && item.icon ? item.icon : ""}
|
||||
onClick={() => onClickContextItem(item)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledDropDown>
|
||||
);
|
||||
};
|
||||
|
||||
const toolbars = useMemo(() => {
|
||||
const {
|
||||
onDeleteClick,
|
||||
onDownloadClick,
|
||||
playlist,
|
||||
playlistPos,
|
||||
userAccess,
|
||||
} = props;
|
||||
|
||||
const customToolbar = getCustomToolbar(onDeleteClick, onDownloadClick);
|
||||
|
||||
const canShare = playlist[playlistPos].canShare;
|
||||
const toolbars =
|
||||
!canShare && userAccess
|
||||
? customToolbar.filter(
|
||||
(x) => x.key !== "share" && x.key !== "share-separator"
|
||||
)
|
||||
: customToolbar.filter((x) => x.key !== "delete");
|
||||
|
||||
return toolbars;
|
||||
}, [
|
||||
props.onDeleteClick,
|
||||
props.onDownloadClick,
|
||||
props.playlist,
|
||||
props.playlistPos,
|
||||
props.userAccess,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Viewer
|
||||
title={props.title}
|
||||
images={props.images}
|
||||
isAudio={props.isAudio}
|
||||
isVideo={props.isVideo}
|
||||
visible={props.visible}
|
||||
isImage={props.isImage}
|
||||
playlist={props.playlist}
|
||||
inactive={props.inactive}
|
||||
audioIcon={props.audioIcon}
|
||||
zoomSpeed={DefaultSpeedZoom}
|
||||
errorTitle={props.errorTitle}
|
||||
headerIcon={props.headerIcon}
|
||||
customToolbar={() => toolbars}
|
||||
playlistPos={props.playlistPos}
|
||||
archiveRoom={props.archiveRoom}
|
||||
isPreviewFile={props.isPreviewFile}
|
||||
onMaskClick={props.onClose}
|
||||
onNextClick={props.onNextClick}
|
||||
onPrevClick={props.onPrevClick}
|
||||
contextModel={props.contextModel}
|
||||
onDownloadClick={props.onDownloadClick}
|
||||
generateContextMenu={generateContextMenu}
|
||||
onSetSelectionFile={props.onSetSelectionFile}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(ViewerWrapper, (prevProps, nextProps) =>
|
||||
equal(prevProps, nextProps)
|
||||
);
|
@ -87,3 +87,21 @@ export interface IFile {
|
||||
viewUrl: string;
|
||||
webUrl: string;
|
||||
}
|
||||
|
||||
export type ContextMenuType = {
|
||||
id?: string;
|
||||
key: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
disabled: boolean;
|
||||
onClick: VoidFunction;
|
||||
isSeparator?: undefined;
|
||||
};
|
||||
|
||||
export type SeparatorType = {
|
||||
key: string;
|
||||
isSeparator: boolean;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
export type ContextMenuModel = ContextMenuType | SeparatorType;
|
||||
|
@ -80,12 +80,12 @@ export const Viewer = (props) => {
|
||||
};
|
||||
}
|
||||
return () => document.removeEventListener("touchstart", onTouch);
|
||||
}, [isPlay, isOpenContextMenu]);
|
||||
}, [isPlay, isOpenContextMenu, isImage]);
|
||||
|
||||
function resetTimer() {
|
||||
setPanelVisible(true);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => setPanelVisible(false), 5000);
|
||||
timer = setTimeout(() => setPanelVisible(false), 2500);
|
||||
setImageTimer(timer);
|
||||
}
|
||||
|
||||
|
@ -240,6 +240,10 @@ const StyledMobileDetails = styled.div`
|
||||
.title {
|
||||
font-weight: 600;
|
||||
margin-top: 6px;
|
||||
width: calc(100% - 100px);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -907,7 +907,7 @@ export default function ViewerPlayer(props) {
|
||||
}
|
||||
if (isMobileOnly && videoRef.current && displayUI) {
|
||||
clearTimeout(globalTimer);
|
||||
setGlobalTimer(setTimeout(() => setPanelVisible(false), 5000));
|
||||
setGlobalTimer(setTimeout(() => setPanelVisible(false), 2500));
|
||||
}
|
||||
}, [displayUI, isOpenContextMenu, state.isControlTouch, props.isPlay]);
|
||||
|
||||
|
@ -44,6 +44,7 @@ public class FilesChunkedUploadSessionHolder : CommonChunkedUploadSessionHolder
|
||||
private async Task<T> InternalFinalizeAsync<T>(CommonChunkedUploadSession commonChunkedUploadSession)
|
||||
{
|
||||
var chunkedUploadSession = commonChunkedUploadSession as ChunkedUploadSession<T>;
|
||||
chunkedUploadSession.BytesTotal = chunkedUploadSession.BytesUploaded;
|
||||
var fileDao = GetFileDao<T>();
|
||||
var file = await fileDao.FinalizeUploadSessionAsync(chunkedUploadSession);
|
||||
return file.Id;
|
||||
|
Loading…
Reference in New Issue
Block a user