Merge branch 'release/rc-v1.2.0' into feature/static-versioned

This commit is contained in:
Timofey Boyko 2023-01-16 10:36:22 +03:00
commit 0cb238e5d3
29 changed files with 349 additions and 253 deletions

View File

@ -3,7 +3,7 @@
"AdvancedSettings": "Advanced Settings",
"AdvancedSettingsTooltip": "Hide default authentication page and automatically redirect to SSO service",
"AttributeMatching": "Attribute Mapping",
"AttributeMatchingTooltip": "The correspondence of the single sign-on attributes to the fields of the portal 'People' module",
"AttributeMatchingTooltip": "The correspondence of the Single Sign-On attributes to the entries of the 'Accounts' section",
"Binding": "Binding:",
"CertificateExist": "Certificate with same action type already exists",
"ConfirmationText": "All the data you entered will be lost. Are you sure you want to continue?",

View File

@ -3,7 +3,7 @@
"AdvancedSettings": "Дополнительно",
"AdvancedSettingsTooltip": "Скрыть страницу аутентификации по умолчанию и автоматически перенаправлять на сервис SSO",
"AttributeMatching": "Сопоставление аттрибутов:",
"AttributeMatchingTooltip": "Соответствие атрибутов единого входа полям в модуле портала 'Люди'",
"AttributeMatchingTooltip": "Соответствие атрибутов единого входа записям раздела 'Аккаунты'",
"Binding": "Привязка:",
"CertificateExist": "Сертификат с таким же типом действия уже существует",
"ConfirmationText": "Все введенные данные будут потеряны. Вы уверены, что хотите продолжить?",

View File

