Merge branch 'develop' into feature/table-view

# Conflicts:
#	packages/asc-web-common/components/PageLayout/index.js
#	products/ASC.Files/Client/src/pages/Home/Section/Header/index.js
#	public/locales/de/Common.json
#	public/locales/it/Common.json
#	public/locales/lo/Common.json
#	public/locales/pt-BR/Common.json
#	public/locales/ro/Common.json
This commit is contained in:
Nikita Gopienko 2021-08-26 16:05:15 +03:00
commit ff7f3926ce
257 changed files with 3544 additions and 227 deletions

View File

@ -88,6 +88,11 @@ server {
try_files /$basename /index.html =404;
}
location ~* /static/images/icons/(?<content>[^/]+) {
root $public_root;
try_files /images/icons/$content/$basename /index.html =404;
}
location ~* /static/images/ {
root $public_root;
try_files /images/$basename /index.html =404;

View File

@ -538,10 +538,13 @@ export function getNewFiles(folderId) {
});
}
export function convertFile(fileId) {
export function convertFile(fileId, sync = false) {
const data = { sync };
return request({
method: "put",
url: `/files/file/${fileId}/checkconversion`,
data,
});
}
@ -767,3 +770,10 @@ export function createThumbnails(fileIds) {
return request(options);
}
export function getPresignedUri(fileId) {
return request({
method: "get",
url: `files/file/${fileId}/presigned`,
});
}

View File

@ -315,3 +315,11 @@ export function validateTfaCode(code) {
data,
});
}
export function getCommonThirdPartyList() {
const options = {
method: "get",
url: "/files/thirdparty/common",
};
return request(options);
}

View File

