+
{info.comment}
>
@@ -232,6 +268,7 @@ const VersionRow = (props) => {
export default inject(({ auth, versionHistoryStore, selectedFolderStore }) => {
const { user } = auth.userStore;
+ const { openUser, setIsVisible } = auth.infoPanelStore;
const { culture, isTabletView } = auth.settingsStore;
const language = (user && user.cultureName) || culture || "en";
@@ -256,6 +293,8 @@ export default inject(({ auth, versionHistoryStore, selectedFolderStore }) => {
updateCommentVersion,
isEditing: isEdit,
canChangeVersionFileHistory,
+ openUser,
+ setIsVisible,
};
})(
withRouter(
diff --git a/packages/client/src/pages/VersionHistory/Section/Body/index.js b/packages/client/src/pages/VersionHistory/Section/Body/index.js
index 22f56cbf4c..4536dc917f 100644
--- a/packages/client/src/pages/VersionHistory/Section/Body/index.js
+++ b/packages/client/src/pages/VersionHistory/Section/Body/index.js
@@ -64,7 +64,7 @@ class SectionBodyContent extends React.Component {
this.setState((prevState) => ({
rowSizes: {
...prevState.rowSizes,
- [i]: itemHeight + 24, //composed of itemHeight = clientHeight of div and padding-top = 12px and padding-bottom = 12px
+ [i]: itemHeight + 27, //composed of itemHeight = clientHeight of div and padding-top = 13px and padding-bottom = 12px
},
}));
};
@@ -74,7 +74,7 @@ class SectionBodyContent extends React.Component {
};
renderRow = memo(({ index, style }) => {
- const { versions, culture } = this.props;
+ const { versions, culture, onClose } = this.props;
const prevVersion = versions[index > 0 ? index - 1 : index].versionGroup;
let isVersion = true;
@@ -85,6 +85,7 @@ class SectionBodyContent extends React.Component {
return (
{
+ toastr.error(t("Files:DocumentEdited"));
+ };
+
onClickMute = (e, item, t) => {
const data = (e.currentTarget && e.currentTarget.dataset) || e;
const { action } = data;
@@ -676,7 +680,7 @@ class ContextOptionsStore {
return { pinOptions, muteOptions };
};
getFilesContextOptions = (item, t, isInfoPanel) => {
- const { contextOptions } = item;
+ const { contextOptions, isEditing } = item;
const { enablePlugins } = this.authStore.settingsStore;
@@ -729,7 +733,10 @@ class ContextOptionsStore {
key: "finalize-version",
label: t("FinalizeVersion"),
icon: HistoryFinalizedReactSvgUrl,
- onClick: () => this.finalizeVersion(item.id, item.security),
+ onClick: () =>
+ isEditing
+ ? this.onShowEditingToast(t)
+ : this.finalizeVersion(item.id, item.security),
disabled: false,
},
{
@@ -750,7 +757,10 @@ class ContextOptionsStore {
key: "finalize-version",
label: t("FinalizeVersion"),
icon: HistoryFinalizedReactSvgUrl,
- onClick: () => this.finalizeVersion(item.id),
+ onClick: () =>
+ isEditing
+ ? this.onShowEditingToast(t)
+ : this.finalizeVersion(item.id),
disabled: false,
},
{
@@ -776,7 +786,9 @@ class ContextOptionsStore {
key: "move-to",
label: t("Common:MoveTo"),
icon: MoveReactSvgUrl,
- onClick: this.onMoveAction,
+ onClick: isEditing
+ ? () => this.onShowEditingToast(t)
+ : this.onMoveAction,
disabled: false,
},
{
@@ -804,7 +816,9 @@ class ContextOptionsStore {
key: "move-to",
label: t("Common:MoveTo"),
icon: MoveReactSvgUrl,
- onClick: this.onMoveAction,
+ onClick: isEditing
+ ? () => this.onShowEditingToast(t)
+ : this.onMoveAction,
disabled: false,
},
{
@@ -1099,7 +1113,8 @@ class ContextOptionsStore {
? t("Common:Disconnect")
: t("Common:Delete"),
icon: TrashReactSvgUrl,
- onClick: () => this.onClickDelete(item, t),
+ onClick: () =>
+ isEditing ? this.onShowEditingToast(t) : this.onClickDelete(item, t),
disabled: false,
},
];
@@ -1144,7 +1159,7 @@ class ContextOptionsStore {
getGroupContextOptions = (t) => {
const { personal } = this.authStore.settingsStore;
- const { selection } = this.filesStore;
+ const { selection, allFilesIsEditing } = this.filesStore;
const { setDeleteDialogVisible } = this.dialogsStore;
const {
isRecycleBinFolder,
@@ -1152,12 +1167,7 @@ class ContextOptionsStore {
isArchiveFolder,
} = this.treeFoldersStore;
- const {
- pinRooms,
- unpinRooms,
-
- deleteRooms,
- } = this.filesActionsStore;
+ const { pinRooms, unpinRooms, deleteRooms } = this.filesActionsStore;
if (isRoomsFolder || isArchiveFolder) {
const isPinOption = selection.filter((item) => !item.pinned).length > 0;
@@ -1339,7 +1349,9 @@ class ContextOptionsStore {
key: "move-to",
label: t("Common:MoveTo"),
icon: MoveReactSvgUrl,
- onClick: this.onMoveAction,
+ onClick: allFilesIsEditing
+ ? () => this.onShowEditingToast(t)
+ : this.onMoveAction,
disabled: isRecycleBinFolder || !moveItems,
},
{
@@ -1365,23 +1377,25 @@ class ContextOptionsStore {
key: "delete",
label: t("Common:Delete"),
icon: TrashReactSvgUrl,
- onClick: () => {
- if (this.settingsStore.confirmDelete) {
- setDeleteDialogVisible(true);
- } else {
- const translations = {
- deleteOperation: t("Translations:DeleteOperation"),
- deleteFromTrash: t("Translations:DeleteFromTrash"),
- deleteSelectedElem: t("Translations:DeleteSelectedElem"),
- FileRemoved: t("Files:FileRemoved"),
- FolderRemoved: t("Files:FolderRemoved"),
- };
+ onClick: allFilesIsEditing
+ ? () => this.onShowEditingToast(t)
+ : () => {
+ if (this.settingsStore.confirmDelete) {
+ setDeleteDialogVisible(true);
+ } else {
+ const translations = {
+ deleteOperation: t("Translations:DeleteOperation"),
+ deleteFromTrash: t("Translations:DeleteFromTrash"),
+ deleteSelectedElem: t("Translations:DeleteSelectedElem"),
+ FileRemoved: t("Files:FileRemoved"),
+ FolderRemoved: t("Files:FolderRemoved"),
+ };
- this.filesActionsStore
- .deleteAction(translations)
- .catch((err) => toastr.error(err));
- }
- },
+ this.filesActionsStore
+ .deleteAction(translations)
+ .catch((err) => toastr.error(err));
+ }
+ },
disabled: !deleteItems || isRootThirdPartyFolder,
},
];
diff --git a/packages/client/src/store/FilesStore.js b/packages/client/src/store/FilesStore.js
index 7e2c470926..3a4bbd8f7f 100644
--- a/packages/client/src/store/FilesStore.js
+++ b/packages/client/src/store/FilesStore.js
@@ -28,6 +28,7 @@ import { isDesktop } from "@docspace/components/utils/device";
import { getContextMenuKeysByType } from "SRC_DIR/helpers/plugins";
import { PluginContextMenuItemType } from "SRC_DIR/helpers/plugins/constants";
import debounce from "lodash.debounce";
+import Queue from "queue-promise";
const { FilesFilter, RoomsFilter } = api;
const storageViewAs = localStorage.getItem("viewAs");
@@ -129,6 +130,11 @@ class FilesStore {
highlightFile = {};
thumbnails = new Set();
movingInProgress = false;
+ createNewFilesQueue = new Queue({
+ concurrent: 5,
+ interval: 500,
+ start: true,
+ });
constructor(
authStore,
@@ -272,8 +278,43 @@ class FilesStore {
this.createThumbnail(this.files[foundIndex]);
});
+
+ this.createNewFilesQueue.on("resolve", this.onResolveNewFile);
}
+ onResolveNewFile = (fileInfo) => {
+ if (!fileInfo) return;
+
+ //console.log("onResolveNewFiles", { fileInfo });
+
+ if (this.files.findIndex((x) => x.id === fileInfo.id) > -1) return;
+
+ console.log("[WS] create new file", { fileInfo });
+
+ const newFiles = [fileInfo, ...this.files];
+
+ if (
+ newFiles.length > this.filter.pageCount &&
+ this.authStore.settingsStore.withPaging
+ ) {
+ newFiles.pop(); // Remove last
+ }
+
+ const newFilter = this.filter;
+ newFilter.total += 1;
+
+ runInAction(() => {
+ this.setFilter(newFilter);
+ this.setFiles(newFiles);
+ });
+
+ this.debouncefetchTreeFolders();
+ };
+
+ debouncefetchTreeFolders = debounce(() => {
+ this.treeFoldersStore.fetchTreeFolders();
+ }, 1000);
+
debounceRemoveFiles = debounce(() => {
this.removeFiles(this.tempActionFilesIds);
}, 1000);
@@ -296,34 +337,35 @@ class FilesStore {
//To update a file version
if (foundIndex > -1 && !this.authStore.settingsStore.withPaging) {
- this.getFileInfo(file.id);
+ if (
+ this.files[foundIndex].version !== file.version ||
+ this.files[foundIndex].versionGroup !== file.versionGroup
+ ) {
+ this.files[foundIndex].version = file.version;
+ this.files[foundIndex].versionGroup = file.versionGroup;
+ }
this.checkSelection(file);
}
if (foundIndex > -1) return;
- const fileInfo = await api.files.getFileInfo(file.id);
+ setTimeout(() => {
+ const foundIndex = this.files.findIndex((x) => x.id === file.id);
+ if (foundIndex > -1) {
+ //console.log("Skip in timeout");
+ return null;
+ }
- if (this.files.findIndex((x) => x.id === opt?.id) > -1) return;
- console.log("[WS] create new file", fileInfo.id, fileInfo.title);
+ this.createNewFilesQueue.enqueue(() => {
+ const foundIndex = this.files.findIndex((x) => x.id === file.id);
+ if (foundIndex > -1) {
+ //console.log("Skip in queue");
+ return null;
+ }
- const newFiles = [fileInfo, ...this.files];
-
- if (
- newFiles.length > this.filter.pageCount &&
- this.authStore.settingsStore.withPaging
- ) {
- newFiles.pop(); // Remove last
- }
-
- const newFilter = this.filter;
- newFilter.total += 1;
-
- runInAction(() => {
- this.setFilter(newFilter);
- this.setFiles(newFiles);
- this.treeFoldersStore.fetchTreeFolders();
- });
+ return api.files.getFileInfo(file.id);
+ });
+ }, 300);
} else if (opt?.type === "folder" && opt?.id) {
const foundIndex = this.folders.findIndex((x) => x.id === opt?.id);
@@ -1550,10 +1592,7 @@ class FilesStore {
const canConvert = this.filesSettingsStore.extsConvertible[item.fileExst];
const isEncrypted = item.encrypted;
const isDocuSign = false; //TODO: need this prop;
- const isEditing =
- (item.fileStatus & FileStatus.IsEditing) === FileStatus.IsEditing;
- // const isFileOwner =
- // item.createdBy?.id === this.authStore.userStore.user?.id;
+ const isEditing = false; // (item.fileStatus & FileStatus.IsEditing) === FileStatus.IsEditing;
const { isRecycleBinFolder, isMy, isArchiveFolder } = this.treeFoldersStore;
@@ -1614,7 +1653,7 @@ class FilesStore {
"send-by-email",
"docu-sign",
"version", //category
- "finalize-version",
+ // "finalize-version",
"show-version-history",
"show-info",
"block-unblock-version", //need split
diff --git a/packages/client/src/store/UploadDataStore.js b/packages/client/src/store/UploadDataStore.js
index 21123866ce..3cd0fc8403 100644
--- a/packages/client/src/store/UploadDataStore.js
+++ b/packages/client/src/store/UploadDataStore.js
@@ -906,7 +906,7 @@ class UploadDataStore {
return Promise.reject(res.data.message);
}
- const { uploaded, id: fileId } = res.data.data;
+ const { uploaded, id: fileId, file: fileInfo } = res.data.data;
let uploadedSize, newPercent;
@@ -947,7 +947,6 @@ class UploadDataStore {
});
if (uploaded) {
- const fileInfo = await getFileInfo(fileId);
runInAction(() => {
this.files[indexOfFile].action = "uploaded";
this.files[indexOfFile].fileId = fileId;
diff --git a/packages/common/components/Article/sub-components/article-apps.js b/packages/common/components/Article/sub-components/article-apps.js
index eaac6f65a0..47fde040e0 100644
--- a/packages/common/components/Article/sub-components/article-apps.js
+++ b/packages/common/components/Article/sub-components/article-apps.js
@@ -96,12 +96,13 @@ const ArticleApps = React.memo(({ theme, showText }) => {
title={t("Translations:MobileAndroid")}
/>
window.open(iosLink)}
iconName={IOSReactSvgUrl}
iconHoverName={IOSHoverReactSvgUrl}
size="32"
isFill={false}
title={t("Translations:MobileIos")}
+ onMouseDown={() => window.open(iosLink)}
+ isClickable={true}
/>
diff --git a/packages/common/components/ColorTheme/styled/versionBadge.js b/packages/common/components/ColorTheme/styled/versionBadge.js
index 4adf8265e6..cf7aa5c87c 100644
--- a/packages/common/components/ColorTheme/styled/versionBadge.js
+++ b/packages/common/components/ColorTheme/styled/versionBadge.js
@@ -8,15 +8,11 @@ const getDefaultStyles = ({ $currentColorScheme, $isVersion, theme, index }) =>
path {
fill: ${!$isVersion
? theme.filesVersionHistory.badge.defaultFill
- : index === 0
- ? theme.filesVersionHistory.badge.fill
- : $currentColorScheme.main.accent};
+ : theme.filesVersionHistory.badge.fill};
stroke: ${!$isVersion
? theme.filesVersionHistory.badge.stroke
- : index === 0
- ? theme.filesVersionHistory.badge.fill
- : $currentColorScheme.main.accent};
+ : theme.filesVersionHistory.badge.fill};
}
}
diff --git a/packages/common/components/MediaViewer/sub-components/PlayerTimeline/PlayerTimeline.styled.ts b/packages/common/components/MediaViewer/sub-components/PlayerTimeline/PlayerTimeline.styled.ts
index 5547c88f00..d0126a9c20 100644
--- a/packages/common/components/MediaViewer/sub-components/PlayerTimeline/PlayerTimeline.styled.ts
+++ b/packages/common/components/MediaViewer/sub-components/PlayerTimeline/PlayerTimeline.styled.ts
@@ -62,7 +62,7 @@ export const PlayerTimelineWrapper = styled.div`
display: flex;
align-items: center;
- margin-top: 92px;
+ margin-top: 12px;
height: 4px;
width: 100%;
diff --git a/packages/common/components/MediaViewer/sub-components/ViewerPlayer/ViewerPlayer.styled.ts b/packages/common/components/MediaViewer/sub-components/ViewerPlayer/ViewerPlayer.styled.ts
index 26fbf22bf3..64511eba98 100644
--- a/packages/common/components/MediaViewer/sub-components/ViewerPlayer/ViewerPlayer.styled.ts
+++ b/packages/common/components/MediaViewer/sub-components/ViewerPlayer/ViewerPlayer.styled.ts
@@ -83,6 +83,12 @@ export const ControlContainer = styled.div`
export const PlayerControlsWrapper = styled.div`
padding: 0 30px;
width: 100%;
+ margin-top: 80px;
+
+ ${isMobile &&
+ css`
+ margin-top: 0px;
+ `}
${isMobileOnly &&
css`
diff --git a/packages/common/components/MediaViewer/sub-components/ViewerPlayer/index.tsx b/packages/common/components/MediaViewer/sub-components/ViewerPlayer/index.tsx
index 0434155ec0..2649d7e409 100644
--- a/packages/common/components/MediaViewer/sub-components/ViewerPlayer/index.tsx
+++ b/packages/common/components/MediaViewer/sub-components/ViewerPlayer/index.tsx
@@ -533,6 +533,14 @@ function ViewerPlayer({
const onTouchStart = () => {
if (isPlaying && isVideo) restartToolbarVisibleTimer();
};
+
+ const stopPropagation = useCallback(
+ (event: React.MouseEvent) => {
+ event?.stopPropagation();
+ },
+ []
+ );
+
const onTouchMove = () => {
if (isPlaying && isVideo) restartToolbarVisibleTimer();
};
@@ -599,8 +607,9 @@ function ViewerPlayer({
$isShow={panelVisible && !isLoading}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
+ onClick={handleClickVideo}
>
-
+
_logger;
public ChunkedUploaderHandlerService(
@@ -66,7 +65,8 @@ public class ChunkedUploaderHandlerService
InstanceCrypto instanceCrypto,
ChunkedUploadSessionHolder chunkedUploadSessionHolder,
ChunkedUploadSessionHelper chunkedUploadSessionHelper,
- SocketManager socketManager)
+ SocketManager socketManager,
+ FileDtoHelper filesWrapperHelper)
{
_tenantManager = tenantManager;
_fileUploader = fileUploader;
@@ -78,6 +78,7 @@ public class ChunkedUploaderHandlerService
_chunkedUploadSessionHolder = chunkedUploadSessionHolder;
_chunkedUploadSessionHelper = chunkedUploadSessionHelper;
_socketManager = socketManager;
+ _filesWrapperHelper = filesWrapperHelper;
_logger = logger;
}
@@ -139,7 +140,7 @@ public class ChunkedUploaderHandlerService
if (resumedSession.BytesUploaded == resumedSession.BytesTotal)
{
- await WriteSuccess(context, ToResponseObject(resumedSession.File), (int)HttpStatusCode.Created);
+ await WriteSuccess(context, await ToResponseObject(resumedSession.File), (int)HttpStatusCode.Created);
_ = _filesMessageService.Send(resumedSession.File, MessageAction.FileUploaded, resumedSession.File.Title);
await _socketManager.CreateFileAsync(resumedSession.File);
@@ -218,10 +219,10 @@ public class ChunkedUploaderHandlerService
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json";
- return context.Response.WriteAsync(JsonConvert.SerializeObject(new { success, data, message }));
+ return context.Response.WriteAsync(JsonSerializer.Serialize(new { success, data, message }, SocketManager.GetSerializerSettings()));
}
- private static object ToResponseObject(File file)
+ private async Task