Web: Files: refactoring FilesRowContent, withContentActions

This commit is contained in:
Artem Tarasov 2021-04-22 16:00:19 +03:00
parent 0131520988
commit 6381a0c524
3 changed files with 896 additions and 822 deletions

View File

@ -1,77 +1,24 @@
import React from "react"; import React from "react";
import { withRouter } from "react-router"; import { withRouter } from "react-router";
import { Trans, withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import styled from "styled-components"; import styled from "styled-components";
import { isMobile } from "react-device-detect";
import Link from "@appserver/components/link"; import Link from "@appserver/components/link";
import Text from "@appserver/components/text"; import Text from "@appserver/components/text";
import RowContent from "@appserver/components/row-content"; import RowContent from "@appserver/components/row-content";
import IconButton from "@appserver/components/icon-button"; import IconButton from "@appserver/components/icon-button";
import Badge from "@appserver/components/badge"; import Badge from "@appserver/components/badge";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
import withContentActions from "../hoc/withContentActions";
import { import {
convertFile, StyledFavoriteIcon,
getFileConversationProgress, StyledFileActionsConvertEditDocIcon,
} from "@appserver/common/api/files"; StyledFileActionsLockedIcon,
import { } from "../sub-components/icons";
AppServerConfig,
FileAction,
ShareAccessRights,
} from "@appserver/common/constants";
import toastr from "studio/toastr";
import FavoriteIcon from "../../../../../../../public/images/favorite.react.svg";
import FileActionsConvertEditDocIcon from "../../../../../../../public/images/file.actions.convert.edit.doc.react.svg";
import FileActionsLockedIcon from "../../../../../../../public/images/file.actions.locked.react.svg";
import CheckIcon from "../../../../../../../public/images/check.react.svg";
import CrossIcon from "../../../../../../../../../../public/images/cross.react.svg";
import { TIMEOUT } from "../../../../../../helpers/constants";
import { getTitleWithoutExst } from "../../../../../../helpers/files-helpers";
import { ConvertDialog } from "../../../../../dialogs";
import EditingWrapperComponent from "../sub-components/EditingWrapperComponent";
import { isMobile } from "react-device-detect";
import { observer, inject } from "mobx-react";
import config from "../../../../../../../package.json";
import { combineUrl } from "@appserver/common/utils";
const sideColor = "#A3A9AE"; const sideColor = "#A3A9AE";
const StyledCheckIcon = styled(CheckIcon)`
${commonIconsStyles}
path {
fill: #a3a9ae;
}
:hover {
fill: #657077;
}
`;
const StyledCrossIcon = styled(CrossIcon)`
${commonIconsStyles}
path {
fill: #a3a9ae;
}
:hover {
fill: #657077;
}
`;
const StyledFavoriteIcon = styled(FavoriteIcon)`
${commonIconsStyles}
`;
const StyledFileActionsConvertEditDocIcon = styled(
FileActionsConvertEditDocIcon
)`
${commonIconsStyles}
path {
fill: #3b72a7;
}
`;
const StyledFileActionsLockedIcon = styled(FileActionsLockedIcon)`
${commonIconsStyles}
path {
fill: #3b72a7;
}
`;
const SimpleFilesRowContent = styled(RowContent)` const SimpleFilesRowContent = styled(RowContent)`
.badge-ext { .badge-ext {
margin-left: -8px; margin-left: -8px;
@ -107,427 +54,29 @@ const SimpleFilesRowContent = styled(RowContent)`
} }
`; `;
const okIcon = <StyledCheckIcon className="edit-ok-icon" size="scale" />; const FilesRowContent = ({
const cancelIcon = (
<StyledCrossIcon className="edit-cancel-icon" size="scale" />
);
class FilesRowContent extends React.PureComponent {
constructor(props) {
super(props);
let titleWithoutExt = getTitleWithoutExst(props.item);
if (props.fileActionId === -1) {
titleWithoutExt = this.getDefaultName(props.fileActionExt);
}
this.state = {
itemTitle: titleWithoutExt,
showConvertDialog: false,
//loading: false
};
}
completeAction = (id) => {
const isCancel =
(id.currentTarget && id.currentTarget.dataset.action === "cancel") ||
id.keyCode === 27;
this.props.editCompleteAction(id, this.props.item, isCancel);
};
updateItem = () => {
const {
updateFile,
renameFolder,
item,
setIsLoading,
fileActionId,
editCompleteAction,
} = this.props;
const { itemTitle } = this.state;
const originalTitle = getTitleWithoutExst(item);
setIsLoading(true);
const isSameTitle =
originalTitle.trim() === itemTitle.trim() || itemTitle.trim() === "";
if (isSameTitle) {
this.setState({
itemTitle: originalTitle,
});
return editCompleteAction(fileActionId, item, isSameTitle);
}
item.fileExst || item.contentLength
? updateFile(fileActionId, itemTitle)
.then(() => this.completeAction(fileActionId))
.finally(() => setIsLoading(false))
: renameFolder(fileActionId, itemTitle)
.then(() => this.completeAction(fileActionId))
.finally(() => setIsLoading(false));
};
createItem = (e) => {
const {
createFile,
item,
setIsLoading,
openDocEditor,
isPrivacy,
isDesktop,
replaceFileStream,
t,
setEncryptionAccess,
createFolder,
} = this.props;
const { itemTitle } = this.state;
setIsLoading(true);
const itemId = e.currentTarget.dataset.itemid;
if (itemTitle.trim() === "") {
toastr.warning(this.props.t("CreateWithEmptyTitle"));
return this.completeAction(itemId);
}
let tab =
!isDesktop && item.fileExst
? window.open(
combineUrl(
AppServerConfig.proxyURL,
config.homepage,
"/products/files/doceditor"
),
"_blank"
)
: null;
!item.fileExst && !item.contentLength
? createFolder(item.parentId, itemTitle)
.then(() => this.completeAction(itemId))
.then(() =>
toastr.success(
<Trans t={t} i18nKey="FolderCreated" ns="Home">
New folder {{ itemTitle }} is created
</Trans>
)
)
.catch((e) => toastr.error(e))
.finally(() => {
return setIsLoading(false);
})
: createFile(item.parentId, `${itemTitle}.${item.fileExst}`)
.then((file) => {
if (isPrivacy) {
return setEncryptionAccess(file).then((encryptedFile) => {
if (!encryptedFile) return Promise.resolve();
toastr.info(t("EncryptedFileSaving"));
return replaceFileStream(
file.id,
encryptedFile,
true,
false
).then(() =>
openDocEditor(file.id, file.providerKey, tab, file.webUrl)
);
});
}
return openDocEditor(file.id, file.providerKey, tab, file.webUrl);
})
.then(() => this.completeAction(itemId))
.then(() => {
const exst = item.fileExst;
return toastr.success(
<Trans i18nKey="FileCreated" ns="Home">
New file {{ itemTitle }}.{{ exst }} is created
</Trans>
);
})
.catch((e) => toastr.error(e))
.finally(() => {
return setIsLoading(false);
});
};
componentDidUpdate(prevProps) {
const { fileActionId, fileActionExt } = this.props;
if (fileActionId === -1 && fileActionExt !== prevProps.fileActionExt) {
const itemTitle = this.getDefaultName(fileActionExt);
this.setState({ itemTitle });
}
// if (fileAction) {
// if (fileActionId !== prevProps.fileActionId) {
// this.setState({ editingId: fileActionId });
// }
// }
}
renameTitle = (e) => {
let title = e.target.value;
//const chars = '*+:"<>?|/'; TODO: think how to solve problem with interpolation escape values in i18n translate
const regexp = new RegExp('[*+:"<>?|\\\\/]', "gim");
if (title.match(regexp)) {
toastr.warning(this.props.t("ContainsSpecCharacter"));
}
title = title.replace(regexp, "_");
return this.setState({ itemTitle: title });
};
cancelUpdateItem = (e) => {
const originalTitle = getTitleWithoutExst(this.props.item);
this.setState({
itemTitle: originalTitle,
});
return this.completeAction(e);
};
onClickUpdateItem = (e) => {
this.props.fileActionType === FileAction.Create
? this.createItem(e)
: this.updateItem(e);
};
onFilesClick = () => {
const {
filter,
parentFolder,
setIsLoading,
fetchFiles,
isImage,
isSound,
isVideo,
canWebEdit,
item,
isTrashFolder,
openDocEditor,
expandedKeys,
addExpandedKeys,
setMediaViewerData,
} = this.props;
const { id, fileExst, viewUrl, providerKey, contentLength } = item;
if (isTrashFolder) return;
if (!fileExst && !contentLength) {
setIsLoading(true);
if (!expandedKeys.includes(parentFolder + "")) {
addExpandedKeys(parentFolder + "");
}
fetchFiles(id, filter)
.catch((err) => {
toastr.error(err);
setIsLoading(false);
})
.finally(() => setIsLoading(false));
} else {
if (canWebEdit) {
return openDocEditor(id, providerKey);
}
if (isImage || isSound || isVideo) {
setMediaViewerData({ visible: true, id });
return;
}
return window.open(viewUrl, "_blank");
}
};
onMobileRowClick = () => {
if (this.props.isTrashFolder || window.innerWidth > 1024) return;
this.onFilesClick();
};
getStatusByDate = () => {
const { culture, t, item, sectionWidth } = this.props;
const { created, updated, version, fileExst } = item;
const title =
version > 1
? t("TitleModified")
: fileExst
? t("TitleUploaded")
: t("TitleCreated");
const date = fileExst ? updated : created;
const dateLabel = new Date(date).toLocaleString(culture);
const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
return mobile ? dateLabel : `${title}: ${dateLabel}`;
};
getDefaultName = (format) => {
const { t } = this.props;
switch (format) {
case "docx":
return t("NewDocument");
case "xlsx":
return t("NewSpreadsheet");
case "pptx":
return t("NewPresentation");
default:
return t("NewFolder");
}
};
onShowVersionHistory = () => {
const {
homepage,
isTabletView,
item,
setIsVerHistoryPanel,
fetchFileVersions,
history,
isTrashFolder,
} = this.props;
if (isTrashFolder) return;
if (!isTabletView) {
fetchFileVersions(item.id + "");
setIsVerHistoryPanel(true);
} else {
history.push(
combineUrl(AppServerConfig.proxyURL, homepage, `/${item.id}/history`)
);
}
};
onBadgeClick = () => {
const {
item,
selectedFolderPathParts,
markAsRead,
setNewFilesPanelVisible,
setNewFilesIds,
updateRootBadge,
updateFileBadge,
} = this.props;
if (item.fileExst) {
markAsRead([], [item.id])
.then(() => {
updateRootBadge(selectedFolderPathParts[0], 1);
updateFileBadge(item.id);
})
.catch((err) => toastr.error(err));
} else {
setNewFilesPanelVisible(true);
const newFolderIds = this.props.selectedFolderPathParts;
newFolderIds.push(item.id);
setNewFilesIds(newFolderIds);
}
};
setConvertDialogVisible = () =>
this.setState({ showConvertDialog: !this.state.showConvertDialog });
getConvertProgress = (fileId) => {
const {
selectedFolderId,
filter,
setIsLoading,
setSecondaryProgressBarData,
t,
clearSecondaryProgressData,
fetchFiles,
} = this.props;
getFileConversationProgress(fileId).then((res) => {
if (res && res[0] && res[0].progress !== 100) {
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: res[0].progress,
label: t("Convert"),
alert: false,
});
setTimeout(() => this.getConvertProgress(fileId), 1000);
} else {
if (res[0].error) {
setSecondaryProgressBarData({
visible: true,
alert: true,
});
toastr.error(res[0].error);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
} else {
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 100,
label: t("Convert"),
alert: false,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
const newFilter = filter.clone();
fetchFiles(selectedFolderId, newFilter)
.catch((err) => {
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
})
.finally(() => setIsLoading(false));
}
}
});
};
onConvert = () => {
const { item, t, setSecondaryProgressBarData } = this.props;
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label: t("Convert"),
alert: false,
});
this.setState({ showConvertDialog: false }, () =>
convertFile(item.id).then((convertRes) => {
if (convertRes && convertRes[0] && convertRes[0].progress !== 100) {
this.getConvertProgress(item.id);
}
})
);
};
onClickLock = () => {
const { item } = this.props;
const { locked, id } = item;
this.props.lockFileAction(id, !locked).catch((err) => toastr.error(err));
};
onClickFavorite = () => {
const { t, item } = this.props;
this.props
.setFavoriteAction("remove", item.id)
.then(() => toastr.success(t("RemovedFromFavorites")))
.catch((err) => toastr.error(err));
};
render() {
const {
t, t,
item, item,
isTrashFolder, sectionWidth,
isLoading, titleWithoutExt,
isMobile, updatedDate,
fileOwner,
accessToEdit,
linkStyles,
newItems,
showNew,
canWebEdit, canWebEdit,
/* canConvert,*/ /* canConvert,*/
sectionWidth, isTrashFolder,
fileActionId, onFilesClick,
fileActionExt, onShowVersionHistory,
} = this.props; onBadgeClick,
const { itemTitle, showConvertDialog } = this.state; onClickLock,
onClickFavorite,
/*setConvertDialogVisible*/
}) => {
const { const {
contentLength, contentLength,
updated,
createdBy,
fileExst, fileExst,
filesCount, filesCount,
foldersCount, foldersCount,
@ -537,51 +86,20 @@ class FilesRowContent extends React.PureComponent {
locked, locked,
providerKey, providerKey,
} = item; } = item;
const titleWithoutExt = getTitleWithoutExst(item);
const fileOwner =
createdBy &&
((this.props.viewer.id === createdBy.id && t("AuthorMe")) ||
createdBy.displayName);
const updatedDate = updated && this.getStatusByDate();
const accessToEdit = const onMobileRowClick = () => {
item.access === ShareAccessRights.FullAccess || if (isTrashFolder || window.innerWidth > 1024) return;
item.access === ShareAccessRights.None; // TODO: fix access type for owner (now - None) onFilesClick();
const isEdit = id === fileActionId && fileExst === fileActionExt; };
const linkStyles = isTrashFolder //|| window.innerWidth <= 1024 return (
? { noHover: true }
: { onClick: this.onFilesClick };
const newItems = item.new || fileStatus === 2;
const showNew = !!newItems;
return isEdit ? (
<EditingWrapperComponent
itemTitle={itemTitle}
okIcon={okIcon}
cancelIcon={cancelIcon}
renameTitle={this.renameTitle}
onClickUpdateItem={this.onClickUpdateItem}
cancelUpdateItem={this.cancelUpdateItem}
itemId={id}
isLoading={isLoading}
/>
) : (
<> <>
{showConvertDialog && (
<ConvertDialog
visible={showConvertDialog}
onClose={this.setConvertDialogVisible}
onConvert={this.onConvert}
/>
)}
<SimpleFilesRowContent <SimpleFilesRowContent
sectionWidth={sectionWidth} sectionWidth={sectionWidth}
isMobile={isMobile} isMobile={isMobile}
sideColor={sideColor} sideColor={sideColor}
isFile={fileExst || contentLength} isFile={fileExst || contentLength}
//onClick={this.onMobileRowClick} //onClick={onMobileRowClick}
> >
<Link <Link
containerWidth="55%" containerWidth="55%"
@ -611,7 +129,7 @@ class FilesRowContent extends React.PureComponent {
</Text> </Text>
{/* TODO: Uncomment after fix conversation {canConvert && !isTrashFolder && ( {/* TODO: Uncomment after fix conversation {canConvert && !isTrashFolder && (
<IconButton <IconButton
onClick={this.setConvertDialogVisible} onClick={setConvertDialogVisible}
iconName="FileActionsConvertIcon" iconName="FileActionsConvertIcon"
className="badge" className="badge"
size="small" size="small"
@ -622,7 +140,7 @@ class FilesRowContent extends React.PureComponent {
)} */} )} */}
{canWebEdit && !isTrashFolder && accessToEdit && ( {canWebEdit && !isTrashFolder && accessToEdit && (
<IconButton <IconButton
onClick={this.onFilesClick} onClick={onFilesClick}
iconName="/static/images/access.edit.react.svg" iconName="/static/images/access.edit.react.svg"
className="badge" className="badge"
size="small" size="small"
@ -637,7 +155,7 @@ class FilesRowContent extends React.PureComponent {
size="small" size="small"
data-id={item.id} data-id={item.id}
data-locked={true} data-locked={true}
onClick={this.onClickLock} onClick={onClickLock}
/> />
)} )}
{fileStatus === 32 && !isTrashFolder && ( {fileStatus === 32 && !isTrashFolder && (
@ -647,7 +165,7 @@ class FilesRowContent extends React.PureComponent {
data-action="remove" data-action="remove"
data-id={item.id} data-id={item.id}
data-title={item.title} data-title={item.title}
onClick={this.onClickFavorite} onClick={onClickFavorite}
/> />
)} )}
{fileStatus === 1 && ( {fileStatus === 1 && (
@ -668,7 +186,7 @@ class FilesRowContent extends React.PureComponent {
version: versionGroup, version: versionGroup,
})} })}
maxWidth="50px" maxWidth="50px"
onClick={this.onShowVersionHistory} onClick={onShowVersionHistory}
padding="0 5px" padding="0 5px"
data-id={id} data-id={id}
/> />
@ -683,7 +201,7 @@ class FilesRowContent extends React.PureComponent {
fontWeight={800} fontWeight={800}
label={t("New")} label={t("New")}
maxWidth="50px" maxWidth="50px"
onClick={this.onBadgeClick} onClick={onBadgeClick}
padding="0 5px" padding="0 5px"
data-id={id} data-id={id}
/> />
@ -701,7 +219,7 @@ class FilesRowContent extends React.PureComponent {
fontWeight={800} fontWeight={800}
label={newItems} label={newItems}
maxWidth="50px" maxWidth="50px"
onClick={this.onBadgeClick} onClick={onBadgeClick}
padding="0 5px" padding="0 5px"
data-id={id} data-id={id}
/> />
@ -755,129 +273,8 @@ class FilesRowContent extends React.PureComponent {
</SimpleFilesRowContent> </SimpleFilesRowContent>
</> </>
); );
}
}
export default inject(
(
{
auth,
filesStore,
formatsStore,
uploadDataStore,
treeFoldersStore,
selectedFolderStore,
filesActionsStore,
mediaViewerDataStore,
versionHistoryStore,
dialogsStore,
},
{ item }
) => {
const { replaceFileStream, setEncryptionAccess } = auth;
const { culture, isDesktopClient, isTabletView } = auth.settingsStore;
const { secondaryProgressDataStore } = uploadDataStore;
const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
const {
iconFormatsStore,
mediaViewersFormatsStore,
docserviceStore,
} = formatsStore;
const {
fetchFiles,
filter,
createFile,
updateFile,
renameFolder,
createFolder,
openDocEditor,
setIsLoading,
isLoading,
updateFileBadge,
} = filesStore;
const {
isRecycleBinFolder,
isPrivacyFolder,
expandedKeys,
addExpandedKeys,
updateRootBadge,
} = treeFoldersStore;
const {
type: fileActionType,
extension: fileActionExt,
id: fileActionId,
} = filesStore.fileActionStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = secondaryProgressDataStore;
const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
const canConvert = docserviceStore.canConvert(item.fileExst);
const isVideo = mediaViewersFormatsStore.isVideo(item.fileExst);
const isImage = iconFormatsStore.isImage(item.fileExst);
const isSound = iconFormatsStore.isSound(item.fileExst);
const { setMediaViewerData } = mediaViewerDataStore;
const {
editCompleteAction,
lockFileAction,
setFavoriteAction,
markAsRead,
} = filesActionsStore;
const { setNewFilesPanelVisible, setNewFilesIds } = dialogsStore;
return {
isDesktop: isDesktopClient,
isTabletView,
homepage: config.homepage,
viewer: auth.userStore.user,
culture,
fileActionId,
fileActionType,
fileActionExt,
selectedFolderId: selectedFolderStore.id,
selectedFolderPathParts: selectedFolderStore.pathParts,
parentFolder: selectedFolderStore.parentId,
isLoading,
isTrashFolder: isRecycleBinFolder,
isPrivacy: isPrivacyFolder,
filter,
canWebEdit,
canConvert,
isVideo,
isImage,
isSound,
expandedKeys,
setIsLoading,
fetchFiles,
setSecondaryProgressBarData,
clearSecondaryProgressData,
createFile,
createFolder,
updateFile,
renameFolder,
replaceFileStream,
setEncryptionAccess,
addExpandedKeys,
openDocEditor,
editCompleteAction,
lockFileAction,
setFavoriteAction,
setMediaViewerData,
setIsVerHistoryPanel,
fetchFileVersions,
markAsRead,
setNewFilesPanelVisible,
setNewFilesIds,
updateRootBadge,
updateFileBadge,
}; };
}
)(withRouter(withTranslation("Home")(observer(FilesRowContent)))); export default withRouter(
withTranslation("Home")(withContentActions(FilesRowContent))
);

View File

@ -0,0 +1,624 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { Trans } from "react-i18next";
import { isMobile } from "react-device-detect";
import toastr from "studio/toastr";
import {
AppServerConfig,
FileAction,
ShareAccessRights,
} from "@appserver/common/constants";
import { combineUrl } from "@appserver/common/utils";
import {
convertFile,
getFileConversationProgress,
} from "@appserver/common/api/files";
import config from "../../../../../../../package.json";
import EditingWrapperComponent from "../sub-components/EditingWrapperComponent";
import { getTitleWithoutExst } from "../../../../../../helpers/files-helpers";
import { cancelIcon, okIcon } from "../sub-components/icons";
import { ConvertDialog } from "../../../../../dialogs";
export default function withContentActions(WrappedContent) {
class WithContentActions extends React.Component {
constructor(props) {
super(props);
let titleWithoutExt = getTitleWithoutExst(props.item);
if (props.fileActionId === -1) {
titleWithoutExt = this.getDefaultName(props.fileActionExt);
}
this.state = {
itemTitle: titleWithoutExt,
showConvertDialog: false,
//loading: false
};
}
componentDidUpdate(prevProps) {
const { fileActionId, fileActionExt } = this.props;
if (fileActionId === -1 && fileActionExt !== prevProps.fileActionExt) {
const itemTitle = this.getDefaultName(fileActionExt);
this.setState({ itemTitle });
}
// if (fileAction) {
// if (fileActionId !== prevProps.fileActionId) {
// this.setState({ editingId: fileActionId });
// }
// }
}
getDefaultName = (format) => {
const { t } = this.props;
switch (format) {
case "docx":
return t("NewDocument");
case "xlsx":
return t("NewSpreadsheet");
case "pptx":
return t("NewPresentation");
default:
return t("NewFolder");
}
};
completeAction = (id) => {
const { editCompleteAction, item } = this.props;
const isCancel =
(id.currentTarget && id.currentTarget.dataset.action === "cancel") ||
id.keyCode === 27;
editCompleteAction(id, item, isCancel);
};
onFilesClick = () => {
const {
filter,
parentFolder,
setIsLoading,
fetchFiles,
isImage,
isSound,
isVideo,
canWebEdit,
item,
isTrashFolder,
openDocEditor,
expandedKeys,
addExpandedKeys,
setMediaViewerData,
} = this.props;
const { id, fileExst, viewUrl, providerKey, contentLength } = item;
if (isTrashFolder) return;
if (!fileExst && !contentLength) {
setIsLoading(true);
if (!expandedKeys.includes(parentFolder + "")) {
addExpandedKeys(parentFolder + "");
}
fetchFiles(id, filter)
.catch((err) => {
toastr.error(err);
setIsLoading(false);
})
.finally(() => setIsLoading(false));
} else {
if (canWebEdit) {
return openDocEditor(id, providerKey);
}
if (isImage || isSound || isVideo) {
setMediaViewerData({ visible: true, id });
return;
}
return window.open(viewUrl, "_blank");
}
};
updateItem = () => {
const {
updateFile,
renameFolder,
item,
setIsLoading,
fileActionId,
editCompleteAction,
} = this.props;
const { itemTitle } = this.state;
const originalTitle = getTitleWithoutExst(item);
setIsLoading(true);
const isSameTitle =
originalTitle.trim() === itemTitle.trim() || itemTitle.trim() === "";
if (isSameTitle) {
this.setState({
itemTitle: originalTitle,
});
return editCompleteAction(fileActionId, item, isSameTitle);
}
item.fileExst || item.contentLength
? updateFile(fileActionId, itemTitle)
.then(() => this.completeAction(fileActionId))
.finally(() => setIsLoading(false))
: renameFolder(fileActionId, itemTitle)
.then(() => this.completeAction(fileActionId))
.finally(() => setIsLoading(false));
};
cancelUpdateItem = (e) => {
const { item } = this.props;
const originalTitle = getTitleWithoutExst(item);
this.setState({
itemTitle: originalTitle,
});
return this.completeAction(e);
};
onClickUpdateItem = (e) => {
const { fileActionType } = this.props;
fileActionType === FileAction.Create
? this.createItem(e)
: this.updateItem(e);
};
createItem = (e) => {
const {
createFile,
item,
setIsLoading,
openDocEditor,
isPrivacy,
isDesktop,
replaceFileStream,
t,
setEncryptionAccess,
createFolder,
} = this.props;
const { itemTitle } = this.state;
setIsLoading(true);
const itemId = e.currentTarget.dataset.itemid;
if (itemTitle.trim() === "") {
toastr.warning(t("CreateWithEmptyTitle"));
return this.completeAction(itemId);
}
let tab =
!isDesktop && item.fileExst
? window.open(
combineUrl(
AppServerConfig.proxyURL,
config.homepage,
"/products/files/doceditor"
),
"_blank"
)
: null;
!item.fileExst && !item.contentLength
? createFolder(item.parentId, itemTitle)
.then(() => this.completeAction(itemId))
.then(() =>
toastr.success(
<Trans t={t} i18nKey="FolderCreated" ns="Home">
New folder {{ itemTitle }} is created
</Trans>
)
)
.catch((e) => toastr.error(e))
.finally(() => {
return setIsLoading(false);
})
: createFile(item.parentId, `${itemTitle}.${item.fileExst}`)
.then((file) => {
if (isPrivacy) {
return setEncryptionAccess(file).then((encryptedFile) => {
if (!encryptedFile) return Promise.resolve();
toastr.info(t("EncryptedFileSaving"));
return replaceFileStream(
file.id,
encryptedFile,
true,
false
).then(() =>
openDocEditor(file.id, file.providerKey, tab, file.webUrl)
);
});
}
return openDocEditor(file.id, file.providerKey, tab, file.webUrl);
})
.then(() => this.completeAction(itemId))
.then(() => {
const exst = item.fileExst;
return toastr.success(
<Trans i18nKey="FileCreated" ns="Home">
New file {{ itemTitle }}.{{ exst }} is created
</Trans>
);
})
.catch((e) => toastr.error(e))
.finally(() => {
return setIsLoading(false);
});
};
renameTitle = (e) => {
const { t } = this.props;
let title = e.target.value;
//const chars = '*+:"<>?|/'; TODO: think how to solve problem with interpolation escape values in i18n translate
const regexp = new RegExp('[*+:"<>?|\\\\/]', "gim");
if (title.match(regexp)) {
toastr.warning(t("ContainsSpecCharacter"));
}
title = title.replace(regexp, "_");
return this.setState({ itemTitle: title });
};
getStatusByDate = () => {
const { culture, t, item, sectionWidth } = this.props;
const { created, updated, version, fileExst } = item;
const title =
version > 1
? t("TitleModified")
: fileExst
? t("TitleUploaded")
: t("TitleCreated");
const date = fileExst ? updated : created;
const dateLabel = new Date(date).toLocaleString(culture);
const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
return mobile ? dateLabel : `${title}: ${dateLabel}`;
};
onShowVersionHistory = () => {
const {
homepage,
isTabletView,
item,
setIsVerHistoryPanel,
fetchFileVersions,
history,
isTrashFolder,
} = this.props;
if (isTrashFolder) return;
if (!isTabletView) {
fetchFileVersions(item.id + "");
setIsVerHistoryPanel(true);
} else {
history.push(
combineUrl(AppServerConfig.proxyURL, homepage, `/${item.id}/history`)
);
}
};
onBadgeClick = () => {
const {
item,
selectedFolderPathParts,
markAsRead,
setNewFilesPanelVisible,
setNewFilesIds,
updateRootBadge,
updateFileBadge,
} = this.props;
if (item.fileExst) {
markAsRead([], [item.id])
.then(() => {
updateRootBadge(selectedFolderPathParts[0], 1);
updateFileBadge(item.id);
})
.catch((err) => toastr.error(err));
} else {
setNewFilesPanelVisible(true);
const newFolderIds = selectedFolderPathParts;
newFolderIds.push(item.id);
setNewFilesIds(newFolderIds);
}
};
setConvertDialogVisible = () =>
this.setState({ showConvertDialog: !this.state.showConvertDialog });
onConvert = () => {
const { item, t, setSecondaryProgressBarData } = this.props;
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label: t("Convert"),
alert: false,
});
this.setState({ showConvertDialog: false }, () =>
convertFile(item.id).then((convertRes) => {
if (convertRes && convertRes[0] && convertRes[0].progress !== 100) {
this.getConvertProgress(item.id);
}
})
);
};
getConvertProgress = (fileId) => {
const {
selectedFolderId,
filter,
setIsLoading,
setSecondaryProgressBarData,
t,
clearSecondaryProgressData,
fetchFiles,
} = this.props;
getFileConversationProgress(fileId).then((res) => {
if (res && res[0] && res[0].progress !== 100) {
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: res[0].progress,
label: t("Convert"),
alert: false,
});
setTimeout(() => this.getConvertProgress(fileId), 1000);
} else {
if (res[0].error) {
setSecondaryProgressBarData({
visible: true,
alert: true,
});
toastr.error(res[0].error);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
} else {
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 100,
label: t("Convert"),
alert: false,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
const newFilter = filter.clone();
fetchFiles(selectedFolderId, newFilter)
.catch((err) => {
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
})
.finally(() => setIsLoading(false));
}
}
});
};
onClickLock = () => {
const { item, lockFileAction } = this.props;
const { locked, id } = item;
lockFileAction(id, !locked).catch((err) => toastr.error(err));
};
onClickFavorite = () => {
const { t, item, setFavoriteAction } = this.props;
setFavoriteAction("remove", item.id)
.then(() => toastr.success(t("RemovedFromFavorites")))
.catch((err) => toastr.error(err));
};
render() {
const { itemTitle, showConvertDialog } = this.state;
const {
item,
fileActionId,
fileActionExt,
isLoading,
viewer,
t,
isTrashFolder,
canWebEdit,
canConvert,
} = this.props;
const { id, fileExst, updated, createdBy, access, fileStatus } = item;
const titleWithoutExt = getTitleWithoutExst(item);
const isEdit = id === fileActionId && fileExst === fileActionExt;
const updatedDate = updated && this.getStatusByDate();
const fileOwner =
createdBy &&
((viewer.id === createdBy.id && t("AuthorMe")) ||
createdBy.displayName);
const accessToEdit =
access === ShareAccessRights.FullAccess ||
access === ShareAccessRights.None; // TODO: fix access type for owner (now - None)
const linkStyles = isTrashFolder //|| window.innerWidth <= 1024
? { noHover: true }
: { onClick: this.onFilesClick };
const newItems = item.new || fileStatus === 2;
const showNew = !!newItems;
return isEdit ? (
<EditingWrapperComponent
itemTitle={itemTitle}
okIcon={okIcon}
cancelIcon={cancelIcon}
itemId={id}
isLoading={isLoading}
renameTitle={this.renameTitle}
onClickUpdateItem={this.onClickUpdateItem}
cancelUpdateItem={this.cancelUpdateItem}
/>
) : (
<>
{showConvertDialog && (
<ConvertDialog
visible={showConvertDialog}
onClose={this.setConvertDialogVisible}
onConvert={this.onConvert}
/>
)}
<WrappedContent
titleWithoutExt={titleWithoutExt}
updatedDate={updatedDate}
fileOwner={fileOwner}
accessToEdit={accessToEdit}
linkStyles={linkStyles}
newItems={newItems}
showNew={showNew}
canWebEdit={canWebEdit}
canConvert={canConvert}
isTrashFolder={isTrashFolder}
onFilesClick={this.onFilesClick}
onShowVersionHistory={this.onShowVersionHistory}
onBadgeClick={this.onBadgeClick}
onClickLock={this.onClickLock}
onClickFavorite={this.onClickFavorite}
setConvertDialogVisible={this.setConvertDialogVisible}
{...this.props}
/>
</>
);
}
}
return inject(
(
{
filesActionsStore,
filesStore,
selectedFolderStore,
formatsStore,
treeFoldersStore,
mediaViewerDataStore,
auth,
versionHistoryStore,
dialogsStore,
uploadDataStore,
},
{ item, t, history }
) => {
const {
editCompleteAction,
markAsRead,
lockFileAction,
setFavoriteAction,
} = filesActionsStore;
const {
filter,
setIsLoading,
fetchFiles,
openDocEditor,
updateFile,
renameFolder,
createFile,
createFolder,
isLoading,
updateFileBadge,
} = filesStore;
const {
iconFormatsStore,
mediaViewersFormatsStore,
docserviceStore,
} = formatsStore;
const {
isRecycleBinFolder,
expandedKeys,
addExpandedKeys,
isPrivacyFolder,
updateRootBadge,
} = treeFoldersStore;
const { setMediaViewerData } = mediaViewerDataStore;
const {
type: fileActionType,
extension: fileActionExt,
id: fileActionId,
} = filesStore.fileActionStore;
const { replaceFileStream, setEncryptionAccess } = auth;
const { culture, isDesktopClient, isTabletView } = auth.settingsStore;
const { setIsVerHistoryPanel, fetchFileVersions } = versionHistoryStore;
const { setNewFilesPanelVisible, setNewFilesIds } = dialogsStore;
const { secondaryProgressDataStore } = uploadDataStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = secondaryProgressDataStore;
const isImage = iconFormatsStore.isImage(item.fileExst);
const isSound = iconFormatsStore.isSound(item.fileExst);
const isVideo = mediaViewersFormatsStore.isVideo(item.fileExst);
const canWebEdit = docserviceStore.canWebEdit(item.fileExst);
return {
t,
editCompleteAction,
filter,
parentFolder: selectedFolderStore.parentId,
setIsLoading,
fetchFiles,
isImage,
isSound,
isVideo,
canWebEdit,
isTrashFolder: isRecycleBinFolder,
openDocEditor,
expandedKeys,
addExpandedKeys,
setMediaViewerData,
updateFile,
renameFolder,
fileActionId,
editCompleteAction,
fileActionType,
createFile,
isPrivacy: isPrivacyFolder,
isDesktop: isDesktopClient,
replaceFileStream,
setEncryptionAccess,
createFolder,
fileActionExt,
isLoading,
culture,
homepage: config.homepage,
isTabletView,
setIsVerHistoryPanel,
fetchFileVersions,
history,
selectedFolderPathParts: selectedFolderStore.pathParts,
markAsRead,
setNewFilesPanelVisible,
setNewFilesIds,
updateRootBadge,
updateFileBadge,
setSecondaryProgressBarData,
clearSecondaryProgressData,
selectedFolderId: selectedFolderStore.id,
lockFileAction,
setFavoriteAction,
viewer: auth.userStore.user,
};
}
)(observer(WithContentActions));
}

View File

@ -1,5 +1,14 @@
import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
import CheckIcon from "../../../../../../../public/images/check.react.svg";
import CrossIcon from "../../../../../../../../../../public/images/cross.react.svg";
import FavoriteIcon from "../../../../../../../public/images/favorite.react.svg";
import FileActionsConvertEditDocIcon from "../../../../../../../public/images/file.actions.convert.edit.doc.react.svg";
import FileActionsLockedIcon from "../../../../../../../public/images/file.actions.locked.react.svg";
export const EncryptedFileIcon = styled.div` export const EncryptedFileIcon = styled.div`
background: url("images/security.svg") no-repeat 0 0 / 16px 16px transparent; background: url("images/security.svg") no-repeat 0 0 / 16px 16px transparent;
height: 16px; height: 16px;
@ -8,3 +17,47 @@ export const EncryptedFileIcon = styled.div`
margin-top: 14px; margin-top: 14px;
margin-left: ${(props) => (props.isEdit ? "40px" : "12px")}; margin-left: ${(props) => (props.isEdit ? "40px" : "12px")};
`; `;
const StyledCheckIcon = styled(CheckIcon)`
${commonIconsStyles}
path {
fill: #a3a9ae;
}
:hover {
fill: #657077;
}
`;
export const okIcon = <StyledCheckIcon className="edit-ok-icon" size="scale" />;
const StyledCrossIcon = styled(CrossIcon)`
${commonIconsStyles}
path {
fill: #a3a9ae;
}
:hover {
fill: #657077;
}
`;
export const cancelIcon = (
<StyledCrossIcon className="edit-cancel-icon" size="scale" />
);
export const StyledFavoriteIcon = styled(FavoriteIcon)`
${commonIconsStyles}
`;
export const StyledFileActionsConvertEditDocIcon = styled(
FileActionsConvertEditDocIcon
)`
${commonIconsStyles}
path {
fill: #3b72a7;
}
`;
export const StyledFileActionsLockedIcon = styled(FileActionsLockedIcon)`
${commonIconsStyles}
path {
fill: #3b72a7;
}
`;