@ -75,14 +75,6 @@ class PageLayout extends React.Component {
constructor(props) {
super(props);
const isArticleVisibleAndPinned = !!this.props.isArticlePinned;
this.state = {
isBackdropVisible: false,
isArticleVisible: isArticleVisibleAndPinned,
isArticlePinned: isArticleVisibleAndPinned,
};
this.timeoutHandler = null;
this.intervalHandler = null;
@ -95,13 +87,9 @@ class PageLayout extends React.Component {
}
if (
(this.props.hideAside &&
!this.state.isArticlePinned &&
this.props.hideAside !== prevProps.hideAside) ||
(this.props.isLoading !== prevProps.isLoading &&
this.props.isLoaded &&
this.state.isArticleVisible &&
!this.state.isArticlePinned)
this.props.hideAside &&
!this.props.isArticlePinned &&
this.props.hideAside !== prevProps.hideAside
) {
this.backdropClick();
}
@ -136,42 +124,30 @@ class PageLayout extends React.Component {
};
backdropClick = () => {
this.setState({
isBackdropVisible: false,
isArticleVisible: false,
isArticlePinned: false,
});
this.props.setArticlePinned(false);
this.props.setIsBackdropVisible(false);
this.props.setIsArticleVisible(false);
isMobile && this.props.setArticleVisibleOnUnpin(false);
};
pinArticle = () => {
this.setState({
isBackdropVisible: false,
isArticlePinned: true,
isArticleVisible: true,
});
this.props.setIsBackdropVisible(false);
this.props.setIsArticleVisible(true);
this.props.setArticlePinned(true);
isMobile && this.props.setArticleVisibleOnUnpin(false);
};
unpinArticle = () => {
this.setState({
isBackdropVisible: true,
isArticlePinned: false,
isArticleVisible: true,
});
this.props.setIsBackdropVisible(true);
this.props.setIsArticleVisible(true);
this.props.setArticlePinned(false);
isMobile && this.props.setArticleVisibleOnUnpin(true);
};
showArticle = () => {
this.setState({
isBackdropVisible: true,
isArticleVisible: true,
isArticlePinned: false,
});
this.props.setArticlePinned(false);
this.props.setIsBackdropVisible(true);
this.props.setIsArticleVisible(true);
isMobile && this.props.setArticleVisibleOnUnpin(true);
};
@ -224,8 +200,10 @@ class PageLayout extends React.Component {
onOpenUploadPanel,
isTabletView,
firstLoad,
isLoading,
dragging,
isArticleVisible,
isBackdropVisible,
isArticlePinned,
} = this.props;
let articleHeaderContent = null;
let articleMainButtonContent = null;
@ -294,16 +272,15 @@ class PageLayout extends React.Component {
{isBackdropAvailable && (
<Backdrop
zIndex={400}
visible={this.state.isBackdropVisible}
visible={isBackdropVisible}
onClick={this.backdropClick}
/>
)}
{isArticleAvailable && (
<Article
visible={this.state.isArticleVisible}
pinned={this.state.isArticlePinned}
visible={isArticleVisible}
pinned={isArticlePinned}
firstLoad={firstLoad}
isLoading={!isLoading}
>
{isArticleHeaderAvailable && (
<SubArticleHeader>
@ -320,7 +297,7 @@ class PageLayout extends React.Component {
</SubArticleMainButton>
)}
{isArticleBodyAvailable && (
<SubArticleBody pinned={this.state.isArticlePinned}>
<SubArticleBody pinned={isArticlePinned}>
{articleBodyContent
? articleBodyContent.props.children
: null}
@ -328,7 +305,7 @@ class PageLayout extends React.Component {
)}
{isArticleBodyAvailable && (
<ArticlePinPanel
pinned={this.state.isArticlePinned}
pinned={isArticlePinned}
onPin={this.pinArticle}
onUnpin={this.unpinArticle}
/>
@ -351,12 +328,12 @@ class PageLayout extends React.Component {
<Section
widthProp={width}
unpinArticle={this.unpinArticle}
pinned={this.state.isArticlePinned}
pinned={isArticlePinned}
>
{isSectionHeaderAvailable && (
<SubSectionHeader
isHeaderVisible={isHeaderVisible}
isArticlePinned={this.state.isArticlePinned}
isArticlePinned={isArticlePinned}
>
{sectionHeaderContent
? sectionHeaderContent.props.children
@ -388,7 +365,7 @@ class PageLayout extends React.Component {
uploadFiles={uploadFiles}
withScroll={withBodyScroll}
autoFocus={isMobile || isTabletView ? false : true}
pinned={this.state.isArticlePinned}
pinned={isArticlePinned}
viewAs={viewAs}
>
{isSectionFilterAvailable && (
@ -451,7 +428,7 @@ class PageLayout extends React.Component {
{isArticleAvailable && (
<SectionToggler
visible={!this.state.isArticleVisible}
visible={!isArticleVisible}
onClick={this.showArticle}
/>
)}
@ -520,7 +497,6 @@ PageLayout.propTypes = {
isTabletView: PropTypes.bool,
isHeaderVisible: PropTypes.bool,
firstLoad: PropTypes.bool,
isLoading: PropTypes.bool,
};
PageLayout.defaultProps = {
@ -542,15 +518,24 @@ export default inject(({ auth }) => {
isHeaderVisible,
isTabletView,
isArticlePinned,
isArticleVisible,
isBackdropVisible,
setArticlePinned,
setArticleVisibleOnUnpin,
setIsArticleVisible,
setIsBackdropVisible,
} = settingsStore;
return {
isLoaded,
isTabletView,
isHeaderVisible,
isArticlePinned,
isArticleVisible,
setArticlePinned,
setArticleVisibleOnUnpin,
setIsArticleVisible,
isBackdropVisible,
setIsBackdropVisible,
};
})(observer(PageLayout));

View File

@ -47,7 +47,7 @@ const StyledArticle = styled.article`
? props.pinned
? `
min-width: 240px;
max-width: ${props.isLoading ? "calc(100vw - 368px)" : "240px"};
max-width: 240px;
.increaseHeight {

View File

@ -60,6 +60,8 @@ class SettingsStore {
isTabletView = false;
isArticlePinned =
localStorage.getItem(ARTICLE_PINNED_KEY) === "true" || false;
isArticleVisible = false;
isBackdropVisible = false;
isArticleVisibleOnUnpin = false;
@ -105,6 +107,19 @@ class SettingsStore {
return `https://helpcenter.onlyoffice.com/${lang}/administration/configuration.aspx#CustomizingPortal_block`;
}
setIsArticleVisible = (visible) => {
this.isArticleVisible = this.isArticlePinned ? true : visible;
};
setIsBackdropVisible = (visible) => {
this.isBackdropVisible = visible;
};
hideArticle = () => {
this.setIsArticleVisible(false);
this.setIsBackdropVisible(false);
};
setValue = (key, value) => {
this[key] = value;
};

View File

@ -6,13 +6,21 @@ import StyledAside from "./styled-aside";
const Aside = React.memo((props) => {
//console.log("Aside render");
const { visible, children, scale, zIndex, className } = props;
const {
visible,
children,
scale,
zIndex,
className,
contentPaddingBottom,
} = props;
return (
<StyledAside
visible={visible}
scale={scale}
zIndex={zIndex}
contentPaddingBottom={contentPaddingBottom}
className={`${className} not-selectable aside`}
>
<Scrollbar>{children}</Scrollbar>
@ -26,6 +34,7 @@ Aside.propTypes = {
visible: PropTypes.bool,
scale: PropTypes.bool,
className: PropTypes.string,
contentPaddingBottom: PropTypes.string,
zIndex: PropTypes.number,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),

View File

@ -4,9 +4,13 @@ import Base from "../themes/base";
/* eslint-disable no-unused-vars */
/* eslint-disable react/prop-types */
const Container = ({ visible, scale, zIndex, ...props }) => (
<aside {...props} />
);
const Container = ({
visible,
scale,
zIndex,
contentPaddingBottom,
...props
}) => <aside {...props} />;
/* eslint-enable react/prop-types */
/* eslint-enable no-unused-vars */
@ -27,7 +31,10 @@ const StyledAside = styled(Container)`
box-sizing: border-box;
&.modal-dialog-aside {
padding-bottom: ${(props) => props.theme.aside.paddingBottom};
padding-bottom: ${(props) =>
props.contentPaddingBottom
? props.contentPaddingBottom
: props.theme.aside.paddingBottom};
.modal-dialog-aside-footer {
position: fixed;

View File

@ -106,6 +106,8 @@ class ModalDialog extends React.Component {
style,
children,
isLoading,
contentPaddingBottom,
removeScroll,
} = this.props;
let header = null;
@ -176,9 +178,14 @@ class ModalDialog extends React.Component {
visible={visible}
scale={scale}
zIndex={zIndex}
contentPaddingBottom={contentPaddingBottom}
className="modal-dialog-aside not-selectable"
>
<Content contentHeight={contentHeight} contentWidth={contentWidth}>
<Content
contentHeight={contentHeight}
contentWidth={contentWidth}
removeScroll={removeScroll}
>
{isLoading ? (
<Loaders.DialogAsideLoader withoutAside />
) : (
@ -192,6 +199,7 @@ class ModalDialog extends React.Component {
<BodyBox
className="modal-dialog-aside-body"
paddingProp={bodyPadding}
removeScroll={removeScroll}
>
{body ? body.props.children : null}
</BodyBox>
@ -229,9 +237,11 @@ ModalDialog.propTypes = {
contentHeight: PropTypes.string,
contentWidth: PropTypes.string,
isLoading: PropTypes.bool,
removeScroll: PropTypes.bool,
className: PropTypes.string,
id: PropTypes.string,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
contentPaddingBottom: PropTypes.string,
};
ModalDialog.defaultProps = {

View File

@ -1,4 +1,4 @@
import styled from "styled-components";
import styled, { css } from "styled-components";
import Base from "../themes/base";
import Box from "../box";
import CrossSidebarIcon from "../../../public/images/cross.sidebar.react.svg";
@ -34,6 +34,12 @@ const Content = styled.div`
font-weight: ${(props) =>
props.theme.modalDialog.content.heading.fontWeight};
}
${(props) =>
props.removeScroll &&
css`
overflow: hidden;
`}
`;
Content.defaultProps = { theme: Base };
@ -66,6 +72,12 @@ CloseButton.defaultProps = { theme: Base };
const BodyBox = styled(Box)`
position: relative;
${(props) =>
props.removeScroll &&
css`
height: 100%;
`}
`;
export { CloseButton, StyledHeader, Content, Dialog, BodyBox };

View File

@ -45,6 +45,8 @@ const TableRow = (props) => {
onContentSelect && onContentSelect(e.target.checked, item);
};
console.log("selectionProp", selectionProp);
return (
<StyledTableRow
onContextMenu={onContextMenu}
@ -55,7 +57,7 @@ const TableRow = (props) => {
checked={checked}
{...selectionProp}
style={style}
className={`${selectionProp.className} table-container_row-checkbox-wrapper`}
className={`${selectionProp?.className} table-container_row-checkbox-wrapper`}
>
<div className="table-container_element">{element}</div>
<Checkbox

View File

@ -77,7 +77,7 @@
"react-tooltip": "^4.2.11",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.6",
"react-window-infinite-loader": "^1.0.5",
"react-window-infinite-loader": "^1.0.7",
"resize-image": "^0.1.0",
"sjcl": "^1.0.8",
"styled-components": "^5.2.1",

View File

@ -77,4 +77,4 @@
"UploadToFolder": "In den Ordner hochladen",
"ViewList": "Liste",
"ViewTiles": "Kacheln"
}
}

View File

@ -0,0 +1,3 @@
{
"SelectFile": "Select file"
}

View File

@ -0,0 +1,3 @@
{
"NotAvailableFolder": "No folders available"
}

View File

@ -20,5 +20,6 @@
"Restore": "Restore",
"Spreadsheets": "Spreadsheets",
"ThirdPartyInfo": "Change the third-party info",
"DownloadApps": "Download applications"
"DownloadApps": "Download applications",
"SelectFolder": "Select folder"
}

View File

@ -77,4 +77,4 @@
"UploadToFolder": "Carica nella cartella",
"ViewList": "Lista",
"ViewTiles": "Selezioni"
}
}

View File

@ -77,4 +77,4 @@
"UploadToFolder": "ອັບໂຫລດໄປຍັງໂຟຣເດີ",
"ViewList": "ລາຍການ",
"ViewTiles": "ຫົວຂໍ້"
}
}

View File

@ -77,4 +77,4 @@
"UploadToFolder": "Upload para pasta",
"ViewList": "Lista",
"ViewTiles": "Azulejos"
}
}

View File

@ -77,4 +77,4 @@
"UploadToFolder": "Încarcă în dosarul",
"ViewList": "Listă",
"ViewTiles": "Cadre"
}
}

View File

@ -0,0 +1,3 @@
{
"SelectFile": "Выбрать файл"
}

View File

@ -0,0 +1,3 @@
{
"NotAvailableFolder": "Нет доступных папок"
}

View File

@ -20,5 +20,6 @@
"Restore": "Восстановить",
"Spreadsheets": "Таблицы",
"ThirdPartyInfo": "Изменить настройки подключения",
"DownloadApps": "Скачать приложения"
"DownloadApps": "Скачать приложения",
"SelectFolder": "Выбрать папку"
}

View File

@ -300,7 +300,7 @@ export default function withContextOptions(WrappedComponent) {
return {
key: option,
label: t("SharingSettings"),
icon: "images/catalog.shared.react.svg",
icon: "/static/images/catalog.shared.react.svg",
onClick: this.onClickShare,
disabled: !isShareable,
};
@ -315,7 +315,7 @@ export default function withContextOptions(WrappedComponent) {
return {
key: option,
label: t("Translations:OwnerChange"),
icon: "images/catalog.user.react.svg",
icon: "/static/images/catalog.user.react.svg",
onClick: this.onOwnerChange,
disabled: false,
};

View File

@ -76,22 +76,22 @@ class TreeFolders extends React.Component {
switch (item.rootFolderType) {
case FolderType.USER:
iconUrl = "images/catalog.user.react.svg";
iconUrl = "/static/images/catalog.user.react.svg";
break;
case FolderType.SHARE:
iconUrl = "images/catalog.shared.react.svg";
iconUrl = "/static/images/catalog.shared.react.svg";
break;
case FolderType.COMMON:
iconUrl = "images/catalog.portfolio.react.svg";
iconUrl = "/static/images/catalog.portfolio.react.svg";
break;
case FolderType.Favorites:
iconUrl = "images/catalog.favorites.react.svg";
iconUrl = "/static/images/catalog.favorites.react.svg";
break;
case FolderType.Recent:
iconUrl = "images/catalog.recent.react.svg";
iconUrl = "/static/images/catalog.recent.react.svg";
break;
case FolderType.Privacy:
iconUrl = "images/catalog.private.react.svg";
iconUrl = "/static/images/catalog.private.react.svg";
break;
case FolderType.TRASH:
iconUrl = "/static/images/catalog.trash.react.svg";
@ -100,38 +100,39 @@ class TreeFolders extends React.Component {
break;
}
if (item.parentId !== 0) iconUrl = "images/catalog.folder.react.svg";
if (item.parentId !== 0)
iconUrl = "/static/images/catalog.folder.react.svg";
switch (item.providerKey) {
case "GoogleDrive":
iconUrl = "images/cloud.services.google.drive.react.svg";
iconUrl = "/static/images/cloud.services.google.drive.react.svg";
break;
case "Box":
iconUrl = "images/cloud.services.box.react.svg";
iconUrl = "/static/images/cloud.services.box.react.svg";
break;
case "DropboxV2":
iconUrl = "images/cloud.services.dropbox.react.svg";
iconUrl = "/static/images/cloud.services.dropbox.react.svg";
break;
case "OneDrive":
iconUrl = "images/cloud.services.onedrive.react.svg";
iconUrl = "/static/images/cloud.services.onedrive.react.svg";
break;
case "SharePoint":
iconUrl = "images/cloud.services.onedrive.react.svg";
iconUrl = "/static/images/cloud.services.onedrive.react.svg";
break;
case "kDrive":
iconUrl = "images/catalog.folder.react.svg";
iconUrl = "/static/images/catalog.folder.react.svg";
break;
case "Yandex":
iconUrl = "images/catalog.folder.react.svg";
iconUrl = "/static/images/catalog.folder.react.svg";
break;
case "NextCloud":
iconUrl = "images/cloud.services.nextcloud.react.svg";
iconUrl = "/static/images/cloud.services.nextcloud.react.svg";
break;
case "OwnCloud":
iconUrl = "images/catalog.folder.react.svg";
iconUrl = "/static/images/catalog.folder.react.svg";
break;
case "WebDav":
iconUrl = "images/catalog.folder.react.svg";
iconUrl = "/static/images/catalog.folder.react.svg";
break;
default:
break;
@ -157,7 +158,8 @@ class TreeFolders extends React.Component {
return false;
}
if (draggableItems.find((x) => x.id === item.id)) return false;
if (!draggableItems || draggableItems.find((x) => x.id === item.id))
return false;
// const isMy = rootFolderType === FolderType.USER;
// const isCommon = rootFolderType === FolderType.COMMON;
@ -196,6 +198,7 @@ class TreeFolders extends React.Component {
};
getItems = (data) => {
const { withoutProvider } = this.props;
return data.map((item) => {
const dragging = this.props.dragging ? this.showDragItems(item) : false;
@ -203,8 +206,13 @@ class TreeFolders extends React.Component {
? item.newItems > 0 && this.props.needUpdate
: false;
const provider = item.providerKey;
const serviceFolder = !!item.providerKey;
let className = `tree-drag tree-id_${item.id}`;
if (withoutProvider && provider) return;
if (dragging) className += " dragging";
if ((item.folders && item.folders.length > 0) || serviceFolder) {
return (
@ -339,6 +347,7 @@ class TreeFolders extends React.Component {
};
onLoadData = (treeNode, isExpand) => {
const { data: incomingDate, certainFolders } = this.props;
isExpand && this.setState({ isExpand: true });
this.props.setIsLoading && this.props.setIsLoading(true);
//console.log("load data...", treeNode);
@ -353,7 +362,9 @@ class TreeFolders extends React.Component {
const listIds = data.listIds;
listIds.push(itemId);
const treeData = [...this.props.treeFolders];
const treeData = certainFolders
? incomingDate
: [...this.props.treeFolders];
this.getNewTreeData(treeData, listIds, data.folders, data.level);
this.props.setTreeFolders(treeData);
@ -462,7 +473,10 @@ TreeFolders.defaultProps = {
};
export default inject(
({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => {
(
{ auth, filesStore, treeFoldersStore, selectedFolderStore },
{ useDefaultSelectedKeys, selectedKeys }
) => {
const {
filter,
selection,
@ -494,9 +508,12 @@ export default inject(
commonId: commonFolderId,
isPrivacy: isPrivacyFolder,
filter,
draggableItems: dragging ? selection : [],
draggableItems: dragging ? selection : null,
expandedKeys,
treeFolders,
selectedKeys: useDefaultSelectedKeys
? treeFoldersStore.selectedKeys
: selectedKeys,
setDragging,
setIsLoading,

View File

@ -12,57 +12,41 @@ import Banner from "./Banner";
import { inject, observer } from "mobx-react";
import { withRouter } from "react-router-dom";
import config from "../../../../package.json";
import { clickBackdrop, combineUrl } from "@appserver/common/utils";
import { combineUrl } from "@appserver/common/utils";
import { AppServerConfig } from "@appserver/common/constants";
import FilesFilter from "@appserver/common/api/files/filter";
import { isDesktop, isTablet } from "react-device-detect";
class ArticleBodyContent extends React.Component {
constructor(props) {
super(props);
const { selectedFolderTitle } = props;
selectedFolderTitle
? setDocumentTitle(selectedFolderTitle)
: setDocumentTitle();
}
/*componentDidMount() {
if (this.props.currentId) {
const currentId = [this.props.currentId + ""];
this.props.setSelectedNode(currentId);
}
}*/
onSelect = (data, e) => {
const {
filter,
setIsLoading,
selectedTreeNode,
setSelectedNode,
fetchFiles,
homepage,
history,
hideArticle,
} = this.props;
//if (!selectedTreeNode || selectedTreeNode[0] !== data[0]) {
setSelectedNode(data);
setIsLoading(true);
hideArticle(false);
const selectedFolderTitle =
(e.node && e.node.props && e.node.props.title) || null;
// const selectedFolderTitle =
// (e.node && e.node.props && e.node.props.title) || null;
selectedFolderTitle
? setDocumentTitle(selectedFolderTitle)
: setDocumentTitle();
// selectedFolderTitle
// ? setDocumentTitle(selectedFolderTitle)
// : setDocumentTitle();
if (window.location.pathname.indexOf("/filter") > 0) {
fetchFiles(data[0])
.catch((err) => toastr.error(err))
.finally(() => setIsLoading(false));
} else {
const urlFilter = FilesFilter.getDefault().toUrlParams();
const newFilter = FilesFilter.getDefault();
newFilter.folder = data[0];
const urlFilter = newFilter.toUrlParams();
history.push(
combineUrl(AppServerConfig.proxyURL, homepage, `/filter?${urlFilter}`)
);
@ -78,12 +62,13 @@ class ArticleBodyContent extends React.Component {
const {
treeFolders,
onTreeDrop,
selectedTreeNode,
enableThirdParty,
isVisitor,
personal,
} = this.props;
//console.log("Article Body render");
const campaigns = (localStorage.getItem("campaigns") || "")
.split(",")
.filter((campaign) => campaign.length > 0);
@ -93,7 +78,7 @@ class ArticleBodyContent extends React.Component {
) : (
<>
<TreeFolders
selectedKeys={selectedTreeNode}
useDefaultSelectedKeys
onSelect={this.onSelect}
data={treeFolders}
onBadgeClick={this.onShowNewFilesPanel}
@ -119,39 +104,32 @@ export default inject(
dialogsStore,
settingsStore,
}) => {
const { fetchFiles, filter, setIsLoading } = filesStore;
const { fetchFiles, setIsLoading } = filesStore;
const { treeFolders, setSelectedNode, setTreeFolders } = treeFoldersStore;
const selectedNode = treeFoldersStore.selectedTreeNode;
const selectedTreeNode =
selectedNode.length > 0 &&
selectedNode[0] !== "@my" &&
selectedNode[0] !== "@common"
? selectedNode
: [selectedFolderStore.id + ""];
const { setNewFilesPanelVisible } = dialogsStore;
const { personal } = auth.settingsStore;
const { personal, hideArticle } = auth.settingsStore;
const selectedFolderTitle = selectedFolderStore.title;
selectedFolderTitle
? setDocumentTitle(selectedFolderTitle)
: setDocumentTitle();
return {
selectedFolderTitle: selectedFolderStore.title,
treeFolders,
selectedTreeNode,
filter,
enableThirdParty: settingsStore.enableThirdParty,
isVisitor: auth.userStore.user.isVisitor,
homepage: config.homepage,
personal,
setIsLoading,
fetchFiles,
setSelectedNode,
setTreeFolders,
setNewFilesPanelVisible,
homepage: config.homepage,
personal,
hideArticle,
};
}
)(observer(withRouter(ArticleBodyContent)));

View File

@ -73,7 +73,7 @@ const EmptyFolderContainer = ({
return (
<EmptyContainer
headerText={t("EmptyFolderHeader")}
imageSrc="images/empty_screen.png"
imageSrc="/static/images/empty_screen.png"
buttons={buttons}
/>
);

View File

@ -52,7 +52,7 @@ const RootFolderContainer = (props) => {
case FolderType.USER:
return {
descriptionText: myDescription,
imageSrc: "images/empty_screen.png",
imageSrc: "/static/images/empty_screen.png",
buttons: commonButtons,
};
case FolderType.SHARE:

View File

@ -0,0 +1,96 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import Scrollbar from "@appserver/components/scrollbar";
import TreeFolders from "../Article/Body/TreeFolders";
import { StyledSelectFolderPanel } from "../panels/StyledPanels";
const FolderTreeBody = ({
isLoadingData,
expandedKeys,
folderList,
onSelect,
withoutProvider,
certainFolders,
isAvailable,
filter,
selectedKeys,
heightContent,
displayType,
isHeaderChildren,
}) => {
const { t } = useTranslation(["SelectFolder", "Common"]);
return (
<>
{!isLoadingData ? (
isAvailable ? (
<StyledSelectFolderPanel
heightContent={heightContent}
displayType={displayType}
isHeaderChildren={isHeaderChildren}
>
<div className="select-folder-dialog_tree-folder">
<Scrollbar id="folder-tree-scroll-bar">
<TreeFolders
expandedPanelKeys={expandedKeys}
data={folderList}
filter={filter}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
selectedKeys={selectedKeys}
needUpdate={false}
/>
</Scrollbar>
</div>
</StyledSelectFolderPanel>
) : (
<StyledSelectFolderPanel
heightContent={heightContent}
isHeaderChildren={isHeaderChildren}
>
<div className="tree-folder-empty-list select-folder-dialog_tree-folder">
<Text as="span">{t("NotAvailableFolder")}</Text>
</div>
</StyledSelectFolderPanel>
)
) : (
<StyledSelectFolderPanel heightContent={heightContent}>
<div className="tree-folder-Loader" key="loader">
<Loader
type="oval"
size="16px"
style={{
display: "inline",
marginRight: "10px",
marginTop: "16px",
}}
/>
<Text as="span">{`${t("Common:LoadingProcessing")} ${t(
"Common:LoadingDescription"
)}`}</Text>
</div>
</StyledSelectFolderPanel>
)}
</>
);
};
FolderTreeBody.defaultProps = {
isAvailable: true,
isLoadingData: false,
};
export default inject(
({ filesStore, treeFoldersStore, selectedFolderStore }) => {
const { filter } = filesStore;
const { expandedPanelKeys } = treeFoldersStore;
return {
expandedKeys: expandedPanelKeys
? expandedPanelKeys
: selectedFolderStore.pathParts,
filter,
};
}
)(observer(FolderTreeBody));

View File

@ -1,7 +1,13 @@
import React from "react";
import { ReactSVG } from "react-svg";
import { EncryptedFileIcon } from "./Icons";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
const StyledIcon = styled.img`
width: 24px;
height: 24px;
margin-top: 4px;
`;
const ItemIcon = ({
id,
@ -17,14 +23,11 @@ const ItemIcon = ({
(actionType !== null && actionId === id && fileExst === actionExtension) ||
id <= 0;
const svgLoader = () => <div style={{ width: "24px" }}></div>;
return (
<>
<ReactSVG
<StyledIcon
className={`react-svg-icon${isEdit ? " is-edit" : ""}`}
src={icon}
loading={svgLoader}
/>
{isPrivacy && fileExst && (
<EncryptedFileIcon isEdit={isEdit && viewAs !== "tile"} />

View File

@ -3,7 +3,7 @@ import Text from "@appserver/components/text";
import IconButton from "@appserver/components/icon-button";
import { inject, observer } from "mobx-react";
const getSharedButton = ({
const SharedButton = ({
t,
id,
isFolder,
@ -34,7 +34,7 @@ const getSharedButton = ({
color={color}
hoverColor="#657077"
size={18}
iconName="images/catalog.shared.react.svg"
iconName="/static/images/catalog.shared.react.svg"
/>
{t("Share")}
</Text>
@ -46,4 +46,4 @@ export default inject(({ filesActionsStore, dialogsStore }) => {
onSelectItem: filesActionsStore.onSelectItem,
setSharingPanelVisible: dialogsStore.setSharingPanelVisible,
};
})(observer(getSharedButton));
})(observer(SharedButton));

View File

@ -0,0 +1,90 @@
import styled from "styled-components";
import Base from "@appserver/components/themes/base";
const paddingRightStyle = (props) =>
props.theme.fileInput.paddingRight[props.size];
const widthIconStyle = (props) => props.theme.fileInput.icon.width[props.size];
const heightIconStyle = (props) =>
props.theme.fileInput.icon.height[props.size];
const StyledFileInput = styled.div`
display: flex;
position: relative;
outline: none;
.file-text-input {
width: 100%;
max-width: 820px;
}
width: ${(props) =>
(props.scale && "100%") ||
(props.size === "base" && props.theme.input.width.base) ||
(props.size === "middle" && props.theme.input.width.middle) ||
(props.size === "big" && props.theme.input.width.big) ||
(props.size === "huge" && props.theme.input.width.huge) ||
(props.size === "large" && props.theme.input.width.large)};
.text-input {
border-color: ${(props) =>
(props.hasError && props.theme.input.errorBorderColor) ||
(props.hasWarning && props.theme.input.warningBorderColor) ||
(props.isDisabled && props.theme.input.disabledBorderColor) ||
props.theme.input.borderColor};
text-overflow: ellipsis;
padding-right: 40px;
padding-right: ${(props) => paddingRightStyle(props)};
cursor: ${(props) => (props.isDisabled ? "default" : "pointer")};
}
:hover {
.icon {
border-color: ${(props) =>
(props.hasError && props.theme.input.hoverErrorBorderColor) ||
(props.hasWarning && props.theme.input.hoverWarningBorderColor) ||
(props.isDisabled && props.theme.input.hoverDisabledBorderColor) ||
props.theme.input.hoverBorderColor};
}
}
:active {
.icon {
border-color: ${(props) =>
(props.hasError && props.theme.input.focusErrorBorderColor) ||
(props.hasWarning && props.theme.input.focusWarningBorderColor) ||
(props.isDisabled && props.theme.input.focusDisabledBorderColor) ||
props.theme.input.focusBorderColor};
}
}
.icon {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 0;
background: white;
width: ${(props) => widthIconStyle(props)};
height: ${(props) => heightIconStyle(props)};
margin: 0;
border: ${(props) => props.theme.fileInput.icon.border};
border-radius: ${(props) => props.theme.fileInput.icon.borderRadius};
border-color: ${(props) =>
(props.hasError && props.theme.input.errorBorderColor) ||
(props.hasWarning && props.theme.input.warningBorderColor) ||
(props.isDisabled && props.theme.input.disabledBorderColor) ||
props.theme.input.borderColor};
cursor: ${(props) => (props.isDisabled ? "default" : "pointer")};
}
.icon-button {
cursor: ${(props) => (props.isDisabled ? "default" : "pointer")};
}
`;
StyledFileInput.defaultProps = { theme: Base };
export default StyledFileInput;

View File

@ -0,0 +1,110 @@
import React from "react";
import PropTypes from "prop-types";
import IconButton from "@appserver/components/icon-button";
import TextInput from "@appserver/components/text-input";
import StyledFileInput from "./StyledSimpleFileInput";
let iconSize;
const SimpleFileInput = ({
size,
placeholder,
isDisabled,
scale,
isError,
hasWarning,
id,
onClickInput,
name,
className,
textField,
...rest
}) => {
switch (size) {
case "base":
iconSize = 15;
break;
case "middle":
iconSize = 15;
break;
case "big":
iconSize = 16;
break;
case "huge":
iconSize = 16;
break;
case "large":
iconSize = 16;
break;
}
return (
<StyledFileInput
size={size}
scale={scale ? 1 : 0}
hasError={isError}
hasWarning={hasWarning}
isDisabled={isDisabled}
className={className}
{...rest}
>
<TextInput
id={id}
className="file-text-input"
placeholder={placeholder}
value={textField}
size={size}
isDisabled={isDisabled}
hasError={isError}
hasWarning={hasWarning}
scale={scale}
onClick={onClickInput}
isReadOnly
name={name}
/>
<div className="icon" onClick={!isDisabled ? onClickInput : null}>
<IconButton
className="icon-button"
iconName={"/static/images/catalog.folder.react.svg"}
color={"#A3A9AE"}
isDisabled={isDisabled}
size={iconSize}
/>
</div>
</StyledFileInput>
);
};
SimpleFileInput.propTypes = {
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Placeholder text for the input */
placeholder: PropTypes.string,
/** Supported size of the input fields */
size: PropTypes.oneOf(["base", "middle", "big", "huge", "large"]),
/** Indicates the input field has scale */
scale: PropTypes.bool,
/** Accepts class */
className: PropTypes.string,
/** Indicates the input field has an error */
hasError: PropTypes.bool,
/** Indicates the input field has a warning */
hasWarning: PropTypes.bool,
/** Used as HTML `id` property */
id: PropTypes.string,
/** Indicates that the field cannot be used (e.g not authorised, or changes not saved) */
isDisabled: PropTypes.bool,
/** Used as HTML `name` property */
name: PropTypes.string,
};
SimpleFileInput.defaultProps = {
size: "base",
scale: false,
hasWarning: false,
hasError: false,
isDisabled: false,
baseFolder: "",
};
export default SimpleFileInput;

View File

@ -0,0 +1,153 @@
import React, { useState } from "react";
import {
StyledAsidePanel,
StyledSelectFilePanel,
StyledHeaderContent,
} from "../StyledPanels";
import Text from "@appserver/components/text";
import SelectFolderInput from "../SelectFolderInput";
import FilesListBody from "./FilesListBody";
import Aside from "@appserver/components/aside";
import Heading from "@appserver/components/heading";
import Backdrop from "@appserver/components/backdrop";
import Button from "@appserver/components/button";
import Loaders from "@appserver/common/components/Loaders";
import Loader from "@appserver/components/loader";
import EmptyContainer from "../../EmptyContainer/EmptyContainer";
const DISPLAY_TYPE = "aside";
const SelectFileDialogAsideView = ({
t,
isPanelVisible,
zIndex,
onClose,
isVisible,
withoutProvider,
foldersType,
onSelectFile,
onClickInput,
onCloseSelectFolderDialog,
onSelectFolder,
filesList,
hasNextPage,
isNextPageLoading,
loadNextPage,
selectedFolder,
header,
loadingText,
selectedFile,
onClickSave,
onSetFileName,
fileName,
displayType,
isTranslationsReady,
passedId,
headerName,
isAvailableFolderList,
}) => {
const [isLoadingData, setIsLoadingData] = useState(false);
const onSetLoadingData = (loading) => {
setIsLoadingData(loading);
};
const isHeaderChildren = !!header;
return (
<StyledAsidePanel visible={isPanelVisible}>
<Backdrop
onClick={onClose}
visible={isPanelVisible}
zIndex={zIndex}
isAside={true}
/>
<Aside visible={isPanelVisible} zIndex={zIndex}>
{isTranslationsReady ? (
<StyledSelectFilePanel
displayType={DISPLAY_TYPE}
isHeaderChildren={isHeaderChildren}
>
<StyledHeaderContent className="select-file-dialog_aside-header">
<Heading
size="medium"
className="select-file-dialog_aside-header_title"
>
{headerName ? headerName : t("SelectFile")}
</Heading>
</StyledHeaderContent>
<div className="select-file-dialog_aside-body_wrapper">
<div className="select-file-dialog_aside-children">{header}</div>
<Text fontWeight="600" fontSize="14px">
{t("Translations:SelectFolder")}
</Text>
<div className="select-file-dialog_aside_body">
<SelectFolderInput
onClickInput={onClickInput}
onClose={onCloseSelectFolderDialog}
onSelectFolder={onSelectFolder}
onSetLoadingData={onSetLoadingData}
isPanelVisible={isVisible}
foldersType={foldersType}
isNeedArrowIcon
withoutProvider={withoutProvider}
isSetFolderImmediately
selectedFolderId={selectedFolder}
id={passedId}
onSetFileName={onSetFileName}
fileName={fileName}
displayType={displayType}
dialogWithFiles
/>
{selectedFolder && !isLoadingData ? (
<FilesListBody
filesList={filesList}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
selectedFolder={selectedFolder}
displayType={DISPLAY_TYPE}
loadingText={loadingText}
selectedFile={selectedFile}
/>
) : isAvailableFolderList ? (
<div key="loader">
<Loader type="oval" size="16px" className="panel-loader" />
<Text as="span">{`${t("Common:LoadingProcessing")} ${t(
"Common:LoadingDescription"
)}`}</Text>
</div>
) : (
<div className="select-file-dialog_empty-container">
<EmptyContainer
headerText={t("Home:EmptyFolderHeader")}
imageSrc="/static/images/empty_screen.png"
/>
</div>
)}
</div>
</div>
<div className="select-file-dialog-aside_buttons">
<Button
className="select-file-dialog-buttons-save"
primary
size="big"
label={t("Common:SaveButton")}
onClick={onClickSave}
isDisabled={selectedFile.length === 0}
/>
<Button
primary
size="big"
label={t("Common:CloseButton")}
onClick={onClose}
/>
</div>
</StyledSelectFilePanel>
) : (
<Loaders.DialogAsideLoader withoutAside isPanel />
)}
</Aside>
</StyledAsidePanel>
);
};
export default SelectFileDialogAsideView;

View File

@ -0,0 +1,177 @@
import React, { useCallback, useEffect, useRef } from "react";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import { useTranslation, withTranslation } from "react-i18next";
import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-scrollbars-virtual-list";
import InfiniteLoader from "react-window-infinite-loader";
import AutoSizer from "react-virtualized-auto-sizer";
import { FixedSizeList as List } from "react-window";
import { inject, observer } from "mobx-react";
import FilesListRow from "./FilesListRow";
import EmptyContainer from "../../EmptyContainer/EmptyContainer";
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
const FilesListBody = ({
filesList,
onSelectFile,
loadNextPage,
hasNextPage,
isNextPageLoading,
displayType,
viewer,
listHeight,
needRowSelection,
loadingText,
selectedFolder,
isMultiSelect,
selectedFile,
}) => {
const { t } = useTranslation(["SelectFile", "Common"]);
const filesListRef = useRef(null);
useEffect(() => {
if (filesListRef && filesListRef.current) {
filesListRef.current.resetloadMoreItemsCache(true);
}
}, [selectedFolder, displayType]);
// Every row is loaded except for our loading indicator row.
const isItemLoaded = useCallback(
(index) => {
return !hasNextPage || index < filesList.length;
},
[filesList, hasNextPage]
);
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasNextPage ? filesList.length + 1 : filesList.length;
const loadMoreItems = useCallback(() => {
if (isNextPageLoading) return;
loadNextPage && loadNextPage();
}, [isNextPageLoading, filesList, displayType]);
const renderLoader = useCallback(
(style) => {
return (
<div style={style}>
<div key="loader" className="panel-loader-wrapper">
<Loader type="oval" size="16px" className="panel-loader" />
<Text as="span">{loadingText}</Text>
</div>
</div>
);
},
[loadingText]
);
const isFileChecked = useCallback(
(file) => {
const checked = selectedFile ? file.id === selectedFile.id : false;
return checked;
},
[selectedFile]
);
const Item = useCallback(
({ index, style }) => {
const isLoaded = isItemLoaded(index);
if (!isLoaded) {
return renderLoader(style);
}
const file = filesList[index];
const fileName = file.title;
const fileExst = file.fileExst;
const modifyFileName = fileName.substring(
0,
fileName.indexOf(`${fileExst}`)
);
const fileOwner =
file.createdBy &&
((viewer.id === file.createdBy.id && t("Common:MeLabel")) ||
file.createdBy.displayName);
const isChecked = isFileChecked(file);
return (
<div style={style}>
<FilesListRow
displayType={displayType}
needRowSelection={needRowSelection}
index={index}
onSelectFile={onSelectFile}
fileName={modifyFileName}
fileExst={fileExst}
isMultiSelect={isMultiSelect}
isChecked={isChecked}
>
<Text data-index={index} className="files-list_file-owner">
{fileOwner}
</Text>
</FilesListRow>
</div>
);
},
[filesList, selectedFile, displayType, renderLoader]
);
return (
<>
<AutoSizer>
{({ width, height }) => (
<InfiniteLoader
ref={filesListRef}
isItemLoaded={isItemLoaded}
itemCount={itemCount}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
height={displayType === "aside" ? height : listHeight}
itemCount={itemCount}
itemSize={displayType === "aside" ? 56 : 36}
onItemsRendered={onItemsRendered}
ref={ref}
width={width + 8}
outerElementType={CustomScrollbarsVirtualList}
>
{Item}
</List>
)}
</InfiniteLoader>
)}
</AutoSizer>
{!hasNextPage && itemCount === 0 && (
<div className="select-file-dialog_empty-container">
<EmptyContainer
headerText={t("Home:EmptyFolderHeader")}
imageSrc="/static/images/empty_screen.png"
/>
</div>
)}
</>
);
};
FilesListBody.defaultProps = {
listHeight: 300,
isMultiSelect: false,
};
const FilesListBodyWrapper = inject(({ auth }) => {
const { user } = auth.userStore;
return {
viewer: user,
};
})(observer(withTranslation(["Common", "Home"])(FilesListBody)));
class FilesList extends React.Component {
render() {
return (
<I18nextProvider i18n={i18n}>
<FilesListBodyWrapper {...this.props} />
</I18nextProvider>
);
}
}
export default FilesList;

View File

@ -0,0 +1,76 @@
import React from "react";
import { StyledFilesList } from "../StyledPanels";
import { ReactSVG } from "react-svg";
import { inject, observer } from "mobx-react";
import Text from "@appserver/components/text";
import Checkbox from "@appserver/components/checkbox";
import RadioButton from "@appserver/components/radio-button";
const FilesListRow = ({
displayType,
needRowSelection,
index,
onSelectFile,
fileName,
children,
fileExst,
iconSrc,
isMultiSelect, // it will be needed
isChecked,
}) => {
return (
<StyledFilesList
displayType={displayType}
needRowSelection={needRowSelection}
isChecked={isChecked}
>
<div
data-index={index}
className="modal-dialog_file-name"
onClick={onSelectFile}
>
{isMultiSelect ? ( // it will be needed
<Checkbox
label=""
isChecked={isChecked}
className="select-file-dialog_checked"
/>
) : (
<RadioButton
fontSize="13px"
fontWeight="400"
name={`${index}`}
label=""
isChecked={isChecked}
onClick={onSelectFile}
value=""
className="select-file-dialog_checked"
/>
)}
<ReactSVG src={iconSrc} className="select-file-dialog_icon" />
<div data-index={index} className="files-list_full-name">
<Text data-index={index} className="entry-title">
{fileName}
</Text>
<div data-index={index} className="file-exst">
{fileExst}
</div>
</div>
<div className="files-list_file-children_wrapper">{children}</div>
</div>
</StyledFilesList>
);
};
FilesListRow.defaultProps = {
needRowSelection: true,
isMultiSelect: false,
};
export default inject(({ formatsStore }, { fileExst }) => {
const { iconFormatsStore } = formatsStore;
const iconSrc = iconFormatsStore.getIconSrc(fileExst, 24);
return {
iconSrc,
};
})(observer(FilesListRow));

View File

@ -0,0 +1,259 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import stores from "../../../store/index";
import { StyledAsidePanel, StyledSelectFilePanel } from "../StyledPanels";
import ModalDialog from "@appserver/components/modal-dialog";
import SelectFolderDialog from "../SelectFolderDialog";
import FolderTreeBody from "../../FolderTreeBody";
import FilesListBody from "./FilesListBody";
import Button from "@appserver/components/button";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import { isArrayEqual } from "@appserver/components/utils/array";
import { FolderType } from "@appserver/common/constants";
import { getFoldersTree } from "@appserver/common/api/files";
const exceptSortedByTagsFolders = [
FolderType.Recent,
FolderType.TRASH,
FolderType.Favorites,
];
const exceptTrashFolder = [FolderType.TRASH];
class SelectFileDialogModalView extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
isAvailable: true,
};
this.folderList = "";
}
componentDidMount() {
const { onSetLoadingData } = this.props;
this.setState({ isLoadingData: true }, function () {
onSetLoadingData && onSetLoadingData(true);
this.trySwitch();
});
}
trySwitch = async () => {
const {
foldersType,
onSelectFolder,
selectedFolder,
passedId,
} = this.props;
switch (foldersType) {
case "exceptSortedByTags":
try {
const foldersTree = await getFoldersTree();
this.folderList = SelectFolderDialog.convertFolders(
foldersTree,
exceptSortedByTagsFolders
);
this.onSetSelectedFolder();
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "exceptTrashFolder":
try {
const foldersTree = await getFoldersTree();
this.folderList = SelectFolderDialog.convertFolders(
foldersTree,
exceptTrashFolder
);
this.onSetSelectedFolder();
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "common":
try {
this.folderList = await SelectFolderDialog.getCommonFolders();
!selectedFolder &&
onSelectFolder &&
onSelectFolder(
`${
selectedFolder
? selectedFolder
: passedId
? passedId
: this.folderList[0].id
}`
);
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "third-party":
try {
this.folderList = await SelectFolderDialog.getCommonThirdPartyList();
this.folderList.length !== 0
? this.onSetSelectedFolder()
: this.setState({ isAvailable: false });
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
}
};
loadersCompletes = () => {
const { onSetLoadingData } = this.props;
onSetLoadingData && onSetLoadingData(false);
this.setState({
isLoading: false,
});
};
onSetSelectedFolder = () => {
const { onSelectFolder, selectedFolder, passedId } = this.props;
onSelectFolder &&
onSelectFolder(
`${
selectedFolder
? selectedFolder
: passedId
? passedId
: this.folderList[0].id
}`
);
};
onSelect = (folder) => {
const { onSelectFolder, selectedFolder } = this.props;
if (isArrayEqual([folder[0]], [selectedFolder])) {
return;
}
onSelectFolder && onSelectFolder(folder[0]);
};
render() {
const {
t,
isPanelVisible,
onClose,
zIndex,
withoutProvider,
expandedKeys,
filter,
onSelectFile,
filesList,
hasNextPage,
isNextPageLoading,
loadNextPage,
selectedFolder,
header,
loadingText,
selectedFile,
onClickSave,
headerName,
} = this.props;
const { isLoading, isAvailable } = this.state;
const isHeaderChildren = !!header;
return (
<StyledAsidePanel visible={isPanelVisible}>
<ModalDialog
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
className="select-file-modal-dialog"
style={{ maxWidth: "890px" }}
displayType="modal"
bodyPadding="0"
>
<ModalDialog.Header>
{headerName ? headerName : t("SelectFile")}
</ModalDialog.Header>
<ModalDialog.Body className="select-file_body-modal-dialog">
<StyledSelectFilePanel isHeaderChildren={isHeaderChildren}>
{!isLoading ? (
<div className="modal-dialog_body">
<div className="modal-dialog_children">{header}</div>
<div className="modal-dialog_tree-body">
<FolderTreeBody
expandedKeys={expandedKeys}
folderList={this.folderList}
onSelect={this.onSelect}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
filter={filter}
selectedKeys={[selectedFolder]}
isHeaderChildren={isHeaderChildren}
/>
</div>
<div className="modal-dialog_files-body">
{selectedFolder && (
<FilesListBody
filesList={filesList}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
selectedFolder={selectedFolder}
loadingText={loadingText}
selectedFile={selectedFile}
listHeight={isHeaderChildren ? 280 : 310}
/>
)}
</div>
</div>
) : (
<div
key="loader"
className="select-file-dialog_modal-loader panel-loader-wrapper"
>
<Loader type="oval" size="16px" className="panel-loader" />
<Text as="span">{`${t("Common:LoadingProcessing")} ${t(
"Common:LoadingDescription"
)}`}</Text>
</div>
)}
</StyledSelectFilePanel>
</ModalDialog.Body>
<ModalDialog.Footer>
<StyledSelectFilePanel isHeaderChildren={isHeaderChildren}>
<div className="select-file-dialog-modal_buttons">
<Button
className="select-file-modal-dialog-buttons-save"
primary
size="medium"
label={t("Common:SaveButton")}
onClick={onClickSave}
isDisabled={selectedFile.length === 0}
/>
<Button
className="modal-dialog-button"
primary
size="medium"
label={t("Common:CloseButton")}
onClick={onClose}
/>
</div>
</StyledSelectFilePanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
);
}
}
export default SelectFileDialogModalView;

View File

@ -0,0 +1,35 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
import { loadLanguagePath } from "@appserver/common/utils";
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "all",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFile", "Common", "Home", "Translations"],
defaultNS: "SelectFile",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -0,0 +1,389 @@
import React from "react";
import { inject, observer, Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import { withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import throttle from "lodash/throttle";
import stores from "../../../store/index";
import i18n from "./i18n";
import SelectFileDialogModalView from "./ModalView";
import SelectFileDialogAsideView from "./AsideView";
import utils from "@appserver/components/utils";
import SelectFolderDialog from "../SelectFolderDialog";
import { getFolder } from "@appserver/common/api/files";
import { FilterType } from "@appserver/common/constants";
const { desktop } = utils.device;
import store from "studio/store";
const { auth: authStore } = store;
class SelectFileDialogBody extends React.Component {
constructor(props) {
super(props);
const { folderId, storeFolderId, fileInfo, filter } = this.props;
this.state = {
isVisible: false,
selectedFolder: storeFolderId || "",
passedId: folderId,
selectedFile: fileInfo || "",
fileName: (fileInfo && fileInfo.title) || "",
filesList: [],
hasNextPage: true,
isNextPageLoading: false,
displayType: this.getDisplayType(),
page: 0,
filterParams: this.getFilterParameters(),
isAvailableFolderList: true,
};
this.throttledResize = throttle(this.setDisplayType, 300);
this.newFilter = filter.clone();
this._isLoadNextPage = false;
}
getFilterParameters = () => {
const {
isImageOnly,
isDocumentsOnly,
isArchiveOnly,
isPresentationOnly,
isTablesOnly,
isMediaOnly,
searchParam = "",
} = this.props;
if (isImageOnly) {
return { filterType: FilterType.ImagesOnly, filterValue: searchParam };
}
if (isDocumentsOnly) {
return { filterType: FilterType.DocumentsOnly, filterValue: searchParam };
}
if (isArchiveOnly) {
return { filterType: FilterType.ArchiveOnly, filterValue: searchParam };
}
if (isPresentationOnly) {
return {
filterType: FilterType.PresentationsOnly,
filterValue: searchParam,
};
}
if (isTablesOnly) {
return {
filterType: FilterType.SpreadsheetsOnly,
filterValue: searchParam,
};
}
if (isMediaOnly) {
return { filterType: FilterType.MediaOnly, filterValue: searchParam };
}
return { filterType: FilterType.FilesOnly, filterValue: "" };
};
setFilter = () => {
const { filterParams } = this.state;
const { withSubfolders = true } = this.props;
this.newFilter.filterType = filterParams.filterType;
this.newFilter.search = filterParams.filterValue;
this.newFilter.withSubfolders = withSubfolders;
};
componentDidMount() {
authStore.init(true); // it will work if authStore is not initialized
window.addEventListener("resize", this.throttledResize);
this.setFilter();
}
componentWillUnmount() {
const {
resetTreeFolders,
setExpandedPanelKeys,
setDefaultSelectedFolder,
setFolderId,
setFile,
} = this.props;
this.throttledResize && this.throttledResize.cancel();
window.removeEventListener("resize", this.throttledResize);
if (resetTreeFolders) {
setExpandedPanelKeys(null);
setDefaultSelectedFolder();
setFolderId(null);
setFile(null);
}
}
getDisplayType = () => {
const displayType =
window.innerWidth < desktop.match(/\d+/)[0] ? "aside" : "modal";
return displayType;
};
setDisplayType = () => {
const displayType = this.getDisplayType();
this.setState({ displayType: displayType });
};
onClickInput = () => {
this.setState({
isVisible: true,
});
};
onCloseSelectFolderDialog = () => {
this.setState({
isVisible: false,
});
};
onSelectFolder = (id) => {
const { setFolderId } = this.props;
if (id) {
setFolderId(id);
this.setState({
selectedFolder: id,
hasNextPage: true,
filesList: [],
page: 0,
});
} else
this.setState({
isAvailableFolderList: false,
});
};
onSelectFile = (e) => {
const { filesList } = this.state;
const { setFile } = this.props;
const index = e.target.dataset.index || e.target.name;
if (!index) return;
setFile(filesList[+index]);
this.setState({
selectedFile: filesList[+index],
fileName: filesList[+index].title,
});
};
onClickSave = () => {
const { onSetFileName, onClose, onSelectFile } = this.props;
const { fileName, selectedFile } = this.state;
onSetFileName && onSetFileName(fileName);
onSelectFile && onSelectFile(selectedFile);
onClose && onClose();
};
loadNextPage = () => {
const { setSelectedNode, setSelectedFolder } = this.props;
const { selectedFolder, page } = this.state;
if (this._isLoadNextPage) return;
this._isLoadNextPage = true;
const pageCount = 30;
this.newFilter.page = page;
this.newFilter.pageCount = pageCount;
this.setState({ isNextPageLoading: true }, () => {
getFolder(selectedFolder, this.newFilter)
.then((data) => {
let newFilesList = page
? this.state.filesList.concat(data.files)
: data.files;
setSelectedNode([selectedFolder + ""]);
const newPathParts = SelectFolderDialog.convertPathParts(
data.pathParts
);
setSelectedFolder({
folders: data.folders,
...data.current,
pathParts: newPathParts,
...{ new: data.new },
});
this.setState({
hasNextPage: newFilesList.length < data.total,
isNextPageLoading: false,
filesList: newFilesList,
page: page + 1,
});
})
.catch((error) => console.log(error))
.finally(() => (this._isLoadNextPage = false));
});
};
render() {
const {
t,
isPanelVisible,
onClose,
zIndex,
foldersType,
withoutProvider,
header,
loadingLabel,
folderId,
onSetFileName,
tReady,
headerName,
} = this.props;
const {
isVisible,
filesList,
hasNextPage,
isNextPageLoading,
selectedFolder,
displayType,
selectedFile,
fileName,
passedId,
isAvailableFolderList,
} = this.state;
const loadingText = loadingLabel
? loadingLabel
: `${t("Common:LoadingProcessing")} ${t("Common:LoadingDescription")}`;
return displayType === "aside" ? (
<SelectFileDialogAsideView
t={t}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
isVisible={isVisible}
withoutProvider={withoutProvider}
foldersType={foldersType}
filesList={filesList}
onSelectFile={this.onSelectFile}
onClickInput={this.onClickInput}
onClickSave={this.onClickSave}
onCloseSelectFolderDialog={this.onCloseSelectFolderDialog}
onSelectFolder={this.onSelectFolder}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this.loadNextPage}
selectedFolder={selectedFolder}
headerName={headerName}
loadingText={loadingText}
selectedFile={selectedFile}
folderId={folderId}
onSetFileName={onSetFileName}
fileName={fileName}
displayType={displayType}
isTranslationsReady={tReady}
passedId={passedId}
header={header}
isAvailableFolderList={isAvailableFolderList}
/>
) : (
<SelectFileDialogModalView
t={t}
isPanelVisible={isPanelVisible}
onClose={onClose}
onSelectFolder={this.onSelectFolder}
onSelectFile={this.onSelectFile}
foldersType={foldersType}
onClickSave={this.onClickSave}
filesList={filesList}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this.loadNextPage}
selectedFolder={selectedFolder}
withoutProvider={withoutProvider}
headerName={headerName}
loadingText={loadingText}
selectedFile={selectedFile}
folderId={folderId}
passedId={passedId}
header={header}
/>
);
}
}
SelectFileDialogBody.propTypes = {
onClose: PropTypes.func.isRequired,
isPanelVisible: PropTypes.bool.isRequired,
onSelectFile: PropTypes.func.isRequired,
foldersType: PropTypes.oneOf([
"common",
"third-party",
"exceptSortedByTags",
"exceptTrashFolder",
]),
folderId: PropTypes.string,
withoutProvider: PropTypes.bool,
headerName: PropTypes.string,
zIndex: PropTypes.number,
};
SelectFileDialogBody.defaultProps = {
folderId: "",
header: "",
withoutProvider: false,
zIndex: 310,
};
const SelectFileDialogWrapper = inject(
({
filesStore,
selectedFilesStore,
treeFoldersStore,
selectedFolderStore,
}) => {
const {
folderId: storeFolderId,
fileInfo,
setFolderId,
setFile,
} = selectedFilesStore;
const { setSelectedNode, setExpandedPanelKeys } = treeFoldersStore;
const { filter } = filesStore;
const {
setSelectedFolder,
toDefault: setDefaultSelectedFolder,
} = selectedFolderStore;
return {
storeFolderId,
fileInfo,
setFile,
setFolderId,
setSelectedFolder,
setSelectedNode,
filter,
setDefaultSelectedFolder,
setExpandedPanelKeys,
};
}
)(
observer(
withTranslation(["SelectFile", "Common", "Translations"])(
SelectFileDialogBody
)
)
);
class SelectFileDialog extends React.Component {
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileDialogWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileDialog;

View File

@ -0,0 +1,10 @@
import styled from "styled-components";
const StyledComponent = styled.div`
.file-input {
margin: 16px 0;
width: 100%;
max-width: 820px;
}
`;
export default StyledComponent;

View File

@ -0,0 +1,111 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import PropTypes from "prop-types";
import stores from "../../../store/index";
import SelectFileDialog from "../SelectFileDialog";
import StyledComponent from "./StyledSelectFileInput";
import SimpleFileInput from "../../SimpleFileInput";
class SelectFileInputBody extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
fileName: "",
};
}
onSetFileName = (fileName) => {
this.setState({
fileName: fileName,
});
};
render() {
const {
name,
onClickInput,
isPanelVisible,
withoutProvider,
onClose,
isError,
isDisabled,
foldersType,
withSubfolders,
onSelectFile,
folderId,
headerName,
isImageOnly,
isArchiveOnly,
isDocumentsOnly,
searchParam,
isPresentationOnly,
isTablesOnly,
isMediaOnly,
loadingLabel,
header,
zIndex,
} = this.props;
const { fileName } = this.state;
return (
<StyledComponent>
<SimpleFileInput
name={name}
className="file-input"
textField={fileName}
isDisabled={isDisabled}
isError={isError}
onClickInput={onClickInput}
/>
{isPanelVisible && (
<SelectFileDialog
zIndex={zIndex}
onClose={onClose}
isPanelVisible={isPanelVisible}
foldersType={foldersType}
onSetFileName={this.onSetFileName}
withoutProvider={withoutProvider}
withSubfolders={withSubfolders}
onSelectFile={onSelectFile}
folderId={folderId}
headerName={headerName}
searchParam={searchParam}
isImageOnly={isImageOnly}
isArchiveOnly={isArchiveOnly}
isDocumentsOnly={isDocumentsOnly}
isPresentation={isPresentationOnly}
isTables={isTablesOnly}
isMediaOnly={isMediaOnly}
loadingLabel={loadingLabel}
header={header}
/>
)}
</StyledComponent>
);
}
}
SelectFileInputBody.propTypes = {
onClickInput: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
SelectFileInputBody.defaultProps = {
withoutProvider: false,
isDisabled: false,
zIndex: 310,
};
class SelectFileInput extends React.Component {
render() {
return (
<MobxProvider {...stores}>
<SelectFileInputBody {...this.props} />
</MobxProvider>
);
}
}
export default SelectFileInput;

View File

@ -0,0 +1,109 @@
import React from "react";
import IconButton from "@appserver/components/icon-button";
import FolderTreeBody from "../../FolderTreeBody";
import { StyledAsidePanel, StyledSelectFolderPanel } from "../StyledPanels";
import Button from "@appserver/components/button";
import ModalDialog from "@appserver/components/modal-dialog";
const DISPLAY_TYPE = "aside";
const SelectFolderDialogAsideView = ({
t,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
asideHeightContent,
isAvailable,
certainFolders,
folderId,
isLoadingData,
folderList,
onSelect,
footer,
showButtons,
onSave,
headerName,
header,
canCreate,
isLoading,
}) => {
return (
<StyledAsidePanel visible={isPanelVisible}>
<ModalDialog
visible={isPanelVisible}
zIndex={zIndex}
contentHeight="100%"
contentPaddingBottom={!footer && !showButtons ? "0px" : "80px"}
onClose={onClose}
removeScroll
displayType="aside"
>
<ModalDialog.Header>
<StyledSelectFolderPanel>
<div className="select-folder-dialog_header">
{isNeedArrowIcon && (
<IconButton
className="select-folder-dialog_header-icon"
size="16"
iconName="/static/images/arrow.path.react.svg"
onClick={onClose}
color="#A3A9AE"
/>
)}
{headerName ? headerName : t("Translations:SelectFolder")}
</div>
</StyledSelectFolderPanel>
</ModalDialog.Header>
<ModalDialog.Body>
<StyledSelectFolderPanel
displayType={DISPLAY_TYPE}
showButtons={showButtons}
>
<div className="select-folder-dialog_aside_body">
<div>{header} </div>
<FolderTreeBody
isLoadingData={isLoadingData}
folderList={folderList}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
isAvailable={isAvailable}
selectedKeys={[folderId]}
heightContent={asideHeightContent}
displayType={DISPLAY_TYPE}
/>
</div>
</StyledSelectFolderPanel>
</ModalDialog.Body>
<ModalDialog.Footer>
<StyledSelectFolderPanel>
{footer}
{showButtons && (
<div className="select-folder-dialog-modal_buttons">
<Button
className="select-folder-dialog-buttons-save"
primary
size="big"
label={t("Common:SaveButton")}
onClick={onSave}
isDisabled={isLoadingData || !isAvailable || !canCreate}
/>
<Button
primary
size="big"
label={t("Common:CloseButton")}
onClick={onClose}
isDisabled={isLoadingData || isLoading}
/>
</div>
)}
</StyledSelectFolderPanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
);
};
export default SelectFolderDialogAsideView;

View File

@ -0,0 +1,86 @@
import React, { useEffect } from "react";
import ModalDialog from "@appserver/components/modal-dialog";
import { StyledAsidePanel, StyledSelectFolderPanel } from "../StyledPanels";
import FolderTreeBody from "../../FolderTreeBody";
import Button from "@appserver/components/button";
const SelectFolderDialogModalView = ({
t,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
modalHeightContent,
isAvailable,
certainFolders,
folderId,
isLoadingData,
folderList,
onSelect,
header,
footer,
headerName,
showButtons,
onSave,
canCreate,
isLoading,
}) => {
return (
<StyledAsidePanel visible={isPanelVisible}>
<ModalDialog
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
displayType="modal"
{...(!header && !footer && !showButtons && { contentHeight: "416px" })}
>
<ModalDialog.Header>
{headerName ? headerName : t("Translations:SelectFolder")}
</ModalDialog.Header>
<ModalDialog.Body>
<StyledSelectFolderPanel isNeedArrowIcon={isNeedArrowIcon}>
<div className="select-folder-modal-dialog-header">{header} </div>
<FolderTreeBody
isLoadingData={isLoadingData}
folderList={folderList}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
isAvailable={isAvailable}
selectedKeys={[folderId]}
heightContent={modalHeightContent}
/>
</StyledSelectFolderPanel>
</ModalDialog.Body>
<ModalDialog.Footer>
<StyledSelectFolderPanel>
{footer}
{showButtons && (
<div className="select-folder-dialog-modal_buttons">
<Button
className="select-folder-dialog-buttons-save"
primary
size="medium"
label={t("Common:SaveButton")}
onClick={onSave}
isDisabled={isLoadingData || !isAvailable || !canCreate}
/>
<Button
primary
size="medium"
label={t("Common:CloseButton")}
onClick={onClose}
isDisabled={isLoadingData || isLoading}
/>
</div>
)}
</StyledSelectFolderPanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
);
};
export default SelectFolderDialogModalView;

View File

@ -0,0 +1,35 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
import { loadLanguagePath } from "@appserver/common/utils";
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "all",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFolder", "Common", "Translations"],
defaultNS: "SelectFolder",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -0,0 +1,636 @@
import React from "react";
import { inject, observer, Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import { withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import throttle from "lodash/throttle";
import { getCommonThirdPartyList } from "@appserver/common/api/settings";
import {
getCommonFolderList,
getFolder,
getFolderPath,
getFoldersTree,
} from "@appserver/common/api/files";
import SelectFolderInput from "../SelectFolderInput";
import i18n from "./i18n";
import SelectFolderDialogAsideView from "./AsideView";
import SelectFolderDialogModalView from "./ModalView";
import stores from "../../../store/index";
import utils from "@appserver/components/utils";
import { FolderType } from "@appserver/common/constants";
import { isArrayEqual } from "@appserver/components/utils/array";
import store from "studio/store";
import toastr from "studio/toastr";
const { auth: authStore } = store;
const { desktop } = utils.device;
let pathName = "";
let folderList;
const exceptSortedByTagsFolders = [
FolderType.Recent,
FolderType.TRASH,
FolderType.Favorites,
];
const exceptTrashFolder = [FolderType.TRASH];
class SelectFolderModalDialog extends React.Component {
constructor(props) {
super(props);
const { isSetFolderImmediately, id, displayType } = this.props;
const isNeedFolder = id ? true : isSetFolderImmediately;
this.state = {
isLoadingData: false,
isLoading: false,
isAvailable: true,
certainFolders: true,
folderId: "",
displayType: displayType || this.getDisplayType(),
isSetFolderImmediately: isNeedFolder,
canCreate: true,
};
this.throttledResize = throttle(this.setDisplayType, 300);
this.folderTitle = "";
}
componentDidMount() {
const { onSetLoadingData, onSetLoadingInput, displayType } = this.props;
authStore.init(true); // it will work if authStore is not initialized
!displayType && window.addEventListener("resize", this.throttledResize);
this.setState({ isLoadingData: true }, function () {
onSetLoadingData && onSetLoadingData(true);
onSetLoadingInput && onSetLoadingInput(true);
this.trySwitch();
});
}
componentDidUpdate(prevProps) {
const { storeFolderId, canCreate, showButtons } = this.props;
if (showButtons && storeFolderId !== prevProps.storeFolderId) {
this.setState({
canCreate: canCreate,
isLoading: false,
});
}
}
trySwitch = async () => {
const {
folderPath,
onSelectFolder,
onSetBaseFolderPath,
foldersType,
id,
selectedFolderId,
} = this.props;
switch (foldersType) {
case "exceptSortedByTags":
try {
const foldersTree = await getFoldersTree();
folderList = SelectFolderDialog.convertFolders(
foldersTree,
exceptSortedByTagsFolders
);
this.setBaseSettings();
} catch (err) {
console.error("error", err);
this.loadersCompletes();
}
break;
case "exceptTrashFolder":
try {
const foldersTree = await getFoldersTree();
folderList = SelectFolderDialog.convertFolders(
foldersTree,
exceptTrashFolder
);
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "common":
try {
folderList = await SelectFolderDialog.getCommonFolders();
folderPath.length === 0 &&
!selectedFolderId &&
onSelectFolder &&
onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${
selectedFolderId ? selectedFolderId : id ? id : folderList[0].id
}`,
});
!id &&
!selectedFolderId &&
onSetBaseFolderPath &&
onSetBaseFolderPath(folderList[0].title);
this.setFolderInfo();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "third-party":
try {
folderList = await SelectFolderDialog.getCommonThirdPartyList();
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
}
};
loadersCompletes = () => {
const {
onSetLoadingData,
onSetLoadingInput,
} = this.props;
onSetLoadingData && onSetLoadingData(false);
onSetLoadingInput && onSetLoadingInput(false);
this.setState({
isLoadingData: false,
});
};
setBaseSettings = async () => {
const { isSetFolderImmediately } = this.state;
const {
onSelectFolder,
onSetBaseFolderPath,
id,
selectedFolderId,
showButtons,
} = this.props;
if (folderList.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
this.loadersCompletes();
return;
}
!id && showButtons && this.setFolderToTree(folderList[0].id);
isSetFolderImmediately &&
!selectedFolderId &&
onSelectFolder &&
onSelectFolder(
`${selectedFolderId ? selectedFolderId : id ? id : folderList[0].id}`
);
isSetFolderImmediately &&
this.setState({
folderId: `${
selectedFolderId ? selectedFolderId : id ? id : folderList[0].id
}`,
});
if (onSetBaseFolderPath) {
try {
this.folderTitle = await SelectFolderDialog.getFolderPath(
id ? id : folderList[0].id
);
!id &&
!selectedFolderId &&
isSetFolderImmediately &&
onSetBaseFolderPath(this.folderTitle);
} catch (err) {
console.error(err);
}
}
this.setFolderInfo();
};
setFolderInfo = () => {
const {
id,
onSetFileName,
fileName,
selectedFolderId,
dialogWithFiles,
onSetBaseFolderPath,
} = this.props;
fileName && onSetFileName && onSetFileName(fileName);
if (!id && !selectedFolderId) {
this.loadersCompletes();
return;
}
if (selectedFolderId) {
onSetBaseFolderPath
? this.setBaseFolderPath(selectedFolderId)
: this.loadersCompletes();
}
if (id && !selectedFolderId) {
if (!dialogWithFiles) this.setSelectedFolder(id);
else {
this.setBaseFolderPath(id);
}
}
};
setBaseFolderPath = () => {
const { onSetBaseFolderPath, selectedFolderId } = this.props;
SelectFolderDialog.getFolderPath(selectedFolderId)
.then((folderPath) => (this.folderTitle = folderPath))
.then(() => onSetBaseFolderPath(this.folderTitle))
.catch((error) => console.log("error", error))
.finally(() => {
this.loadersCompletes();
});
};
setSelectedFolder = async (id) => {
const { onSetBaseFolderPath } = this.props;
let folder,
folderPath,
requests = [];
requests.push(getFolder(id));
if (onSetBaseFolderPath) {
requests.push(getFolderPath(id));
}
try {
[folder, folderPath] = await Promise.all(requests);
} catch (e) {
console.error(e);
}
folder && this.setFolderObjectToTree(id, folder);
if (onSetBaseFolderPath && folderPath) {
this.folderTitle = SelectFolderInput.setFullFolderPath(folderPath);
onSetBaseFolderPath(this.folderTitle);
}
this.loadersCompletes();
};
setFolderToTree = (id) => {
getFolder(id)
.then((data) => {
this.setFolderObjectToTree(id, data);
})
.catch((error) => console.log("error", error));
};
setFolderObjectToTree = (id, data) => {
const { setSelectedNode, setSelectedFolder } = this.props;
setSelectedNode([id + ""]);
const newPathParts = SelectFolderDialog.convertPathParts(data.pathParts);
setSelectedFolder({
folders: data.folders,
...data.current,
pathParts: newPathParts,
...{ new: data.new },
});
};
componentWillUnmount() {
const {
setExpandedPanelKeys,
setDefaultSelectedFolder,
resetTreeFolders,
dialogWithFiles,
} = this.props;
if (this.throttledResize) {
this.throttledResize && this.throttledResize.cancel();
window.removeEventListener("resize", this.throttledResize);
}
if (resetTreeFolders && !dialogWithFiles) {
setExpandedPanelKeys(null);
setDefaultSelectedFolder();
}
}
getDisplayType = () => {
const displayType =
window.innerWidth < desktop.match(/\d+/)[0] ? "aside" : "modal";
return displayType;
};
setDisplayType = () => {
const displayType = this.getDisplayType();
this.setState({ displayType: displayType });
};
onSelect = async (folder) => {
const { onSelectFolder, onClose, showButtons, onSetFullPath } = this.props;
const { folderId } = this.state;
let requests = [];
if (isArrayEqual([folder[0]], [folderId])) {
return;
}
this.setState({
folderId: folder[0],
});
let folderInfo, folderPath;
if (showButtons) {
this.setState({
isLoading: true,
canCreate: false,
});
}
try {
if (showButtons && onSetFullPath) {
requests.push(getFolder(folder[0]), getFolderPath(folder));
[folderInfo, folderPath] = await Promise.all(requests);
} else {
showButtons
? (folderInfo = await getFolder(folder[0]))
: (folderPath = await getFolderPath(folder));
}
if (folderInfo) {
this.setFolderObjectToTree(folder[0], folderInfo);
}
if (folderPath) {
pathName = SelectFolderInput.setFullFolderPath(folderPath);
onSetFullPath && onSetFullPath(pathName);
}
} catch (e) {
console.error(e);
toastr.error();
if (showButtons) {
this.setState({
isLoading: false,
canCreate: true,
});
onClose && onClose();
}
}
onSelectFolder && onSelectFolder(folder[0]);
!showButtons && onClose && onClose();
this.loadersCompletes();
};
onSave = (e) => {
const { onClose, onSave } = this.props;
const { folderId } = this.state;
onSave && onSave(e, folderId);
onClose && onClose();
};
render() {
const {
t,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
modalHeightContent,
asideHeightContent,
header,
headerName,
footer,
showButtons,
} = this.props;
const {
isAvailable,
certainFolders,
folderId,
displayType,
isLoadingData,
canCreate,
isLoading,
} = this.state;
return displayType === "aside" ? (
<SelectFolderDialogAsideView
t={t}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
withoutProvider={withoutProvider}
isNeedArrowIcon={isNeedArrowIcon}
asideHeightContent={asideHeightContent}
isAvailable={isAvailable}
certainFolders={certainFolders}
folderId={folderId}
folderList={folderList}
onSelect={this.onSelect}
onSave={this.onSave}
header={header}
headerName={headerName}
footer={footer}
showButtons={showButtons}
isLoadingData={isLoadingData}
canCreate={canCreate}
isLoading={isLoading}
/>
) : (
<SelectFolderDialogModalView
t={t}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
withoutProvider={withoutProvider}
modalHeightContent={modalHeightContent}
isAvailable={isAvailable}
certainFolders={certainFolders}
folderId={folderId}
folderList={folderList}
onSelect={this.onSelect}
onSave={this.onSave}
header={header}
headerName={headerName}
footer={footer}
showButtons={showButtons}
canCreate={canCreate}
isLoadingData={isLoadingData}
isLoading={isLoading}
/>
);
}
}
SelectFolderModalDialog.propTypes = {
onSelectFolder: PropTypes.func,
onClose: PropTypes.func.isRequired,
isPanelVisible: PropTypes.bool.isRequired,
foldersType: PropTypes.oneOf([
"common",
"third-party",
"exceptSortedByTags",
"exceptTrashFolder",
]),
displayType: PropTypes.oneOf(["aside", "modal"]),
id: PropTypes.string,
zIndex: PropTypes.number,
withoutProvider: PropTypes.bool,
isNeedArrowIcon: PropTypes.bool,
dialogWithFiles: PropTypes.bool,
showButtons: PropTypes.bool,
modalHeightContent: PropTypes.string,
asideHeightContent: PropTypes.string,
};
SelectFolderModalDialog.defaultProps = {
isSetFolderImmediately: false,
dialogWithFiles: false,
isNeedArrowIcon: false,
id: "",
modalHeightContent: "325px",
asideHeightContent: "100%",
zIndex: 310,
withoutProvider: false,
folderPath: "",
};
const SelectFolderDialogWrapper = inject(
({
treeFoldersStore,
selectedFolderStore,
selectedFilesStore,
filesStore,
}) => {
const { setSelectedNode, setExpandedPanelKeys } = treeFoldersStore;
const { canCreate } = filesStore;
const {
setSelectedFolder,
id,
toDefault: setDefaultSelectedFolder,
} = selectedFolderStore;
const { setFolderId, setFile } = selectedFilesStore;
return {
setSelectedFolder,
setSelectedNode,
canCreate,
storeFolderId: id,
setExpandedPanelKeys,
setDefaultSelectedFolder,
setFolderId,
setFile,
};
}
)(
observer(
withTranslation(["SelectFolder", "Common", "Translations"])(
SelectFolderModalDialog
)
)
);
class SelectFolderDialog extends React.Component {
static getCommonThirdPartyList = async () => {
const commonThirdPartyArray = await getCommonThirdPartyList();
commonThirdPartyArray.map((currentValue, index) => {
commonThirdPartyArray[index].key = `0-${index}`;
});
return commonThirdPartyArray;
};
static getCommonFolders = async () => {
const commonFolders = await getCommonFolderList();
const convertedData = {
id: commonFolders.current.id,
key: 0 - 1,
parentId: commonFolders.current.parentId,
title: commonFolders.current.title,
rootFolderType: +commonFolders.current.rootFolderType,
rootFolderName: "@common",
folders: commonFolders.folders.map((folder) => {
return {
id: folder.id,
title: folder.title,
access: folder.access,
foldersCount: folder.foldersCount,
rootFolderType: folder.rootFolderType,
providerKey: folder.providerKey,
newItems: folder.new,
};
}),
pathParts: commonFolders.pathParts,
foldersCount: commonFolders.current.foldersCount,
newItems: commonFolders.new,
};
return [convertedData];
};
static getFolderPath = async (folderId) => {
const foldersArray = await getFolderPath(folderId);
const convertFoldersArray = SelectFolderInput.setFullFolderPath(
foldersArray
);
return convertFoldersArray;
};
static convertPathParts = (pathParts) => {
let newPathParts = [];
for (let i = 0; i < pathParts.length - 1; i++) {
if (typeof pathParts[i] === "number") {
newPathParts.push(String(pathParts[i]));
} else {
newPathParts.push(pathParts[i]);
}
}
return newPathParts;
};
static convertFolders = (folders, arrayOfExceptions) => {
let newArray = [];
for (let i = 0; i < folders.length; i++) {
!arrayOfExceptions.includes(folders[i].rootFolderType) &&
newArray.push(folders[i]);
}
return newArray;
};
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderDialogWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderDialog;

View File

@ -0,0 +1,21 @@
import styled from "styled-components";
const StyledComponent = styled.div`
.input-with-folder-path {
margin: 16px 0;
width: 100%;
max-width: 820px;
}
.panel-loader-wrapper {
margin-top: 8px;
padding-left: 32px;
}
.panel-loader {
display: inline;
margin-right: 10px;
}
`;
export default StyledComponent;

View File

@ -0,0 +1,186 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import PropTypes from "prop-types";
import stores from "../../../store/index";
import SelectFolderDialog from "../SelectFolderDialog/index";
import StyledComponent from "./StyledSelectFolderInput";
import SimpleFileInput from "../../SimpleFileInput";
let path = "";
class SelectFolderInputBody extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isLoading: false,
baseFolderPath: "",
fullFolderPath: "",
fullFolderPathDefault: "",
};
}
componentDidMount() {
const { folderPath } = this.props;
if (folderPath.length !== 0) {
this.setState({
fullFolderPath: folderPath,
fullFolderPathDefault: folderPath,
});
}
}
componentDidUpdate(prevProps) {
const { isSetDefaultFolderPath, folderPath } = this.props;
if (
isSetDefaultFolderPath &&
isSetDefaultFolderPath !== prevProps.isSetDefaultFolderPath
) {
this.setState({
fullFolderPath: this.state.fullFolderPathDefault,
});
}
if (folderPath !== prevProps.folderPath) {
this.setState({
fullFolderPath: folderPath,
fullFolderPathDefault: folderPath,
});
}
}
onSetFullPath = (pathName) => {
this.setState({
fullFolderPath: pathName,
});
};
onSetBaseFolderPath = (pathName) => {
this.setState({
baseFolderPath: pathName,
});
};
onSetLoadingInput = (loading) => {
this.setState({
isLoading: loading,
});
};
render() {
const {
name,
onClickInput,
isPanelVisible,
withoutProvider,
onClose,
isError,
isSavingProcess,
isDisabled,
onSelectFolder,
onSetLoadingData,
foldersType,
folderPath,
isNeedArrowIcon,
isSetFolderImmediately,
id,
selectedFolderId,
displayType,
dialogWithFiles,
modalHeightContent,
asideHeightContent,
zIndex,
showButtons,
header,
headerName,
footer,
} = this.props;
const { isLoading, baseFolderPath, fullFolderPath } = this.state;
return (
<StyledComponent>
<SimpleFileInput
name={name}
className="input-with-folder-path"
textField={fullFolderPath || baseFolderPath}
isDisabled={isLoading || isSavingProcess || isDisabled}
isError={isError}
onClickInput={onClickInput}
/>
<SelectFolderDialog
zIndex={zIndex}
isPanelVisible={isPanelVisible}
onClose={onClose}
folderPath={folderPath}
onSelectFolder={onSelectFolder}
onSetLoadingData={onSetLoadingData}
foldersType={foldersType}
withoutProvider={withoutProvider}
onSetFullPath={this.onSetFullPath}
onSetBaseFolderPath={this.onSetBaseFolderPath}
onSetLoadingInput={this.onSetLoadingInput}
isNeedArrowIcon={isNeedArrowIcon}
isSetFolderImmediately={isSetFolderImmediately}
id={id}
selectedFolderId={selectedFolderId}
displayType={displayType}
dialogWithFiles={dialogWithFiles}
modalHeightContent={modalHeightContent}
asideHeightContent={asideHeightContent}
showButtons={showButtons}
header={header}
headerName={headerName}
footer={footer}
/>
</StyledComponent>
);
}
}
SelectFolderInputBody.propTypes = {
onClickInput: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onSelectFolder: PropTypes.func.isRequired,
onSetLoadingData: PropTypes.func,
isPanelVisible: PropTypes.bool.isRequired,
name: PropTypes.string,
withoutProvider: PropTypes.bool,
isError: PropTypes.bool,
isSavingProcess: PropTypes.bool,
};
SelectFolderInputBody.defaultProps = {
withoutProvider: false,
isDisabled: false,
isError: false,
folderPath: "",
};
class SelectFolderInput extends React.Component {
static setFullFolderPath = (foldersArray) => {
path = "";
if (foldersArray.length > 1) {
for (let item of foldersArray) {
if (!path) {
path = path + `${item.title}`;
} else path = path + " " + "/" + " " + `${item.title}`;
}
} else {
for (let item of foldersArray) {
path = `${item.title}`;
}
}
return path;
};
render() {
return (
<MobxProvider {...stores}>
<SelectFolderInputBody {...this.props} />
</MobxProvider>
);
}
}
export default SelectFolderInput;

View File

@ -514,6 +514,258 @@ const StyledLinkRow = styled.div`
}
`;
const StyledSelectFolderPanel = styled.div`
${(props) =>
props.displayType === "aside" &&
css`
height: 100%;
`}
.modal-dialog_header {
display: flex;
align-items: center;
}
.select-folder-modal-dialog-header {
margin-bottom: 16px;
}
.modal-dialog_header-title {
${(props) => props.isNeedArrowIcon && `margin-left:16px;`}
}
.select-folder-dialog_tree-folder {
height: ${(props) =>
props.heightContent
? props.heightContent
: props.isHeaderChildren
? "284px"
: "300px"};
}
.rc-tree-child-tree-open {
width: fit-content;
}
.select-folder-dialog-buttons-save {
margin-right: 8px;
}
.select-folder-dialog-modal_buttons {
margin-top: 16px;
}
.select-folder-dialog_header {
display: flex;
align-items: center;
}
.select-folder-dialog_header-icon {
margin-right: 16px;
}
.select-folder-dialog_aside_body {
height: calc(100% - 64px);
width: 296px;
}
#folder-tree-scroll-bar {
.nav-thumb-horizontal {
height: 0px !important;
}
}
.tree-folder-Loader {
${(props) =>
props.displayType === "aside"
? css`
margin-top: 16px;
`
: css`
height: ${props.heightContent};
`}
}
`;
const StyledSelectFilePanel = styled.div`
.select-file-dialog_empty-container {
.ec-header {
word-break: break-word;
}
}
${(props) =>
props.displayType === "aside" &&
css`
height: 100%;
overflow: hidden;
`}
.select-file-dialog-modal_buttons {
${(props) =>
props.isHeaderChildren ? "margin-top: 40px" : "margin-top:20px"};
}
.select-file-dialog_aside-body_wrapper {
height: ${(props) =>
props.isHeaderChildren ? "calc(100% - 260px);" : "calc(100% - 212px);"};
}
.select-file-dialog_aside-body_wrapper,
.select-folder-dialog_aside-body_wrapper {
width: 320px;
padding: 0 16px;
box-sizing: border-box;
}
.select-folder-dialog_aside-body_wrapper {
height: 100%;
}
.select-file-dialog_aside-children {
${(props) => props.isHeaderChildren && `padding-bottom: 16px;`}
}
.select-file-dialog_aside_body {
margin-top: 16px;
height: 100%;
width: 290px;
}
.select-file-dialog_aside-header_title {
margin: 0px;
line-height: 56px;
max-width: 474px;
width: 400px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.select-file-dialog_aside-header {
margin-bottom: 16px;
}
.select-file-dialog_aside-header,
.file-name {
border-bottom: 1px solid #eceef1;
}
.file-name {
display: flex;
padding: 7px 0px;
}
.panel-loader-wrapper {
margin-top: 8px;
}
.select-file-dialog_modal-loader {
height: 290px;
padding-top: 16px;
box-sizing: border-box;
}
.panel-loader {
display: inline;
margin-right: 10px;
}
.modal-dialog_children {
grid-area: children;
${(props) => props.isHeaderChildren && `padding: 16px 0;`}
}
.modal-dialog_tree-body {
grid-area: tree;
}
.modal-dialog_files-body {
grid-area: files-list;
}
.modal-dialog_body {
display: grid;
grid-template-columns: 240px 1fr;
height: 300px;
grid-column-gap: 8px;
grid-template-areas: "children children" "tree files-list";
.modal-dialog_tree-body {
${(props) =>
props.isHeaderChildren ? `padding-top: 0;` : `padding-top: 16px;`}
border-right: 1px solid #dee2e6;
}
}
.select-file-dialog-aside_buttons {
position: fixed;
bottom: 0;
padding-top: 8px;
background-color: white;
height: 40px;
width: 100%;
}
.select-file-dialog-buttons-save {
margin-right: 8px;
margin-left: 16px;
}
.select-file-modal-dialog-buttons-save {
margin-right: 8px;
}
.select-folder-dialog-buttons-save {
margin-right: 8px;
}
.select-folder-dialog-modal_buttons {
margin-top: 8px;
}
`;
const StyledFilesList = styled.div`
.select-file-dialog_icon {
margin-right: 8px;
}
.entry-title {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: ${(props) =>
props.displayType === "aside" ? "240px" : "250px"};
}
.files-list_file-owner {
//margin-left: auto;
max-width: 207px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #a3a9ae;
}
.entry-title {
font-weight: 600;
}
.file-exst {
color: #a3a9ae;
}
.modal-dialog_file-name:hover {
background-color: #eceef1;
}
.files-list_full-name {
grid-area: full-name;
display: flex;
${(props) =>
props.displayType === "aside" &&
css`
padding-top: 4px;
`}
}
.select-file-dialog_icon {
grid-area: icon-name;
}
.select-file-dialog_checked {
grid-area: checked-button;
padding-left: 6px;
}
.files-list_file-children_wrapper {
grid-area: owner-name;
margin-right: 16px;
${(props) =>
props.displayType === "aside" &&
css`
margin-top: -17px;
`}
}
.modal-dialog_file-name {
border-radius: 3px;
${(props) => props.isChecked && `background:#eceef1;`}
cursor: ${(props) => (props.needRowSelection ? "pointer" : "default")};
border-bottom: 1px solid #eceef1;
align-items: center;
display: grid;
${(props) =>
props.displayType === "aside"
? css`
height: 56px;
grid-template-areas: "checked-button icon-name full-name full-name" "checked-button icon-name owner-name owner-name";
`
: css`
height: 36px;
grid-template-areas: "checked-button icon-name full-name owner-name";
`}
grid-template-columns: 22px 32px 1fr;
}
`;
export {
StyledAsidePanel,
StyledAddGroupsPanel,
@ -526,4 +778,7 @@ export {
StyledSharingBody,
StyledFooter,
StyledLinkRow,
StyledSelectFolderPanel,
StyledSelectFilePanel,
StyledFilesList,
};

View File

@ -21,7 +21,7 @@ const ShareButton = (props) => {
return (
<IconButton
iconName="images/catalog.shared.react.svg"
iconName="/static/images/catalog.shared.react.svg"
className="upload_panel-icon"
color={color}
isClickable

View File

@ -1,10 +1,13 @@
import authStore from "@appserver/common/store/AuthStore";
import { AppServerConfig } from "@appserver/common/constants";
import config from "../../package.json";
import { combineUrl } from "@appserver/common/utils";
import { combineUrl, toUrlParams } from "@appserver/common/utils";
import { addFileToRecentlyViewed } from "@appserver/common/api/files";
import i18n from "./i18n";
import { request } from "@appserver/common/api/client";
import docserviceStore from "../store/DocserviceStore";
export const setDocumentTitle = (subTitle = null) => {
const { isAuthenticated, settingsStore, product: currentModule } = authStore;
const { organizationName } = settingsStore;
@ -72,3 +75,36 @@ export const openDocEditor = async (
return Promise.resolve();
};
export const SaveAs = (title, url, folderId, openNewTab) => {
const options = {
action: "create",
fileuri: url,
title: title,
folderid: folderId,
response: openNewTab ? null : "message",
};
const params = toUrlParams(options, true);
!openNewTab
? request({
baseURL: combineUrl(AppServerConfig.proxyURL, config.homepage),
method: "get",
url: `/httphandlers/filehandler.ashx?${params}`,
})
.then((data) => console.log("data", data))
.catch((e) => console.error("error", e))
: window.open(
combineUrl(
AppServerConfig.proxyURL,
config.homepage,
`/httphandlers/filehandler.ashx?${params}`
),
"_blank"
);
};
export const canConvert = (fileExst) => {
const { canConvert } = docserviceStore;
return canConvert(fileExst);
};

View File

@ -74,7 +74,12 @@ const SimpleFilesRow = (props) => {
const sharedButton =
item.canShare && showShare ? (
<SharedButton t={t} id={item.id} isFolder={item.isFolder} />
<SharedButton
t={t}
id={item.id}
shared={item.shared}
isFolder={item.isFolder}
/>
) : null;
const element = (

View File

@ -46,7 +46,12 @@ const FilesTile = (props) => {
const { thumbnailUrl } = item;
const sharedButton =
item.canShare && showShare ? (
<SharedButton t={t} id={item.id} isFolder={item.isFolder} />
<SharedButton
t={t}
id={item.id}
shared={item.shared}
isFolder={item.isFolder}
/>
) : null;
const element = (
<ItemIcon id={item.id} icon={item.icon} fileExst={item.fileExst} />

View File

@ -190,20 +190,12 @@ class PureHome extends React.Component {
};
componentDidUpdate(prevProps) {
const {
isLoading,
isProgressFinished,
secondaryProgressDataStoreIcon,
selectionLength,
selectionTitle,
firstLoad,
} = this.props;
if (isLoading !== prevProps.isLoading && !firstLoad) {
if (isLoading) {
showLoader();
} else {
hideLoader();
}
}
if (this.props.isHeaderVisible !== prevProps.isHeaderVisible) {
this.props.setHeaderVisible(this.props.isHeaderVisible);
}
@ -239,7 +231,6 @@ class PureHome extends React.Component {
secondaryProgressDataStoreIcon,
secondaryProgressDataStoreAlert,
isLoading,
dragging,
tReady,
} = this.props;
@ -271,7 +262,6 @@ class PureHome extends React.Component {
isLoaded={!firstLoad}
isHeaderVisible={isHeaderVisible}
onOpenUploadPanel={this.showUploadPanel}
isLoading={isLoading}
firstLoad={firstLoad}
dragging={dragging}
>
@ -365,12 +355,19 @@ export default inject(
? filesStore.selectionTitle
: null;
if (!firstLoad) {
if (isLoading) {
showLoader();
} else {
hideLoader();
}
}
return {
homepage: config.homepage,
firstLoad,
dragging,
fileActionId: id,
isLoading,
viewAs,
uploaded,
converted,

View File

@ -254,7 +254,7 @@ class ConnectClouds extends React.Component {
<EmptyFolderContainer
headerText={t("ConnectAccounts")}
subheadingText={t("ConnectAccountsSubTitle")}
imageSrc="images/empty_screen.png"
imageSrc="/static/images/empty_screen.png"
buttons={
<div className="empty-folder_container-links empty-connect_container-links">
<img

View File

@ -661,6 +661,7 @@ class FilesActionStore {
isThirdPartySelection,
userAccess,
isViewedSelected,
hasSelection,
} = this.filesStore;
const {
@ -682,7 +683,7 @@ class FilesActionStore {
},
{
label: t("Common:Download"),
disabled: !selectionCount,
disabled: !hasSelection,
onClick: () =>
this.downloadAction(t("Translations:ArchivingData")).catch((err) =>
toastr.error(err)
@ -690,7 +691,7 @@ class FilesActionStore {
},
{
label: t("Translations:DownloadAs"),
disabled: !selectionCount || !isWebEditSelected,
disabled: !hasSelection || !isWebEditSelected,
onClick: () => setDownloadDialogVisible(true),
},
{
@ -699,18 +700,18 @@ class FilesActionStore {
isFavoritesFolder ||
isRecentFolder ||
!isAccessedSelected ||
!selectionCount ||
!hasSelection ||
isThirdPartySelection,
onClick: () => setMoveToPanelVisible(true),
},
{
label: t("Translations:Copy"),
disabled: !selectionCount,
disabled: !hasSelection,
onClick: () => setCopyPanelVisible(true),
},
{
label: t("Common:Delete"),
disabled: !selectionCount || !userAccess || isThirdPartySelection,
disabled: !hasSelection || isThirdPartySelection,
onClick: () => {
if (this.settingsStore.confirmDelete) {
setDeleteDialogVisible(true);
@ -751,13 +752,16 @@ class FilesActionStore {
headerMenu.splice(4, 1);
}
if (isRecentFolder || isFavoritesFolder) {
menu.splice(1, 1);
}
if (
(this.authStore.settingsStore.personal &&
!isWebEditSelected &&
!isViewedSelected) ||
selectionCount > 1
this.authStore.settingsStore.personal &&
!isWebEditSelected &&
!isViewedSelected
) {
headerMenu.splice(1, 1);
menu.splice(1, 1);
}
return headerMenu;

View File

@ -1246,6 +1246,10 @@ class FilesStore {
return this.selection.find((el) => el.title).title;
}
get hasSelection() {
return !!this.selection.length;
}
getOptions = (selection, externalAccess = false) => {
const {
canWebEdit,

View File

@ -236,7 +236,7 @@ class IconFormatsStore {
sound = false,
html = false
) => {
const folderPath = `images/icons/${size}`;
const folderPath = `/static/images/icons/${size}`;
if (archive) return `${folderPath}/file_archive.svg`;
@ -331,7 +331,7 @@ class IconFormatsStore {
};
getIconSrc = (ext, size = 24) => {
const folderPath = `images/icons/${size}`;
const folderPath = `/static/images/icons/${size}`;
if (presentInArray(this.archive, ext, true))
return `${folderPath}/file_archive.svg`;

View File

@ -0,0 +1,25 @@
import { makeObservable, action, observable } from "mobx";
class SelectedFilesStore {
folderId = null;
fileInfo = null;
constructor() {
makeObservable(this, {
fileInfo: observable,
folderId: observable,
setFolderId: action,
setFile: action,
});
}
setFolderId = (id) => {
this.folderId = id;
};
setFile = (obj) => {
this.fileInfo = obj;
};
}
export default new SelectedFilesStore();

View File

@ -162,6 +162,16 @@ class TreeFoldersStore {
);
}
}
get selectedKeys() {
const selectedKeys =
this.selectedTreeNode.length > 0 &&
this.selectedTreeNode[0] !== "@my" &&
this.selectedTreeNode[0] !== "@common"
? this.selectedTreeNode
: [this.selectedFolderStore.id + ""];
return selectedKeys;
}
}
export default TreeFoldersStore;

View File

@ -16,7 +16,7 @@ import PrimaryProgressDataStore from "./PrimaryProgressDataStore";
import VersionHistoryStore from "./VersionHistoryStore";
import DialogsStore from "./DialogsStore";
import selectedFilesStore from "./SelectedFilesStore";
import store from "studio/store";
const formatsStore = new FormatsStore(
@ -36,7 +36,8 @@ const filesStore = new FilesStore(
selectedFolderStore,
treeFoldersStore,
formatsStore,
settingsStore
settingsStore,
selectedFilesStore
);
const mediaViewerDataStore = new MediaViewerDataStore(filesStore);
@ -71,6 +72,7 @@ const filesActionsStore = new FilesActionsStore(
const versionHistoryStore = new VersionHistoryStore(filesStore);
//const selectedFilesStore = new SelectedFilesStore(selectedFilesStore);
const stores = {
filesStore,
settingsStore,
@ -82,6 +84,7 @@ const stores = {
treeFoldersStore,
selectedFolderStore,
filesActionsStore,
selectedFilesStore,
};
export default stores;

View File

@ -152,6 +152,8 @@ var config = {
"./app": "./src/Files.jsx",
"./SharingDialog": "./src/components/panels/SharingDialog",
"./utils": "./src/helpers/utils.js",
"./SelectFileDialog": "./src/components/panels/SelectFileDialog",
"./SelectFolderDialog": "./src/components/panels/SelectFolderDialog",
},
shared: {
...deps,

View File

@ -1516,10 +1516,11 @@ namespace ASC.Web.Files.Services.WCFService
return FileOperationsManager.Delete(AuthContext.CurrentAccount.ID, TenantManager.GetCurrentTenant(), foldersId, filesId, false, true, false, GetHttpHeaders());
}
public List<FileOperationResult> CheckConversion(List<List<string>> filesInfoJSON)
public List<FileOperationResult> CheckConversion(List<List<string>> filesInfoJSON, bool sync = false)
{
if (filesInfoJSON == null || filesInfoJSON.Count == 0) return new List<FileOperationResult>();
var results = new List<FileOperationResult>();
var fileDao = GetFileDao();
var files = new List<KeyValuePair<File<T>, bool>>();
foreach (var fileInfo in filesInfoJSON)
@ -1547,7 +1548,14 @@ namespace ASC.Web.Files.Services.WCFService
{
try
{
FileConverter.ExecAsync(file, false, fileInfo.Count > 3 ? fileInfo[3] : null);
if (sync)
{
results.Add(FileConverter.ExecSync(file, fileInfo.Count > 3 ? fileInfo[3] : null));
}
else
{
FileConverter.ExecAsync(file, false, fileInfo.Count > 3 ? fileInfo[3] : null);
}
}
catch (Exception e)
{
@ -1558,9 +1566,14 @@ namespace ASC.Web.Files.Services.WCFService
files.Add(new KeyValuePair<File<T>, bool>(file, false));
}
var results = FileConverter.GetStatus(files).ToList();
return new List<FileOperationResult>(results);
if (sync)
{
return results;
}
else
{
return FileConverter.GetStatus(files).ToList();
}
}
public void ReassignStorage(Guid userFromId, Guid userToId)

View File

@ -115,7 +115,8 @@ namespace ASC.Web.Files
private FileShareLink FileShareLink { get; }
private FileConverter FileConverter { get; }
private FFmpegService FFmpegService { get; }
private IServiceProvider ServiceProvider { get; }
private IServiceProvider ServiceProvider { get; }
public TempStream TempStream { get; }
private UserManager UserManager { get; }
private ILog Logger { get; }
@ -143,7 +144,8 @@ namespace ASC.Web.Files
FileShareLink fileShareLink,
FileConverter fileConverter,
FFmpegService fFmpegService,
IServiceProvider serviceProvider)
IServiceProvider serviceProvider,
TempStream tempStream)
{
FilesLinkUtility = filesLinkUtility;
TenantExtra = tenantExtra;
@ -165,7 +167,8 @@ namespace ASC.Web.Files
FileShareLink = fileShareLink;
FileConverter = fileConverter;
FFmpegService = fFmpegService;
ServiceProvider = serviceProvider;
ServiceProvider = serviceProvider;
TempStream = tempStream;
UserManager = userManager;
Logger = optionsMonitor.CurrentValue;
}
@ -1056,8 +1059,15 @@ namespace ASC.Web.Files
await CreateFile(context, GlobalFolderHelper.FolderMy);
}
else
{
await CreateFile(context, folderId);
{
if (int.TryParse(folderId, out var id))
{
await CreateFile(context, id);
}
else
{
await CreateFile(context, folderId);
}
}
}
@ -1174,10 +1184,21 @@ namespace ASC.Web.Files
}
var fileDao = DaoFactory.GetFileDao<T>();
using var fileStream = req.GetResponse().GetResponseStream();
file.ContentLength = fileStream.Length;
return fileDao.SaveFile(file, fileStream);
using var fileStream = req.GetResponse().GetResponseStream();
if (fileStream.CanSeek)
{
file.ContentLength = fileStream.Length;
return fileDao.SaveFile(file, fileStream);
}
else
{
using var buffered = TempStream.GetBuffered(fileStream);
file.ContentLength = buffered.Length;
return fileDao.SaveFile(file, buffered);
}
}
private void Redirect(HttpContext context)
@ -1235,8 +1256,8 @@ namespace ASC.Web.Files
private async Task TrackFile(HttpContext context)
{
var q = context.Request.Query[FilesLinkUtility.FileId];
var q = context.Request.Query[FilesLinkUtility.FileId];
if (int.TryParse(q, out var id))
{
await TrackFile(context, id);
@ -1281,7 +1302,7 @@ namespace ASC.Web.Files
};
fileData = JsonSerializer.Deserialize<DocumentServiceTracker.TrackerData>(body, options);
}
catch(JsonException e)
catch (JsonException e)
{
Logger.Error("DocService track error read body", e);
throw new HttpException((int)HttpStatusCode.BadRequest, "DocService request is incorrect");

View File

@ -0,0 +1,7 @@
namespace ASC.Files.Core.Model
{
public class CheckConversionModel
{
public bool Sync { get; set; }
}
}

View File

@ -396,7 +396,7 @@ namespace ASC.Web.Files.Utils
return string.Format("fileConvertation-{0}", f.ID);
}
private string FileJsonSerializer(EntryStatusManager EntryManager, File<T> file, string folderTitle)
internal string FileJsonSerializer(EntryStatusManager EntryManager, File<T> file, string folderTitle)
{
if (file == null) return string.Empty;
@ -539,6 +539,7 @@ namespace ASC.Web.Files.Utils
private DocumentServiceConnector DocumentServiceConnector { get; }
private FileTrackerHelper FileTracker { get; }
private BaseCommonLinkUtility BaseCommonLinkUtility { get; }
private EntryStatusManager EntryStatusManager { get; }
private IServiceProvider ServiceProvider { get; }
private IHttpContextAccessor HttpContextAccesor { get; }
@ -560,7 +561,8 @@ namespace ASC.Web.Files.Utils
DocumentServiceHelper documentServiceHelper,
DocumentServiceConnector documentServiceConnector,
FileTrackerHelper fileTracker,
BaseCommonLinkUtility baseCommonLinkUtility,
BaseCommonLinkUtility baseCommonLinkUtility,
EntryStatusManager entryStatusManager,
IServiceProvider serviceProvider)
{
FileUtility = fileUtility;
@ -581,6 +583,7 @@ namespace ASC.Web.Files.Utils
DocumentServiceConnector = documentServiceConnector;
FileTracker = fileTracker;
BaseCommonLinkUtility = baseCommonLinkUtility;
EntryStatusManager = entryStatusManager;
ServiceProvider = serviceProvider;
}
public FileConverter(
@ -601,13 +604,14 @@ namespace ASC.Web.Files.Utils
DocumentServiceHelper documentServiceHelper,
DocumentServiceConnector documentServiceConnector,
FileTrackerHelper fileTracker,
BaseCommonLinkUtility baseCommonLinkUtility,
BaseCommonLinkUtility baseCommonLinkUtility,
EntryStatusManager entryStatusManager,
IServiceProvider serviceProvider,
IHttpContextAccessor httpContextAccesor)
: this(fileUtility, filesLinkUtility, daoFactory, setupInfo, pathProvider, fileSecurity,
fileMarker, tenantManager, authContext, entryManager, filesSettingsHelper,
globalFolderHelper, filesMessageService, fileShareLink, documentServiceHelper, documentServiceConnector, fileTracker,
baseCommonLinkUtility, serviceProvider)
baseCommonLinkUtility, entryStatusManager, serviceProvider)
{
HttpContextAccesor = httpContextAccesor;
}
@ -682,7 +686,7 @@ namespace ASC.Web.Files.Utils
return new ResponseStream(((HttpWebRequest)WebRequest.Create(convertUri)).GetResponse());
}
public File<T> ExecSync<T>(File<T> file, string doc)
public FileOperationResult ExecSync<T>(File<T> file, string doc)
{
var fileDao = DaoFactory.GetFileDao<T>();
var fileSecurity = FileSecurity;
@ -705,9 +709,47 @@ namespace ASC.Web.Files.Utils
var docKey = DocumentServiceHelper.GetDocKey(file);
fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri);
DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, null, null, false, out var convertUri);
return SaveConvertedFile(file, convertUri);
DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, null, null, false, out var convertUri);
var operationResult = new ConvertFileOperationResult
{
Source = string.Format("{{\"id\":\"{0}\", \"version\":\"{1}\"}}", file.ID, file.Version),
OperationType = FileOperationType.Convert,
Error = string.Empty,
Progress = 0,
Result = string.Empty,
Processed = "",
Id = string.Empty,
TenantId = TenantManager.GetCurrentTenant().TenantId,
Account = AuthContext.CurrentAccount,
Delete = false,
StartDateTime = DateTime.Now,
Url = HttpContextAccesor?.HttpContext != null ? HttpContextAccesor.HttpContext.Request.GetUrlRewriter().ToString() : null,
Password = null,
ServerRootPath = BaseCommonLinkUtility.ServerRootPath
};
var operationResultError = string.Empty;
var newFile = SaveConvertedFile(file, fileUri);
if (newFile != null)
{
var folderDao = DaoFactory.GetFolderDao<T>();
var folder = folderDao.GetFolder(newFile.FolderID);
var folderTitle = fileSecurity.CanRead(folder) ? folder.Title : null;
operationResult.Result = GetFileConverter<T>().FileJsonSerializer(EntryStatusManager, newFile, folderTitle);
}
operationResult.Progress = 100;
operationResult.StopDateTime = DateTime.UtcNow;
operationResult.Processed = "1";
if (!string.IsNullOrEmpty(operationResultError))
{
operationResult.Error = operationResultError;
}
return operationResult;
}
public void ExecAsync<T>(File<T> file, bool deleteAfter, string password = null)

View File

@ -1200,15 +1200,15 @@ namespace ASC.Api.Documents
/// <param name="fileId"></param>
/// <returns>Operation result</returns>
[Update("file/{fileId}/checkconversion")]
public IEnumerable<ConversationResult<string>> StartConversion(string fileId)
public IEnumerable<ConversationResult<string>> StartConversion(string fileId, [FromBody(EmptyBodyBehavior = Microsoft.AspNetCore.Mvc.ModelBinding.EmptyBodyBehavior.Allow)] CheckConversionModel model)
{
return FilesControllerHelperString.StartConversion(fileId);
return FilesControllerHelperString.StartConversion(fileId, model?.Sync ?? false);
}
[Update("file/{fileId:int}/checkconversion")]
public IEnumerable<ConversationResult<int>> StartConversion(int fileId)
public IEnumerable<ConversationResult<int>> StartConversion(int fileId, [FromBody(EmptyBodyBehavior = Microsoft.AspNetCore.Mvc.ModelBinding.EmptyBodyBehavior.Allow)] CheckConversionModel model)
{
return FilesControllerHelperInt.StartConversion(fileId);
return FilesControllerHelperInt.StartConversion(fileId, model?.Sync ?? false);
}
/// <summary>
@ -1443,6 +1443,18 @@ namespace ASC.Api.Documents
return FilesControllerHelperInt.GetFileVersionInfo(fileId);
}
[Read("file/{fileId}/presigned")]
public DocumentService.FileLink GetPresignedUri(string fileId)
{
return FilesControllerHelperString.GetPresignedUri(fileId);
}
[Read("file/{fileId:int}/presigned")]
public DocumentService.FileLink GetPresignedUri(int fileId)
{
return FilesControllerHelperInt.GetPresignedUri(fileId);
}
/// <summary>
/// Change version history
/// </summary>

View File

@ -362,17 +362,17 @@ namespace ASC.Files.Helpers
.Select(FileOperationWraperHelper.Get);
}
public IEnumerable<ConversationResult<T>> StartConversion(T fileId)
public IEnumerable<ConversationResult<T>> StartConversion(T fileId, bool sync = false)
{
return CheckConversion(fileId, true);
return CheckConversion(fileId, true, sync);
}
public IEnumerable<ConversationResult<T>> CheckConversion(T fileId, bool start)
public IEnumerable<ConversationResult<T>> CheckConversion(T fileId, bool start, bool sync = false)
{
return FileStorageService.CheckConversion(new List<List<string>>
{
new List<string> { fileId.ToString(), "0", start.ToString() }
})
}, sync)
.Select(r =>
{
var o = new ConversationResult<T>
@ -508,6 +508,11 @@ namespace ASC.Files.Helpers
return FileWrapperHelper.Get(result);
}
public DocumentService.FileLink GetPresignedUri(T fileId)
{
return FileStorageService.GetPresignedUri(fileId);
}
public string UpdateComment(T fileId, int version, string comment)
{
return FileStorageService.UpdateComment(fileId, version, comment);

View File

Before

Width:  |  Height:  |  Size: 790 B

After

Width:  |  Height:  |  Size: 790 B

View File

Before

Width:  |  Height:  |  Size: 941 B

After

Width:  |  Height:  |  Size: 941 B

View File

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 762 B

View File

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 417 B

View File

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

View File

Before

Width:  |  Height:  |  Size: 579 B

After

Width:  |  Height:  |  Size: 579 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 929 B

After

Width:  |  Height:  |  Size: 929 B

View File

Before

Width:  |  Height:  |  Size: 817 B

After

Width:  |  Height:  |  Size: 817 B

View File

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 768 B

View File

Before

Width:  |  Height:  |  Size: 788 B

After

Width:  |  Height:  |  Size: 788 B

View File

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 825 B

View File

Before

Width:  |  Height:  |  Size: 918 B

After

Width:  |  Height:  |  Size: 918 B

View File

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 723 B

View File

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 710 B

View File

Before

Width:  |  Height:  |  Size: 748 B

After

Width:  |  Height:  |  Size: 748 B

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 478 B

After

Width:  |  Height:  |  Size: 478 B

View File

Before

Width:  |  Height:  |  Size: 819 B

After

Width:  |  Height:  |  Size: 819 B

View File

Before

Width:  |  Height:  |  Size: 885 B

After

Width:  |  Height:  |  Size: 885 B

View File

Before

Width:  |  Height:  |  Size: 913 B

After

Width:  |  Height:  |  Size: 913 B

View File

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 884 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 856 B

After

Width:  |  Height:  |  Size: 856 B

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 860 B

After

Width:  |  Height:  |  Size: 860 B

View File

Before

Width:  |  Height:  |  Size: 870 B

After

Width:  |  Height:  |  Size: 870 B

Some files were not shown because too many files have changed in this diff Show More