Merge branch 'develop' into bugfix/backup
This commit is contained in:
commit
64893f12f9
@ -24,9 +24,9 @@
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
using Amazon.S3.Internal;
|
||||
using Amazon.Extensions.S3.Encryption;
|
||||
using Amazon.Extensions.S3.Encryption.Primitives;
|
||||
using Amazon.S3.Internal;
|
||||
|
||||
namespace ASC.Data.Storage.S3;
|
||||
|
||||
@ -47,7 +47,7 @@ public class S3Storage : BaseStorage
|
||||
private string _serviceurl;
|
||||
private bool _forcepathstyle;
|
||||
private string _secretAccessKeyId = string.Empty;
|
||||
private ServerSideEncryptionMethod _sse = ServerSideEncryptionMethod.AES256;
|
||||
private readonly ServerSideEncryptionMethod _sse = ServerSideEncryptionMethod.AES256;
|
||||
private bool _useHttp = true;
|
||||
private bool _lowerCasing = true;
|
||||
private bool _revalidateCloudFront;
|
||||
@ -55,7 +55,7 @@ public class S3Storage : BaseStorage
|
||||
private string _subDir = "";
|
||||
|
||||
private EncryptionMethod _encryptionMethod = EncryptionMethod.None;
|
||||
private string _encryptionKey = null;
|
||||
private string _encryptionKey;
|
||||
|
||||
public S3Storage(
|
||||
TempStream tempStream,
|
||||
@ -184,17 +184,17 @@ public class S3Storage : BaseStorage
|
||||
return SaveAsync(domain, path, stream, contentType, contentDisposition, ACL.Auto);
|
||||
}
|
||||
|
||||
private bool EnableQuotaCheck(string domain)
|
||||
{
|
||||
return (QuotaController != null) && !domain.EndsWith("_temp");
|
||||
}
|
||||
private bool EnableQuotaCheck(string domain)
|
||||
{
|
||||
return (QuotaController != null) && !domain.EndsWith("_temp");
|
||||
}
|
||||
|
||||
public async Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentType,
|
||||
string contentDisposition, ACL acl, string contentEncoding = null, int cacheDays = 5)
|
||||
{
|
||||
var buffered = _tempStream.GetBuffered(stream);
|
||||
|
||||
if (EnableQuotaCheck(domain))
|
||||
if (EnableQuotaCheck(domain))
|
||||
{
|
||||
QuotaController.QuotaUsedCheck(buffered.Length);
|
||||
}
|
||||
|
@ -45,7 +45,11 @@ public class ProtobufSerializer : IIntegrationEventSerializer
|
||||
|
||||
private void BuildTypeModelFromAssembly(Assembly assembly)
|
||||
{
|
||||
if (!assembly.GetName().Name.StartsWith("ASC.")) return;
|
||||
var name = assembly.GetName().Name;
|
||||
if (name == null || !name.StartsWith("ASC."))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var types = assembly.GetExportedTypes()
|
||||
.Where(t => t.GetCustomAttributes<ProtoContractAttribute>().Any());
|
||||
@ -64,7 +68,7 @@ public class ProtobufSerializer : IIntegrationEventSerializer
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
Serializer.Serialize(ms, item);
|
||||
@ -74,7 +78,7 @@ public class ProtobufSerializer : IIntegrationEventSerializer
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T Deserialize<T>(byte[] serializedObject)
|
||||
{
|
||||
{
|
||||
using var ms = new MemoryStream(serializedObject);
|
||||
|
||||
return Serializer.Deserialize<T>(ms);
|
||||
@ -107,7 +111,7 @@ public class ProtobufSerializer : IIntegrationEventSerializer
|
||||
if (!baseType.GetSubtypes().Any(s => s.DerivedType == itemType))
|
||||
{
|
||||
baseType.AddSubType(_baseFieldNumber, protoType);
|
||||
|
||||
|
||||
_baseFieldNumber++;
|
||||
|
||||
_processedProtoTypes.Add(protoType.FullName);
|
||||
|
@ -120,7 +120,6 @@ export default function withContent(WrappedContent) {
|
||||
},
|
||||
{ item }
|
||||
) => {
|
||||
const { editCompleteAction } = filesActionsStore;
|
||||
const {
|
||||
createFile,
|
||||
createFolder,
|
||||
@ -164,7 +163,6 @@ export default function withContent(WrappedContent) {
|
||||
createFile,
|
||||
createFolder,
|
||||
culture,
|
||||
editCompleteAction,
|
||||
|
||||
folderFormValidation,
|
||||
homepage: config.homepage,
|
||||
|
@ -331,7 +331,8 @@ export default function withFileActions(WrappedFileItem) {
|
||||
)
|
||||
isActive = true;
|
||||
|
||||
const showHotkeyBorder = hotkeyCaret?.id === item.id;
|
||||
const showHotkeyBorder =
|
||||
hotkeyCaret?.id === item.id && hotkeyCaret?.isFolder === item.isFolder;
|
||||
|
||||
return {
|
||||
t,
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { FileAction } from "@docspace/common/constants";
|
||||
import { Events } from "@docspace/client/src/helpers/filesConstants";
|
||||
import toastr from "client/toastr";
|
||||
import throttle from "lodash/throttle";
|
||||
|
||||
const withHotkeys = (Component) => {
|
||||
const WithHotkeys = (props) => {
|
||||
@ -51,6 +51,7 @@ const withHotkeys = (Component) => {
|
||||
|
||||
selection,
|
||||
setFavoriteAction,
|
||||
filesIsLoading,
|
||||
} = props;
|
||||
|
||||
const hotkeysFilter = {
|
||||
@ -58,7 +59,11 @@ const withHotkeys = (Component) => {
|
||||
ev.target?.type === "checkbox" || ev.target?.tagName !== "INPUT",
|
||||
filterPreventDefault: false,
|
||||
enableOnTags: ["INPUT"],
|
||||
enabled: !someDialogIsOpen && enabledHotkeys && !mediaViewerIsVisible,
|
||||
enabled:
|
||||
!someDialogIsOpen &&
|
||||
enabledHotkeys &&
|
||||
!mediaViewerIsVisible &&
|
||||
!filesIsLoading,
|
||||
// keyup: true,
|
||||
// keydown: false,
|
||||
};
|
||||
@ -87,9 +92,12 @@ const withHotkeys = (Component) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
const throttledKeyDownEvent = throttle(onKeyDown, 300);
|
||||
|
||||
return () => window.removeEventListener("keypress", onKeyDown);
|
||||
window.addEventListener("keydown", throttledKeyDownEvent);
|
||||
|
||||
return () =>
|
||||
window.removeEventListener("keypress", throttledKeyDownEvent);
|
||||
});
|
||||
|
||||
//Select/deselect item
|
||||
@ -322,9 +330,9 @@ const withHotkeys = (Component) => {
|
||||
setSelected,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
fileActionStore,
|
||||
enabledHotkeys,
|
||||
selection,
|
||||
filesIsLoading,
|
||||
} = filesStore;
|
||||
|
||||
const {
|
||||
@ -413,6 +421,7 @@ const withHotkeys = (Component) => {
|
||||
|
||||
selection,
|
||||
setFavoriteAction,
|
||||
filesIsLoading,
|
||||
};
|
||||
}
|
||||
)(observer(WithHotkeys));
|
||||
|
@ -45,6 +45,7 @@ const ArticleBodyContent = (props) => {
|
||||
theme,
|
||||
toggleArticleOpen,
|
||||
categoryType,
|
||||
filesIsLoading,
|
||||
} = props;
|
||||
|
||||
const campaigns = (localStorage.getItem("campaigns") || "")
|
||||
@ -67,6 +68,7 @@ const ArticleBodyContent = (props) => {
|
||||
archiveFolderId,
|
||||
} = props;
|
||||
|
||||
if (filesIsLoading) return;
|
||||
const filesSection = window.location.pathname.indexOf("/filter") > 0;
|
||||
|
||||
if (filesSection) {
|
||||
@ -191,6 +193,7 @@ export default inject(
|
||||
isLoading,
|
||||
isLoaded,
|
||||
categoryType,
|
||||
filesIsLoading,
|
||||
} = filesStore;
|
||||
|
||||
const {
|
||||
@ -253,6 +256,7 @@ export default inject(
|
||||
archiveFolderId,
|
||||
|
||||
categoryType,
|
||||
filesIsLoading,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -103,7 +103,7 @@ const CreateEvent = ({
|
||||
addActiveItems(null, [folder.id]);
|
||||
setCreatedItem({ id: createdFolderId, type: "folder" });
|
||||
})
|
||||
.then(() => editCompleteAction(id, item, false, type))
|
||||
.then(() => editCompleteAction(item, type, true))
|
||||
.catch((e) => toastr.error(e))
|
||||
.finally(() => {
|
||||
const folderIds = [+id];
|
||||
@ -123,7 +123,7 @@ const CreateEvent = ({
|
||||
|
||||
open && openDocEditor(file.id, file.providerKey, tab);
|
||||
})
|
||||
.then(() => editCompleteAction(id, item, false, type))
|
||||
.then(() => editCompleteAction(item, type))
|
||||
.catch((err) => {
|
||||
if (err.indexOf("password") == -1) {
|
||||
toastr.error(err, t("Common:Warning"));
|
||||
@ -173,7 +173,7 @@ const CreateEvent = ({
|
||||
|
||||
return open && openDocEditor(file.id, file.providerKey, tab);
|
||||
})
|
||||
.then(() => editCompleteAction(id, item, false, type))
|
||||
.then(() => editCompleteAction(item, type))
|
||||
.catch((e) => toastr.error(e))
|
||||
.finally(() => {
|
||||
const fileIds = [+id];
|
||||
@ -209,7 +209,7 @@ const CreateEvent = ({
|
||||
|
||||
return open && openDocEditor(file.id, file.providerKey, tab);
|
||||
})
|
||||
.then(() => editCompleteAction(id, item, false, type))
|
||||
.then(() => editCompleteAction(item, type))
|
||||
.catch((e) => toastr.error(e))
|
||||
.finally(() => {
|
||||
const fileIds = [+id];
|
||||
|
@ -48,7 +48,7 @@ const RenameEvent = ({
|
||||
if (isSameTitle) {
|
||||
setStartValue(originalTitle);
|
||||
|
||||
return editCompleteAction(item.id, item, isSameTitle, type);
|
||||
return editCompleteAction(item, type);
|
||||
} else {
|
||||
timerId = setTimeout(() => {
|
||||
isFile ? addActiveItems([item.id]) : addActiveItems(null, [item.id]);
|
||||
@ -57,7 +57,7 @@ const RenameEvent = ({
|
||||
|
||||
isFile
|
||||
? updateFile(item.id, value)
|
||||
.then(() => editCompleteAction(item.id, item, false, type))
|
||||
.then(() => editCompleteAction(item, type))
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
t("FileRenamed", {
|
||||
@ -68,7 +68,7 @@ const RenameEvent = ({
|
||||
)
|
||||
.catch((err) => {
|
||||
toastr.error(err);
|
||||
editCompleteAction(item.id, item, false, type);
|
||||
editCompleteAction(item, type);
|
||||
})
|
||||
.finally(() => {
|
||||
clearTimeout(timerId);
|
||||
@ -79,7 +79,7 @@ const RenameEvent = ({
|
||||
onClose();
|
||||
})
|
||||
: renameFolder(item.id, value)
|
||||
.then(() => editCompleteAction(item.id, item, false, type))
|
||||
.then(() => editCompleteAction(item, type))
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
t("FolderRenamed", {
|
||||
@ -90,7 +90,7 @@ const RenameEvent = ({
|
||||
)
|
||||
.catch((err) => {
|
||||
toastr.error(err);
|
||||
editCompleteAction(item.id, item, false, type);
|
||||
editCompleteAction(item, type);
|
||||
})
|
||||
.finally(() => {
|
||||
clearTimeout(timerId);
|
||||
|
@ -86,7 +86,7 @@ const Dialog = ({
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
key="GlobalSendBtn"
|
||||
label={t("Common:SaveButton")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -153,7 +153,7 @@ class ChangeEmailDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
key="ChangeEmailSendBtn"
|
||||
label={t("Common:SendButton")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -82,7 +82,7 @@ class ChangePasswordDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
key="ChangePasswordSendBtn"
|
||||
label={t("Common:SendButton")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -37,7 +37,7 @@ class ChangePhoneDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
key="ChangePhoneSendBtn"
|
||||
label={t("Common:SendButton")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -114,7 +114,7 @@ const ConvertPasswordDialogComponent = (props) => {
|
||||
open && openDocEditor(file.id, file.providerKey, tab);
|
||||
})
|
||||
.then(() => {
|
||||
editCompleteAction(actionId, fileInfo, false);
|
||||
editCompleteAction(fileInfo);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.indexOf("password") == -1) {
|
||||
|
@ -67,7 +67,7 @@ class DeleteSelfProfileDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
key="DeleteSelfSendBtn"
|
||||
label={t("Common:SendButton")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -40,7 +40,7 @@ class ResetApplicationDialogComponent extends React.Component {
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
key="ResetSendBtn"
|
||||
label={t("Common:ResetApplication")}
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -43,7 +43,7 @@ const SectionBodyContent = ({
|
||||
descriptionText={t("EmptyScreenDescription")}
|
||||
/>
|
||||
) : (
|
||||
<TileContainer useReactWindow={false} className="tile-container">
|
||||
<TileContainer className="tile-container">
|
||||
{oformFiles.map((item, index) => (
|
||||
<FileTile key={`${item.id}_${index}`} item={item} />
|
||||
))}
|
||||
|
@ -1,71 +1,25 @@
|
||||
import React, { memo } from "react";
|
||||
import React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
import { FixedSizeList as List, areEqual } from "react-window";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import CustomScrollbarsVirtualList from "@docspace/components/scrollbar";
|
||||
import { StyledGridWrapper, StyledTileContainer } from "../StyledTileView";
|
||||
|
||||
class TileContainer extends React.PureComponent {
|
||||
renderTile = memo(({ data, index, style }) => {
|
||||
return <div style={style}>{data[index]}</div>;
|
||||
}, areEqual);
|
||||
|
||||
render() {
|
||||
const {
|
||||
itemHeight,
|
||||
children,
|
||||
useReactWindow,
|
||||
id,
|
||||
className,
|
||||
style,
|
||||
} = this.props;
|
||||
|
||||
const renderList = ({ height, width }) => (
|
||||
<List
|
||||
className="list"
|
||||
height={height}
|
||||
width={width}
|
||||
itemSize={itemHeight}
|
||||
itemCount={children.length}
|
||||
itemData={children}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{this.renderTile}
|
||||
</List>
|
||||
);
|
||||
const { children, id, className, style } = this.props;
|
||||
|
||||
return (
|
||||
<StyledTileContainer
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
useReactWindow={useReactWindow}
|
||||
>
|
||||
{useReactWindow ? (
|
||||
<AutoSizer>{renderList}</AutoSizer>
|
||||
) : (
|
||||
<StyledGridWrapper>{children}</StyledGridWrapper>
|
||||
)}
|
||||
<StyledTileContainer id={id} className={className} style={style}>
|
||||
<StyledGridWrapper>{children}</StyledGridWrapper>
|
||||
</StyledTileContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TileContainer.propTypes = {
|
||||
itemHeight: PropTypes.number,
|
||||
manualHeight: PropTypes.string,
|
||||
children: PropTypes.any.isRequired,
|
||||
useReactWindow: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
};
|
||||
|
||||
TileContainer.defaultProps = {
|
||||
itemHeight: 50,
|
||||
useReactWindow: true,
|
||||
id: "rowContainer",
|
||||
};
|
||||
|
||||
export default withTranslation(["Files", "Common"])(TileContainer);
|
||||
|
@ -64,7 +64,12 @@ const FilesRowContainer = ({
|
||||
viewAs,
|
||||
setViewAs,
|
||||
infoPanelVisible,
|
||||
filterTotal,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
isRooms,
|
||||
selectedFolderId,
|
||||
withPaging,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
if ((viewAs !== "table" && viewAs !== "row") || !sectionWidth) return;
|
||||
@ -84,8 +89,14 @@ const FilesRowContainer = ({
|
||||
return (
|
||||
<StyledRowContainer
|
||||
className="files-row-container"
|
||||
filesLength={filesList.length}
|
||||
itemCount={filterTotal}
|
||||
fetchMoreFiles={fetchMoreFiles}
|
||||
hasMoreFiles={hasMoreFiles}
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
useReactWindow={!withPaging}
|
||||
selectedFolderId={selectedFolderId}
|
||||
itemHeight={58}
|
||||
>
|
||||
{filesList.map((item, index) => (
|
||||
<SimpleFilesRow
|
||||
@ -100,18 +111,34 @@ const FilesRowContainer = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore, auth, treeFoldersStore }) => {
|
||||
const { filesList, viewAs, setViewAs } = filesStore;
|
||||
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
|
||||
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
|
||||
export default inject(
|
||||
({ filesStore, auth, treeFoldersStore, selectedFolderStore }) => {
|
||||
const {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
filterTotal,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
withPaging,
|
||||
roomsFilterTotal,
|
||||
} = filesStore;
|
||||
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
|
||||
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
|
||||
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
|
||||
return {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
infoPanelVisible,
|
||||
isRooms,
|
||||
};
|
||||
})(observer(FilesRowContainer));
|
||||
return {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
infoPanelVisible,
|
||||
filterTotal: isRooms ? roomsFilterTotal : filterTotal,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
isRooms,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
withPaging,
|
||||
};
|
||||
}
|
||||
)(observer(FilesRowContainer));
|
||||
|
@ -7,7 +7,6 @@ import TableHeader from "./TableHeader";
|
||||
import TableBody from "@docspace/components/table-container/TableBody";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import styled, { css } from "styled-components";
|
||||
import { isTablet } from "@docspace/components/utils/device";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const marginCss = css`
|
||||
@ -119,7 +118,12 @@ const Table = ({
|
||||
theme,
|
||||
infoPanelVisible,
|
||||
userId,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
filterTotal,
|
||||
isRooms,
|
||||
selectedFolderId,
|
||||
withPaging,
|
||||
}) => {
|
||||
const [tagCount, setTagCount] = React.useState(null);
|
||||
|
||||
@ -181,7 +185,7 @@ const Table = ({
|
||||
: `${COLUMNS_SIZE_INFO_PANEL}=${userId}`;
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={ref}>
|
||||
<StyledTableContainer useReactWindow={!withPaging} forwardedRef={ref}>
|
||||
<TableHeader
|
||||
sectionWidth={sectionWidth}
|
||||
containerRef={ref}
|
||||
@ -194,7 +198,19 @@ const Table = ({
|
||||
roomsColumnInfoPanelStorageName={`${COLUMNS_ROOMS_SIZE_INFO_PANEL}=${userId}`}
|
||||
isRooms={isRooms}
|
||||
/>
|
||||
<TableBody>
|
||||
|
||||
<TableBody
|
||||
fetchMoreFiles={fetchMoreFiles}
|
||||
columnStorageName={columnStorageName}
|
||||
filesLength={filesList.length}
|
||||
hasMoreFiles={hasMoreFiles}
|
||||
itemCount={filterTotal}
|
||||
useReactWindow={!withPaging}
|
||||
infoPanelVisible={infoPanelVisible}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
selectedFolderId={selectedFolderId}
|
||||
itemHeight={isRooms ? 49 : 41}
|
||||
>
|
||||
{filesList.map((item, index) => {
|
||||
return index === 0 && item.isRoom ? (
|
||||
<TableRow
|
||||
@ -234,34 +250,45 @@ const Table = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore, treeFoldersStore, auth }) => {
|
||||
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
|
||||
export default inject(
|
||||
({ filesStore, treeFoldersStore, auth, selectedFolderStore }) => {
|
||||
const { isVisible: infoPanelVisible } = auth.infoPanelStore;
|
||||
|
||||
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
|
||||
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
|
||||
|
||||
const isRooms =
|
||||
isRoomsFolder ||
|
||||
isArchiveFolder ||
|
||||
window.location.href.includes("/rooms?");
|
||||
const isRooms =
|
||||
isRoomsFolder ||
|
||||
isArchiveFolder ||
|
||||
window.location.href.includes("/rooms?");
|
||||
|
||||
const {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
setFirsElemChecked,
|
||||
setHeaderBorder,
|
||||
} = filesStore;
|
||||
const {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
setFirsElemChecked,
|
||||
setHeaderBorder,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
filterTotal,
|
||||
withPaging,
|
||||
roomsFilterTotal,
|
||||
} = filesStore;
|
||||
|
||||
return {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
setFirsElemChecked,
|
||||
setHeaderBorder,
|
||||
theme: auth.settingsStore.theme,
|
||||
userId: auth.userStore.user.id,
|
||||
infoPanelVisible,
|
||||
|
||||
isRooms,
|
||||
};
|
||||
})(observer(Table));
|
||||
return {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
setFirsElemChecked,
|
||||
setHeaderBorder,
|
||||
theme: auth.settingsStore.theme,
|
||||
userId: auth.userStore.user.id,
|
||||
infoPanelVisible,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
filterTotal: isRooms ? roomsFilterTotal : filterTotal,
|
||||
isRooms,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
withPaging,
|
||||
};
|
||||
}
|
||||
)(observer(Table));
|
||||
|
@ -167,7 +167,6 @@ class FilesTableHeader extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
this.customScrollElm = document.getElementsByClassName("section-scroll")[0];
|
||||
|
||||
this.customScrollElm.addEventListener("scroll", this.onBeginScroll);
|
||||
}
|
||||
|
||||
@ -301,6 +300,7 @@ class FilesTableHeader extends React.Component {
|
||||
columnInfoPanelStorageName,
|
||||
filesColumnInfoPanelStorageName,
|
||||
roomsColumnInfoPanelStorageName,
|
||||
withPaging,
|
||||
} = this.props;
|
||||
|
||||
// const { sortBy, sortOrder } = filter;
|
||||
@ -340,6 +340,7 @@ class FilesTableHeader extends React.Component {
|
||||
resetColumnsSize={resetColumnsSize || needReset}
|
||||
sortingVisible={sortingVisible}
|
||||
infoPanelVisible={infoPanelVisible}
|
||||
useReactWindow={!withPaging}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -357,7 +358,7 @@ export default inject(
|
||||
canShare,
|
||||
firstElemChecked,
|
||||
headerBorder,
|
||||
|
||||
withPaging,
|
||||
roomsFilter,
|
||||
fetchRooms,
|
||||
} = filesStore;
|
||||
@ -385,6 +386,7 @@ export default inject(
|
||||
headerBorder,
|
||||
|
||||
infoPanelVisible,
|
||||
withPaging,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -28,7 +28,7 @@ const getThumbSize = (width) => {
|
||||
return `${imgWidth}x300`;
|
||||
};
|
||||
|
||||
const FilesTileContainer = ({ filesList, t, sectionWidth }) => {
|
||||
const FilesTileContainer = ({ filesList, t, sectionWidth, withPaging }) => {
|
||||
const firstRef = useRef();
|
||||
const [thumbSize, setThumbSize] = useState("");
|
||||
const [columnCount, setColumnCount] = useState(null);
|
||||
@ -77,7 +77,7 @@ const FilesTileContainer = ({ filesList, t, sectionWidth }) => {
|
||||
<TileContainer
|
||||
className="tile-container"
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
useReactWindow={!withPaging}
|
||||
headingFolders={t("Folders")}
|
||||
headingFiles={t("Files")}
|
||||
>
|
||||
@ -108,9 +108,10 @@ const FilesTileContainer = ({ filesList, t, sectionWidth }) => {
|
||||
};
|
||||
|
||||
export default inject(({ filesStore }) => {
|
||||
const { filesList } = filesStore;
|
||||
const { filesList, withPaging } = filesStore;
|
||||
|
||||
return {
|
||||
filesList,
|
||||
withPaging,
|
||||
};
|
||||
})(observer(FilesTileContainer));
|
||||
|
@ -0,0 +1,207 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import InfiniteLoaderComponent from "@docspace/components/infinite-loader";
|
||||
import { StyledCard, StyledItem, StyledHeaderItem } from "./StyledInfiniteGrid";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import uniqueid from "lodash/uniqueId";
|
||||
|
||||
const HeaderItem = ({ children, className, ...rest }) => {
|
||||
return (
|
||||
<StyledHeaderItem className={`${className} header-item`} {...rest}>
|
||||
{children}
|
||||
</StyledHeaderItem>
|
||||
);
|
||||
};
|
||||
|
||||
const Card = ({ children, ...rest }) => {
|
||||
const getItemSize = (child) => {
|
||||
const isFile = child?.props?.className?.includes("file");
|
||||
const isFolder = child?.props?.className?.includes("folder");
|
||||
const isRoom = child?.props?.className?.includes("room");
|
||||
|
||||
const horizontalGap = 16;
|
||||
const verticalGap = 14;
|
||||
const headerMargin = 15;
|
||||
|
||||
const folderHeight = 64 + verticalGap;
|
||||
const roomHeight = 122 + verticalGap;
|
||||
const fileHeight = 220 + horizontalGap;
|
||||
const titleHeight = 20 + headerMargin;
|
||||
|
||||
if (isRoom) return roomHeight;
|
||||
if (isFolder) return folderHeight;
|
||||
if (isFile) return fileHeight;
|
||||
return titleHeight;
|
||||
};
|
||||
|
||||
const cardHeight = getItemSize(children);
|
||||
|
||||
return (
|
||||
<StyledCard className="Card" cardHeight={cardHeight} {...rest}>
|
||||
{children}
|
||||
</StyledCard>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ children, className, ...rest }) => {
|
||||
return (
|
||||
<StyledItem className={`Item ${className}`} {...rest}>
|
||||
{children}
|
||||
</StyledItem>
|
||||
);
|
||||
};
|
||||
|
||||
const InfiniteGrid = (props) => {
|
||||
const {
|
||||
children,
|
||||
hasMoreFiles,
|
||||
filterTotal,
|
||||
fetchMoreFiles,
|
||||
filesLength,
|
||||
className,
|
||||
getCountTilesInRow,
|
||||
selectedFolderId,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const countTilesInRow = getCountTilesInRow();
|
||||
|
||||
let cards = [];
|
||||
const list = [];
|
||||
|
||||
const addItemToList = (key, className, clear) => {
|
||||
list.push(
|
||||
<Item key={key} className={className}>
|
||||
{cards}
|
||||
</Item>
|
||||
);
|
||||
if (clear) cards = [];
|
||||
};
|
||||
|
||||
const checkType = (useTempList = true) => {
|
||||
const isFile = useTempList
|
||||
? cards.at(-1).props.children.props.className.includes("file")
|
||||
: list.at(-1).props.className.includes("isFile");
|
||||
|
||||
if (isFile) return "isFile";
|
||||
|
||||
const isFolder = useTempList
|
||||
? cards.at(-1).props.children.props.className.includes("folder")
|
||||
: list.at(-1).props.className.includes("isFolder");
|
||||
|
||||
if (isFolder) return "isFolder";
|
||||
|
||||
return "isRoom";
|
||||
};
|
||||
|
||||
React.Children.map(children.props.children, (child) => {
|
||||
if (child) {
|
||||
if (child.props.className === "tile-items-heading") {
|
||||
// If cards is not empty then put the cards into the list
|
||||
if (cards.length) {
|
||||
const type = checkType();
|
||||
|
||||
addItemToList(`last-item-of_${type}`, type, true);
|
||||
}
|
||||
|
||||
list.push(
|
||||
<HeaderItem
|
||||
className={list.length ? "files_header" : "folder_header"}
|
||||
key="header_item"
|
||||
>
|
||||
{child}
|
||||
</HeaderItem>
|
||||
);
|
||||
} else {
|
||||
const isFile = child?.props?.className?.includes("file");
|
||||
const isRoom = child?.props?.className?.includes("room");
|
||||
const className = isFile ? "isFile" : isRoom ? "isRoom" : "isFolder";
|
||||
|
||||
if (cards.length && cards.length === countTilesInRow) {
|
||||
const listKey = uniqueid("list-item_");
|
||||
addItemToList(listKey, className, true);
|
||||
}
|
||||
|
||||
const cardKey = uniqueid("card-item_");
|
||||
cards.push(<Card key={cardKey}>{child}</Card>);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const type = checkType(!!cards.length);
|
||||
const otherClassName = type;
|
||||
|
||||
if (hasMoreFiles) {
|
||||
// If cards elements are full, it will add the full line of loaders
|
||||
if (cards.length === countTilesInRow) {
|
||||
addItemToList("loaded-row", otherClassName, true);
|
||||
}
|
||||
|
||||
// Added line of loaders
|
||||
while (cards.length !== countTilesInRow) {
|
||||
const key = `tiles-loader_${countTilesInRow - cards.length}`;
|
||||
cards.push(
|
||||
<Loaders.Tile
|
||||
key={key}
|
||||
className={`tiles-loader ${otherClassName}`}
|
||||
isFolder={type === "isFolder"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
addItemToList("loaded-row", otherClassName);
|
||||
} else if (cards.length) {
|
||||
// Adds loaders until the row is full
|
||||
const listKey = uniqueid("list-item_");
|
||||
addItemToList(listKey, otherClassName);
|
||||
}
|
||||
|
||||
// console.log("InfiniteGrid render", list);
|
||||
|
||||
return (
|
||||
<InfiniteLoaderComponent
|
||||
viewAs="tile"
|
||||
countTilesInRow={countTilesInRow}
|
||||
filesLength={filesLength}
|
||||
hasMoreFiles={hasMoreFiles}
|
||||
itemCount={hasMoreFiles ? list.length + 1 : list.length}
|
||||
loadMoreItems={fetchMoreFiles}
|
||||
className={`TileList ${className}`}
|
||||
selectedFolderId={selectedFolderId}
|
||||
{...rest}
|
||||
>
|
||||
{list}
|
||||
</InfiniteLoaderComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ filesStore, selectedFolderStore, treeFoldersStore }) => {
|
||||
const {
|
||||
filesList,
|
||||
hasMoreFiles,
|
||||
filterTotal,
|
||||
fetchMoreFiles,
|
||||
getCountTilesInRow,
|
||||
roomsFilterTotal,
|
||||
} = filesStore;
|
||||
|
||||
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
|
||||
|
||||
const filesLength = filesList.length;
|
||||
const isRooms =
|
||||
isRoomsFolder ||
|
||||
isArchiveFolder ||
|
||||
window.location.href.includes("/rooms?");
|
||||
|
||||
return {
|
||||
filesList,
|
||||
hasMoreFiles,
|
||||
filterTotal: isRooms ? roomsFilterTotal : filterTotal,
|
||||
fetchMoreFiles,
|
||||
filesLength,
|
||||
getCountTilesInRow,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
};
|
||||
}
|
||||
)(observer(InfiniteGrid));
|
@ -0,0 +1,39 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { desktop, tablet } from "@docspace/components/utils/device";
|
||||
|
||||
const paddingCss = css`
|
||||
@media ${desktop} {
|
||||
margin-left: 1px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
margin-left: -1px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCard = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
|
||||
height: ${({ cardHeight }) => `${cardHeight}px`};
|
||||
`;
|
||||
|
||||
const StyledItem = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
|
||||
gap: 14px 16px;
|
||||
width: 100%;
|
||||
|
||||
@media ${tablet} {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
${paddingCss};
|
||||
`;
|
||||
|
||||
const StyledHeaderItem = styled.div`
|
||||
height: 20px;
|
||||
grid-column: -1 / 1;
|
||||
`;
|
||||
|
||||
export { StyledCard, StyledItem, StyledHeaderItem };
|
@ -1,11 +1,8 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { memo } from "react";
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import styled, { css } from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { FixedSizeList as List, areEqual } from "react-window";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import Heading from "@docspace/components/heading";
|
||||
import ContextMenu from "@docspace/components/context-menu";
|
||||
import CustomScrollbarsVirtualList from "@docspace/components/scrollbar";
|
||||
@ -18,6 +15,7 @@ import {
|
||||
} from "@docspace/components/utils/device";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
import InfiniteGrid from "./InfiniteGrid";
|
||||
|
||||
const paddingCss = css`
|
||||
@media ${desktop} {
|
||||
@ -46,10 +44,19 @@ const StyledGridWrapper = styled.div`
|
||||
@media ${tablet} {
|
||||
grid-gap: 14px;
|
||||
}
|
||||
|
||||
.tiles-loader {
|
||||
padding-top: 14px;
|
||||
|
||||
&:nth-of-type(n + 3) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTileContainer = styled.div`
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
.tile-item-wrapper {
|
||||
position: relative;
|
||||
@ -165,8 +172,6 @@ class TileContainer extends React.PureComponent {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
contextOptions: [],
|
||||
isOpen: false,
|
||||
selectedFilterData: {
|
||||
sortId: props.filter.sortBy,
|
||||
sortDirection: props.filter.sortOrder,
|
||||
@ -174,54 +179,8 @@ class TileContainer extends React.PureComponent {
|
||||
};
|
||||
}
|
||||
|
||||
onRowContextClick = (options) => {
|
||||
if (Array.isArray(options)) {
|
||||
this.setState({
|
||||
contextOptions: options,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
toggleDropdown = () => {
|
||||
this.setState((prev) => ({
|
||||
isOpen: !prev.isOpen,
|
||||
}));
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("contextmenu", this.onRowContextClick);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("contextmenu", this.onRowContextClick);
|
||||
}
|
||||
|
||||
renderFolders = () => {
|
||||
return <div />;
|
||||
};
|
||||
|
||||
renderFiles = () => {
|
||||
return <div />;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
renderTile = memo(({ data, index, style }) => {
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const options = data[index].props.contextOptions;
|
||||
|
||||
return (
|
||||
<div
|
||||
onContextMenu={this.onRowContextClick.bind(this, options)}
|
||||
style={style}
|
||||
>
|
||||
{data[index]}
|
||||
</div>
|
||||
);
|
||||
}, areEqual);
|
||||
|
||||
render() {
|
||||
const {
|
||||
itemHeight,
|
||||
children,
|
||||
useReactWindow,
|
||||
id,
|
||||
@ -231,66 +190,85 @@ class TileContainer extends React.PureComponent {
|
||||
headingFiles,
|
||||
} = this.props;
|
||||
|
||||
const { selectedFilterData } = this.state;
|
||||
|
||||
const Rooms = [];
|
||||
const Folders = [];
|
||||
const Files = [];
|
||||
|
||||
React.Children.map(children, (item, index) => {
|
||||
React.Children.map(children, (item) => {
|
||||
const { isFolder, isRoom, fileExst, id } = item.props.item;
|
||||
if ((isFolder || id === -1) && !fileExst && !isRoom) {
|
||||
Folders.push(
|
||||
<div
|
||||
className="tile-item-wrapper folder"
|
||||
key={index}
|
||||
onContextMenu={this.onRowContextClick.bind(
|
||||
this,
|
||||
item.props.contextOptions
|
||||
)}
|
||||
>
|
||||
<div className="tile-item-wrapper folder" key={id}>
|
||||
{item}
|
||||
</div>
|
||||
);
|
||||
} else if (isRoom) {
|
||||
Rooms.push(
|
||||
<div
|
||||
className="tile-item-wrapper folder"
|
||||
key={index}
|
||||
onContextMenu={this.onRowContextClick.bind(
|
||||
this,
|
||||
item.props.contextOptions
|
||||
)}
|
||||
>
|
||||
<div className="tile-item-wrapper room" key={id}>
|
||||
{item}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
Files.push(
|
||||
<div
|
||||
className="tile-item-wrapper file"
|
||||
key={index}
|
||||
onContextMenu={this.onRowContextClick.bind(
|
||||
this,
|
||||
item.props.contextOptions
|
||||
)}
|
||||
>
|
||||
<div className="tile-item-wrapper file" key={id}>
|
||||
{item}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const renderList = ({ height, width }) => (
|
||||
<List
|
||||
className="list"
|
||||
height={height}
|
||||
width={width}
|
||||
itemSize={itemHeight}
|
||||
itemCount={children.length}
|
||||
itemData={children}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{this.renderTile}
|
||||
</List>
|
||||
const renderTile = (
|
||||
<>
|
||||
{Rooms.length > 0 && (
|
||||
<Heading
|
||||
size="xsmall"
|
||||
id={"room-tile-heading"}
|
||||
className="tile-items-heading"
|
||||
>
|
||||
{"Rooms"}
|
||||
</Heading>
|
||||
)}
|
||||
|
||||
{Rooms.length > 0 ? (
|
||||
useReactWindow ? (
|
||||
Rooms
|
||||
) : (
|
||||
<StyledGridWrapper isRooms>{Rooms}</StyledGridWrapper>
|
||||
)
|
||||
) : null}
|
||||
|
||||
{Folders.length > 0 && (
|
||||
<Heading
|
||||
size="xsmall"
|
||||
id={"folder-tile-heading"}
|
||||
className="tile-items-heading"
|
||||
>
|
||||
{headingFolders}
|
||||
</Heading>
|
||||
)}
|
||||
{Folders.length > 0 ? (
|
||||
useReactWindow ? (
|
||||
Folders
|
||||
) : (
|
||||
<StyledGridWrapper isFolders>{Folders}</StyledGridWrapper>
|
||||
)
|
||||
) : null}
|
||||
|
||||
{Files.length > 0 && (
|
||||
<Heading size="xsmall" className="tile-items-heading">
|
||||
{headingFiles}
|
||||
</Heading>
|
||||
)}
|
||||
{Files.length > 0 ? (
|
||||
useReactWindow ? (
|
||||
Files
|
||||
) : (
|
||||
<StyledGridWrapper>{Files}</StyledGridWrapper>
|
||||
)
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
@ -299,64 +277,19 @@ class TileContainer extends React.PureComponent {
|
||||
className={className}
|
||||
style={style}
|
||||
useReactWindow={useReactWindow}
|
||||
isDesc={this.state.selectedFilterData.sortDirection === "desc"}
|
||||
isDesc={selectedFilterData.sortDirection === "desc"}
|
||||
>
|
||||
{Rooms.length > 0 && (
|
||||
<>
|
||||
<Heading
|
||||
size="xsmall"
|
||||
id={"room-tile-heading"}
|
||||
className="tile-items-heading"
|
||||
>
|
||||
{"Rooms"}
|
||||
</Heading>
|
||||
{useReactWindow ? (
|
||||
<AutoSizer>{renderList}</AutoSizer>
|
||||
) : (
|
||||
<StyledGridWrapper isRooms>{Rooms}</StyledGridWrapper>
|
||||
)}
|
||||
</>
|
||||
{useReactWindow ? (
|
||||
<InfiniteGrid>{renderTile}</InfiniteGrid>
|
||||
) : (
|
||||
renderTile
|
||||
)}
|
||||
|
||||
{Folders.length > 0 && (
|
||||
<>
|
||||
<Heading
|
||||
size="xsmall"
|
||||
id={"folder-tile-heading"}
|
||||
className="tile-items-heading"
|
||||
>
|
||||
{headingFolders}
|
||||
</Heading>
|
||||
{useReactWindow ? (
|
||||
<AutoSizer>{renderList}</AutoSizer>
|
||||
) : (
|
||||
<StyledGridWrapper isFolders>{Folders}</StyledGridWrapper>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{Files.length > 0 && (
|
||||
<>
|
||||
<Heading size="xsmall" className="tile-items-heading">
|
||||
{headingFiles}
|
||||
</Heading>
|
||||
{useReactWindow ? (
|
||||
<AutoSizer>{renderList}</AutoSizer>
|
||||
) : (
|
||||
<StyledGridWrapper>{Files}</StyledGridWrapper>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<ContextMenu targetAreaId={id} options={this.state.contextOptions} />
|
||||
</StyledTileContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TileContainer.propTypes = {
|
||||
itemHeight: PropTypes.number,
|
||||
manualHeight: PropTypes.string,
|
||||
children: PropTypes.any.isRequired,
|
||||
useReactWindow: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
@ -365,54 +298,23 @@ TileContainer.propTypes = {
|
||||
};
|
||||
|
||||
TileContainer.defaultProps = {
|
||||
itemHeight: 50,
|
||||
useReactWindow: true,
|
||||
id: "rowContainer",
|
||||
id: "tileContainer",
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => {
|
||||
const {
|
||||
fetchFiles,
|
||||
filter,
|
||||
setIsLoading,
|
||||
setViewAs,
|
||||
viewAs,
|
||||
files,
|
||||
folders,
|
||||
createThumbnails,
|
||||
} = filesStore;
|
||||
|
||||
const { user } = auth.userStore;
|
||||
const { customNames, personal } = auth.settingsStore;
|
||||
const { personal } = auth.settingsStore;
|
||||
const { fetchFiles, filter, setIsLoading } = filesStore;
|
||||
const { isFavoritesFolder, isRecentFolder } = treeFoldersStore;
|
||||
|
||||
const { search, filterType, authorType } = filter;
|
||||
const isFiltered =
|
||||
(!!files.length ||
|
||||
!!folders.length ||
|
||||
search ||
|
||||
filterType ||
|
||||
authorType) &&
|
||||
!(treeFoldersStore.isPrivacyFolder && isMobile);
|
||||
|
||||
return {
|
||||
customNames,
|
||||
user,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
selectedItem: filter.selectedItem,
|
||||
personal,
|
||||
fetchFiles,
|
||||
filter,
|
||||
viewAs,
|
||||
isFiltered,
|
||||
setIsLoading,
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
|
||||
setIsLoading,
|
||||
fetchFiles,
|
||||
setViewAs,
|
||||
createThumbnails,
|
||||
|
||||
personal,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
};
|
||||
}
|
||||
)(observer(withTranslation(["Files", "Common"])(TileContainer)));
|
||||
|
@ -502,6 +502,7 @@ class PureHome extends React.Component {
|
||||
showTitle,
|
||||
showFilter,
|
||||
frameConfig,
|
||||
withPaging,
|
||||
} = this.props;
|
||||
|
||||
if (window.parent && !frameConfig) {
|
||||
@ -513,6 +514,7 @@ class PureHome extends React.Component {
|
||||
<MediaViewer />
|
||||
<DragTooltip />
|
||||
<Section
|
||||
withPaging={withPaging}
|
||||
dragging={dragging}
|
||||
withBodyScroll
|
||||
withBodyAutoFocus={!isMobile}
|
||||
@ -581,9 +583,11 @@ class PureHome extends React.Component {
|
||||
<InfoPanelBodyContent />
|
||||
</Section.InfoPanelBody>
|
||||
|
||||
<Section.SectionPaging>
|
||||
<SectionPagingContent tReady={tReady} />
|
||||
</Section.SectionPaging>
|
||||
{withPaging && (
|
||||
<Section.SectionPaging>
|
||||
<SectionPagingContent tReady={tReady} />
|
||||
</Section.SectionPaging>
|
||||
)}
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
@ -634,6 +638,7 @@ export default inject(
|
||||
createRoom,
|
||||
refreshFiles,
|
||||
setViewAs,
|
||||
withPaging,
|
||||
} = filesStore;
|
||||
|
||||
const {
|
||||
@ -770,6 +775,7 @@ export default inject(
|
||||
createRoom,
|
||||
refreshFiles,
|
||||
setViewAs,
|
||||
withPaging,
|
||||
};
|
||||
}
|
||||
)(withRouter(observer(Home)));
|
||||
|
@ -199,6 +199,18 @@ class FilesActionStore {
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
};
|
||||
|
||||
updateFilesAfterDelete = () => {
|
||||
const { setSelected } = this.filesStore;
|
||||
const {
|
||||
clearSecondaryProgressData,
|
||||
} = this.uploadDataStore.secondaryProgressDataStore;
|
||||
|
||||
setSelected("close");
|
||||
|
||||
this.dialogsStore.setIsFolderActions(false);
|
||||
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
|
||||
};
|
||||
|
||||
deleteAction = async (
|
||||
translations,
|
||||
newSelection = null,
|
||||
@ -268,7 +280,28 @@ class FilesActionStore {
|
||||
label: translations.deleteOperation,
|
||||
};
|
||||
await this.uploadDataStore.loopFilesOperations(data, pbData);
|
||||
this.updateCurrentFolder(fileIds, folderIds, false);
|
||||
|
||||
const showToast = () => {
|
||||
if (isRecycleBinFolder) {
|
||||
return toastr.success(translations.deleteFromTrash);
|
||||
}
|
||||
|
||||
if (selection.length > 1 || isThirdPartyFile) {
|
||||
return toastr.success(translations.deleteSelectedElem);
|
||||
}
|
||||
if (selection[0].fileExst) {
|
||||
return toastr.success(translations.FileRemoved);
|
||||
}
|
||||
return toastr.success(translations.FolderRemoved);
|
||||
};
|
||||
|
||||
if (this.filesStore.withPaging) {
|
||||
this.updateCurrentFolder(fileIds, folderIds, false);
|
||||
showToast();
|
||||
} else {
|
||||
this.updateFilesAfterDelete(folderIds);
|
||||
this.filesStore.removeFiles(fileIds, folderIds, showToast);
|
||||
}
|
||||
|
||||
if (currentFolderId) {
|
||||
const { socketHelper } = this.authStore.settingsStore;
|
||||
@ -278,18 +311,6 @@ class FilesActionStore {
|
||||
data: currentFolderId,
|
||||
});
|
||||
}
|
||||
|
||||
if (isRecycleBinFolder) {
|
||||
return toastr.success(translations.deleteFromTrash);
|
||||
}
|
||||
|
||||
if (selection.length > 1 || isThirdPartyFile) {
|
||||
return toastr.success(translations.deleteSelectedElem);
|
||||
}
|
||||
if (selection[0].fileExst) {
|
||||
return toastr.success(translations.FileRemoved);
|
||||
}
|
||||
return toastr.success(translations.FolderRemoved);
|
||||
})
|
||||
.finally(() => {
|
||||
clearActiveOperations(fileIds, folderIds);
|
||||
@ -469,35 +490,12 @@ class FilesActionStore {
|
||||
return this.downloadFiles(fileIds, folderIds, label);
|
||||
};
|
||||
|
||||
editCompleteAction = async (id, selectedItem, isCancelled = false, type) => {
|
||||
const {
|
||||
filter,
|
||||
folders,
|
||||
files,
|
||||
editCompleteAction = async (selectedItem, type, isFolder = false) => {
|
||||
if (type === FileAction.Create) {
|
||||
this.filesStore.addFile(selectedItem, isFolder);
|
||||
}
|
||||
|
||||
fetchFiles,
|
||||
setIsLoading,
|
||||
} = this.filesStore;
|
||||
|
||||
const { treeFolders, setTreeFolders } = this.treeFoldersStore;
|
||||
|
||||
const items = [...folders, ...files];
|
||||
const item = items.find((o) => o.id === id && !o.fileExst); //TODO: maybe need files find and folders find, not at one function?
|
||||
if (type === FileAction.Create || type === FileAction.Rename) {
|
||||
setIsLoading(true);
|
||||
|
||||
if (!isCancelled) {
|
||||
const data = await fetchFiles(this.selectedFolderStore.id, filter);
|
||||
const newItem = (item && item.id) === -1 ? null : item; //TODO: not add new folders?
|
||||
if (!selectedItem.fileExst && !selectedItem.contentLength) {
|
||||
const path = data.selectedFolder.pathParts;
|
||||
const folders = await getSubfolders(this.selectedFolderStore.id);
|
||||
loopTreeFolders(path, treeFolders, folders, null, newItem);
|
||||
setTreeFolders(treeFolders);
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
type === FileAction.Rename &&
|
||||
this.onSelectItem(
|
||||
{
|
||||
@ -619,14 +617,21 @@ class FilesActionStore {
|
||||
if (isFile) {
|
||||
addActiveItems([itemId]);
|
||||
this.isMediaOpen();
|
||||
return deleteFile(itemId)
|
||||
.then(async (res) => {
|
||||
if (res[0]?.error) return Promise.reject(res[0].error);
|
||||
const data = res[0] ? res[0] : null;
|
||||
await this.uploadDataStore.loopFilesOperations(data, pbData);
|
||||
return deleteFile(itemId).then(async (res) => {
|
||||
if (res[0]?.error) return Promise.reject(res[0].error);
|
||||
const data = res[0] ? res[0] : null;
|
||||
await this.uploadDataStore.loopFilesOperations(data, pbData);
|
||||
|
||||
if (this.filesStore.withPaging) {
|
||||
this.updateCurrentFolder([itemId]);
|
||||
})
|
||||
.then(() => toastr.success(translations.successRemoveFile));
|
||||
toastr.success(translations.successRemoveFile);
|
||||
} else {
|
||||
this.updateFilesAfterDelete();
|
||||
this.filesStore.removeFiles([itemId], null, () =>
|
||||
toastr.success(translations.successRemoveFile)
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (isRoom) {
|
||||
const items = Array.isArray(itemId) ? itemId : [itemId];
|
||||
addActiveItems(null, items);
|
||||
@ -643,15 +648,23 @@ class FilesActionStore {
|
||||
.then(() => toastr.success(translations?.successRemoveRoom));
|
||||
} else {
|
||||
addActiveItems(null, [itemId]);
|
||||
return deleteFolder(itemId)
|
||||
.then(async (res) => {
|
||||
if (res[0]?.error) return Promise.reject(res[0].error);
|
||||
const data = res[0] ? res[0] : null;
|
||||
await this.uploadDataStore.loopFilesOperations(data, pbData);
|
||||
return deleteFolder(itemId).then(async (res) => {
|
||||
if (res[0]?.error) return Promise.reject(res[0].error);
|
||||
const data = res[0] ? res[0] : null;
|
||||
await this.uploadDataStore.loopFilesOperations(data, pbData);
|
||||
|
||||
if (this.filesStore.withPaging) {
|
||||
this.updateCurrentFolder(null, [itemId]);
|
||||
getIsEmptyTrash();
|
||||
})
|
||||
.then(() => toastr.success(translations.successRemoveFolder));
|
||||
toastr.success(translations.successRemoveFolder);
|
||||
} else {
|
||||
this.updateFilesAfterDelete([itemId]);
|
||||
this.filesStore.removeFiles([itemId], null, () =>
|
||||
toastr.success(translations.successRemoveFolder)
|
||||
);
|
||||
}
|
||||
|
||||
getIsEmptyTrash();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
import history from "@docspace/common/history";
|
||||
import { combineUrl } from "@docspace/common/utils";
|
||||
import { updateTempContent } from "@docspace/common/utils";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import toastr from "client/toastr";
|
||||
|
||||
import config from "PACKAGE_FILE";
|
||||
@ -25,6 +25,7 @@ import {
|
||||
getCategoryType,
|
||||
getCategoryTypeByFolderType,
|
||||
} from "SRC_DIR/helpers/utils";
|
||||
import { isDesktop } from "@docspace/components/utils/device";
|
||||
|
||||
import { getContextMenuKeysByType } from "SRC_DIR/helpers/plugins";
|
||||
import { PluginContextMenuItemType } from "SRC_DIR/helpers/plugins/constants";
|
||||
@ -93,6 +94,8 @@ class FilesStore {
|
||||
pageItemsLength = null;
|
||||
isHidePagination = false;
|
||||
trashIsEmpty = false;
|
||||
filesIsLoading = false;
|
||||
withPaging = false;
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
@ -132,7 +135,7 @@ class FilesStore {
|
||||
|
||||
const newFiles = [file, ...this.files];
|
||||
|
||||
if (newFiles.length > this.filter.pageCount) {
|
||||
if (newFiles.length > this.filter.pageCount && this.withPaging) {
|
||||
newFiles.pop(); // Remove last
|
||||
}
|
||||
|
||||
@ -181,6 +184,10 @@ class FilesStore {
|
||||
})
|
||||
);
|
||||
|
||||
const newFilter = this.filter.clone();
|
||||
newFilter.total -= 1;
|
||||
this.setFilter(newFilter);
|
||||
|
||||
// Hide pagination when deleting files
|
||||
runInAction(() => {
|
||||
this.isHidePagination = true;
|
||||
@ -524,9 +531,7 @@ class FilesStore {
|
||||
};
|
||||
|
||||
setHotkeyCaret = (hotkeyCaret) => {
|
||||
if (hotkeyCaret) {
|
||||
this.hotkeyCaret = hotkeyCaret;
|
||||
} else if (this.hotkeyCaret) {
|
||||
if (hotkeyCaret || this.hotkeyCaret) {
|
||||
this.hotkeyCaret = hotkeyCaret;
|
||||
}
|
||||
};
|
||||
@ -570,6 +575,8 @@ class FilesStore {
|
||||
const value = `${filter.sortBy},${filter.pageCount},${filter.sortOrder}`;
|
||||
localStorage.setItem(key, value);
|
||||
|
||||
if (!this.withPaging) filter.pageCount = 100;
|
||||
|
||||
this.setFilterUrl(filter, true);
|
||||
this.roomsFilter = filter;
|
||||
|
||||
@ -587,6 +594,7 @@ class FilesStore {
|
||||
};
|
||||
|
||||
setFilter = (filter) => {
|
||||
if (!this.withPaging) filter.pageCount = 100;
|
||||
this.filter = filter;
|
||||
};
|
||||
|
||||
@ -659,9 +667,9 @@ class FilesStore {
|
||||
treeFolders,
|
||||
setSelectedNode,
|
||||
getSubfolders,
|
||||
selectedTreeNode,
|
||||
} = this.treeFoldersStore;
|
||||
const { id } = this.selectedFolderStore;
|
||||
|
||||
this.scrollToTop();
|
||||
|
||||
const filterData = filter ? filter.clone() : FilesFilter.getDefault();
|
||||
filterData.folder = folderId;
|
||||
@ -678,6 +686,11 @@ class FilesStore {
|
||||
filterData.sortOrder = splitFilter[2];
|
||||
}
|
||||
|
||||
if (!this.withPaging) {
|
||||
filterData.page = 0;
|
||||
filterData.pageCount = 100;
|
||||
}
|
||||
|
||||
setSelectedNode([folderId + ""]);
|
||||
|
||||
//TODO: fix @my
|
||||
@ -841,6 +854,11 @@ class FilesStore {
|
||||
filterData.sortOrder = splitFilter[2];
|
||||
}
|
||||
|
||||
if (!this.withPaging) {
|
||||
filterData.page = 0;
|
||||
filterData.pageCount = 100;
|
||||
}
|
||||
|
||||
if (folderId) setSelectedNode([folderId + ""]);
|
||||
|
||||
const searchArea = folderId
|
||||
@ -1655,6 +1673,63 @@ class FilesStore {
|
||||
this.folders[idx].pinned = !this.folders[idx].pinned;
|
||||
};
|
||||
|
||||
scrollToTop = () => {
|
||||
if (this.withPaging) return;
|
||||
|
||||
const scrollElm = isMobileOnly
|
||||
? document.querySelector("#customScrollBar > .scroll-body")
|
||||
: document.querySelector("#sectionScroll > .scroll-body");
|
||||
|
||||
scrollElm && scrollElm.scrollTo(0, 0);
|
||||
};
|
||||
|
||||
addFile = (item, isFolder) => {
|
||||
const filter = this.filter.clone();
|
||||
filter.total += 1;
|
||||
this.setFilter(filter);
|
||||
|
||||
isFolder ? this.folders.unshift(item) : this.files.unshift(item);
|
||||
|
||||
this.scrollToTop();
|
||||
};
|
||||
|
||||
removeFiles = (fileIds, folderIds, showToast) => {
|
||||
const newFilter = this.filter.clone();
|
||||
const deleteCount = fileIds.length + folderIds.length;
|
||||
newFilter.startIndex =
|
||||
(newFilter.page + 1) * newFilter.pageCount - deleteCount;
|
||||
newFilter.pageCount = deleteCount;
|
||||
|
||||
api.files
|
||||
.getFolder(newFilter.folder, newFilter)
|
||||
.then((res) => {
|
||||
const files = fileIds
|
||||
? this.files.filter((x) => !fileIds.includes(x.id))
|
||||
: [];
|
||||
const folders = folderIds
|
||||
? this.folders.filter((x) => !folderIds.includes(x.id))
|
||||
: [];
|
||||
|
||||
const newFiles = [...files, ...res.files];
|
||||
const newFolders = [...folders, ...res.folders];
|
||||
|
||||
const filter = this.filter.clone();
|
||||
filter.total = res.total;
|
||||
|
||||
runInAction(() => {
|
||||
this.setFilter(filter);
|
||||
this.setFiles(newFiles);
|
||||
this.setFolders(newFolders);
|
||||
});
|
||||
|
||||
showToast && showToast();
|
||||
})
|
||||
.catch(() => {
|
||||
toastr.error(err);
|
||||
console.log("Need page reload");
|
||||
});
|
||||
};
|
||||
|
||||
updateFile = (fileId, title) => {
|
||||
return api.files
|
||||
.updateFile(fileId, title)
|
||||
@ -2497,6 +2572,73 @@ class FilesStore {
|
||||
setTrashIsEmpty = (isEmpty) => {
|
||||
this.trashIsEmpty = isEmpty;
|
||||
};
|
||||
|
||||
get roomsFilterTotal() {
|
||||
return this.roomsFilter.total;
|
||||
}
|
||||
|
||||
get filterTotal() {
|
||||
return this.filter.total;
|
||||
}
|
||||
|
||||
get hasMoreFiles() {
|
||||
const { Shared, Archive } = CategoryType;
|
||||
const isRoom = this.categoryType == Shared || this.categoryType == Archive;
|
||||
|
||||
const filterTotal = isRoom ? this.roomsFilter.total : this.filter.total;
|
||||
|
||||
console.log("hasMoreFiles isRoom", isRoom);
|
||||
console.log("hasMoreFiles filesList", this.filesList.length);
|
||||
console.log("hasMoreFiles this.filter.total", this.filter.total);
|
||||
console.log("hasMoreFiles this.roomsFilter.total", this.roomsFilter.total);
|
||||
console.log("hasMoreFiles filterTotal", filterTotal);
|
||||
console.log("hasMoreFiles", this.filesList.length < filterTotal);
|
||||
console.log("----------------------------");
|
||||
|
||||
return this.filesList.length < filterTotal;
|
||||
}
|
||||
|
||||
setFilesIsLoading = (filesIsLoading) => {
|
||||
this.filesIsLoading = filesIsLoading;
|
||||
};
|
||||
|
||||
fetchMoreFiles = async () => {
|
||||
if (!this.hasMoreFiles || this.filesIsLoading || this.isLoading) return;
|
||||
|
||||
const { Shared, Archive } = CategoryType;
|
||||
const isRoom = this.categoryType == Shared || this.categoryType == Archive;
|
||||
|
||||
this.setFilesIsLoading(true);
|
||||
// console.log("fetchMoreFiles");
|
||||
|
||||
const newFilter = isRoom ? this.roomsFilter.clone() : this.filter.clone();
|
||||
newFilter.page += 1;
|
||||
if (isRoom) this.setRoomsFilter(newFilter);
|
||||
else this.setFilter(newFilter);
|
||||
|
||||
const newFiles = isRoom
|
||||
? await api.rooms.getRooms(newFilter)
|
||||
: await api.files.getFolder(newFilter.folder, newFilter);
|
||||
|
||||
runInAction(() => {
|
||||
this.setFiles([...this.files, ...newFiles.files]);
|
||||
this.setFolders([...this.folders, ...newFiles.folders]);
|
||||
this.setFilesIsLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
//Duplicate of countTilesInRow, used to update the number of tiles in a row after the window is resized.
|
||||
getCountTilesInRow = () => {
|
||||
const isDesktopView = isDesktop();
|
||||
const tileGap = isDesktopView ? 16 : 14;
|
||||
const minTileWidth = 216 + tileGap;
|
||||
const sectionPadding = isDesktopView ? 24 : 16;
|
||||
|
||||
const body = document.getElementById("section");
|
||||
const sectionWidth = body ? body.offsetWidth - sectionPadding : 0;
|
||||
|
||||
return Math.floor(sectionWidth / minTileWidth);
|
||||
};
|
||||
}
|
||||
|
||||
export default FilesStore;
|
||||
|
@ -14,6 +14,8 @@ class HotkeyStore {
|
||||
treeFoldersStore;
|
||||
uploadDataStore;
|
||||
|
||||
elemOffset = 0;
|
||||
|
||||
constructor(
|
||||
filesStore,
|
||||
dialogsStore,
|
||||
@ -31,6 +33,29 @@ class HotkeyStore {
|
||||
this.uploadDataStore = uploadDataStore;
|
||||
}
|
||||
|
||||
scrollToCaret = () => {
|
||||
const { offsetTop, item } = this.getItemOffset();
|
||||
const scroll = document.getElementsByClassName("section-scroll")[0];
|
||||
const scrollRect = scroll.getBoundingClientRect();
|
||||
|
||||
if (item && item[0]) {
|
||||
const el = item[0];
|
||||
const rect = el.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
scrollRect.top + scrollRect.height - rect.height > rect.top &&
|
||||
scrollRect.top < rect.top + el.offsetHeight
|
||||
) {
|
||||
//console.log("element is visible");
|
||||
} else {
|
||||
scroll.scrollTo(0, offsetTop - scrollRect.height / 2);
|
||||
//console.log("element is not visible");
|
||||
}
|
||||
} else {
|
||||
scroll.scrollTo(0, this.elemOffset - scrollRect.height / 2);
|
||||
}
|
||||
};
|
||||
|
||||
activateHotkeys = (e) => {
|
||||
if (
|
||||
this.dialogsStore.someDialogIsOpen ||
|
||||
@ -50,7 +75,7 @@ class HotkeyStore {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
const { selection: s, hotkeyCaret, viewAs, filesList } = this.filesStore;
|
||||
const { selection: s, hotkeyCaret, filesList } = this.filesStore;
|
||||
const selection = s.length ? s : filesList;
|
||||
|
||||
if (!hotkeyCaret) {
|
||||
@ -59,11 +84,28 @@ class HotkeyStore {
|
||||
}
|
||||
|
||||
if (!hotkeyCaret && selection.length) {
|
||||
this.filesStore.setHotkeyCaret(selection[0]);
|
||||
this.setCaret(selection[0]);
|
||||
this.filesStore.setHotkeyCaretStart(selection[0]);
|
||||
}
|
||||
|
||||
if (!hotkeyCaret || isDefaultKeys) return;
|
||||
if (!hotkeyCaret || isDefaultKeys) return e;
|
||||
};
|
||||
|
||||
setCaret = (caret) => {
|
||||
//TODO: inf-scroll
|
||||
// const id = caret.isFolder ? `folder_${caret.id}` : `file_${caret.id}`;
|
||||
// const elem = document.getElementById(id);
|
||||
// if (!elem) return;
|
||||
|
||||
this.filesStore.setHotkeyCaret(caret);
|
||||
this.scrollToCaret();
|
||||
|
||||
const { offsetTop } = this.getItemOffset();
|
||||
if (offsetTop) this.elemOffset = offsetTop;
|
||||
};
|
||||
|
||||
getItemOffset = () => {
|
||||
const { hotkeyCaret, viewAs } = this.filesStore;
|
||||
|
||||
let item = document.getElementsByClassName(
|
||||
`${hotkeyCaret.id}_${hotkeyCaret.fileExst}`
|
||||
@ -75,35 +117,38 @@ class HotkeyStore {
|
||||
|
||||
if (item && item[0]) {
|
||||
const el = item[0];
|
||||
const rect = el.getBoundingClientRect();
|
||||
const scroll = document.getElementsByClassName("section-scroll")[0];
|
||||
const scrollRect = scroll.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
scrollRect.top + scrollRect.height - rect.height > rect.top &&
|
||||
scrollRect.top < rect.top + el.offsetHeight
|
||||
) {
|
||||
//console.log("element is visible");
|
||||
} else {
|
||||
scroll.scrollTo(0, el.offsetTop - scrollRect.height / 2);
|
||||
//console.log("element is not visible");
|
||||
}
|
||||
const offset = el.closest(".window-item")?.offsetTop;
|
||||
|
||||
const offsetTop = offset
|
||||
? offset
|
||||
: viewAs === "tile"
|
||||
? el.parentElement.parentElement.offsetTop
|
||||
: el.offsetTop;
|
||||
|
||||
return { offsetTop, item };
|
||||
}
|
||||
|
||||
return { offsetTop: null, item: null };
|
||||
};
|
||||
|
||||
selectFirstFile = () => {
|
||||
const { filesList } = this.filesStore;
|
||||
|
||||
if (filesList.length) {
|
||||
// scroll to first element
|
||||
const scroll = document.querySelector("#sectionScroll > .scroll-body");
|
||||
scroll.scrollTo(0, 0);
|
||||
|
||||
this.filesStore.setSelection([filesList[0]]);
|
||||
this.filesStore.setHotkeyCaret(filesList[0]);
|
||||
this.setCaret(filesList[0]);
|
||||
this.filesStore.setHotkeyCaretStart(filesList[0]);
|
||||
}
|
||||
};
|
||||
|
||||
setSelectionWithCaret = (selection) => {
|
||||
this.filesStore.setSelection(selection);
|
||||
this.filesStore.setHotkeyCaret(selection[0]);
|
||||
this.setCaret(selection[0]);
|
||||
this.filesStore.setHotkeyCaretStart(selection[0]);
|
||||
};
|
||||
|
||||
@ -112,7 +157,6 @@ class HotkeyStore {
|
||||
selection,
|
||||
setSelection,
|
||||
hotkeyCaret,
|
||||
setHotkeyCaret,
|
||||
setHotkeyCaretStart,
|
||||
} = this.filesStore;
|
||||
|
||||
@ -128,7 +172,7 @@ class HotkeyStore {
|
||||
setHotkeyCaretStart(hotkeyCaret);
|
||||
} else {
|
||||
if (selection.length) {
|
||||
setHotkeyCaret(selection[0]);
|
||||
this.setCaret(selection[0]);
|
||||
setHotkeyCaretStart(selection[0]);
|
||||
} else this.selectFirstFile();
|
||||
}
|
||||
@ -197,7 +241,6 @@ class HotkeyStore {
|
||||
setHotkeyCaretStart,
|
||||
hotkeyCaret,
|
||||
viewAs,
|
||||
setHotkeyCaret,
|
||||
deselectFile,
|
||||
} = this.filesStore;
|
||||
|
||||
@ -213,14 +256,14 @@ class HotkeyStore {
|
||||
...this.selectionsDown,
|
||||
...[hotkeyCaretStart ? hotkeyCaretStart : hotkeyCaret],
|
||||
]);
|
||||
setHotkeyCaret(this.nextForTileDown);
|
||||
this.setCaret(this.nextForTileDown);
|
||||
} else if (this.nextFile) {
|
||||
if (selection.findIndex((f) => f.id === this.nextFile.id) !== -1) {
|
||||
deselectFile(hotkeyCaret);
|
||||
} else {
|
||||
setSelection([...selection, ...[this.nextFile]]);
|
||||
}
|
||||
setHotkeyCaret(this.nextFile);
|
||||
this.setCaret(this.nextFile);
|
||||
}
|
||||
};
|
||||
|
||||
@ -232,7 +275,6 @@ class HotkeyStore {
|
||||
setHotkeyCaretStart,
|
||||
hotkeyCaret,
|
||||
viewAs,
|
||||
setHotkeyCaret,
|
||||
deselectFile,
|
||||
} = this.filesStore;
|
||||
|
||||
@ -248,7 +290,7 @@ class HotkeyStore {
|
||||
...this.selectionsUp,
|
||||
...[hotkeyCaretStart ? hotkeyCaretStart : hotkeyCaret],
|
||||
]);
|
||||
setHotkeyCaret(this.prevForTileUp);
|
||||
this.setCaret(this.prevForTileUp);
|
||||
} else if (this.prevFile) {
|
||||
if (selection.findIndex((f) => f.id === this.prevFile.id) !== -1) {
|
||||
deselectFile(hotkeyCaret);
|
||||
@ -256,7 +298,7 @@ class HotkeyStore {
|
||||
setSelection([...[this.prevFile], ...selection]);
|
||||
}
|
||||
|
||||
setHotkeyCaret(this.prevFile);
|
||||
this.setCaret(this.prevFile);
|
||||
}
|
||||
};
|
||||
|
||||
@ -266,7 +308,6 @@ class HotkeyStore {
|
||||
setSelection,
|
||||
hotkeyCaret,
|
||||
viewAs,
|
||||
setHotkeyCaret,
|
||||
deselectFile,
|
||||
hotkeyCaretStart,
|
||||
filesList,
|
||||
@ -307,7 +348,7 @@ class HotkeyStore {
|
||||
|
||||
if (viewAs === "tile") {
|
||||
setSelection(nextForTileRight);
|
||||
setHotkeyCaret(nextFile);
|
||||
this.setCaret(nextFile);
|
||||
} else if (nextFile) {
|
||||
if (selection.findIndex((f) => f.id === nextFile.id) !== -1) {
|
||||
deselectFile(hotkeyCaret);
|
||||
@ -315,7 +356,7 @@ class HotkeyStore {
|
||||
setSelection([...selection, ...[nextFile]]);
|
||||
}
|
||||
|
||||
setHotkeyCaret(nextFile);
|
||||
this.setCaret(nextFile);
|
||||
}
|
||||
};
|
||||
|
||||
@ -325,7 +366,6 @@ class HotkeyStore {
|
||||
setSelection,
|
||||
hotkeyCaret,
|
||||
viewAs,
|
||||
setHotkeyCaret,
|
||||
deselectFile,
|
||||
filesList,
|
||||
hotkeyCaretStart,
|
||||
@ -366,7 +406,7 @@ class HotkeyStore {
|
||||
|
||||
if (viewAs === "tile") {
|
||||
setSelection(prevForTileLeft);
|
||||
setHotkeyCaret(prevFile);
|
||||
this.setCaret(prevFile);
|
||||
} else if (prevFile) {
|
||||
if (selection.findIndex((f) => f.id === prevFile.id) !== -1) {
|
||||
deselectFile(hotkeyCaret);
|
||||
@ -374,30 +414,30 @@ class HotkeyStore {
|
||||
setSelection([...[prevFile], ...selection]);
|
||||
}
|
||||
|
||||
setHotkeyCaret(prevFile);
|
||||
this.setCaret(prevFile);
|
||||
}
|
||||
};
|
||||
|
||||
moveCaretBottom = () => {
|
||||
const { viewAs, setHotkeyCaret } = this.filesStore;
|
||||
const { viewAs } = this.filesStore;
|
||||
|
||||
if (viewAs === "tile") setHotkeyCaret(this.nextForTileDown);
|
||||
else if (this.nextFile) setHotkeyCaret(this.nextFile);
|
||||
if (viewAs === "tile") this.setCaret(this.nextForTileDown);
|
||||
else if (this.nextFile) this.setCaret(this.nextFile);
|
||||
};
|
||||
|
||||
moveCaretUpper = () => {
|
||||
const { viewAs, setHotkeyCaret } = this.filesStore;
|
||||
const { viewAs } = this.filesStore;
|
||||
|
||||
if (viewAs === "tile") setHotkeyCaret(this.prevForTileUp);
|
||||
else if (this.prevFile) setHotkeyCaret(this.prevFile);
|
||||
if (viewAs === "tile") this.setCaret(this.prevForTileUp);
|
||||
else if (this.prevFile) this.setCaret(this.prevFile);
|
||||
};
|
||||
|
||||
moveCaretLeft = () => {
|
||||
if (this.prevFile) this.filesStore.setHotkeyCaret(this.prevFile);
|
||||
if (this.prevFile) this.setCaret(this.prevFile);
|
||||
};
|
||||
|
||||
moveCaretRight = () => {
|
||||
if (this.nextFile) this.filesStore.setHotkeyCaret(this.nextFile);
|
||||
if (this.nextFile) this.setCaret(this.nextFile);
|
||||
};
|
||||
|
||||
openItem = () => {
|
||||
@ -411,14 +451,13 @@ class HotkeyStore {
|
||||
const {
|
||||
filesList,
|
||||
hotkeyCaret,
|
||||
setHotkeyCaret,
|
||||
setHotkeyCaretStart,
|
||||
setSelected,
|
||||
} = this.filesStore;
|
||||
|
||||
setSelected("all");
|
||||
if (!hotkeyCaret) {
|
||||
setHotkeyCaret(filesList[0]);
|
||||
this.setCaret(filesList[0]);
|
||||
setHotkeyCaretStart(filesList[0]);
|
||||
}
|
||||
};
|
||||
@ -489,11 +528,14 @@ class HotkeyStore {
|
||||
|
||||
get caretIndex() {
|
||||
const { filesList, hotkeyCaret, selection } = this.filesStore;
|
||||
const id =
|
||||
const item =
|
||||
selection.length && selection.length === 1 && !hotkeyCaret
|
||||
? selection[0].id
|
||||
: hotkeyCaret?.id;
|
||||
const caretIndex = filesList.findIndex((f) => f.id === id);
|
||||
? selection[0]
|
||||
: hotkeyCaret;
|
||||
|
||||
const caretIndex = filesList.findIndex(
|
||||
(f) => f.id === item?.id && f.isFolder === item?.isFolder
|
||||
);
|
||||
|
||||
if (caretIndex !== -1) return caretIndex;
|
||||
else return null;
|
||||
|
@ -581,6 +581,7 @@ class UploadDataStore {
|
||||
filter,
|
||||
setFilter,
|
||||
} = this.filesStore;
|
||||
|
||||
if (window.location.pathname.indexOf("/history") === -1) {
|
||||
const newFiles = files;
|
||||
const newFolders = folders;
|
||||
@ -617,7 +618,7 @@ class UploadDataStore {
|
||||
newFolders.unshift(folderInfo);
|
||||
setFolders(newFolders);
|
||||
const newFilter = filter;
|
||||
newFilter.total = newFilter.total += 1;
|
||||
newFilter.total += 1;
|
||||
setFilter(newFilter);
|
||||
}
|
||||
} else {
|
||||
@ -626,7 +627,7 @@ class UploadDataStore {
|
||||
newFiles.unshift(currentFile.fileInfo);
|
||||
setFiles(newFiles);
|
||||
const newFilter = filter;
|
||||
newFilter.total = newFilter.total += 1;
|
||||
newFilter.total += 1;
|
||||
setFilter(newFilter);
|
||||
} else if (!this.settingsStore.storeOriginalFiles) {
|
||||
newFiles[fileIndex] = currentFile.fileInfo;
|
||||
@ -640,7 +641,7 @@ class UploadDataStore {
|
||||
filter.filterType ||
|
||||
filter.authorType ||
|
||||
filter.search ||
|
||||
filter.page !== 0;
|
||||
(this.filesStore.withPaging && filter.page !== 0);
|
||||
|
||||
if ((!currentFile && !folderInfo) || isFiltered) return;
|
||||
if (folderInfo && this.selectedFolderStore.id === folderInfo.id) return;
|
||||
@ -653,7 +654,7 @@ class UploadDataStore {
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.total >= filter.pageCount) {
|
||||
if (filter.total >= filter.pageCount && this.filesStore.withPaging) {
|
||||
if (files.length) {
|
||||
fileIndex === -1 && newFiles.pop();
|
||||
addNewFile();
|
||||
@ -926,7 +927,7 @@ class UploadDataStore {
|
||||
};
|
||||
|
||||
finishUploadFiles = () => {
|
||||
const { fetchFiles, filter } = this.filesStore;
|
||||
const { fetchFiles, filter, withPaging } = this.filesStore;
|
||||
|
||||
const totalErrorsCount = sumBy(this.files, (f) => (f.error ? 1 : 0));
|
||||
|
||||
@ -947,7 +948,7 @@ class UploadDataStore {
|
||||
|
||||
if (this.files.length > 0) {
|
||||
const toFolderId = this.files[0]?.toFolderId;
|
||||
fetchFiles(toFolderId, filter);
|
||||
withPaging && fetchFiles(toFolderId, filter);
|
||||
|
||||
if (toFolderId) {
|
||||
const { socketHelper } = this.filesStore.settingsStore;
|
||||
|
@ -135,6 +135,7 @@ class FilesFilter {
|
||||
sortBy,
|
||||
sortOrder,
|
||||
withSubfolders,
|
||||
startIndex,
|
||||
} = this;
|
||||
|
||||
const isFilterSet =
|
||||
@ -148,7 +149,7 @@ class FilesFilter {
|
||||
|
||||
const dtoFilter = {
|
||||
count: pageCount,
|
||||
startIndex: this.getStartIndex(),
|
||||
startIndex: startIndex ? startIndex : this.getStartIndex(),
|
||||
page: page,
|
||||
sortby: sortBy,
|
||||
sortOrder: sortOrder,
|
||||
|
@ -150,6 +150,7 @@ class Section extends React.Component {
|
||||
this.intervalHandler = null;
|
||||
|
||||
this.scroll = null;
|
||||
this.selectoRef = React.createRef(null);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -224,6 +225,7 @@ class Section extends React.Component {
|
||||
isInfoPanelAvailable,
|
||||
settingsStudio,
|
||||
clearUploadedFilesHistory,
|
||||
withPaging,
|
||||
} = this.props;
|
||||
|
||||
let sectionHeaderContent = null;
|
||||
@ -362,6 +364,8 @@ class Section extends React.Component {
|
||||
viewAs={viewAs}
|
||||
isHomepage={isHomepage}
|
||||
settingsStudio={settingsStudio}
|
||||
withPaging={withPaging}
|
||||
selectoRef={this.selectoRef}
|
||||
>
|
||||
{isMobile && (
|
||||
<StyledMainBar
|
||||
@ -498,11 +502,12 @@ class Section extends React.Component {
|
||||
return (
|
||||
<>
|
||||
{renderSection()}
|
||||
{!isMobile && uploadFiles && !dragging && (
|
||||
{!isMobile && uploadFiles && !dragging && withPaging && (
|
||||
<StyledSelectoWrapper>
|
||||
<Selecto
|
||||
boundContainer={".section-wrapper"}
|
||||
dragContainer={".section-body"}
|
||||
ref={this.selectoRef}
|
||||
boundContainer=".section-wrapper-content"
|
||||
dragContainer=".section-wrapper"
|
||||
selectableTargets={[".files-item"]}
|
||||
hitRate={0}
|
||||
selectByClick={false}
|
||||
@ -546,6 +551,7 @@ Section.propTypes = {
|
||||
isHomepage: PropTypes.bool,
|
||||
isInfoPanelAvailable: PropTypes.bool,
|
||||
settingsStudio: PropTypes.bool,
|
||||
withPaging: PropTypes.bool,
|
||||
};
|
||||
|
||||
Section.defaultProps = {
|
||||
@ -553,6 +559,7 @@ Section.defaultProps = {
|
||||
withBodyAutoFocus: false,
|
||||
isInfoPanelAvailable: true,
|
||||
settingsStudio: false,
|
||||
withPaging: true,
|
||||
};
|
||||
|
||||
Section.InfoPanelHeader = InfoPanelHeader;
|
||||
|
@ -10,33 +10,45 @@ import Scrollbar from "@docspace/components/scrollbar";
|
||||
import DragAndDrop from "@docspace/components/drag-and-drop";
|
||||
import {
|
||||
tablet,
|
||||
mobile,
|
||||
desktop,
|
||||
smallTablet,
|
||||
} from "@docspace/components/utils/device";
|
||||
|
||||
const settingsStudioStyles = css`
|
||||
${({ settingsStudio }) =>
|
||||
settingsStudio
|
||||
? css`
|
||||
padding: 0 7px 16px 20px;
|
||||
|
||||
@media ${tablet} {
|
||||
padding: 0 0 16px 24px;
|
||||
}
|
||||
|
||||
@media ${smallTablet} {
|
||||
padding: 8px 0 16px 24px;
|
||||
}
|
||||
`
|
||||
: css`
|
||||
@media ${tablet} {
|
||||
padding: ${({ viewAs, withPaging }) =>
|
||||
viewAs === "tile"
|
||||
? "19px 0 16px 24px"
|
||||
: withPaging
|
||||
? "19px 0 16px 24px"
|
||||
: "19px 0 16px 8px"};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const paddingStyles = css`
|
||||
padding: ${(props) =>
|
||||
props.settingsStudio
|
||||
? "0 7px 16px 20px"
|
||||
: props.viewAs === "row"
|
||||
? "19px 3px 16px 16px"
|
||||
padding: ${({ viewAs, withPaging }) =>
|
||||
viewAs === "row"
|
||||
? withPaging
|
||||
? "19px 3px 16px 16px"
|
||||
: "19px 3px 16px 0px"
|
||||
: "19px 3px 16px 20px"};
|
||||
|
||||
@media ${tablet} {
|
||||
padding: ${(props) =>
|
||||
props.settingsStudio ? "0 0 16px 24px" : "19px 0 16px 24px"};
|
||||
}
|
||||
|
||||
@media ${smallTablet} {
|
||||
padding: ${(props) =>
|
||||
props.settingsStudio ? "8px 0 16px 24px" : "19px 0 16px 24px"};
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
padding: ${(props) =>
|
||||
props.settingsStudio ? "8px 0 16px 24px" : "19px 0 16px 24px"};
|
||||
}
|
||||
${settingsStudioStyles};
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
@ -60,6 +72,7 @@ const commonStyles = css`
|
||||
border-top: none;
|
||||
|
||||
.section-wrapper {
|
||||
height: 100%;
|
||||
${(props) =>
|
||||
!props.withScroll &&
|
||||
`display: flex; flex-direction: column; height: 100%; box-sizing:border-box`};
|
||||
@ -134,6 +147,7 @@ const StyledSectionBody = styled.div`
|
||||
|
||||
const StyledDropZoneBody = styled(DragAndDrop)`
|
||||
max-width: 100vw !important;
|
||||
|
||||
${commonStyles} .drag-and-drop {
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
@ -188,6 +202,12 @@ class SectionBody extends React.Component {
|
||||
this.focusRef = null;
|
||||
}
|
||||
|
||||
onScroll = (e) => {
|
||||
this.props.selectoRef.current &&
|
||||
this.props.selectoRef.current.checkScroll();
|
||||
return e;
|
||||
};
|
||||
|
||||
render() {
|
||||
//console.log(" SectionBody render" );
|
||||
const {
|
||||
@ -202,6 +222,7 @@ class SectionBody extends React.Component {
|
||||
isDesktop,
|
||||
isHomepage,
|
||||
settingsStudio,
|
||||
withPaging,
|
||||
} = this.props;
|
||||
|
||||
const focusProps = autoFocus
|
||||
@ -221,11 +242,17 @@ class SectionBody extends React.Component {
|
||||
isLoaded={isLoaded}
|
||||
isDesktop={isDesktop}
|
||||
settingsStudio={settingsStudio}
|
||||
withPaging={withPaging}
|
||||
className="section-body"
|
||||
>
|
||||
{withScroll ? (
|
||||
!isMobileOnly ? (
|
||||
<Scrollbar scrollclass="section-scroll" stype="mediumBlack">
|
||||
<Scrollbar
|
||||
id="sectionScroll"
|
||||
scrollclass="section-scroll"
|
||||
stype="mediumBlack"
|
||||
onScroll={this.onScroll}
|
||||
>
|
||||
<div className="section-wrapper">
|
||||
<div className="section-wrapper-content" {...focusProps}>
|
||||
{children}
|
||||
@ -256,10 +283,11 @@ class SectionBody extends React.Component {
|
||||
isLoaded={isLoaded}
|
||||
isDesktop={isDesktop}
|
||||
settingsStudio={settingsStudio}
|
||||
withPaging={withPaging}
|
||||
>
|
||||
{withScroll ? (
|
||||
!isMobileOnly ? (
|
||||
<Scrollbar stype="mediumBlack">
|
||||
<Scrollbar id="sectionScroll" stype="mediumBlack">
|
||||
<div className="section-wrapper">
|
||||
<div className="section-wrapper-content" {...focusProps}>
|
||||
{children}
|
||||
|
@ -32,7 +32,7 @@
|
||||
"react-resize-detector": "^6.7.6",
|
||||
"react-router": "^5.2.1",
|
||||
"react-router-dom": "^5.3.0",
|
||||
"react-selecto": "^1.12.0",
|
||||
"react-selecto": "^1.18.2",
|
||||
"react-tooltip": "^4.2.21",
|
||||
"react-viewer": "^3.2.2",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
|
103
packages/components/infinite-loader/Grid.js
Normal file
103
packages/components/infinite-loader/Grid.js
Normal file
@ -0,0 +1,103 @@
|
||||
import React, { useCallback, useEffect, createRef } from "react";
|
||||
import { InfiniteLoader, WindowScroller } from "react-virtualized";
|
||||
import { StyledList } from "./StyledInfiniteLoader";
|
||||
|
||||
const GridComponent = ({
|
||||
hasMoreFiles,
|
||||
filesLength,
|
||||
itemCount,
|
||||
loadMoreItems,
|
||||
onScroll,
|
||||
countTilesInRow,
|
||||
children,
|
||||
className,
|
||||
scroll,
|
||||
selectedFolderId,
|
||||
}) => {
|
||||
const loaderRef = createRef();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => loaderRef?.current?.resetLoadMoreRowsCache(true), 1000);
|
||||
}, [loaderRef, selectedFolderId, filesLength]);
|
||||
|
||||
const isItemLoaded = useCallback(
|
||||
({ index }) => {
|
||||
return !hasMoreFiles || (index + 1) * countTilesInRow < filesLength;
|
||||
},
|
||||
[filesLength, hasMoreFiles, countTilesInRow]
|
||||
);
|
||||
|
||||
const renderTile = ({ index, style, key }) => {
|
||||
return (
|
||||
<div className="window-item" style={style} key={key}>
|
||||
{children[index]}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getItemSize = ({ index }) => {
|
||||
const itemClassNames = children[index]?.props?.className;
|
||||
const isFile = itemClassNames?.includes("isFile");
|
||||
const isFolder = itemClassNames?.includes("isFolder");
|
||||
const isRoom = itemClassNames?.includes("isRoom");
|
||||
const isFolderHeader = itemClassNames?.includes("folder_header");
|
||||
|
||||
const horizontalGap = 16;
|
||||
const verticalGap = 14;
|
||||
const headerMargin = 15;
|
||||
|
||||
const folderHeight = 64 + verticalGap;
|
||||
const roomHeight = 122 + verticalGap;
|
||||
const fileHeight = 220 + horizontalGap;
|
||||
const titleHeight = 20 + headerMargin + (isFolderHeader ? 0 : 11);
|
||||
|
||||
if (isRoom) return roomHeight;
|
||||
if (isFolder) return folderHeight;
|
||||
if (isFile) return fileHeight;
|
||||
return titleHeight;
|
||||
};
|
||||
|
||||
return (
|
||||
<InfiniteLoader
|
||||
isRowLoaded={isItemLoaded}
|
||||
rowCount={itemCount}
|
||||
loadMoreRows={loadMoreItems}
|
||||
ref={loaderRef}
|
||||
>
|
||||
{({ onRowsRendered, registerChild }) => (
|
||||
<WindowScroller scrollElement={scroll}>
|
||||
{({ height, isScrolling, onChildScroll, scrollTop }) => {
|
||||
if (height === undefined) {
|
||||
height = scroll.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
const width =
|
||||
document.getElementById("tileContainer")?.getBoundingClientRect()
|
||||
.width ?? 0;
|
||||
|
||||
return (
|
||||
<StyledList
|
||||
autoHeight
|
||||
height={height}
|
||||
onRowsRendered={onRowsRendered}
|
||||
ref={registerChild}
|
||||
rowCount={children.length}
|
||||
rowHeight={getItemSize}
|
||||
rowRenderer={renderTile}
|
||||
width={width}
|
||||
className={className}
|
||||
isScrolling={isScrolling}
|
||||
onChildScroll={onChildScroll}
|
||||
scrollTop={scrollTop}
|
||||
overscanRowCount={3}
|
||||
onScroll={onScroll}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</WindowScroller>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
);
|
||||
};
|
||||
|
||||
export default GridComponent;
|
36
packages/components/infinite-loader/InfiniteLoader.js
Normal file
36
packages/components/infinite-loader/InfiniteLoader.js
Normal file
@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import ListComponent from "./List";
|
||||
import GridComponent from "./Grid";
|
||||
import { isMobile } from "../utils/device";
|
||||
|
||||
const InfiniteLoaderComponent = (props) => {
|
||||
const { viewAs } = props;
|
||||
|
||||
const scroll = isMobileOnly
|
||||
? document.querySelector("#customScrollBar > .scroll-body")
|
||||
: document.querySelector("#sectionScroll > .scroll-body");
|
||||
|
||||
if (viewAs === "row" && scroll) scroll.style.paddingRight = 0;
|
||||
else scroll.style.paddingRight = isMobile() ? "8px" : "17px";
|
||||
|
||||
return viewAs === "tile" ? (
|
||||
<GridComponent scroll={scroll} {...props} />
|
||||
) : (
|
||||
<ListComponent scroll={scroll} {...props} />
|
||||
);
|
||||
};
|
||||
InfiniteLoaderComponent.propTypes = {
|
||||
viewAs: PropTypes.string.isRequired,
|
||||
hasMoreFiles: PropTypes.bool.isRequired,
|
||||
filesLength: PropTypes.number.isRequired,
|
||||
itemCount: PropTypes.number.isRequired,
|
||||
loadMoreItems: PropTypes.func.isRequired,
|
||||
itemSize: PropTypes.number,
|
||||
children: PropTypes.any.isRequired,
|
||||
/** Called when the list scroll positions changes */
|
||||
onScroll: PropTypes.func,
|
||||
};
|
||||
|
||||
export default InfiniteLoaderComponent;
|
139
packages/components/infinite-loader/List.js
Normal file
139
packages/components/infinite-loader/List.js
Normal file
@ -0,0 +1,139 @@
|
||||
import React, { useCallback, useEffect, createRef } from "react";
|
||||
import { InfiniteLoader, WindowScroller } from "react-virtualized";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import { StyledList } from "./StyledInfiniteLoader";
|
||||
|
||||
const ListComponent = ({
|
||||
viewAs,
|
||||
hasMoreFiles,
|
||||
filesLength,
|
||||
itemCount,
|
||||
onScroll,
|
||||
loadMoreItems,
|
||||
itemSize,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
children,
|
||||
className,
|
||||
scroll,
|
||||
infoPanelVisible,
|
||||
selectedFolderId,
|
||||
}) => {
|
||||
const loaderRef = createRef();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => loaderRef?.current?.resetLoadMoreRowsCache(true), 1000);
|
||||
}, [loaderRef, selectedFolderId, filesLength]);
|
||||
|
||||
const renderRow = ({ key, index, style }) => {
|
||||
const isLoaded = isItemLoaded({ index });
|
||||
if (!isLoaded) return getLoader(style, key);
|
||||
|
||||
return (
|
||||
<div className="row-list-item window-item" style={style} key={key}>
|
||||
{children[index]}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const isItemLoaded = useCallback(
|
||||
({ index }) => !hasMoreFiles || index < filesLength,
|
||||
[filesLength, hasMoreFiles]
|
||||
);
|
||||
|
||||
const renderTable = ({ index, style, key }) => {
|
||||
const storageSize = infoPanelVisible
|
||||
? localStorage.getItem(columnInfoPanelStorageName)
|
||||
: localStorage.getItem(columnStorageName);
|
||||
|
||||
const isLoaded = isItemLoaded({ index });
|
||||
if (!isLoaded) return getLoader(style, key);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="table-list-item window-item"
|
||||
style={{
|
||||
...style,
|
||||
display: "grid",
|
||||
gridTemplateColumns: storageSize,
|
||||
}}
|
||||
key={key}
|
||||
>
|
||||
{children[index]}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getLoader = (style, key) => {
|
||||
switch (viewAs) {
|
||||
case "table":
|
||||
return (
|
||||
<Loaders.TableLoader
|
||||
key={key}
|
||||
style={style}
|
||||
className="table-container_body-loader"
|
||||
count={1}
|
||||
/>
|
||||
);
|
||||
case "row":
|
||||
return (
|
||||
<Loaders.Rows
|
||||
key={key}
|
||||
style={style}
|
||||
className="row-loader"
|
||||
count={1}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<InfiniteLoader
|
||||
isRowLoaded={isItemLoaded}
|
||||
rowCount={itemCount}
|
||||
loadMoreRows={loadMoreItems}
|
||||
ref={loaderRef}
|
||||
>
|
||||
{({ onRowsRendered, registerChild }) => (
|
||||
<WindowScroller scrollElement={scroll}>
|
||||
{({ height, isScrolling, onChildScroll, scrollTop }) => {
|
||||
if (height === undefined) {
|
||||
height = scroll.getBoundingClientRect().height;
|
||||
}
|
||||
|
||||
const viewId =
|
||||
viewAs === "table" ? "table-container" : "rowContainer";
|
||||
|
||||
const width =
|
||||
document.getElementById(viewId)?.getBoundingClientRect().width ??
|
||||
0;
|
||||
|
||||
return (
|
||||
<StyledList
|
||||
autoHeight
|
||||
height={height}
|
||||
onRowsRendered={onRowsRendered}
|
||||
ref={registerChild}
|
||||
rowCount={hasMoreFiles ? children.length + 2 : children.length}
|
||||
rowHeight={itemSize}
|
||||
rowRenderer={viewAs === "table" ? renderTable : renderRow}
|
||||
width={width}
|
||||
className={className}
|
||||
isScrolling={isScrolling}
|
||||
onChildScroll={onChildScroll}
|
||||
scrollTop={scrollTop}
|
||||
overscanRowCount={3}
|
||||
onScroll={onScroll}
|
||||
viewAs={viewAs}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</WindowScroller>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListComponent;
|
8
packages/components/infinite-loader/Scroll.js
Normal file
8
packages/components/infinite-loader/Scroll.js
Normal file
@ -0,0 +1,8 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { StyledScroll } from "./StyledInfiniteLoader";
|
||||
|
||||
const Scroll = forwardRef((props, ref) => {
|
||||
return <StyledScroll {...props} forwardedRef={ref} />;
|
||||
});
|
||||
|
||||
export default Scroll;
|
87
packages/components/infinite-loader/StyledInfiniteLoader.js
Normal file
87
packages/components/infinite-loader/StyledInfiniteLoader.js
Normal file
@ -0,0 +1,87 @@
|
||||
import { List } from "react-virtualized";
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "../themes/base";
|
||||
import { desktop, mobile, tablet } from "../utils/device";
|
||||
|
||||
const StyledScroll = styled.div`
|
||||
overflow: scroll;
|
||||
|
||||
/* Chrome, Edge и Safari */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: ${({ theme }) => theme.scrollbar.backgroundColorVertical};
|
||||
border-radius: 3px;
|
||||
|
||||
:hover {
|
||||
background-color: ${({ theme }) =>
|
||||
theme.scrollbar.hoverBackgroundColorVertical};
|
||||
}
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: ${({ theme }) => theme.scrollbar.backgroundColorVertical};
|
||||
`;
|
||||
|
||||
const rowStyles = css`
|
||||
.row-list-item,
|
||||
.row-loader {
|
||||
padding-left: 16px;
|
||||
width: calc(100% - 33px) !important;
|
||||
|
||||
@media ${mobile} {
|
||||
width: calc(100% - 24px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.row-loader {
|
||||
padding-left: 22px;
|
||||
}
|
||||
`;
|
||||
|
||||
const tableStyles = css`
|
||||
margin-left: -20px;
|
||||
width: ${({ width }) => width + 40 + "px !important"};
|
||||
|
||||
.ReactVirtualized__Grid__innerScrollContainer {
|
||||
max-width: ${({ width }) => width + 40 + "px !important"};
|
||||
}
|
||||
.table-container_body-loader {
|
||||
width: calc(100% - 48px) !important;
|
||||
}
|
||||
|
||||
.table-list-item,
|
||||
.table-container_body-loader {
|
||||
padding-left: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const tileStyles = css`
|
||||
.files_header {
|
||||
padding-top: 11px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledList = styled(List)`
|
||||
outline: none;
|
||||
overflow: hidden !important;
|
||||
|
||||
${({ viewAs }) =>
|
||||
viewAs === "row"
|
||||
? rowStyles
|
||||
: viewAs === "table"
|
||||
? tableStyles
|
||||
: tileStyles}
|
||||
`;
|
||||
|
||||
StyledScroll.defaultProps = {
|
||||
theme: Base,
|
||||
};
|
||||
|
||||
export { StyledScroll, StyledList };
|
1
packages/components/infinite-loader/index.js
Normal file
1
packages/components/infinite-loader/index.js
Normal file
@ -0,0 +1 @@
|
||||
export default from "./InfiniteLoader";
|
@ -38,6 +38,7 @@
|
||||
"react-toastify": "^7.0.0",
|
||||
"react-tooltip": "^4.2.21",
|
||||
"react-transition-group": "^4.4.1",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"react-window": "^1.8.6",
|
||||
"resize-image": "^0.1.0"
|
||||
},
|
||||
|
@ -1,16 +1,10 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { memo } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import CustomScrollbarsVirtualList from "../scrollbar/custom-scrollbars-virtual-list";
|
||||
import { FixedSizeList as List, areEqual } from "react-window";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import StyledRowContainer from "./styled-row-container";
|
||||
import InfiniteLoaderComponent from "../infinite-loader";
|
||||
|
||||
class RowContainer extends React.PureComponent {
|
||||
renderRow = memo(({ data, index, style }) => {
|
||||
return <div style={style}>{data[index]}</div>;
|
||||
}, areEqual);
|
||||
|
||||
render() {
|
||||
const {
|
||||
manualHeight,
|
||||
@ -21,23 +15,13 @@ class RowContainer extends React.PureComponent {
|
||||
className,
|
||||
style,
|
||||
onScroll,
|
||||
filesLength,
|
||||
itemCount,
|
||||
fetchMoreFiles,
|
||||
hasMoreFiles,
|
||||
selectedFolderId,
|
||||
} = this.props;
|
||||
|
||||
const renderList = ({ height, width }) => (
|
||||
<List
|
||||
onScroll={onScroll}
|
||||
className="List"
|
||||
height={height}
|
||||
width={width}
|
||||
itemSize={itemHeight}
|
||||
itemCount={children.length}
|
||||
itemData={children}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{this.renderRow}
|
||||
</List>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledRowContainer
|
||||
id={id}
|
||||
@ -46,7 +30,23 @@ class RowContainer extends React.PureComponent {
|
||||
manualHeight={manualHeight}
|
||||
useReactWindow={useReactWindow}
|
||||
>
|
||||
{useReactWindow ? <AutoSizer>{renderList}</AutoSizer> : children}
|
||||
{useReactWindow ? (
|
||||
<InfiniteLoaderComponent
|
||||
className="List"
|
||||
viewAs="row"
|
||||
hasMoreFiles={hasMoreFiles}
|
||||
filesLength={filesLength}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={fetchMoreFiles}
|
||||
itemSize={itemHeight}
|
||||
onScroll={onScroll}
|
||||
selectedFolderId={selectedFolderId}
|
||||
>
|
||||
{children}
|
||||
</InfiniteLoaderComponent>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
}
|
||||
@ -69,6 +69,10 @@ RowContainer.propTypes = {
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
/** Called when the list scroll positions changes */
|
||||
onScroll: PropTypes.func,
|
||||
filesLength: PropTypes.number,
|
||||
itemCount: PropTypes.number,
|
||||
loadMoreItems: PropTypes.func,
|
||||
hasMoreFiles: PropTypes.bool,
|
||||
};
|
||||
|
||||
RowContainer.defaultProps = {
|
||||
|
@ -8,7 +8,7 @@ const StyledScrollbar = styled(Scrollbars)`
|
||||
props.color
|
||||
? props.color
|
||||
: props.theme.scrollbar.backgroundColorVertical};
|
||||
z-index: 1;
|
||||
z-index: 201;
|
||||
}
|
||||
.nav-thumb-horizontal {
|
||||
background-color: ${(props) =>
|
||||
|
@ -4,6 +4,16 @@ import { mobile, tablet, hugeMobile } from "../utils/device";
|
||||
import Scrollbar from "../scrollbar";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
const reactWindowContainerStyles = css`
|
||||
height: 100%;
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const reactWindowBodyStyles = css`
|
||||
display: block;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledTableContainer = styled.div`
|
||||
-moz-user-select: none;
|
||||
|
||||
@ -75,6 +85,8 @@ const StyledTableContainer = styled.div`
|
||||
width: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
${({ useReactWindow }) => useReactWindow && reactWindowContainerStyles}
|
||||
`;
|
||||
|
||||
StyledTableContainer.defaultProps = { theme: Base };
|
||||
@ -278,6 +290,8 @@ StyledTableHeaderCell.defaultProps = { theme: Base };
|
||||
|
||||
const StyledTableBody = styled.div`
|
||||
display: contents;
|
||||
|
||||
${({ useReactWindow }) => useReactWindow && reactWindowBodyStyles}
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled.div`
|
||||
|
@ -1,8 +1,54 @@
|
||||
import React from "react";
|
||||
import { StyledTableBody } from "./StyledTableContainer";
|
||||
import InfiniteLoaderComponent from "../infinite-loader";
|
||||
|
||||
const TableBody = (props) => {
|
||||
return <StyledTableBody className="table-container_body" {...props} />;
|
||||
const {
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
fetchMoreFiles,
|
||||
children,
|
||||
filesLength,
|
||||
hasMoreFiles,
|
||||
itemCount,
|
||||
itemHeight,
|
||||
useReactWindow,
|
||||
onScroll,
|
||||
infoPanelVisible,
|
||||
selectedFolderId,
|
||||
} = props;
|
||||
|
||||
return useReactWindow ? (
|
||||
<StyledTableBody
|
||||
useReactWindow={useReactWindow}
|
||||
className="table-container_body"
|
||||
>
|
||||
<InfiniteLoaderComponent
|
||||
className="TableList"
|
||||
viewAs="table"
|
||||
hasMoreFiles={hasMoreFiles}
|
||||
filesLength={filesLength}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={fetchMoreFiles}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
itemSize={itemHeight}
|
||||
onScroll={onScroll}
|
||||
infoPanelVisible={infoPanelVisible}
|
||||
selectedFolderId={selectedFolderId}
|
||||
>
|
||||
{children}
|
||||
</InfiniteLoaderComponent>
|
||||
</StyledTableBody>
|
||||
) : (
|
||||
<StyledTableBody className="table-container_body" {...props} />
|
||||
);
|
||||
};
|
||||
|
||||
TableBody.defaultProps = {
|
||||
itemHeight: 41,
|
||||
useReactWindow: false,
|
||||
infoPanelVisible: false,
|
||||
};
|
||||
|
||||
export default TableBody;
|
||||
|
@ -3,18 +3,26 @@ import { StyledTableContainer } from "./StyledTableContainer";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const TableContainer = (props) => {
|
||||
const { forwardedRef, useReactWindow, ...rest } = props;
|
||||
|
||||
return (
|
||||
<StyledTableContainer
|
||||
id="table-container"
|
||||
className="table-container"
|
||||
ref={props.forwardedRef}
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
useReactWindow={useReactWindow}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
TableContainer.defaultProps = {
|
||||
useReactWindow: false,
|
||||
};
|
||||
|
||||
TableContainer.propTypes = {
|
||||
forwardedRef: PropTypes.shape({ current: PropTypes.any }),
|
||||
useReactWindow: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default TableContainer;
|
||||
|
@ -190,8 +190,12 @@ class TableHeader extends React.Component {
|
||||
this.moveToRight(widths, newWidth);
|
||||
}
|
||||
|
||||
containerRef.current.style.gridTemplateColumns = widths.join(" ");
|
||||
this.headerRef.current.style.gridTemplateColumns = widths.join(" ");
|
||||
const str = widths.join(" ");
|
||||
|
||||
containerRef.current.style.gridTemplateColumns = str;
|
||||
this.headerRef.current.style.gridTemplateColumns = str;
|
||||
|
||||
this.updateTableRows(str);
|
||||
};
|
||||
|
||||
onMouseUp = () => {
|
||||
@ -544,6 +548,9 @@ class TableHeader extends React.Component {
|
||||
|
||||
if (str) {
|
||||
container.style.gridTemplateColumns = str;
|
||||
|
||||
this.updateTableRows(str);
|
||||
|
||||
if (this.headerRef.current) {
|
||||
this.headerRef.current.style.gridTemplateColumns = str;
|
||||
this.headerRef.current.style.width = containerWidth + "px";
|
||||
@ -559,6 +566,18 @@ class TableHeader extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
updateTableRows = (str) => {
|
||||
if (!this.props.useReactWindow) return;
|
||||
|
||||
const rows = document.querySelectorAll(".table-row, .table-list-item");
|
||||
|
||||
if (rows?.length) {
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
rows[i].style.gridTemplateColumns = str;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
resetColumns = (resetToDefault = false) => {
|
||||
const {
|
||||
containerRef,
|
||||
@ -695,6 +714,7 @@ class TableHeader extends React.Component {
|
||||
TableHeader.defaultProps = {
|
||||
sortingVisible: true,
|
||||
infoPanelVisible: false,
|
||||
useReactWindow: false,
|
||||
};
|
||||
|
||||
TableHeader.propTypes = {
|
||||
@ -709,6 +729,7 @@ TableHeader.propTypes = {
|
||||
isLengthenHeader: PropTypes.bool,
|
||||
sortingVisible: PropTypes.bool,
|
||||
infoPanelVisible: PropTypes.bool,
|
||||
useReactWindow: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default TableHeader;
|
||||
|
@ -118,7 +118,7 @@ const ForgotPasswordModalDialog = (props) => {
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="modal-dialog-button"
|
||||
key="SendBtn"
|
||||
key="ForgotSendBtn"
|
||||
label={
|
||||
isLoading ? t("Common:LoadingProcessing") : t("Common:SendButton")
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ const RecoverAccessModalDialog = ({ t, visible, onClose }) => {
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="recover-button-dialog"
|
||||
key="SendBtn"
|
||||
key="RecoverySendBtn"
|
||||
label={loading ? t("Common:Sending") : t("Common:SendButton")}
|
||||
size="normal"
|
||||
primary={true}
|
||||
|
@ -99,7 +99,7 @@ const RegisterModalDialog = ({
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="modal-dialog-button"
|
||||
key="SendBtn"
|
||||
key="RegisterSendBtn"
|
||||
label={loading ? t("Common:Sending") : t("RegisterSendButton")}
|
||||
size="normal"
|
||||
scale={false}
|
||||
|
@ -219,7 +219,14 @@ internal abstract class SharpBoxDaoBase : ThirdPartyProviderDao<SharpBoxProvider
|
||||
|
||||
protected string MakePath(object entryId)
|
||||
{
|
||||
return $"/{Convert.ToString(entryId, CultureInfo.InvariantCulture).Trim('/')}";
|
||||
var id = Convert.ToString(entryId, CultureInfo.InvariantCulture);
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(id)).Trim('/');
|
||||
}
|
||||
|
||||
return $"/{id}";
|
||||
}
|
||||
|
||||
protected override string MakeId(string path = null)
|
||||
@ -245,7 +252,7 @@ internal abstract class SharpBoxDaoBase : ThirdPartyProviderDao<SharpBoxProvider
|
||||
{
|
||||
path = entry.Id;
|
||||
}
|
||||
var p = string.IsNullOrEmpty(path) || path == "/" ? "" : ("-" + path.Replace('/', '|'));
|
||||
var p = string.IsNullOrEmpty(path) || path == "/" ? "" : ("-" + WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(path)));
|
||||
|
||||
return $"{PathPrefix}{p}";
|
||||
}
|
||||
|
@ -189,7 +189,8 @@ global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.AspNetCore.Http;
|
||||
global using Microsoft.AspNetCore.Mvc;
|
||||
global using Microsoft.AspNetCore.Mvc.Filters;
|
||||
global using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
global using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
global using Microsoft.AspNetCore.WebUtilities;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
global using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
@ -955,6 +955,7 @@ public class EntryManager
|
||||
var folders = entries.Where(r => r.FileEntryType == FileEntryType.Folder).Except(pinnedRooms).Except(rooms);
|
||||
var files = entries.Where(r => r.FileEntryType == FileEntryType.File);
|
||||
pinnedRooms = pinnedRooms.OrderBy(r => r, comparer);
|
||||
rooms = rooms.OrderBy(r => r, comparer);
|
||||
folders = folders.OrderBy(r => r, comparer);
|
||||
files = files.OrderBy(r => r, comparer);
|
||||
|
||||
|
@ -111,7 +111,14 @@ public class FileShareLink
|
||||
}
|
||||
|
||||
var fileId = Parse<T>(doc);
|
||||
var file = await fileDao.GetFileAsync(fileId);
|
||||
|
||||
File<T> file = null;
|
||||
|
||||
if (!EqualityComparer<T>.Default.Equals(fileId, default(T)))
|
||||
{
|
||||
file = await fileDao.GetFileAsync(fileId);
|
||||
}
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
return (FileShare.Restrict, file);
|
||||
|
@ -235,17 +235,20 @@ public class FilesControllerHelper<T> : FilesHelperBase<T>
|
||||
|
||||
public async Task<FileDto<T>> UpdateFileAsync(T fileId, string title, int lastVersion)
|
||||
{
|
||||
File<T> file = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
await _fileStorageService.FileRenameAsync(fileId, title);
|
||||
file = await _fileStorageService.FileRenameAsync(fileId, title);
|
||||
}
|
||||
|
||||
if (lastVersion > 0)
|
||||
{
|
||||
await _fileStorageService.UpdateToVersionAsync(fileId, lastVersion);
|
||||
var result = await _fileStorageService.UpdateToVersionAsync(fileId, lastVersion);
|
||||
file = result.Key;
|
||||
}
|
||||
|
||||
return await GetFileInfoAsync(fileId);
|
||||
return await GetFileInfoAsync(file.Id);
|
||||
}
|
||||
|
||||
public async Task<FileDto<T>> UpdateFileStreamAsync(Stream file, T fileId, string fileExtension, bool encrypted = false, bool forcesave = false)
|
||||
|
62
yarn.lock
62
yarn.lock
@ -2067,7 +2067,7 @@ __metadata:
|
||||
react-resize-detector: ^6.7.6
|
||||
react-router: ^5.2.1
|
||||
react-router-dom: ^5.3.0
|
||||
react-selecto: ^1.12.0
|
||||
react-selecto: ^1.18.2
|
||||
react-tooltip: ^4.2.21
|
||||
react-viewer: ^3.2.2
|
||||
react-virtualized-auto-sizer: ^1.0.6
|
||||
@ -2155,6 +2155,7 @@ __metadata:
|
||||
react-tooltip: ^4.2.21
|
||||
react-transition-group: ^4.4.1
|
||||
react-values: ^0.3.3
|
||||
react-virtualized: ^9.22.3
|
||||
react-window: ^1.8.6
|
||||
resize-image: ^0.1.0
|
||||
styled-components: ^5.3.1
|
||||
@ -3591,13 +3592,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@scena/dragscroll@npm:^1.1.1":
|
||||
version: 1.2.0
|
||||
resolution: "@scena/dragscroll@npm:1.2.0"
|
||||
"@scena/dragscroll@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "@scena/dragscroll@npm:1.2.1"
|
||||
dependencies:
|
||||
"@daybrush/utils": 1.6.0
|
||||
"@scena/event-emitter": ^1.0.2
|
||||
checksum: cac9db269d865a0acacf836327d26966d8acf10764bdeb52c4be5953797a805169dbcd6df15d090864046c6a2ed6bc49a0e22309b20e36dda8e73844e6833fa1
|
||||
checksum: 217b0612b0b5d2e721cd41b7534cc1b831aca236a4256b30bdeb965b3b0b7cf9f3934801a7158cb38483242e2568f8b734eeccfaee43b5df4e5df9ead7653dd5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -8899,7 +8900,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clsx@npm:^1.1.1":
|
||||
"clsx@npm:^1.0.4, clsx@npm:^1.1.1":
|
||||
version: 1.2.1
|
||||
resolution: "clsx@npm:1.2.1"
|
||||
checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12
|
||||
@ -10551,7 +10552,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-helpers@npm:^5.0.1":
|
||||
"dom-helpers@npm:^5.0.1, dom-helpers@npm:^5.1.3":
|
||||
version: 5.2.1
|
||||
resolution: "dom-helpers@npm:5.2.1"
|
||||
dependencies:
|
||||
@ -12727,13 +12728,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gesto@npm:^1.9.0":
|
||||
version: 1.11.1
|
||||
resolution: "gesto@npm:1.11.1"
|
||||
"gesto@npm:^1.11.1":
|
||||
version: 1.12.1
|
||||
resolution: "gesto@npm:1.12.1"
|
||||
dependencies:
|
||||
"@daybrush/utils": ^1.7.1
|
||||
"@scena/event-emitter": ^1.0.2
|
||||
checksum: cfd0c17bc84865f062b8ae03a99fe21373239a5e0a593d185a0d0713d3dc0ebf21738859b7473c7949ae9b79f40e02188d5c384656bab6932e439c380ed04a91
|
||||
checksum: e9b3f461e3ac05cc77187052660dbd0efa0aa7922bc650ced2d1d24ee77633046a92b8093eb584475bed1e3e82236999c86aef7007f949d55b4cce45c1d2bc5b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -20537,12 +20538,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-selecto@npm:^1.12.0":
|
||||
version: 1.17.0
|
||||
resolution: "react-selecto@npm:1.17.0"
|
||||
"react-selecto@npm:^1.18.2":
|
||||
version: 1.18.2
|
||||
resolution: "react-selecto@npm:1.18.2"
|
||||
dependencies:
|
||||
selecto: ~1.17.0
|
||||
checksum: 135bc3f9100a6631232b408ecb4ce008219ccce5b106ccc1cf03c7a0aba62cbe8bdf0427d88db5323988f181affc1635adb448f149eed93b7aa72d24c1a41e35
|
||||
selecto: ~1.18.2
|
||||
checksum: f1207f66b0a806e0129e9e611b720c160f5362a28cc9c381f8428fca0c4dc1df92a0a03c31f3b61a6c3de02f615eabbcf43cb785d908103548cc5ef3d1acda27
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -20755,6 +20756,23 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-virtualized@npm:^9.22.3":
|
||||
version: 9.22.3
|
||||
resolution: "react-virtualized@npm:9.22.3"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.7.2
|
||||
clsx: ^1.0.4
|
||||
dom-helpers: ^5.1.3
|
||||
loose-envify: ^1.4.0
|
||||
prop-types: ^15.7.2
|
||||
react-lifecycles-compat: ^3.0.4
|
||||
peerDependencies:
|
||||
react: ^15.3.0 || ^16.0.0-alpha
|
||||
react-dom: ^15.3.0 || ^16.0.0-alpha
|
||||
checksum: 5e3b566592293bc0057bc6be4f6ee29c58c8931421d2882a3ef45ca9b24c6f3ea78bbc5a182c2916af6520845e5a90f569b3a63c9b5e89428720913e6d6239cc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-window-infinite-loader@npm:^1.0.7":
|
||||
version: 1.0.8
|
||||
resolution: "react-window-infinite-loader@npm:1.0.8"
|
||||
@ -21894,21 +21912,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"selecto@npm:~1.17.0":
|
||||
version: 1.17.0
|
||||
resolution: "selecto@npm:1.17.0"
|
||||
"selecto@npm:~1.18.2":
|
||||
version: 1.18.2
|
||||
resolution: "selecto@npm:1.18.2"
|
||||
dependencies:
|
||||
"@daybrush/utils": ^1.7.1
|
||||
"@egjs/children-differ": ^1.0.1
|
||||
"@scena/dragscroll": ^1.1.1
|
||||
"@scena/dragscroll": ^1.2.1
|
||||
"@scena/event-emitter": ^1.0.5
|
||||
css-styled: ^1.0.0
|
||||
css-to-mat: ^1.0.3
|
||||
framework-utils: ^1.1.0
|
||||
gesto: ^1.9.0
|
||||
gesto: ^1.11.1
|
||||
keycon: ^1.2.0
|
||||
overlap-area: ^1.1.0
|
||||
checksum: ad288ee4be3bc2610925010a5a215370e0664f2075d7157ef2c020d08ac7244e9d6818fccb67998dbd655be8c5b9f2912cac329b8a9961787c9e86873adba47d
|
||||
checksum: 192221746c8688c11e20ea1fc5918de8674e5eeba2b6d3a15324fb6befec3005abfaa42625eda36095f79df5bc76036576e8aa9a29a0a05476e1528666809b1e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user