Merge branch 'release/v1.2' of github.com:ONLYOFFICE/AppServer into feature/ip-security

This commit is contained in:
Viktor Fomin 2022-05-31 18:03:11 +03:00
commit aa7d285025
41 changed files with 1407 additions and 917 deletions

View File

@ -147,6 +147,11 @@ const StyledArticleHeader = styled.div`
padding: 16px 16px 17px;
margin: 0;
justify-content: ${(props) => (props.showText ? "flex-start" : "center")};
height: 61px;
min-height: 61px;
max-height: 61px;
box-sizing: border-box;
}
@media ${mobile} {
@ -204,7 +209,7 @@ const StyledHeading = styled(Heading)`
const StyledIconBox = styled.div`
display: none;
align-items: center;
height: 28px;
height: 20px;
@media ${tablet} {
display: flex;

View File

@ -42,7 +42,11 @@ const ArticleHeader = ({
return (
<StyledArticleHeader showText={showText} {...rest}>
{isTabletView && (isBurgerLoading || showLoader) ? (
<Loaders.ArticleHeader height="20px" />
<Loaders.ArticleHeader
height="20px"
width="20px"
style={{ height: "20px" }}
/>
) : (
<StyledIconBox name="article-burger">
<StyledMenuIcon onClick={onClick} />

View File

@ -559,22 +559,19 @@ Section.SectionFilter = SectionFilter;
Section.SectionBody = SectionBody;
Section.SectionPaging = SectionPaging;
export default inject(({ auth, infoPanelStore }) => {
const { isLoaded, settingsStore } = auth;
export default inject(({ auth }) => {
const { infoPanelStore, isLoaded, settingsStore } = auth;
const {
isHeaderVisible,
isTabletView,
isDesktopClient,
maintenanceExist,
snackbarExist,
setMaintenanceExist,
showText,
} = settingsStore;
let infoPanelIsVisible = false;
if (infoPanelStore) infoPanelIsVisible = infoPanelStore.isVisible;
const { isVisible: infoPanelIsVisible } = infoPanelStore;
return {
isLoaded,
@ -588,6 +585,6 @@ export default inject(({ auth, infoPanelStore }) => {
showText,
infoPanelIsVisible: infoPanelIsVisible,
infoPanelIsVisible,
};
})(observer(Section));

View File

@ -51,9 +51,11 @@ const StyledInfoPanelToggleWrapper = styled.div`
`;
StyledInfoPanelToggleWrapper.defaultProps = { theme: Base };
const SubInfoPanelHeader = ({ children, closeInfoPanel }) => {
const SubInfoPanelHeader = ({ children, setIsVisible }) => {
const content = children?.props?.children;
const closeInfoPanel = () => setIsVisible(false);
return (
<StyledInfoPanelHeader>
<Text className="header-text" fontSize="21px" fontWeight="700">
@ -91,10 +93,7 @@ SubInfoPanelHeader.defaultProps = { theme: Base };
SubInfoPanelHeader.displayName = "SubInfoPanelHeader";
export default inject(({ infoPanelStore }) => {
let closeInfoPanel = () => {};
if (infoPanelStore) {
closeInfoPanel = () => infoPanelStore.setIsVisible(false);
}
return { closeInfoPanel };
export default inject(({ auth }) => {
const { setIsVisible } = auth.infoPanelStore;
return { setIsVisible };
})(observer(SubInfoPanelHeader));

View File

@ -182,15 +182,8 @@ StyledInfoPanelWrapper.defaultProps = { theme: Base };
StyledInfoPanel.defaultProps = { theme: Base };
InfoPanel.defaultProps = { theme: Base };
export default inject(({ infoPanelStore }) => {
let isVisible = false;
let setIsVisible = () => {};
if (infoPanelStore) {
isVisible = infoPanelStore.isVisible;
setIsVisible = infoPanelStore.setIsVisible;
}
export default inject(({ auth }) => {
const { isVisible, setIsVisible } = auth.infoPanelStore;
return {
isVisible,
setIsVisible,

View File

@ -6,6 +6,7 @@ import ModuleStore from "./ModuleStore";
import SettingsStore from "./SettingsStore";
import UserStore from "./UserStore";
import TfaStore from "./TfaStore";
import InfoPanelStore from "./InfoPanelStore";
import { logout as logoutDesktop, desktopConstants } from "../desktop";
import { combineUrl, isAdmin } from "../utils";
import isEmpty from "lodash/isEmpty";
@ -17,6 +18,7 @@ class AuthStore {
moduleStore = null;
settingsStore = null;
tfaStore = null;
infoPanelStore = null;
isLoading = false;
version = null;
@ -29,6 +31,7 @@ class AuthStore {
this.moduleStore = new ModuleStore();
this.settingsStore = new SettingsStore();
this.tfaStore = new TfaStore();
this.infoPanelStore = new InfoPanelStore();
makeAutoObservable(this);
}

View File

@ -0,0 +1,190 @@
function toFileWithPath(file, path) {
if (typeof file?.path === "string") return file;
// on electron, path is already set to the absolute path
const { webkitRelativePath } = file;
Object.defineProperty(file, "path", {
value:
typeof path === "string"
? path
: // If <input webkitdirectory> is set,
// the File will have a {webkitRelativePath} property
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory
typeof webkitRelativePath === "string" && webkitRelativePath.length > 0
? webkitRelativePath
: file.name,
});
return file;
}
const FILES_TO_IGNORE = [
// Thumbnail cache files for macOS and Windows
".DS_Store",
"Thumbs.db", // Windows
];
/**
* Convert a DragEvent's DataTrasfer object to a list of File objects
* NOTE: If some of the items are folders,
* everything will be flattened and placed in the same list but the paths will be kept as a {path} property.
* @param evt
*/
export default async function getFilesFromEvent(evt) {
return isDragEvt(evt) && evt.dataTransfer
? getDataTransferFiles(evt.dataTransfer, evt.type)
: getInputFiles(evt);
}
function isDragEvt(value) {
return !!value.dataTransfer;
}
function getInputFiles(evt) {
const files = isInput(evt.target)
? evt.target.files
? fromList(evt.target.files)
: []
: [];
return files.map((file) => toFileWithPath(file));
}
function isInput(value) {
return value !== null;
}
async function getDataTransferFiles(dt, type) {
// IE11 does not support dataTransfer.items
// See https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/items#Browser_compatibility
if (dt.items) {
const items = fromList(dt.items).filter((item) => item.kind === "file");
// According to https://html.spec.whatwg.org/multipage/dnd.html#dndevents,
// only 'dragstart' and 'drop' has access to the data (source node)
if (type !== "drop") {
return items;
}
const files = await Promise.all(items.map(toFilePromises));
return noIgnoredFiles(flatten(files));
}
return noIgnoredFiles(fromList(dt.files).map((file) => toFileWithPath(file)));
}
function noIgnoredFiles(files) {
return files.filter((file) => FILES_TO_IGNORE.indexOf(file.name) === -1);
}
// IE11 does not support Array.from()
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
// https://developer.mozilla.org/en-US/docs/Web/API/FileList
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList
function fromList(items) {
const files = [];
// tslint:disable: prefer-for-of
for (let i = 0; i < items.length; i++) {
const file = items[i];
files.push(file);
}
return files;
}
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem
function toFilePromises(item) {
if (typeof item.webkitGetAsEntry !== "function") {
return fromDataTransferItem(item);
}
const entry = item.webkitGetAsEntry();
// Safari supports dropping an image node from a different window and can be retrieved using
// the DataTransferItem.getAsFile() API
// NOTE: FileSystemEntry.file() throws if trying to get the file
if (entry && entry.isDirectory) {
return fromDirEntry(entry);
}
return fromDataTransferItem(item);
}
function flatten(items) {
return items.reduce(
(acc, files) => [
...acc,
...(Array.isArray(files) ? flatten(files) : [files]),
],
[]
);
}
function fromDataTransferItem(item) {
const file = item.getAsFile();
if (!file) {
return Promise.reject(`${item} is not a File`);
}
const fwp = toFileWithPath(file);
return Promise.resolve(fwp);
}
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemEntry
async function fromEntry(entry) {
return entry.isDirectory ? fromDirEntry(entry) : fromFileEntry(entry);
}
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry
function fromDirEntry(entry) {
const reader = entry.createReader();
return new Promise((resolve, reject) => {
const entries = [];
let empty = true;
function readEntries() {
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry/createReader
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader/readEntries
reader.readEntries(
async (batch) => {
if (!batch.length) {
// Done reading directory
try {
const files = await Promise.all(entries);
if (empty) {
files.push([createEmptyDirFile(entry)]);
}
resolve(files);
} catch (err) {
reject(err);
}
} else {
const items = Promise.all(batch.map(fromEntry));
entries.push(items);
// Continue reading
empty = false;
readEntries();
}
},
(err) => {
reject(err);
}
);
}
readEntries();
});
}
function createEmptyDirFile(entry) {
const file = new File([], entry.name);
const fwp = toFileWithPath(file, entry.fullPath + "/");
Object.defineProperty(fwp, "isEmptyDirectory", {
value: true,
});
return fwp;
}
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileEntry
async function fromFileEntry(entry) {
return new Promise((resolve, reject) => {
entry.file(
(file) => {
const fwp = toFileWithPath(file, entry.fullPath);
resolve(fwp);
},
(err) => {
reject(err);
}
);
});
}

View File

@ -1,5 +1,6 @@
import React from "react";
import React, { useCallback } from "react";
import { useDropzone } from "react-dropzone";
import getFilesFromEvent from "./get-files-from-event";
import PropTypes from "prop-types";
import StyledDragAndDrop from "./styled-drag-and-drop";
@ -8,7 +9,7 @@ const DragAndDrop = (props) => {
const { isDropZone, children, dragging, className, ...rest } = props;
const classNameProp = className ? className : "";
const onDrop = (acceptedFiles, array) => {
const onDrop = (acceptedFiles) => {
acceptedFiles.length && props.onDrop && props.onDrop(acceptedFiles);
};
@ -25,6 +26,7 @@ const DragAndDrop = (props) => {
onDrop,
onDragOver,
onDragLeave,
getFilesFromEvent: (event) => getFilesFromEvent(event),
});
return (

View File

@ -30,7 +30,7 @@
"react-countdown": "2.3.2",
"react-custom-scrollbars": "^4.2.1",
"react-device-detect": "^1.17.0",
"react-dropzone": "^11.2.4",
"react-dropzone": "^11.4.2",
"react-lifecycles-compat": "^3.0.4",
"react-onclickoutside": "^6.11.2",
"react-svg": "^12.1.0",

View File

@ -78,7 +78,7 @@ class ToggleButton extends Component {
//console.log("ToggleButton render");
return (
<Container {...this.props}>
<Container id={id} className={className} style={style}>
<ToggleButtonContainer
id={id}
className={className}

View File

@ -34,10 +34,26 @@ export default function withFileActions(WrappedFileItem) {
};
onDropZoneUpload = (files, uploadToFolder) => {
const { t, dragging, setDragging, startUpload } = this.props;
const {
t,
dragging,
setDragging,
startUpload,
uploadEmptyFolders,
} = this.props;
dragging && setDragging(false);
startUpload(files, uploadToFolder, t);
const emptyFolders = files.filter((f) => f.isEmptyDirectory);
if (emptyFolders.length > 0) {
uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => {
const onlyFiles = files.filter((f) => !f.isEmptyDirectory);
if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t);
});
} else {
startUpload(files, uploadToFolder, t);
}
};
onDrop = (items) => {
@ -247,6 +263,7 @@ export default function withFileActions(WrappedFileItem) {
onSelectItem,
setNewBadgeCount,
openFileAction,
uploadEmptyFolders,
} = filesActionsStore;
const { setSharingPanelVisible } = dialogsStore;
const {
@ -315,6 +332,7 @@ export default function withFileActions(WrappedFileItem) {
dragging,
setDragging,
startUpload,
uploadEmptyFolders,
draggable,
setTooltipPosition,
setStartDrag,

View File

@ -27,6 +27,7 @@ const Item = ({
onBadgeClick,
showDragItems,
startUpload,
uploadEmptyFolders,
setDragging,
}) => {
const [isDragActive, setIsDragActive] = React.useState(false);
@ -41,9 +42,18 @@ const Item = ({
const onDropZoneUpload = React.useCallback(
(files, uploadToFolder) => {
dragging && setDragging(false);
startUpload(files, uploadToFolder, t);
const emptyFolders = files.filter((f) => f.isEmptyDirectory);
if (emptyFolders.length > 0) {
uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => {
const onlyFiles = files.filter((f) => !f.isEmptyDirectory);
if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t);
});
} else {
startUpload(files, uploadToFolder, t);
}
},
[t, dragging, setDragging, startUpload]
[t, dragging, setDragging, startUpload, uploadEmptyFolders]
);
const onDrop = React.useCallback(
@ -118,6 +128,7 @@ const Items = ({
dragging,
setDragging,
startUpload,
uploadEmptyFolders,
isAdmin,
myId,
@ -273,6 +284,7 @@ const Items = ({
t={t}
setDragging={setDragging}
startUpload={startUpload}
uploadEmptyFolders={uploadEmptyFolders}
item={item}
dragging={dragging}
getFolderIcon={getFolderIcon}
@ -301,6 +313,7 @@ const Items = ({
showText,
setDragging,
startUpload,
uploadEmptyFolders,
]
);
@ -337,7 +350,7 @@ export default inject(
} = treeFoldersStore;
const { id } = selectedFolderStore;
const { moveDragItems, uploadEmptyFolders } = filesActionsStore;
return {
isAdmin: auth.isAdmin,
myId: myFolderId,
@ -352,8 +365,9 @@ export default inject(
dragging,
setDragging,
setStartDrag,
moveDragItems: filesActionsStore.moveDragItems,
moveDragItems,
startUpload,
uploadEmptyFolders,
};
}
)(withTranslation(["Home", "Common", "Translations"])(observer(Items)));

View File

@ -198,6 +198,8 @@ const EditingWrapperComponent = (props) => {
const [CancelIconIsHovered, setIsHoveredCancel] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const inputRef = React.useRef(null);
const onKeyUpUpdateItem = (e) => {
if (isLoading) return;
@ -228,6 +230,8 @@ const EditingWrapperComponent = (props) => {
)
return false;
if (!document.hasFocus() && inputRef.current === e.target) return false;
!passwordEntryProcess && onClickUpdateItem(e, false);
};
@ -256,6 +260,7 @@ const EditingWrapperComponent = (props) => {
isDisabled={isLoading}
data-itemid={itemId}
withBorder={!isTable}
forwardedRef={inputRef}
/>
)}
{!isUpdatingRowItem && (

View File

@ -33,92 +33,86 @@ const SelectFileDialogAsideView = ({
maxInputWidth,
newFilter,
}) => {
const onMouseEvent = (event) => {
event.stopPropagation();
};
return (
<div onMouseUp={onMouseEvent} onMouseDown={onMouseEvent}>
<ModalDialog
visible={isPanelVisible}
onClose={onClose}
contentHeight="100%"
contentPaddingBottom="0px"
displayType="aside"
withoutBodyScroll
>
<ModalDialog.Header>{dialogName}</ModalDialog.Header>
<ModalDialog.Body className="select-file_body-modal-dialog">
<StyledAsideBody theme={theme}>
<div className="selection-panel_aside-body">
<div className="selection-panel_folder-info">
<Text
className="selection-panel_folder-selection-title"
fontWeight={600}
>
{t("Translations:FolderSelection")}
</Text>
<ModalDialog
visible={isPanelVisible}
onClose={onClose}
contentHeight="100%"
contentPaddingBottom="0px"
displayType="aside"
withoutBodyScroll
>
<ModalDialog.Header>{dialogName}</ModalDialog.Header>
<ModalDialog.Body className="select-file_body-modal-dialog">
<StyledAsideBody theme={theme}>
<div className="selection-panel_aside-body">
<div className="selection-panel_folder-info">
<Text
className="selection-panel_folder-selection-title"
fontWeight={600}
>
{t("Translations:FolderSelection")}
</Text>
<SelectFolderInput
theme={theme}
onClickInput={onClickInput}
onClose={onCloseSelectFolderDialog}
onSelectFolder={onSelectFolder}
isPanelVisible={isFolderPanelVisible}
foldersType={foldersType}
withoutProvider={withoutProvider}
id={folderId}
onSelectFile={onSelectFile}
displayType="aside"
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
files={files}
folderTree={resultingFolderTree}
isFolderTreeLoading={!!!resultingFolderTree}
isNeedArrowIcon
maxInputWidth={maxInputWidth ? maxInputWidth : "446px"}
/>
<SelectFolderInput
theme={theme}
onClickInput={onClickInput}
onClose={onCloseSelectFolderDialog}
onSelectFolder={onSelectFolder}
isPanelVisible={isFolderPanelVisible}
foldersType={foldersType}
withoutProvider={withoutProvider}
id={folderId}
onSelectFile={onSelectFile}
displayType="aside"
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
files={files}
folderTree={resultingFolderTree}
isFolderTreeLoading={!!!resultingFolderTree}
isNeedArrowIcon
maxInputWidth={maxInputWidth ? maxInputWidth : "446px"}
/>
<Text color="#A3A9AE" className="selection-panel_aside-title">
{filesListTitle}
</Text>
</div>
<div className="selection-panel_files">
<FilesListWrapper
<Text color="#A3A9AE" className="selection-panel_aside-title">
{filesListTitle}
</Text>
</div>
<div className="selection-panel_files">
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType="aside"
folderSelection={false}
fileId={fileId}
newFilter={newFilter}
/>
</div>
<div className="selection-panel_aside-footer">
{footer}
<div className="selection-panel_aside-buttons">
<Button
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType="aside"
folderSelection={false}
fileId={fileId}
newFilter={newFilter}
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={!fileId}
/>
<Button
theme={theme}
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
<div className="selection-panel_aside-footer">
{footer}
<div className="selection-panel_aside-buttons">
<Button
theme={theme}
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={!fileId}
/>
<Button
theme={theme}
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
</div>
</div>
</StyledAsideBody>
</ModalDialog.Body>
</ModalDialog>
</div>
</div>
</StyledAsideBody>
</ModalDialog.Body>
</ModalDialog>
);
};
export default SelectFileDialogAsideView;

View File

@ -47,119 +47,109 @@ const SelectionPanelBody = ({
isDisableButton,
parentId,
}) => {
const onMouseEvent = (event) => {
event.stopPropagation();
};
return (
<div onMouseUp={onMouseEvent} onMouseDown={onMouseEvent}>
<StyledModalDialog
theme={theme}
visible={isPanelVisible}
onClose={onClose}
className="select-file-modal-dialog"
style={{ maxWidth: "773px" }}
displayType="modal"
modalBodyPadding="0px"
isLoading={isLoading}
>
<ModalDialog.Header theme={theme}>{dialogName}</ModalDialog.Header>
<ModalDialog.Body
theme={theme}
className="select-file_body-modal-dialog"
>
<StyledBody header={!!header} footer={!!footer}>
<div className="selection-panel_body">
<div className="selection-panel_tree-body">
<Text
fontWeight="700"
fontSize="18px"
className="selection-panel_folder-title"
>
{t("Common:Documents")}
</Text>
<StyledModalDialog
theme={theme}
visible={isPanelVisible}
onClose={onClose}
style={{ maxWidth: "773px" }}
displayType="modal"
modalBodyPadding="0px"
isLoading={isLoading}
>
<ModalDialog.Header theme={theme}>{dialogName}</ModalDialog.Header>
<ModalDialog.Body theme={theme} className="select-file_body-modal-dialog">
<StyledBody header={!!header} footer={!!footer}>
<div className="selection-panel_body">
<div className="selection-panel_tree-body">
<Text
fontWeight="700"
fontSize="18px"
className="selection-panel_folder-title"
>
{t("Common:Documents")}
</Text>
{folderId && resultingFolderTree ? (
<FolderTreeBody
{folderId && resultingFolderTree ? (
<FolderTreeBody
theme={theme}
folderTree={resultingFolderTree}
onSelect={onSelectFolder}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
selectedKeys={[`${folderId}`]}
parentId={parentId}
expandedKeys={expandedKeys}
isDisableTree={isDisableTree}
displayType="modal"
/>
) : (
<Loaders.NewTreeFolders />
)}
</div>
<div className="selection-panel_files-body">
<>
<div className="selection-panel_files-header">
{header}
<Text
color="#A3A9AE"
theme={theme}
folderTree={resultingFolderTree}
onSelect={onSelectFolder}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
selectedKeys={[`${folderId}`]}
parentId={parentId}
expandedKeys={expandedKeys}
isDisableTree={isDisableTree}
displayType="modal"
/>
) : (
<Loaders.NewTreeFolders />
)}
</div>
<div className="selection-panel_files-body">
<>
<div className="selection-panel_files-header">
{header}
<Text
color="#A3A9AE"
theme={theme}
className="selection-panel_title"
>
{folderSelection
? t("FolderContents", { folderTitle })
: filesListTitle}
</Text>
</div>
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType={"modal"}
folderSelection={folderSelection}
newFilter={newFilter}
fileId={fileId}
/>
</>
</div>
<div className="selection-panel_footer">
<div>{footer}</div>
<div className="selection-panel_buttons">
<Button
theme={theme}
className="select-file-modal-dialog-buttons-save"
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={
isDisableButton ||
isDisableTree ||
isLoadingData ||
(!fileId && !folderSelection) ||
!canCreate
}
isLoading={isDisableTree}
/>
<Button
theme={theme}
className="modal-dialog-button"
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData}
/>
className="selection-panel_title"
>
{folderSelection
? t("FolderContents", { folderTitle })
: filesListTitle}
</Text>
</div>
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType={"modal"}
folderSelection={folderSelection}
newFilter={newFilter}
fileId={fileId}
/>
</>
</div>
<div className="selection-panel_footer">
<div>{footer}</div>
<div className="selection-panel_buttons">
<Button
theme={theme}
className="select-file-modal-dialog-buttons-save"
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={
isDisableButton ||
isDisableTree ||
isLoadingData ||
(!fileId && !folderSelection) ||
!canCreate
}
isLoading={isDisableTree}
/>
<Button
theme={theme}
className="modal-dialog-button"
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData}
/>
</div>
</div>
</StyledBody>
</ModalDialog.Body>
</StyledModalDialog>
</div>
</div>
</StyledBody>
</ModalDialog.Body>
</StyledModalDialog>
);
};

View File

@ -58,9 +58,8 @@ const SectionHeaderContent = (props) => {
);
};
export default inject(({ infoPanelStore }) => {
const { toggleIsVisible, isVisible } = infoPanelStore;
export default inject(({ auth }) => {
const { toggleIsVisible, isVisible } = auth.infoPanelStore;
return {
toggleInfoPanel: toggleIsVisible,
isInfoPanelVisible: isVisible,

View File

@ -56,6 +56,11 @@ const StyledContainer = styled.div`
const StyledInfoPanelToggleWrapper = styled.div`
margin-left: auto;
display: ${(props) => (props.isInfoPanelVisible ? "none" : "flex")};
@media ${tablet} {
display: none;
}
.info-panel-toggle-bg {
height: 32px;
width: 32px;

View File

@ -208,20 +208,18 @@ Tile.defaultProps = {
item: {},
};
export default inject(
({ filesStore, settingsStore, infoPanelStore }, { item }) => {
const { gallerySelected, setGallerySelected } = filesStore;
const { getIcon } = settingsStore;
const { isVisible, setIsVisible } = infoPanelStore;
export default inject(({ filesStore, settingsStore, auth }, { item }) => {
const { gallerySelected, setGallerySelected } = filesStore;
const { getIcon } = settingsStore;
const { isVisible, setIsVisible } = auth.infoPanelStore;
const isSelected = item.id === gallerySelected?.id;
const isSelected = item.id === gallerySelected?.id;
return {
isSelected,
setGallerySelected,
getIcon,
setIsInfoPanelVisible: setIsVisible,
isInfoPanelVisible: isVisible,
};
}
)(withTranslation(["FormGallery", "Common"])(withRouter(observer(Tile))));
return {
isSelected,
setGallerySelected,
getIcon,
setIsInfoPanelVisible: setIsVisible,
isInfoPanelVisible: isVisible,
};
})(withTranslation(["FormGallery", "Common"])(withRouter(observer(Tile))));

View File

@ -56,7 +56,7 @@ const SingleItem = (props) => {
<div className="property">
<Text className="property-title">{t("Home:ByLastModifiedDate")}</Text>
<Text className="property-content">
{parseAndFormatDate(selectedItem.updatedAt)}
{parseAndFormatDate(selectedItem.attributes.updatedAt)}
</Text>
</div>
<div className="property">

View File

@ -69,7 +69,11 @@ const InfoPanelBodyContent = ({
<GalleryEmptyScreen />
) : (
<StyledInfoRoomBody>
<GalleryItem selectedItem={gallerySelected} />
<GalleryItem
selectedItem={gallerySelected}
personal={personal}
culture={culture}
/>
</StyledInfoRoomBody>
)
) : (

View File

@ -97,10 +97,9 @@ const FilesRowContainer = ({
);
};
export default inject(({ filesStore, infoPanelStore }) => {
export default inject(({ filesStore, auth }) => {
const { filesList, viewAs, setViewAs } = filesStore;
const { isVisible: infoPanelVisible } = infoPanelStore;
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
return {
filesList,
viewAs,

View File

@ -163,8 +163,8 @@ const Table = ({
);
};
export default inject(({ filesStore, infoPanelStore, auth }) => {
const { isVisible: infoPanelVisible } = infoPanelStore;
export default inject(({ filesStore, auth }) => {
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
const {
filesList,

View File

@ -228,14 +228,8 @@ class FilesTableHeader extends React.Component {
}
export default inject(
({
auth,
filesStore,
selectedFolderStore,
treeFoldersStore,
infoPanelStore,
}) => {
const { isVisible: infoPanelVisible } = infoPanelStore;
({ auth, filesStore, selectedFolderStore, treeFoldersStore }) => {
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
const {
isHeaderChecked,

View File

@ -62,10 +62,15 @@ const StyledTableRow = styled(TableRow)`
.table-container_file-name-cell {
margin-left: -24px;
padding-left: 24px;
z-index: 1;
}
.table-container_row-context-menu-wrapper {
margin-right: -20px;
padding-right: 18px;
position: relative !important;
z-index: 1;
}
}
`}

View File

@ -351,13 +351,7 @@ const SectionFilterContent = ({
};
export default inject(
({
auth,
filesStore,
treeFoldersStore,
selectedFolderStore,
infoPanelStore,
}) => {
({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => {
const {
fetchFiles,
filter,
@ -382,7 +376,7 @@ export default inject(
authorType) &&
!(treeFoldersStore.isPrivacyFolder && isMobile);
const { isVisible: infoPanelVisible } = infoPanelStore;
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
return {
customNames,

View File

@ -442,7 +442,6 @@ export default inject(
treeFoldersStore,
filesActionsStore,
settingsStore,
infoPanelStore,
}) => {
const {
setSelected,
@ -483,7 +482,7 @@ export default inject(
backToParentFolder,
} = filesActionsStore;
const { toggleIsVisible, isVisible } = infoPanelStore;
const { toggleIsVisible, isVisible } = auth.infoPanelStore;
return {
showText: auth.settingsStore.showText,

View File

@ -205,9 +205,24 @@ class PureHome extends React.Component {
};
onDrop = (files, uploadToFolder) => {
const { t, startUpload, setDragging, dragging } = this.props;
const {
t,
startUpload,
setDragging,
dragging,
uploadEmptyFolders,
} = this.props;
dragging && setDragging(false);
startUpload(files, uploadToFolder, t);
const emptyFolders = files.filter((f) => f.isEmptyDirectory);
if (emptyFolders.length > 0) {
uploadEmptyFolders(emptyFolders, uploadToFolder).then(() => {
const onlyFiles = files.filter((f) => !f.isEmptyDirectory);
if (onlyFiles.length > 0) startUpload(onlyFiles, uploadToFolder, t);
});
} else {
startUpload(files, uploadToFolder, t);
}
};
showOperationToast = (type, qty, title) => {
@ -395,6 +410,7 @@ export default inject(
treeFoldersStore,
mediaViewerDataStore,
settingsStore,
filesActionsStore,
}) => {
const {
secondaryProgressDataStore,
@ -453,6 +469,8 @@ export default inject(
converted,
} = uploadDataStore;
const { uploadEmptyFolders } = filesActionsStore;
const selectionLength = isProgressFinished ? selection.length : null;
const selectionTitle = isProgressFinished
? filesStore.selectionTitle
@ -511,6 +529,7 @@ export default inject(
setUploadPanelVisible,
setSelections,
startUpload,
uploadEmptyFolders,
isHeaderVisible: auth.settingsStore.isHeaderVisible,
setHeaderVisible: auth.settingsStore.setHeaderVisible,
personal: auth.settingsStore.personal,

View File

@ -21,7 +21,6 @@ class ContextOptionsStore {
uploadDataStore;
versionHistoryStore;
settingsStore;
infoPanelStore;
filesSettingsStore;
constructor(
@ -33,8 +32,7 @@ class ContextOptionsStore {
treeFoldersStore,
uploadDataStore,
versionHistoryStore,
settingsStore,
infoPanelStore
settingsStore
) {
makeAutoObservable(this);
this.authStore = authStore;
@ -46,7 +44,6 @@ class ContextOptionsStore {
this.uploadDataStore = uploadDataStore;
this.versionHistoryStore = versionHistoryStore;
this.settingsStore = settingsStore;
this.infoPanelStore = infoPanelStore;
}
onOpenFolder = (item) => {
@ -338,7 +335,7 @@ class ContextOptionsStore {
};
onShowInfoPanel = () => {
const { setIsVisible } = this.infoPanelStore;
const { setIsVisible } = this.authStore.infoPanelStore;
setIsVisible(true);
};

View File

@ -10,6 +10,7 @@ import {
markAsRead,
removeFiles,
removeShareFiles,
createFolder,
} from "@appserver/common/api/files";
import {
ConflictResolveType,
@ -34,7 +35,6 @@ class FilesActionStore {
settingsStore;
dialogsStore;
mediaViewerDataStore;
infoPanelStore;
constructor(
authStore,
@ -44,8 +44,7 @@ class FilesActionStore {
selectedFolderStore,
settingsStore,
dialogsStore,
mediaViewerDataStore,
infoPanelStore
mediaViewerDataStore
) {
makeAutoObservable(this);
this.authStore = authStore;
@ -55,7 +54,6 @@ class FilesActionStore {
this.selectedFolderStore = selectedFolderStore;
this.settingsStore = settingsStore;
this.dialogsStore = dialogsStore;
this.infoPanelStore = infoPanelStore;
this.mediaViewerDataStore = mediaViewerDataStore;
}
@ -108,6 +106,76 @@ class FilesActionStore {
});
};
convertToTree = (folders) => {
let result = [];
let level = { result };
try {
folders.forEach((folder) => {
folder.path
.split("/")
.filter((name) => name !== "")
.reduce((r, name, i, a) => {
if (!r[name]) {
r[name] = { result: [] };
r.result.push({ name, children: r[name].result });
}
return r[name];
}, level);
});
} catch (e) {
console.error("convertToTree", e);
}
return result;
};
createFolderTree = async (treeList, parentFolderId) => {
if (!treeList || !treeList.length) return;
for (let i = 0; i < treeList.length; i++) {
const treeNode = treeList[i];
// console.log(
// `createFolderTree parent id = ${parentFolderId} name '${treeNode.name}': `,
// treeNode.children
// );
const folder = await createFolder(parentFolderId, treeNode.name);
const parentId = folder.id;
if (treeNode.children.length == 0) continue;
await this.createFolderTree(treeNode.children, parentId);
}
};
uploadEmptyFolders = async (emptyFolders, folderId) => {
//console.log("uploadEmptyFolders", emptyFolders, folderId);
const { secondaryProgressDataStore } = this.uploadDataStore;
const {
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = secondaryProgressDataStore;
const toFolderId = folderId ? folderId : this.selectedFolderStore.id;
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label: "",
alert: false,
});
const tree = this.convertToTree(emptyFolders);
await this.createFolderTree(tree, toFolderId);
this.updateCurrentFolder(null, [folderId]);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
};
deleteAction = async (
translations,
newSelection = null,
@ -854,8 +922,6 @@ class FilesActionStore {
setDeleteDialogVisible,
} = this.dialogsStore;
const { toggleIsVisible } = this.infoPanelStore;
switch (option) {
case "share":
if (!this.isAvailableOption("share")) return null;

View File

@ -906,7 +906,7 @@ class FilesStore {
"separator2",
"delete",
]);
if (isThirdPartyFolder) {
if (isThirdPartyItem) {
fileOptions = this.removeOptions(fileOptions, ["rename"]);
}
}

View File

@ -16,7 +16,6 @@ import selectFolderDialogStore from "./SelectFolderDialogStore";
import ContextOptionsStore from "./ContextOptionsStore";
import HotkeyStore from "./HotkeyStore";
import store from "studio/store";
import InfoPanelStore from "./InfoPanelStore";
import selectFileDialogStore from "./SelectFileDialogStore";
const selectedFolderStore = new SelectedFolderStore(store.auth.settingsStore);
@ -56,8 +55,6 @@ const uploadDataStore = new UploadDataStore(
settingsStore
);
const infoPanelStore = new InfoPanelStore();
const filesActionsStore = new FilesActionsStore(
store.auth,
uploadDataStore,
@ -66,8 +63,7 @@ const filesActionsStore = new FilesActionsStore(
selectedFolderStore,
settingsStore,
dialogsStore,
mediaViewerDataStore,
infoPanelStore
mediaViewerDataStore
);
const versionHistoryStore = new VersionHistoryStore(filesStore);
@ -80,8 +76,7 @@ const contextOptionsStore = new ContextOptionsStore(
treeFoldersStore,
uploadDataStore,
versionHistoryStore,
settingsStore,
infoPanelStore
settingsStore
);
const hotkeyStore = new HotkeyStore(
@ -106,7 +101,6 @@ const stores = {
selectFolderDialogStore,
contextOptionsStore,
hotkeyStore,
infoPanelStore,
selectFileDialogStore,
};

View File

@ -22,5 +22,5 @@
"Subscriptions": "Abunəliklər",
"SystemTheme": "Sistem temasından istifadə edin",
"TfaLoginSettings": "Daxilolma sazlamaları",
"TwoFactorDescription": "Bütün istifadəçilər üçün ikili audentifikasiya (kod generasiyası ilə) inzibatçı tərəfindən yandırıldı."
"TwoFactorDescription": "Bütün istifadəçilər üçün ikili autentifikasiya (kod generasiyası ilə) inzibatçı tərəfindən yandırıldı."
}

View File

@ -1,4 +1,4 @@
{
"ResetApplicationDescription": "Audentifikasiya üçün tətbiq sıfırlanacaq.",
"ResetApplicationDescription": "Autentifikasiya üçün tətbiq sıfırlanacaq.",
"ResetApplicationTitle": "Tətbiq konfiqurasiyasını sıfırla"
}

View File

@ -3,7 +3,7 @@
"Error403Text": "Təəssüf ki, giriş rədd edilmişdir.",
"Error404Text": "Təəssüf ki, resurs tapıla bilmir.",
"ErrorEmptyResponse": "Boş cavab",
"ErrorInvalidHeader": "Yalnış e-poçt ünvanı və ya vaxtı bitmiş keçid",
"ErrorInvalidHeader": "Yanlış e-poçt ünvanı və ya vaxtı bitmiş keçid",
"ErrorInvalidText": "10 saniyədə <1>giriş səhifəsinə</1> yönləndiriləcəksiniz",
"ErrorOfflineText": "İnternet bağlantısı tapılmadı."
}

View File

@ -32,7 +32,7 @@
"ChangeLogoButton": "Loqonu dəyişin",
"ChangeOwner": "Portal sahibini dəyişin",
"Characters": "İşarələrin {{length}}",
"ChooseOwner": "Sahibi seçinr",
"ChooseOwner": "Sahibi seçin",
"ClearBackupList": "Bütün ehtiyat nüsxələri silin",
"CompanyNameForCanvasLogo": "Şirkət adı",
"ConfirmEmailSended": "Təsdiqləmə e-məktubu {{ownerName}} göndərilmişdir",

View File

@ -116,8 +116,12 @@ const Layout = (props) => {
// height = window.screen.availHeight - correctorTabletSafari;
// }
// }
const isSmartBanner =
document.getElementsByClassName("smartbanner-container")[0].nodeName ===
"DIV";
const bannerHeight = isSmartBanner ? 80 : 0;
let vh = (height - 48) * 0.01;
let vh = (height - 48 - bannerHeight) * 0.01;
document.documentElement.style.setProperty("--vh", `${vh}px`);
setContentHeight(height);

View File

@ -15,7 +15,7 @@
line-height: 80px;
font-family: Helvetica Neue, sans-serif;
background: #f4f4f4;
z-index: 9998;
z-index: 100;
-webkit-font-smoothing: antialiased;
overflow: hidden;
-webkit-text-size-adjust: none;

View File

@ -15,7 +15,7 @@
"PasswordRecoveryTitle": "Parolun bərpa edilməsi",
"Register": "Qeydiyyatdan keç",
"RegisterSendButton": "Sorğunu göndər",
"RegisterTextBodyAfterDomainsList": "Qeydiyyatdan keçmək üçün, elektron poçt ünvanınıı daxil edin və Sorğunu göndər düyməsinə basın.",
"RegisterTextBodyAfterDomainsList": "Qeydiyyatdan keçmək üçün, elektron poçt ünvanını daxil edin və Sorğunu göndər düyməsinə basın.",
"RegisterTextBodyBeforeDomainsList": "Elektron poçtu olan istifadəçilər üçün qeydiyyat mümkündür",
"RegisterTitle": "Sorğunun qeydiyyatı",
"RegistrationEmail": "Sizin qeydiyyat elektron poçtunuz",

View File

@ -16713,7 +16713,7 @@ react-draggable@^4.4.3:
clsx "^1.1.1"
prop-types "^15.6.0"
react-dropzone@^11.2.4:
react-dropzone@^11.4.2:
version "11.7.1"
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.7.1.tgz#3851bb75b26af0bf1b17ce1449fd980e643b9356"
integrity sha512-zxCMwhfPy1olUEbw3FLNPLhAm/HnaYH5aELIEglRbqabizKAdHs0h+WuyOpmA+v1JXn0++fpQDdNfUagWt5hJQ==