@ -17,10 +17,10 @@ const StyledWrapper = styled.div`
`;
const DocspaceLogo = (props) => {
const { className, whiteLabelLogoUrls, userTheme } = props;
const { className, whiteLabelLogoUrls, theme } = props;
const logo = getLogoFromPath(
userTheme === "Dark"
!theme.isBase
? whiteLabelLogoUrls[1]?.path?.dark
: whiteLabelLogoUrls[1]?.path?.light
);
@ -33,12 +33,11 @@ const DocspaceLogo = (props) => {
};
export default inject(({ auth }) => {
const { settingsStore, userStore } = auth;
const { whiteLabelLogoUrls } = settingsStore;
const { userTheme } = userStore;
const { settingsStore } = auth;
const { whiteLabelLogoUrls, theme } = settingsStore;
return {
whiteLabelLogoUrls,
userTheme,
theme,
};
})(observer(DocspaceLogo));

View File

@ -23,7 +23,7 @@ import AppLoader from "@docspace/common/components/AppLoader";
import Snackbar from "@docspace/components/snackbar";
import moment from "moment";
import ReactSmartBanner from "./components/SmartBanner";
import { useThemeDetector } from "SRC_DIR/helpers/utils";
import { useThemeDetector } from "@docspace/common/utils/useThemeDetector";
import { isMobileOnly } from "react-device-detect";
import IndicatorLoader from "./components/IndicatorLoader";
import DialogsWrapper from "./components/dialogs/DialogsWrapper";

View File

@ -21,7 +21,6 @@ const CreateEvent = ({
templateId,
fromTemplate,
onClose,
setIsLoading,
createFile,
createFolder,
@ -36,7 +35,7 @@ const CreateEvent = ({
isPrivacy,
isDesktop,
editCompleteAction,
completeAction,
clearActiveOperations,
fileCopyAs,
@ -49,6 +48,7 @@ const CreateEvent = ({
setEventDialogVisible,
eventDialogVisible,
createWithoutDialog,
}) => {
const [headerTitle, setHeaderTitle] = React.useState(null);
const [startValue, setStartValue] = React.useState("");
@ -74,7 +74,12 @@ const CreateEvent = ({
}
setHeaderTitle(defaultName);
setEventDialogVisible(true);
if (!createWithoutDialog) {
setEventDialogVisible(true);
} else {
onSave(null, title || defaultName);
}
return () => {
setEventDialogVisible(false);
@ -120,7 +125,7 @@ const CreateEvent = ({
addActiveItems(null, [folder.id]);
setCreatedItem({ id: createdFolderId, type: "folder" });
})
.then(() => editCompleteAction(item, type, true))
.then(() => completeAction(item, type, true))
.catch((e) => toastr.error(e))
.finally(() => {
const folderIds = [+id];
@ -140,7 +145,7 @@ const CreateEvent = ({
open && openDocEditor(file.id, file.providerKey, tab);
})
.then(() => editCompleteAction(item, type))
.then(() => completeAction(item, type))
.catch((err) => {
let errorMessage = "";
if (typeof err === "object") {
@ -198,10 +203,9 @@ const CreateEvent = ({
createdFileId = file.id;
setCreatedItem({ id: createdFileId, type: "file" });
addActiveItems([file.id]);
return open && openDocEditor(file.id, file.providerKey, tab);
})
.then(() => editCompleteAction(item, type))
.then(() => completeAction(item, type))
.catch((e) => toastr.error(e))
.finally(() => {
const fileIds = [+id];
@ -237,7 +241,7 @@ const CreateEvent = ({
return open && openDocEditor(file.id, file.providerKey, tab);
})
.then(() => editCompleteAction(item, type))
.then(() => completeAction(item, type))
.catch((e) => toastr.error(e))
.finally(() => {
const fileIds = [+id];
@ -267,6 +271,7 @@ const CreateEvent = ({
onSave={onSave}
onCancel={onCancel}
onClose={onCloseAction}
isCreateDialog={true}
/>
);
};
@ -294,7 +299,7 @@ export default inject(
const { gallerySelected, setGallerySelected } = oformsStore;
const { editCompleteAction } = filesActionsStore;
const { completeAction } = filesActionsStore;
const { clearActiveOperations, fileCopyAs } = uploadDataStore;
@ -313,6 +318,8 @@ export default inject(
eventDialogVisible,
} = dialogsStore;
const { createWithoutDialog } = filesStore;
return {
setEventDialogVisible,
eventDialogVisible,
@ -331,7 +338,7 @@ export default inject(
isDesktop: isDesktopClient,
isPrivacy: isPrivacyFolder,
isTrashFolder: isRecycleBinFolder,
editCompleteAction,
completeAction,
clearActiveOperations,
fileCopyAs,
@ -341,6 +348,8 @@ export default inject(
replaceFileStream,
setEncryptionAccess,
createWithoutDialog,
};
}
)(observer(CreateEvent));

View File

@ -164,7 +164,7 @@ export default inject(
uploadRoomLogo,
addLogoToRoom,
fetchFiles,
addFile,
addItem,
} = filesStore;
const { createTag, fetchTags } = tagsStore;

View File

@ -19,7 +19,7 @@ const RenameEvent = ({
updateFile,
renameFolder,
editCompleteAction,
completeAction,
clearActiveOperations,
setEventDialogVisible,
@ -54,7 +54,7 @@ const RenameEvent = ({
onClose();
return editCompleteAction(item, type);
return completeAction(item, type);
} else {
timerId = setTimeout(() => {
isFile ? addActiveItems([item.id]) : addActiveItems(null, [item.id]);
@ -63,7 +63,7 @@ const RenameEvent = ({
isFile
? updateFile(item.id, value)
.then(() => editCompleteAction(item, type))
.then(() => completeAction(item, type))
.then(() =>
toastr.success(
t("FileRenamed", {
@ -74,7 +74,7 @@ const RenameEvent = ({
)
.catch((err) => {
toastr.error(err);
editCompleteAction(item, type);
completeAction(item, type);
})
.finally(() => {
clearTimeout(timerId);
@ -84,7 +84,7 @@ const RenameEvent = ({
onClose();
})
: renameFolder(item.id, value)
.then(() => editCompleteAction(item, type))
.then(() => completeAction(item, type))
.then(() => {
if (selectedFolderId === item.id) {
setSelectedFolder({ title: value });
@ -98,7 +98,7 @@ const RenameEvent = ({
})
.catch((err) => {
toastr.error(err);
editCompleteAction(item, type);
completeAction(item, type);
})
.finally(() => {
clearTimeout(timerId);
@ -146,7 +146,7 @@ export default inject(
const { id, setSelectedFolder } = selectedFolderStore;
const { editCompleteAction } = filesActionsStore;
const { completeAction } = filesActionsStore;
const { clearActiveOperations } = uploadDataStore;
const { setEventDialogVisible, eventDialogVisible } = dialogsStore;
@ -157,7 +157,7 @@ export default inject(
updateFile,
renameFolder,
editCompleteAction,
completeAction,
clearActiveOperations,
setEventDialogVisible,

View File

@ -6,6 +6,8 @@ import ModalDialog from "@docspace/components/modal-dialog";
import TextInput from "@docspace/components/text-input";
import Button from "@docspace/components/button";
import ComboBox from "@docspace/components/combobox";
import Checkbox from "@docspace/components/checkbox";
import Box from "@docspace/components/box";
const Dialog = ({
t,
@ -19,9 +21,17 @@ const Dialog = ({
onSave,
onCancel,
onClose,
isCreateDialog,
createWithoutDialog,
setCreateWithoutDialog,
}) => {
const [value, setValue] = useState("");
const [isDisabled, setIsDisabled] = useState(false);
const [isChecked, setIsChecked] = useState(false);
useEffect(() => {
createWithoutDialog && isCreateDialog && setIsChecked(createWithoutDialog);
}, [isCreateDialog, createWithoutDialog]);
useEffect(() => {
let input = document?.getElementById("create-text-input");
@ -67,24 +77,31 @@ const Dialog = ({
const onSaveAction = useCallback(
(e) => {
setIsDisabled(true);
isCreateDialog && setCreateWithoutDialog(isChecked);
onSave && onSave(e, value);
},
[onSave, value]
[onSave, isCreateDialog, value, isChecked]
);
const onCancelAction = useCallback((e) => {
onCancel && onCancel(e);
setCreateWithoutDialog(false);
}, []);
const onCloseAction = useCallback(
(e) => {
if (!isDisabled) {
onClose && onClose(e);
setCreateWithoutDialog(false);
}
},
[isDisabled]
);
const onChangeCheckbox = () => {
isCreateDialog && setIsChecked((val) => !val);
};
return (
<ModalDialog
visible={visible}
@ -106,6 +123,16 @@ const Dialog = ({
onFocus={onFocus}
isDisabled={isDisabled}
/>
{isCreateDialog && (
<Box displayProp="flex" alignItems="center" paddingProp="16px 0 0">
<Checkbox
label={t("Common:DontAskAgain")}
isChecked={isChecked}
onChange={onChangeCheckbox}
/>
</Box>
)}
{options && (
<ComboBox
style={{ marginTop: "16px" }}
@ -118,7 +145,7 @@ const Dialog = ({
<ModalDialog.Footer>
<Button
key="GlobalSendBtn"
label={t("Common:SaveButton")}
label={isCreateDialog ? t("Common:Create") : t("Common:Save")}
size="normal"
scale
primary
@ -139,8 +166,9 @@ const Dialog = ({
);
};
export default inject(({ auth }) => {
export default inject(({ auth, filesStore }) => {
const { folderFormValidation } = auth.settingsStore;
const { createWithoutDialog, setCreateWithoutDialog } = filesStore;
return { folderFormValidation };
return { folderFormValidation, createWithoutDialog, setCreateWithoutDialog };
})(observer(Dialog));

View File

@ -121,7 +121,6 @@ const HeaderComponent = ({
theme,
toggleArticleOpen,
logoUrl,
userTheme,
...props
}) => {
const { t } = useTranslation("Common");
@ -200,7 +199,7 @@ const HeaderComponent = ({
}, [history]);
const logo = getLogoFromPath(
userTheme === "Dark" ? logoUrl?.path?.dark : logoUrl?.path?.light
!theme.isBase ? logoUrl?.path?.dark : logoUrl?.path?.light
);
return (
@ -362,6 +361,5 @@ export default inject(({ auth }) => {
currentProductId,
toggleArticleOpen,
//currentProductName: (product && product.title) || "",
userTheme: user.theme,
};
})(observer(HeaderComponent));

View File

@ -22,7 +22,7 @@ const ConvertPasswordDialogComponent = (props) => {
setFormCreationInfo,
setPasswordEntryProcess,
isDesktop,
editCompleteAction,
completeAction,
fileCopyAs,
} = props;
const inputRef = React.useRef(null);
@ -129,7 +129,7 @@ const ConvertPasswordDialogComponent = (props) => {
open && openDocEditor(file.id, file.providerKey, tab);
})
.then(() => {
editCompleteAction(fileInfo);
completeAction(fileInfo);
})
.catch((err) => {
let errorMessage = "";
@ -221,27 +221,25 @@ const ConvertPasswordDialogComponent = (props) => {
</ModalDialog.Body>
<ModalDialog.Footer>
<StyledComponent isTabletView={isTabletView}>
<div className="convert-password_footer">
<Button
id="convert-password-dialog_button-accept"
className="convert-password-dialog_button"
key="ContinueButton"
label={t("Common:SaveButton")}
size="small"
primary
onClick={onConvert}
isLoading={isLoading}
/>
<Button
className="convert-password-dialog_button"
key="CloseButton"
label={t("Common:CloseButton")}
size="small"
onClick={onClose}
/>
</div>
</StyledComponent>
<Button
id="convert-password-dialog_button-accept"
className="convert-password-dialog_button"
key="ContinueButton"
label={t("Common:SaveButton")}
size="normal"
scale
primary
onClick={onConvert}
isLoading={isLoading}
/>
<Button
className="convert-password-dialog_button"
key="CloseButton"
label={t("Common:CloseButton")}
scale
size="normal"
onClick={onClose}
/>
</ModalDialog.Footer>
</ModalDialog>
);
@ -263,7 +261,7 @@ export default inject(
} = dialogsStore;
const { copyAsAction, fileCopyAs } = uploadDataStore;
const { setPasswordEntryProcess } = filesStore;
const { editCompleteAction } = filesActionsStore;
const { completeAction } = filesActionsStore;
const { settingsStore } = auth;
const { isTabletView, isDesktopClient } = settingsStore;
@ -277,7 +275,7 @@ export default inject(
setFormCreationInfo,
setPasswordEntryProcess,
isDesktop: isDesktopClient,
editCompleteAction,
completeAction,
};
}
)(observer(ConvertPasswordDialog));

View File

@ -47,7 +47,7 @@ export const onConvertFiles = (e, resolve) => {
if (entry.isFile) {
entry.file(
(file) => {
addFile(file, entry.fullPath);
addItem(file, entry.fullPath);
callback();
},
() => {
@ -110,7 +110,7 @@ export const onConvertFiles = (e, resolve) => {
const entry = item.webkitGetAsEntry();
if (entry) {
if (entry.isFile) {
addFile(item.getAsFile(), entry.fullPath);
addItem(item.getAsFile(), entry.fullPath);
} else {
entries.push(entry);
}

View File

@ -1,7 +1,6 @@
import authStore from "@docspace/common/store/AuthStore";
import { toCommunityHostname } from "@docspace/common/utils";
import history from "@docspace/common/history";
import { useEffect, useState } from "react";
import { CategoryType } from "./constants";
import { FolderType } from "@docspace/common/constants";
@ -81,38 +80,6 @@ export const getPasswordErrorMessage = (t, settings) => {
} ${settings.specSymbols ? t("Common:PasswordLimitSpecialSymbols") : ""}`;
};
export const useThemeDetector = () => {
const isDesktopClient = window["AscDesktopEditor"] !== undefined;
const [systemTheme, setSystemTheme] = useState(
isDesktopClient
? window.RendererProcessVariable?.theme?.type === "dark"
? "Dark"
: "Base"
: window.matchMedia("(prefers-color-scheme: dark)").matches
? "Dark"
: "Base"
);
const systemThemeListener = (e) => {
setSystemTheme(e.matches ? "Dark" : "Base");
};
useEffect(() => {
if (isDesktopClient) return;
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
mediaQuery.addListener(systemThemeListener);
return () => {
if (isDesktopClient) return;
mediaQuery.removeListener(systemThemeListener);
};
}, []);
return systemTheme;
};
export const getCategoryType = (location) => {
let categoryType = CategoryType.Shared;
const { pathname } = location;

View File

@ -77,7 +77,6 @@ const AboutContent = (props) => {
companyInfoSettingsData,
previewData,
whiteLabelLogoUrls,
userTheme,
} = props;
const { t } = useTranslation("About");
const license = "AGPL-3.0";
@ -103,7 +102,7 @@ const AboutContent = (props) => {
: companyInfoSettingsData?.address;
const logo = getLogoFromPath(
userTheme === "Dark"
!theme.isBase
? whiteLabelLogoUrls[6]?.path.dark
: whiteLabelLogoUrls[6]?.path.light
);
@ -242,15 +241,13 @@ const AboutContent = (props) => {
};
export default inject(({ auth }) => {
const { settingsStore, userStore } = auth;
const { settingsStore } = auth;
const { theme, companyInfoSettingsData, whiteLabelLogoUrls } = settingsStore;
const { userTheme } = userStore;
return {
theme,
companyInfoSettingsData,
whiteLabelLogoUrls,
userTheme,
};
})(observer(AboutContent));

View File

@ -28,6 +28,8 @@ const CommonSettings = ({
t,
showTitle,
createWithoutDialog,
setCreateWithoutDialog,
}) => {
const [isLoadingFavorites, setIsLoadingFavorites] = React.useState(false);
const [isLoadingRecent, setIsLoadingRecent] = React.useState(false);
@ -68,6 +70,10 @@ const CommonSettings = ({
[setIsLoadingRecent, setRecentSetting]
);
const onChangeCheckbox = () => {
setCreateWithoutDialog(!createWithoutDialog);
};
return (
<StyledSettings showTitle={showTitle}>
<Box className="settings-section">
@ -76,6 +82,12 @@ const CommonSettings = ({
{t("Common:Common")}
</Heading>
)}
<ToggleButton
className="toggle-btn"
label={t("Common:DontAskAgain")}
onChange={onChangeCheckbox}
isChecked={createWithoutDialog}
/>
<ToggleButton
className="toggle-btn"
label={t("OriginalCopy")}
@ -143,50 +155,55 @@ const CommonSettings = ({
);
};
export default inject(({ auth, settingsStore, treeFoldersStore }) => {
const {
storeOriginalFiles,
confirmDelete,
updateIfExist,
forcesave,
export default inject(
({ auth, settingsStore, treeFoldersStore, filesStore }) => {
const {
storeOriginalFiles,
confirmDelete,
updateIfExist,
forcesave,
setUpdateIfExist,
setStoreOriginal,
setUpdateIfExist,
setStoreOriginal,
setConfirmDelete,
setConfirmDelete,
setForceSave,
setForceSave,
favoritesSection,
recentSection,
setFavoritesSetting,
setRecentSetting,
} = settingsStore;
favoritesSection,
recentSection,
setFavoritesSetting,
setRecentSetting,
} = settingsStore;
const { myFolderId, commonFolderId } = treeFoldersStore;
const { myFolderId, commonFolderId } = treeFoldersStore;
const { setCreateWithoutDialog, createWithoutDialog } = filesStore;
return {
storeOriginalFiles,
confirmDelete,
updateIfExist,
forceSave: forcesave,
return {
storeOriginalFiles,
confirmDelete,
updateIfExist,
forceSave: forcesave,
myFolderId,
commonFolderId,
isVisitor: auth.userStore.user.isVisitor,
favoritesSection,
recentSection,
myFolderId,
commonFolderId,
isVisitor: auth.userStore.user.isVisitor,
favoritesSection,
recentSection,
setUpdateIfExist,
setStoreOriginal,
setUpdateIfExist,
setStoreOriginal,
setConfirmDelete,
setConfirmDelete,
setForceSave,
setForceSave,
setFavoritesSetting,
setRecentSetting,
myFolderId,
commonFolderId,
};
})(observer(CommonSettings));
setFavoritesSetting,
setRecentSetting,
myFolderId,
commonFolderId,
setCreateWithoutDialog,
createWithoutDialog,
};
}
)(observer(CommonSettings));

View File

@ -160,8 +160,19 @@ class ContextOptionsStore {
this.settingsStore.extsWebRestrictedEditing[0];
this.uploadDataStore.copyAsAction(id, newTitle, folderId).catch((err) => {
if (err.indexOf("password") == -1) {
toastr.error(err, t("Common:Warning"));
let errorMessage = "";
if (typeof err === "object") {
errorMessage =
err?.response?.data?.error?.message ||
err?.statusText ||
err?.message ||
"";
} else {
errorMessage = err;
}
if (errorMessage.indexOf("password") == -1) {
toastr.error(errorMessage, t("Common:Warning"));
return;
}

View File

@ -563,13 +563,12 @@ class FilesActionStore {
return this.downloadFiles(fileIds, folderIds, label);
};
editCompleteAction = async (selectedItem, type, isFolder = false) => {
if (type === FileAction.Create) {
this.filesStore.addFile(selectedItem, isFolder);
}
if (type === FileAction.Create || type === FileAction.Rename) {
type === FileAction.Rename &&
completeAction = async (selectedItem, type, isFolder = false) => {
switch (type) {
case FileAction.Create:
this.filesStore.addItem(selectedItem, isFolder);
break;
case FileAction.Rename:
this.onSelectItem(
{
id: selectedItem.id,
@ -577,6 +576,9 @@ class FilesActionStore {
},
false
);
break;
default:
break;
}
};

View File

@ -28,6 +28,7 @@ import debounce from "lodash.debounce";
const { FilesFilter, RoomsFilter } = api;
const storageViewAs = localStorage.getItem("viewAs");
const storageCheckbox = JSON.parse(localStorage.getItem("createWithoutDialog"));
let requestCounter = 0;
@ -48,6 +49,7 @@ class FilesStore {
isLoaded = false;
isLoading = false;
createWithoutDialog = storageCheckbox ? true : false;
viewAs =
isMobile && storageViewAs !== "tile" ? "row" : storageViewAs || "table";
@ -397,6 +399,11 @@ class FilesStore {
localStorage.setItem("viewAs", viewAs);
};
setCreateWithoutDialog = (checked) => {
this.createWithoutDialog = checked;
localStorage.setItem("createWithoutDialog", JSON.stringify(checked));
};
setPageItemsLength = (pageItemsLength) => {
this.pageItemsLength = pageItemsLength;
};
@ -478,7 +485,19 @@ class FilesStore {
if (!this.isEditor) {
requests.push(
getPortalCultures(),
this.treeFoldersStore.fetchTreeFolders()
this.treeFoldersStore.fetchTreeFolders().then((treeFolders) => {
if (!treeFolders || !treeFolders.length) return;
const trashFolder = treeFolders.find(
(f) => f.rootFolderType == FolderType.TRASH
);
if (!trashFolder) return;
const isEmpty = !trashFolder.foldersCount && !trashFolder.filesCount;
this.setTrashIsEmpty(isEmpty);
})
);
if (isDesktopClient) {
@ -486,7 +505,6 @@ class FilesStore {
}
}
requests.push(getFilesSettings());
requests.push(this.getIsEmptyTrash());
return Promise.all(requests).then(() => this.setIsInit(true));
};
@ -1823,19 +1841,48 @@ class FilesStore {
scrollElm && scrollElm.scrollTo(0, 0);
};
addFile = (item, isFolder) => {
addItem = (item, isFolder) => {
const { socketHelper } = this.authStore.settingsStore;
if (isFolder) {
const foundIndex = this.folders.findIndex((x) => x.id === item?.id);
if (foundIndex > -1) return;
this.folders.unshift(item);
console.log("[WS] subscribe to folder changes", item.id, item.title);
socketHelper.emit({
command: "subscribe",
data: {
roomParts: `DIR-${item.id}`,
individual: true,
},
});
} else {
const foundIndex = this.files.findIndex((x) => x.id === item?.id);
if (foundIndex > -1) return;
console.log("[WS] subscribe to file changes", item.id, item.title);
socketHelper.emit({
command: "subscribe",
data: { roomParts: `FILE-${item.id}`, individual: true },
});
this.files.unshift(item);
}
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const filter = isRooms ? this.roomsFilter.clone() : this.filter.clone();
filter.total += 1;
if (isRooms) this.setRoomsFilter(filter);
else this.setFilter(filter);
isFolder ? this.folders.unshift(item) : this.files.unshift(item);
this.scrollToTop();
};
@ -2791,19 +2838,19 @@ class FilesStore {
setCreatedItem = (createdItem) => {
this.createdItem = createdItem;
const { socketHelper } = this.authStore.settingsStore;
if (createdItem?.type == "file") {
console.log(
"[WS] subscribe to file's changes",
createdItem.id,
createdItem.title
);
// const { socketHelper } = this.authStore.settingsStore;
// if (createdItem?.type == "file") {
// console.log(
// "[WS] subscribe to file's changes",
// createdItem.id,
// createdItem.title
// );
socketHelper.emit({
command: "subscribe",
data: { roomParts: `FILE-${createdItem.id}`, individual: true },
});
}
// socketHelper.emit({
// command: "subscribe",
// data: { roomParts: `FILE-${createdItem.id}`, individual: true },
// });
// }
};
setScrollToItem = (item) => {

View File

@ -1080,9 +1080,7 @@ class UploadDataStore {
const { fetchFiles, filter } = this.filesStore;
return fileCopyAs(fileId, title, folderId, enableExternalExt, password)
.then(() => {
fetchFiles(folderId, filter, true, true);
})
.then(() => fetchFiles(folderId, filter, true, true))
.catch((err) => {
return Promise.reject(err);
});

View File

@ -175,36 +175,27 @@ export function getFoldersTree() {
const folders = sortInDisplayOrder(response);
return folders.map((data, index) => {
const type = +data.current.rootFolderType;
const { new: newItems, pathParts, current, folders, files } = data;
const { foldersCount, filesCount } = current;
const { parentId, title, id, rootFolderType, security } = current;
const type = +rootFolderType;
const name = getFolderClassNameByType(type);
const isRecycleBinFolder = type === FolderType.TRASH;
return {
id: data.current.id,
id,
key: `0-${index}`,
parentId: data.current.parentId,
title: data.current.title,
parentId,
title,
rootFolderType: type,
folderClassName: name,
// folders: !isRecycleBinFolder
// ? data.folders.map((folder) => {
// return {
// id: folder.id,
// title: folder.title,
// access: folder.access,
// foldersCount: folder.foldersCount,
// rootFolderType: folder.rootFolderType,
// providerKey: folder.providerKey,
// newItems: folder.new,
// };
// })
// : null,
folders: null,
pathParts: data.pathParts,
foldersCount: !isRecycleBinFolder
? data.current.foldersCount || data.folders.length
: null,
newItems: data.new,
security: data.current.security,
pathParts,
foldersCount,
filesCount,
newItems,
security,
};
});
});

View File

@ -19,7 +19,7 @@ const ArticleHeader = ({
onClick,
isBurgerLoading,
whiteLabelLogoUrls,
userTheme,
theme,
...rest
}) => {
const history = useHistory();
@ -27,14 +27,12 @@ const ArticleHeader = ({
const isTabletView = (isTabletUtils() || isTablet) && !isMobileOnly;
const onLogoClick = () => history.push("/");
const burgerLogo =
userTheme === "Dark"
? getLogoFromPath(whiteLabelLogoUrls[5].path.dark)
: getLogoFromPath(whiteLabelLogoUrls[5].path.light);
const logo =
userTheme === "Dark"
? getLogoFromPath(whiteLabelLogoUrls[0].path.dark)
: getLogoFromPath(whiteLabelLogoUrls[0].path.light);
const burgerLogo = !theme.isBase
? getLogoFromPath(whiteLabelLogoUrls[5].path.dark)
: getLogoFromPath(whiteLabelLogoUrls[5].path.light);
const logo = !theme.isBase
? getLogoFromPath(whiteLabelLogoUrls[0].path.dark)
: getLogoFromPath(whiteLabelLogoUrls[0].path.light);
if (isMobileOnly) return <></>;
return (
@ -73,13 +71,12 @@ ArticleHeader.propTypes = {
ArticleHeader.displayName = "Header";
export default inject(({ auth }) => {
const { settingsStore, userStore } = auth;
const { isBurgerLoading, whiteLabelLogoUrls } = settingsStore;
const { userTheme } = userStore;
const { settingsStore } = auth;
const { isBurgerLoading, whiteLabelLogoUrls, theme } = settingsStore;
return {
isBurgerLoading,
whiteLabelLogoUrls,
userTheme,
theme,
};
})(observer(ArticleHeader));

View File

@ -116,20 +116,6 @@ class UserStore {
get isAuthenticated() {
return !!this.user;
}
get userTheme() {
const systemTheme =
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
? "Dark"
: "Light";
const theme = this.user?.theme || systemTheme;
if (theme === "System") {
return systemTheme;
}
return theme;
}
}
export default UserStore;

View File

@ -0,0 +1,33 @@
import { useEffect, useState } from "react";
export const useThemeDetector = () => {
const isDesktopClient = window["AscDesktopEditor"] !== undefined;
const [systemTheme, setSystemTheme] = useState(
isDesktopClient
? window?.RendererProcessVariable?.theme?.type === "dark"
? "Dark"
: "Base"
: window?.matchMedia("(prefers-color-scheme: dark)").matches
? "Dark"
: "Base"
);
const systemThemeListener = (e) => {
setSystemTheme(e.matches ? "Dark" : "Base");
};
useEffect(() => {
if (isDesktopClient) return;
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
mediaQuery.addListener(systemThemeListener);
return () => {
if (isDesktopClient) return;
mediaQuery.removeListener(systemThemeListener);
};
}, []);
return systemTheme;
};

View File

@ -13,6 +13,7 @@ import { getBgPattern, getLogoFromPath } from "@docspace/common/utils";
import { useMounted } from "../helpers/useMounted";
import useIsomorphicLayoutEffect from "../hooks/useIsomorphicLayoutEffect";
import LoginContainer from "@docspace/common/components/LoginContainer";
import { useThemeDetector } from "@docspace/common/utils/useThemeDetector";
interface IBarProp {
t: TFuncType;
@ -40,6 +41,7 @@ const Form: React.FC = ({ theme, setTheme, logoUrls }) => {
const email = "test@onlyoffice.com"; //TODO: get email from form
const validCode = "123456"; //TODO: get from api
const systemTheme = typeof window !== "undefined" && useThemeDetector();
useIsomorphicLayoutEffect(() => {
const theme =
@ -50,6 +52,11 @@ const Form: React.FC = ({ theme, setTheme, logoUrls }) => {
setTheme(theme);
}, []);
useIsomorphicLayoutEffect(() => {
if (systemTheme === "Base") setTheme(Base);
else setTheme(Dark);
}, [systemTheme]);
const onSubmit = (code: number | string) => {
if (code !== validCode) {
setInvalidCode(true);

View File

@ -25,6 +25,7 @@ import { useMounted } from "../helpers/useMounted";
import { getBgPattern } from "@docspace/common/utils";
import useIsomorphicLayoutEffect from "../hooks/useIsomorphicLayoutEffect";
import { getLogoFromPath } from "@docspace/common/utils";
import { useThemeDetector } from "@docspace/common/utils/useThemeDetector";
interface ILoginProps extends IInitialState {
isDesktopEditor?: boolean;
@ -50,6 +51,7 @@ const Login: React.FC<ILoginProps> = ({
const { t } = useTranslation(["Login", "Common"]);
const mounted = useMounted();
const systemTheme = typeof window !== "undefined" && useThemeDetector();
useIsomorphicLayoutEffect(() => {
const theme =
@ -60,6 +62,11 @@ const Login: React.FC<ILoginProps> = ({
setTheme(theme);
}, []);
useIsomorphicLayoutEffect(() => {
if (systemTheme === "Base") setTheme(Base);
else setTheme(Dark);
}, [systemTheme]);
const ssoExists = () => {
if (ssoUrl) return true;
else return false;

View File

@ -189,7 +189,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
return;
}
var needToMark = new List<FileEntry<TTo>>();
var needToMark = new List<FileEntry>();
var moveOrCopyFoldersTask = await MoveOrCopyFoldersAsync(scope, Folders, toFolder, _copy, parentFolders);
var moveOrCopyFilesTask = await MoveOrCopyFilesAsync(scope, Files, toFolder, _copy, parentFolders);
@ -200,13 +200,20 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
var ntm = needToMark.Distinct();
foreach (var n in ntm)
{
await fileMarker.MarkAsNewAsync(n);
if (n is FileEntry<T> entry1)
{
await fileMarker.MarkAsNewAsync(entry1);
}
else if (n is FileEntry<TTo> entry2)
{
await fileMarker.MarkAsNewAsync(entry2);
}
}
}
private async Task<List<FileEntry<TTo>>> MoveOrCopyFoldersAsync<TTo>(IServiceScope scope, List<T> folderIds, Folder<TTo> toFolder, bool copy, IEnumerable<Folder<TTo>> toFolderParents, bool checkPermissions = true)
private async Task<List<FileEntry>> MoveOrCopyFoldersAsync<TTo>(IServiceScope scope, List<T> folderIds, Folder<TTo> toFolder, bool copy, IEnumerable<Folder<TTo>> toFolderParents, bool checkPermissions = true)
{
var needToMark = new List<FileEntry<TTo>>();
var needToMark = new List<FileEntry>();
if (folderIds.Count == 0)
{
@ -230,6 +237,7 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
var folder = await FolderDao.GetFolderAsync(folderId);
var isRoom = DocSpaceHelper.IsRoom(folder.FolderType);
var isThirdPartyRoom = isRoom && folder.ProviderEntry;
if (folder == null)
{
@ -396,10 +404,9 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
TTo newFolderId = default;
if (isRoom && folder.ProviderEntry)
if (isThirdPartyRoom)
{
await ProviderDao.UpdateProviderInfoAsync(folder.ProviderId, toFolder.FolderType);
newFolderId = (TTo)Convert.ChangeType(folder, typeof(TTo));
}
else
{
@ -428,8 +435,6 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
}
newFolder = await folderDao.GetFolderAsync(newFolderId);
if (isRoom)
{
if (toFolder.FolderType == FolderType.Archive)
@ -447,14 +452,23 @@ class FileMoveCopyOperation<T> : FileOperation<FileMoveCopyOperationData<T>, T>
}
if (isToFolder)
if (isToFolder && toFolder.FolderType != FolderType.Archive)
{
needToMark.Add(newFolder);
if (isThirdPartyRoom)
{
needToMark.Add(folder);
}
else
{
newFolder = await folderDao.GetFolderAsync(newFolderId);
needToMark.Add(newFolder);
}
}
if (ProcessedFolder(folderId))
{
sb.Append($"folder_{newFolderId}{SplitChar}");
var id = isThirdPartyRoom ? folder.Id.ToString() : newFolderId.ToString();
sb.Append($"folder_{id}{SplitChar}");
}
}
}

View File

@ -52,11 +52,16 @@ public class FileConverterQueue<T>
{
var task = PeekTask(file);
if (Contains(task))
if (task != null)
{
return;
if (task.Progress != 100)
{
return;
}
Dequeue(task);
}
var queueResult = new FileConverterOperationResult
{
Source = JsonSerializer.Serialize(new { id = file.Id, version = file.Version }),
@ -137,15 +142,13 @@ public class FileConverterQueue<T>
public async Task<FileConverterOperationResult> GetStatusAsync(KeyValuePair<File<T>, bool> pair, FileSecurity fileSecurity)
{
var file = pair.Key;
var operation = PeekTask(pair.Key);
var operation = PeekTask(file);
if (operation != null && (pair.Value || await fileSecurity.CanReadAsync(file)))
{
if (operation.Progress == 100)
{
var task = PeekTask(file);
Dequeue(task);
{
Dequeue(operation);
}
return operation;
@ -183,21 +186,6 @@ public class FileConverterQueue<T>
}, options);
}
private bool Contains(FileConverterOperationResult val)
{
if (val == null)
{
return false;
}
var queueTasks = LoadFromCache();
return queueTasks.Any(x =>
{
return String.Compare(x.Source, val.Source) == 0;
});
}
private bool IsOrphanCacheItem(FileConverterOperationResult x)
{
return !string.IsNullOrEmpty(x.Processed)
@ -213,7 +201,7 @@ public class FileConverterQueue<T>
SaveToCache(listTasks);
return queueTasks;
return listTasks;
}
private void SaveToCache(IEnumerable<FileConverterOperationResult> queueTasks)

View File

@ -137,13 +137,13 @@ public class FilesModule : FeedModule
.Where(f => f.ShareRecord == null)
.ToListAsync();
var folderIDs = files.Select(r => r.File.ParentId).ToList();
var folderIDs = files.Select(r => r.File.ParentId).Distinct().ToList();
var folders = await _folderDao.GetFoldersAsync(folderIDs, checkShare: false).ToListAsync();
var roomsIds = await _folderDao.GetParentRoomsAsync(folderIDs).ToDictionaryAsync(k => k.FolderId, v => v.ParentRoomId);
return files.Select(f => new Tuple<Feed.Aggregator.Feed, object>(ToFeed(f, folders.FirstOrDefault(r => r.Id.Equals(f.File.ParentId)),
roomsIds.GetValueOrDefault(f.File.ParentId)), f));
}
}
public override async Task<IEnumerable<int>> GetTenantsWithFeeds(DateTime fromTime)
{

View File

@ -77,6 +77,7 @@
"DocSpaceAdmin": "DocSpace admin",
"Documents": "Documents",
"DomainIpAddress": "Domains as IP addresses are not supported",
"DontAskAgain": "Don't ask file name again on creation",
"Done": "Done",
"Download": "Download",
"Duplicate": "Create copy",

View File

@ -77,6 +77,7 @@
"DocSpaceAdmin": "DocSpace администратор",
"Documents": "Документы",
"DomainIpAddress": "IP адрес в качестве домена не поддерживается",
"DontAskAgain": "Больше не запрашивать имя файла при создани",
"Done": "Успешно",
"Download": "Скачать",
"Duplicate": "Создать копию",