Merge branch 'develop' into feature/security-history-audit
This commit is contained in:
commit
605aee5529
@ -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);
|
||||
|
@ -42,6 +42,7 @@
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"firebase": "^8.10.0",
|
||||
"react-avatar-editor": "^13.0.0",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"react-markdown": "^7.0.1",
|
||||
"react-smartbanner": "^5.1.4",
|
||||
|
3
packages/client/public/images/icon-cropper-grid.svg
Normal file
3
packages/client/public/images/icon-cropper-grid.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="216" height="216" viewBox="0 0 216 216" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M144.761 0.744576V71.7716H215.787V72.7315H144.761L144.761 143.758H215.787V144.718H144.761V215.745H143.802V144.718H72.772V215.745H71.8122V144.718H0.78653V143.758H71.8122L71.8122 72.7315H0.78653V71.7716H71.8122V0.744576H72.772V71.7716H143.802V0.744576H144.761ZM72.772 72.7315L72.772 143.758H143.802V72.7315H72.772ZM2.77727 0.744576H30.6476V2.73532H2.77727V30.6057H0.78653V2.73532V0.744576H2.77727ZM185.925 0.744576H213.796H215.787V2.73532L215.787 30.6057H213.796V2.73532H185.925V0.744576ZM185.925 213.754H213.796V185.883H215.787L215.787 213.754V215.745H213.796H185.925V213.754ZM2.77727 213.754H30.6476V215.745H2.77727H0.78653V213.754V185.883H2.77727V213.754Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 829 B |
41
packages/client/public/locales/en/CreateEditRoomDialog.json
Normal file
41
packages/client/public/locales/en/CreateEditRoomDialog.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"CreateRoom": "Create room",
|
||||
"ChooseRoomType": "Choose room type",
|
||||
"RoomEditing": "Room editing",
|
||||
|
||||
"FillingFormsRoomTitle": "Filling forms room",
|
||||
"CollaborationRoomTitle": "Collaboration room",
|
||||
"ReviewRoomTitle": "Review room",
|
||||
"ViewOnlyRoomTitle": "View-only room",
|
||||
"CustomRoomTitle": "Custom room",
|
||||
|
||||
"FillingFormsRoomDescription": "Build, share and fill document templates or work with the ready presets to quickly create documents of any type",
|
||||
"CollaborationRoomDescription": "Collaborate on one or multiple documents with your team",
|
||||
"ReviewRoomDescription": "Request a review or comments on the documents",
|
||||
"ViewOnlyRoomDescription": "Share any ready documents, reports, documentation, and other files for viewing",
|
||||
"CustomRoomDescription": "Apply your own settings to use this room for any custom purpose",
|
||||
|
||||
"NamePlaceholder": "Enter name",
|
||||
|
||||
"TagsPlaceholder": "Add a tag",
|
||||
"CreateTagOption": "Create tag",
|
||||
|
||||
"MakeRoomPrivateTitle": "Make the Room Private",
|
||||
"MakeRoomPrivateDescription": "All files in this room will be encrypted",
|
||||
"MakeRoomPrivateLimitationsWarningDescription": "With this feature, you can invite only existing users on the portal. After creating a room, you will not be able to change the list of users.",
|
||||
|
||||
"ThirdPartyStorageTitle": "Third party storage",
|
||||
"ThirdPartyStorageDescription": "Use third-party services as data storage for this room. A new folder for storing this room’s data will be created in the connected storage",
|
||||
"ThirdPartyStorageComboBoxPlaceholder": "Select storage",
|
||||
"ThirdPartyStorageNoStorageAlert": "No storage is connected in the portal Settings. Go to the Integrations section to enable data storage on a third-party",
|
||||
"ThirdPartyStorageNoStorageAlertLink": "Third-party services",
|
||||
"ThirdPartyStorageRememberChoice": "Remember this choice for new rooms",
|
||||
"ThirdPartyStoragePermanentSettingDescription": "Files are stored in a third-party {{thirdpartyTitle}} storage in the \"{{thirdpartyFolderName}}\" folder.\n<strong>{{thirdpartyPath}}</strong>",
|
||||
|
||||
"FolderNameTitle": "Folder Name",
|
||||
"FolderNameDescription": "A new folder for storing this room’s data will be created in the connected storage",
|
||||
|
||||
"DropzoneTitleLink": "Select new image",
|
||||
"DropzoneTitleSecondary": "or drop file here",
|
||||
"DropzoneTitleExsts": "(JPG or PNG, max 1 MB)"
|
||||
}
|
@ -55,7 +55,6 @@
|
||||
"NewRoom": "New room",
|
||||
"NewSpreadsheet": "New spreadsheet",
|
||||
"NoSubfolders": "No subfolders",
|
||||
"NoTag": "No tag",
|
||||
"Open": "Open",
|
||||
"OpenLocation": "Open location",
|
||||
"Presentation": "Presentation",
|
||||
|
41
packages/client/public/locales/ru/CreateEditRoomDialog.json
Normal file
41
packages/client/public/locales/ru/CreateEditRoomDialog.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"CreateRoom": "Создание комнаты",
|
||||
"ChooseRoomType": "Выберите тип комнаты",
|
||||
"RoomEditing": "Редактирование комнаты",
|
||||
|
||||
"FillingFormsRoomTitle": "Комната для заполнения форм",
|
||||
"CollaborationRoomTitle": "Комната для совместного редактирования",
|
||||
"ReviewRoomTitle": "Комната для рецензирования",
|
||||
"ViewOnlyRoomTitle": "Комната для просмотра",
|
||||
"CustomRoomTitle": "Пользовательская комната",
|
||||
|
||||
"FillingFormsRoomDescription": "Данная комната подойдет для сбора форм/анкет/тестов и тд.",
|
||||
"CollaborationRoomDescription": "Совместная работа над одним или несколькими документами с вашей командой",
|
||||
"ReviewRoomDescription": "Запроситe рецензию или комментарии к документам",
|
||||
"ViewOnlyRoomDescription": "Предоставляйте доступ к любым готовым документам, отчетам, документации и другим файлам для просмотра",
|
||||
"CustomRoomDescription": "Примените собственные настройки, чтобы использовать эту комнату для любых пользовательских целей",
|
||||
|
||||
"NamePlaceholder": "Введите имя",
|
||||
|
||||
"TagsPlaceholder": "Добавьте тэг",
|
||||
"CreateTagOption": "Создать тэг",
|
||||
|
||||
"MakeRoomPrivateTitle": "Сделайте комнату приватной",
|
||||
"MakeRoomPrivateDescription": "Все файлы в этой комнате будут зашифрованы",
|
||||
"MakeRoomPrivateLimitationsWarningDescription": "С данной функцией Вы можете пригласить только уже существующих пользователей на портале. После создания комнаты изменить список пользобателей будет нельзя.",
|
||||
|
||||
"ThirdPartyStorageTitle": "Место хранения",
|
||||
"ThirdPartyStorageDescription": "Используйте сторонние сервисы в качестве хранилища данных для этой комнаты. В подключенном хранилище будет создана новая папка для хранения данных этой комнаты",
|
||||
"ThirdPartyStorageComboBoxPlaceholder": "Выберите хранилище",
|
||||
"ThirdPartyStorageNoStorageAlert": "В Настройках портала не подключено ни одного хранилища. Перейдите в раздел Интеграций, чтобы включить возможность хранения данных на стороннем сервисе ",
|
||||
"ThirdPartyStorageNoStorageAlertLink": "Сторонние сервисы",
|
||||
"ThirdPartyStorageRememberChoice": "Запомнить этот выбор для новых комнат",
|
||||
"ThirdPartyStoragePermanentSettingDescription": "Файлы хранятся в стороннем хранилище {{thirdpartyTitle}} в папке \"{{thirdpartyFolderName}}\".\n<strong>{{thirdpartyPath}}</strong>",
|
||||
|
||||
"FolderNameTitle": "Имя папки",
|
||||
"FolderNameDescription": "В подключенном хранилище будет создана новая папка для хранения данных этой комнаты",
|
||||
|
||||
"DropzoneTitleLink": "Выберите новое изображение",
|
||||
"DropzoneTitleSecondary": "или перетащите сюда файл",
|
||||
"DropzoneTitleExsts": "(JPG или PNG, не более 1 МБ)"
|
||||
}
|
@ -7,11 +7,13 @@
|
||||
"ByCreationDate": "Создан",
|
||||
"ByLastModifiedDate": "Изменен",
|
||||
"ByTitle": "Название",
|
||||
"CollaborationRooms": "Совместное редактирование",
|
||||
"CommonEmptyContainerDescription": "В разделе «Общие документы» отображаются все документы, которыми администратор портала предоставил общий доступ. Только администраторы портала могут создавать папки в этом разделе, но с предоставленным доступом пользователи портала также могут загружать свои файлы здесь. Перетащите файлы со своего компьютера сюда, чтобы загрузить их на свой портал еще проще.",
|
||||
"ContainsSpecCharacter": "Название не должно содержать следующих символов: *+:\"<>?|/",
|
||||
"Convert": "Конвертация",
|
||||
"CopyItem": "<strong>{{title}}</strong> скопирован",
|
||||
"CopyItems": "Скопировано элементов: <strong>{{qty}}</strong>",
|
||||
"CustomRooms": "Пользовательская",
|
||||
"Document": "Документ",
|
||||
"EmptyFile": "Пустой файл",
|
||||
"EmptyFilterDescriptionText": "В этом разделе нет файлов или папок, соответствующих фильтру. Пожалуйста, выберите другие параметры или очистите фильтр, чтобы показать все файлы в этом разделе. Вы можете также поискать нужный файл в других разделах.",
|
||||
@ -23,6 +25,7 @@
|
||||
"FavoritesEmptyContainerDescription": "Чтобы добавить файлы в избранное или удалить их из этого списка, используйте контекстное меню.",
|
||||
"FileRemoved": "Файл перемещен в корзину",
|
||||
"FileRenamed": "Документ '{{oldTitle}}' переименован в '{{newTitle}}'",
|
||||
"FillingFormRooms": "Заполнение форм",
|
||||
"Filter": "Фильтр",
|
||||
"FinalizeVersion": "Сформировать версию",
|
||||
"Folder": "Папка",
|
||||
@ -64,6 +67,7 @@
|
||||
"RemoveFromList": "Убрать из списка",
|
||||
"RemovedFromFavorites": "Удалено из избранного",
|
||||
"Rename": "Переименовать",
|
||||
"ReviewRooms": "Рецензирование",
|
||||
"SendByEmail": "Отправить по почте",
|
||||
"Share": "Доступ",
|
||||
"SharedEmptyContainerDescription": "Раздел 'Доступно для меня' используется для отображения файлов, к которым ваши друзья или коллеги предоставили вам доступ. Если вы не видели последние изменения в документах, они помечаются как «новые». Вы можете удалить файлы из списка, нажав соответствующую кнопку.",
|
||||
@ -79,5 +83,6 @@
|
||||
"VersionBadge": "B.{{version}}",
|
||||
"VersionHistory": "История версий",
|
||||
"ViewList": "Список",
|
||||
"ViewOnlyRooms": "Просмотр",
|
||||
"ViewTiles": "Плитки"
|
||||
}
|
||||
|
@ -110,17 +110,9 @@ export default function withContent(WrappedContent) {
|
||||
|
||||
return inject(
|
||||
(
|
||||
{
|
||||
filesActionsStore,
|
||||
filesStore,
|
||||
treeFoldersStore,
|
||||
auth,
|
||||
dialogsStore,
|
||||
uploadDataStore,
|
||||
},
|
||||
{ filesStore, treeFoldersStore, auth, dialogsStore, uploadDataStore },
|
||||
{ item }
|
||||
) => {
|
||||
const { editCompleteAction } = filesActionsStore;
|
||||
const {
|
||||
createFile,
|
||||
createFolder,
|
||||
@ -133,9 +125,9 @@ export default function withContent(WrappedContent) {
|
||||
isUpdatingRowItem,
|
||||
passwordEntryProcess,
|
||||
addActiveItems,
|
||||
gallerySelected,
|
||||
setCreatedItem,
|
||||
} = filesStore;
|
||||
|
||||
const { clearActiveOperations, fileCopyAs } = uploadDataStore;
|
||||
const {
|
||||
isRecycleBinFolder,
|
||||
@ -164,7 +156,6 @@ export default function withContent(WrappedContent) {
|
||||
createFile,
|
||||
createFolder,
|
||||
culture,
|
||||
editCompleteAction,
|
||||
|
||||
folderFormValidation,
|
||||
homepage: config.homepage,
|
||||
@ -192,7 +183,6 @@ export default function withContent(WrappedContent) {
|
||||
|
||||
titleWithoutExt,
|
||||
|
||||
gallerySelected,
|
||||
setCreatedItem,
|
||||
personal,
|
||||
};
|
||||
|
@ -176,6 +176,10 @@ export default function withFileActions(WrappedFileItem) {
|
||||
this.props.selectTag(tag);
|
||||
};
|
||||
|
||||
onSelectType = (type) => {
|
||||
this.props.selectType(type);
|
||||
};
|
||||
|
||||
getContextModel = () => {
|
||||
const { getModel, item, t } = this.props;
|
||||
return getModel(item, t);
|
||||
@ -237,6 +241,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
onMouseClick={this.onMouseClick}
|
||||
onHideContextMenu={this.onHideContextMenu}
|
||||
onSelectTag={this.onSelectTag}
|
||||
onSelectType={this.onSelectType}
|
||||
getClassName={this.getClassName}
|
||||
className={className}
|
||||
isDragging={isDragging}
|
||||
@ -271,6 +276,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
const {
|
||||
selectRowAction,
|
||||
selectTag,
|
||||
selectType,
|
||||
onSelectItem,
|
||||
setNewBadgeCount,
|
||||
openFileAction,
|
||||
@ -331,7 +337,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,
|
||||
@ -339,6 +346,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
selectRowAction,
|
||||
onSelectItem,
|
||||
selectTag,
|
||||
selectType,
|
||||
setSharingPanelVisible,
|
||||
isPrivacy: isPrivacyFolder,
|
||||
isRoomsFolder,
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -29,6 +29,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
encrypted,
|
||||
startUpload,
|
||||
setAction,
|
||||
setCreateRoomDialogVisible,
|
||||
setSelectFileDialogVisible,
|
||||
isArticleLoading,
|
||||
isFavoritesFolder,
|
||||
@ -68,7 +69,6 @@ const ArticleMainButtonContent = (props) => {
|
||||
|
||||
const onCreateRoom = React.useCallback(() => {
|
||||
const event = new Event(Events.ROOM_CREATE);
|
||||
|
||||
window.dispatchEvent(event);
|
||||
}, []);
|
||||
|
||||
@ -172,7 +172,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
id: "main-button_new-room",
|
||||
className: "main-button_drop-down",
|
||||
icon: "images/folder.locked.react.svg",
|
||||
label: t("Home:NewRoom"),
|
||||
label: t("Files:NewRoom"),
|
||||
onClick: onCreateRoom,
|
||||
action: "room",
|
||||
key: "room",
|
||||
@ -345,7 +345,10 @@ export default inject(
|
||||
isArchiveFolder,
|
||||
} = treeFoldersStore;
|
||||
const { startUpload } = uploadDataStore;
|
||||
const { setSelectFileDialogVisible } = dialogsStore;
|
||||
const {
|
||||
setCreateRoomDialogVisible,
|
||||
setSelectFileDialogVisible,
|
||||
} = dialogsStore;
|
||||
|
||||
const isArticleLoading = (!isLoaded || isLoading) && firstLoad;
|
||||
|
||||
@ -371,6 +374,7 @@ export default inject(
|
||||
|
||||
startUpload,
|
||||
|
||||
setCreateRoomDialogVisible,
|
||||
setSelectFileDialogVisible,
|
||||
|
||||
isLoading,
|
||||
|
@ -15,7 +15,12 @@ const linkStyles = {
|
||||
display: "flex",
|
||||
};
|
||||
|
||||
const EmptyContainer = ({ isFiltered, parentId, theme }) => {
|
||||
const EmptyContainer = ({
|
||||
isFiltered,
|
||||
parentId,
|
||||
theme,
|
||||
setCreateRoomDialogVisible,
|
||||
}) => {
|
||||
linkStyles.color = theme.filesEmptyContainer.linkColor;
|
||||
|
||||
const onCreate = (e) => {
|
||||
@ -32,11 +37,10 @@ const EmptyContainer = ({ isFiltered, parentId, theme }) => {
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const onCreateRoom = React.useCallback(() => {
|
||||
const onCreateRoom = (e) => {
|
||||
const event = new Event(Events.ROOM_CREATE);
|
||||
|
||||
window.dispatchEvent(event);
|
||||
}, []);
|
||||
};
|
||||
|
||||
return isFiltered ? (
|
||||
<EmptyFilterContainer linkStyles={linkStyles} />
|
||||
@ -52,7 +56,13 @@ const EmptyContainer = ({ isFiltered, parentId, theme }) => {
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ auth, filesStore, treeFoldersStore, selectedFolderStore }) => {
|
||||
({
|
||||
auth,
|
||||
filesStore,
|
||||
dialogsStore,
|
||||
treeFoldersStore,
|
||||
selectedFolderStore,
|
||||
}) => {
|
||||
const {
|
||||
authorType,
|
||||
search,
|
||||
@ -61,6 +71,8 @@ export default inject(
|
||||
} = filesStore.filter;
|
||||
const { isPrivacyFolder } = treeFoldersStore;
|
||||
|
||||
const { setCreateRoomDialogVisible } = dialogsStore;
|
||||
|
||||
const isFiltered =
|
||||
(authorType || search || !withSubfolders || filterType) &&
|
||||
!(isPrivacyFolder && isMobile);
|
||||
@ -68,6 +80,7 @@ export default inject(
|
||||
return {
|
||||
theme: auth.settingsStore.theme,
|
||||
isFiltered,
|
||||
setCreateRoomDialogVisible,
|
||||
|
||||
parentId: selectedFolderStore.parentId,
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
ThirdPartyDialog,
|
||||
ConflictResolveDialog,
|
||||
ConvertDialog,
|
||||
CreateRoomDialog,
|
||||
} from "../dialogs";
|
||||
import ConvertPasswordDialog from "../dialogs/ConvertPasswordDialog";
|
||||
|
||||
@ -47,6 +48,7 @@ const Panels = (props) => {
|
||||
setSelectFileDialogVisible,
|
||||
hotkeyPanelVisible,
|
||||
convertPasswordDialogVisible,
|
||||
createRoomDialogVisible,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation(["Translations", "SelectFile"]);
|
||||
@ -86,6 +88,7 @@ const Panels = (props) => {
|
||||
<ConflictResolveDialog key="conflict-resolve-dialog" />
|
||||
),
|
||||
convertDialogVisible && <ConvertDialog key="convert-dialog" />,
|
||||
createRoomDialogVisible && <CreateRoomDialog key="create-room-dialog" />,
|
||||
selectFileDialogVisible && (
|
||||
<SelectFileDialog
|
||||
key="select-file-dialog"
|
||||
@ -126,6 +129,7 @@ export default inject(
|
||||
newFilesPanelVisible,
|
||||
conflictResolveDialogVisible,
|
||||
convertDialogVisible,
|
||||
createRoomDialogVisible,
|
||||
convertPasswordDialogVisible,
|
||||
connectItem, //TODO:
|
||||
|
||||
@ -155,6 +159,7 @@ export default inject(
|
||||
newFilesPanelVisible,
|
||||
conflictResolveDialogVisible,
|
||||
convertDialogVisible,
|
||||
createRoomDialogVisible,
|
||||
convertPasswordDialogVisible,
|
||||
selectFileDialogVisible,
|
||||
createMasterForm,
|
||||
|
@ -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];
|
||||
@ -252,6 +252,7 @@ export default inject(
|
||||
treeFoldersStore,
|
||||
uploadDataStore,
|
||||
dialogsStore,
|
||||
oformsStore,
|
||||
}) => {
|
||||
const {
|
||||
setIsLoading,
|
||||
@ -260,10 +261,11 @@ export default inject(
|
||||
addActiveItems,
|
||||
openDocEditor,
|
||||
setIsUpdatingRowItem,
|
||||
gallerySelected,
|
||||
setCreatedItem,
|
||||
} = filesStore;
|
||||
|
||||
const { gallerySelected } = oformsStore;
|
||||
|
||||
const { editCompleteAction } = filesActionsStore;
|
||||
|
||||
const { clearActiveOperations, fileCopyAs } = uploadDataStore;
|
||||
|
@ -1,65 +1,208 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CreateRoomDialog } from "../dialogs";
|
||||
|
||||
import toastr from "client/toastr";
|
||||
const CreateRoomEvent = ({
|
||||
visible,
|
||||
onClose,
|
||||
|
||||
import { RoomsType } from "@docspace/common/constants";
|
||||
createRoom,
|
||||
createRoomInThirdpary,
|
||||
createTag,
|
||||
addTagsToRoom,
|
||||
calculateRoomLogoParams,
|
||||
uploadRoomLogo,
|
||||
addLogoToRoom,
|
||||
fetchTags,
|
||||
|
||||
import Dialog from "./sub-components/Dialog";
|
||||
connectItems,
|
||||
connectDialogVisible,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
openConnectWindow,
|
||||
setConnectItem,
|
||||
getOAuthToken,
|
||||
|
||||
const CreateRoomEvent = ({ createRoom, updateCurrentFolder, id, onClose }) => {
|
||||
const options = [
|
||||
{ key: RoomsType.CustomRoom, label: "Custom room" },
|
||||
{ key: RoomsType.FillingFormsRoom, label: "Filling form room" },
|
||||
{ key: RoomsType.EditingRoom, label: "Editing room" },
|
||||
{ key: RoomsType.ReviewRoom, label: "Review room" },
|
||||
{ key: RoomsType.ReadOnlyRoom, label: "View-only room" },
|
||||
];
|
||||
currrentFolderId,
|
||||
updateCurrentFolder,
|
||||
}) => {
|
||||
const { t } = useTranslation([
|
||||
"CreateEditRoomDialog",
|
||||
"Common",
|
||||
"Files",
|
||||
"ToastHeaders",
|
||||
]);
|
||||
const [fetchedTags, setFetchedTags] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [selectedOption, setSelectedOption] = React.useState(options[0]);
|
||||
const onCreate = async (roomParams) => {
|
||||
const createRoomData = {
|
||||
roomType: roomParams.type,
|
||||
title: roomParams.title || t("Files:NewRoom"),
|
||||
};
|
||||
|
||||
const { t } = useTranslation(["Translations", "Common"]);
|
||||
const isThirdparty =
|
||||
roomParams.isThirdparty &&
|
||||
roomParams.storageLocation.isConnected &&
|
||||
roomParams.storageLocation.thirdpartyFolderId;
|
||||
|
||||
const onSelect = (item) => {
|
||||
setSelectedOption(item);
|
||||
const addTagsData = roomParams.tags.map((tag) => tag.name);
|
||||
|
||||
const createTagsData = roomParams.tags
|
||||
.filter((t) => t.isNew)
|
||||
.map((t) => t.name);
|
||||
|
||||
const uploadLogoData = new FormData();
|
||||
uploadLogoData.append(0, roomParams.icon.uploadedFile);
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const room = isThirdparty
|
||||
? await createRoomInThirdpary(
|
||||
roomParams.storageLocation.thirdpartyFolderId,
|
||||
createRoomData
|
||||
)
|
||||
: await createRoom(createRoomData);
|
||||
|
||||
for (let i = 0; i < createTagsData.length; i++)
|
||||
await createTag(createTagsData[i]);
|
||||
|
||||
await addTagsToRoom(room.id, addTagsData);
|
||||
|
||||
if (roomParams.icon.uploadedFile)
|
||||
await uploadRoomLogo(uploadLogoData).then((response) => {
|
||||
const url = URL.createObjectURL(roomParams.icon.uploadedFile);
|
||||
const img = new Image();
|
||||
img.onload = async () => {
|
||||
const { x, y, zoom } = roomParams.icon;
|
||||
await addLogoToRoom(room.id, {
|
||||
tmpFile: response.data,
|
||||
...calculateRoomLogoParams(img, x, y, zoom),
|
||||
});
|
||||
URL.revokeObjectURL(img.src);
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
await updateCurrentFolder(null, currrentFolderId);
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = (e, value) => {
|
||||
createRoom(value, selectedOption.key)
|
||||
.then(() => {
|
||||
updateCurrentFolder(null, id);
|
||||
})
|
||||
.finally(() => {
|
||||
onClose();
|
||||
toastr.success(`${value} success created`);
|
||||
});
|
||||
};
|
||||
useEffect(async () => {
|
||||
let tags = await fetchTags();
|
||||
setFetchedTags(tags);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
<CreateRoomDialog
|
||||
t={t}
|
||||
title={"Create room"}
|
||||
startValue={"New room"}
|
||||
visible={true}
|
||||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
onSelect={onSelect}
|
||||
onSave={onSave}
|
||||
onCancel={onClose}
|
||||
visible={visible && !connectDialogVisible}
|
||||
onClose={onClose}
|
||||
onCreate={onCreate}
|
||||
fetchedTags={fetchedTags}
|
||||
isLoading={isLoading}
|
||||
connectItems={connectItems}
|
||||
connectDialogVisible={connectDialogVisible}
|
||||
setConnectDialogVisible={setConnectDialogVisible}
|
||||
setRoomCreation={setRoomCreation}
|
||||
saveThirdpartyResponse={saveThirdpartyResponse}
|
||||
openConnectWindow={openConnectWindow}
|
||||
setConnectItem={setConnectItem}
|
||||
getOAuthToken={getOAuthToken}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ filesStore, filesActionsStore, selectedFolderStore }) => {
|
||||
const { createRoom } = filesStore;
|
||||
({
|
||||
auth,
|
||||
filesStore,
|
||||
tagsStore,
|
||||
filesActionsStore,
|
||||
selectedFolderStore,
|
||||
settingsStore,
|
||||
dialogsStore,
|
||||
}) => {
|
||||
const {
|
||||
createRoom,
|
||||
createRoomInThirdpary,
|
||||
addTagsToRoom,
|
||||
calculateRoomLogoParams,
|
||||
uploadRoomLogo,
|
||||
addLogoToRoom,
|
||||
} = filesStore;
|
||||
const { createTag, fetchTags } = tagsStore;
|
||||
|
||||
const { id: currrentFolderId } = selectedFolderStore;
|
||||
const { updateCurrentFolder } = filesActionsStore;
|
||||
|
||||
const { id } = selectedFolderStore;
|
||||
const thirdPartyStore = settingsStore.thirdPartyStore;
|
||||
|
||||
return { createRoom, updateCurrentFolder, id };
|
||||
const { openConnectWindow } = settingsStore.thirdPartyStore;
|
||||
|
||||
const connectItems = [
|
||||
thirdPartyStore.googleConnectItem,
|
||||
thirdPartyStore.boxConnectItem,
|
||||
thirdPartyStore.dropboxConnectItem,
|
||||
thirdPartyStore.oneDriveConnectItem,
|
||||
thirdPartyStore.nextCloudConnectItem,
|
||||
thirdPartyStore.kDriveConnectItem,
|
||||
thirdPartyStore.yandexConnectItem,
|
||||
thirdPartyStore.ownCloudConnectItem,
|
||||
thirdPartyStore.webDavConnectItem,
|
||||
thirdPartyStore.sharePointConnectItem,
|
||||
]
|
||||
.map(
|
||||
(item) =>
|
||||
item && {
|
||||
isAvialable: !!item,
|
||||
id: item[0],
|
||||
providerName: item[0],
|
||||
isOauth: item.length > 1,
|
||||
oauthHref: item.length > 1 ? item[1] : "",
|
||||
}
|
||||
)
|
||||
.filter((item) => !!item);
|
||||
|
||||
const { getOAuthToken } = auth.settingsStore;
|
||||
|
||||
const {
|
||||
setConnectItem,
|
||||
connectDialogVisible,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
} = dialogsStore;
|
||||
|
||||
return {
|
||||
createRoom,
|
||||
createRoomInThirdpary,
|
||||
createTag,
|
||||
fetchTags,
|
||||
addTagsToRoom,
|
||||
calculateRoomLogoParams,
|
||||
uploadRoomLogo,
|
||||
addLogoToRoom,
|
||||
|
||||
setConnectItem,
|
||||
connectDialogVisible,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
saveThirdpartyResponse,
|
||||
openConnectWindow,
|
||||
connectItems,
|
||||
getOAuthToken,
|
||||
|
||||
currrentFolderId,
|
||||
updateCurrentFolder,
|
||||
};
|
||||
}
|
||||
)(observer(CreateRoomEvent));
|
||||
|
190
packages/client/src/components/GlobalEvents/EditRoomEvent.js
Normal file
190
packages/client/src/components/GlobalEvents/EditRoomEvent.js
Normal file
@ -0,0 +1,190 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { EditRoomDialog } from "../dialogs";
|
||||
import { Encoder } from "@docspace/common/utils/encoder";
|
||||
|
||||
const EditRoomEvent = ({
|
||||
visible,
|
||||
onClose,
|
||||
item,
|
||||
|
||||
editRoom,
|
||||
addTagsToRoom,
|
||||
removeTagsFromRoom,
|
||||
|
||||
createTag,
|
||||
fetchTags,
|
||||
|
||||
getThirdPartyIcon,
|
||||
|
||||
calculateRoomLogoParams,
|
||||
uploadRoomLogo,
|
||||
setFolder,
|
||||
removeLogoFromRoom,
|
||||
addLogoToRoom,
|
||||
|
||||
currentFolderId,
|
||||
updateCurrentFolder,
|
||||
}) => {
|
||||
const { t } = useTranslation(["CreateEditRoomDialog", "Common", "Files"]);
|
||||
|
||||
const [fetchedTags, setFetchedTags] = useState([]);
|
||||
const [fetchedImage, setFetchedImage] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const startTags = Object.values(item.tags);
|
||||
const startObjTags = startTags.map((tag, i) => ({ id: i, name: tag }));
|
||||
|
||||
const fetchedRoomParams = {
|
||||
title: item.title,
|
||||
type: item.roomType,
|
||||
tags: startObjTags,
|
||||
isThirdparty: !!item.providerKey,
|
||||
storageLocation: {
|
||||
title: item.title,
|
||||
parentId: item.parentId,
|
||||
providerKey: item.providerKey,
|
||||
iconSrc: getThirdPartyIcon(item.providerKey),
|
||||
},
|
||||
isPrivate: false,
|
||||
icon: {
|
||||
uploadedFile: item.logo.original,
|
||||
tmpFile: "",
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
zoom: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const onSave = async (roomParams) => {
|
||||
const editRoomParams = {
|
||||
title: roomParams.title || t("Files:NewRoom"),
|
||||
};
|
||||
|
||||
const tags = roomParams.tags.map((tag) => tag.name);
|
||||
const newTags = roomParams.tags.filter((t) => t.isNew).map((t) => t.name);
|
||||
const removedTags = startTags.filter((sT) => !tags.includes(sT));
|
||||
|
||||
const uploadLogoData = new FormData();
|
||||
uploadLogoData.append(0, roomParams.icon.uploadedFile);
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const room = await editRoom(item.id, editRoomParams);
|
||||
|
||||
for (let i = 0; i < newTags.length; i++) await createTag(newTags[i]);
|
||||
await addTagsToRoom(room.id, tags);
|
||||
await removeTagsFromRoom(room.id, removedTags);
|
||||
|
||||
if (!!item.logo.original && !roomParams.icon.uploadedFile)
|
||||
await removeLogoFromRoom(room.id);
|
||||
|
||||
if (roomParams.icon.uploadedFile) {
|
||||
await setFolder({
|
||||
...room,
|
||||
logo: { big: item.logo.small },
|
||||
});
|
||||
await uploadRoomLogo(uploadLogoData).then((response) => {
|
||||
const url = URL.createObjectURL(roomParams.icon.uploadedFile);
|
||||
const img = new Image();
|
||||
img.onload = async () => {
|
||||
const { x, y, zoom } = roomParams.icon;
|
||||
await addLogoToRoom(room.id, {
|
||||
tmpFile: response.data,
|
||||
...calculateRoomLogoParams(img, x, y, zoom),
|
||||
});
|
||||
URL.revokeObjectURL(img.src);
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
await updateCurrentFolder(null, currentFolderId);
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(async () => {
|
||||
const imgExst = item.logo.original.slice(".")[1];
|
||||
if (item.logo.original) {
|
||||
const file = await fetch(item.logo.original)
|
||||
.then((res) => res.arrayBuffer())
|
||||
.then(
|
||||
(buf) =>
|
||||
new File([buf], "fetchedFile", {
|
||||
type: `image/${imgExst}`,
|
||||
})
|
||||
);
|
||||
setFetchedImage(file);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(async () => {
|
||||
const tags = await fetchTags();
|
||||
setFetchedTags(tags);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EditRoomDialog
|
||||
t={t}
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
fetchedRoomParams={fetchedRoomParams}
|
||||
onSave={onSave}
|
||||
fetchedTags={fetchedTags}
|
||||
fetchedImage={fetchedImage}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({
|
||||
filesStore,
|
||||
tagsStore,
|
||||
filesActionsStore,
|
||||
selectedFolderStore,
|
||||
settingsStore,
|
||||
}) => {
|
||||
const {
|
||||
editRoom,
|
||||
addTagsToRoom,
|
||||
removeTagsFromRoom,
|
||||
calculateRoomLogoParams,
|
||||
uploadRoomLogo,
|
||||
setFolder,
|
||||
addLogoToRoom,
|
||||
removeLogoFromRoom,
|
||||
} = filesStore;
|
||||
|
||||
const { createTag, fetchTags } = tagsStore;
|
||||
const { id: currentFolderId } = selectedFolderStore;
|
||||
const { updateCurrentFolder } = filesActionsStore;
|
||||
const { getThirdPartyIcon } = settingsStore.thirdPartyStore;
|
||||
|
||||
return {
|
||||
editRoom,
|
||||
addTagsToRoom,
|
||||
removeTagsFromRoom,
|
||||
|
||||
createTag,
|
||||
fetchTags,
|
||||
|
||||
getThirdPartyIcon,
|
||||
|
||||
calculateRoomLogoParams,
|
||||
setFolder,
|
||||
uploadRoomLogo,
|
||||
removeLogoFromRoom,
|
||||
addLogoToRoom,
|
||||
|
||||
currentFolderId,
|
||||
updateCurrentFolder,
|
||||
};
|
||||
}
|
||||
)(observer(EditRoomEvent));
|
@ -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);
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect, useCallback, memo } from "react";
|
||||
|
||||
import { FileAction } from "@docspace/common/constants";
|
||||
|
||||
import { Events } from "@docspace/client/src/helpers/filesConstants";
|
||||
|
||||
import CreateEvent from "./CreateEvent";
|
||||
import RenameEvent from "./RenameEvent";
|
||||
import CreateRoomEvent from "./CreateRoomEvent";
|
||||
import EditRoomEvent from "./EditRoomEvent";
|
||||
|
||||
const GlobalEvents = () => {
|
||||
const [createDialogProps, setCreateDialogProps] = React.useState({
|
||||
const [createDialogProps, setCreateDialogProps] = useState({
|
||||
visible: false,
|
||||
id: null,
|
||||
type: null,
|
||||
@ -20,18 +20,24 @@ const GlobalEvents = () => {
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const [createRoomDialogProps, setCreateRoomDialogProps] = React.useState({
|
||||
visible: false,
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const [renameDialogProps, setRenameDialogProps] = React.useState({
|
||||
const [renameDialogProps, setRenameDialogProps] = useState({
|
||||
visible: false,
|
||||
item: null,
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const onCreate = React.useCallback((e) => {
|
||||
const [createRoomDialogProps, setCreateRoomDialogProps] = useState({
|
||||
visible: false,
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const [editRoomDialogProps, setEditRoomDialogProps] = useState({
|
||||
visible: false,
|
||||
item: null,
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const onCreate = useCallback((e) => {
|
||||
const { payload } = e;
|
||||
|
||||
const visible = payload.id ? true : false;
|
||||
@ -59,15 +65,7 @@ const GlobalEvents = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onCreateRoom = React.useCallback((e) => {
|
||||
setCreateRoomDialogProps({
|
||||
visible: true,
|
||||
onClose: () =>
|
||||
setCreateRoomDialogProps({ visible: false, onClose: null }),
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onRename = React.useCallback((e) => {
|
||||
const onRename = useCallback((e) => {
|
||||
const visible = e.item ? true : false;
|
||||
|
||||
setRenameDialogProps({
|
||||
@ -77,36 +75,65 @@ const GlobalEvents = () => {
|
||||
onClose: () => {
|
||||
setRenameDialogProps({
|
||||
visible: false,
|
||||
typ: null,
|
||||
type: null,
|
||||
item: null,
|
||||
});
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const onCreateRoom = useCallback((e) => {
|
||||
setCreateRoomDialogProps({
|
||||
visible: true,
|
||||
onClose: () =>
|
||||
setCreateRoomDialogProps({ visible: false, onClose: null }),
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onEditRoom = useCallback((e) => {
|
||||
const visible = e.item ? true : false;
|
||||
|
||||
setEditRoomDialogProps({
|
||||
visible: visible,
|
||||
item: e.item,
|
||||
onClose: () => {
|
||||
setEditRoomDialogProps({
|
||||
visible: false,
|
||||
item: null,
|
||||
onClose: null,
|
||||
});
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener(Events.CREATE, onCreate);
|
||||
window.addEventListener(Events.ROOM_CREATE, onCreateRoom);
|
||||
window.addEventListener(Events.RENAME, onRename);
|
||||
window.addEventListener(Events.ROOM_CREATE, onCreateRoom);
|
||||
window.addEventListener(Events.ROOM_EDIT, onEditRoom);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener(Events.CREATE, onCreate);
|
||||
window.removeEventListener(Events.ROOM_CREATE, onCreateRoom);
|
||||
window.removeEventListener(Events.RENAME, onRename);
|
||||
window.removeEventListener(Events.ROOM_CREATE, onCreateRoom);
|
||||
window.removeEventListener(Events.ROOM_EDIT, onEditRoom);
|
||||
};
|
||||
}, [onRename, onCreate]);
|
||||
}, [onRename, onCreate, onCreateRoom, onEditRoom]);
|
||||
|
||||
return [
|
||||
createDialogProps.visible && (
|
||||
<CreateEvent key={Events.CREATE} {...createDialogProps} />
|
||||
),
|
||||
renameDialogProps.visible && (
|
||||
<RenameEvent key={Events.RENAME} {...renameDialogProps} />
|
||||
),
|
||||
createRoomDialogProps.visible && (
|
||||
<CreateRoomEvent key={Events.ROOM_CREATE} {...createRoomDialogProps} />
|
||||
),
|
||||
renameDialogProps.visible && (
|
||||
<RenameEvent key={Events.RENAME} {...renameDialogProps} />
|
||||
editRoomDialogProps.visible && (
|
||||
<EditRoomEvent key={Events.ROOM_EDIT} {...editRoomDialogProps} />
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
export default React.memo(GlobalEvents);
|
||||
export default memo(GlobalEvents);
|
||||
|
@ -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
|
||||
|
@ -33,6 +33,8 @@ const PureConnectDialogContainer = (props) => {
|
||||
folderFormValidation,
|
||||
updateInfo,
|
||||
isConnectionViaBackupModule,
|
||||
roomCreation,
|
||||
setSaveThirdpartyResponse,
|
||||
} = props;
|
||||
const {
|
||||
corporate,
|
||||
@ -159,9 +161,12 @@ const PureConnectDialogContainer = (props) => {
|
||||
isCorporate,
|
||||
customerTitle,
|
||||
provider_key || key,
|
||||
provider_id
|
||||
provider_id,
|
||||
roomCreation
|
||||
)
|
||||
.then(async () => {
|
||||
.then(async (res) => {
|
||||
setSaveThirdpartyResponse(res);
|
||||
|
||||
const folderId = isCorporate ? commonFolderId : myFolderId;
|
||||
const subfolders = await getSubfolders(folderId);
|
||||
const node = treeFolders.find((x) => x.id === folderId);
|
||||
@ -240,7 +245,12 @@ const PureConnectDialogContainer = (props) => {
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
{isAccount ? (
|
||||
<FieldContainer labelVisible labelText={t("Account")} isVertical>
|
||||
<FieldContainer
|
||||
style={roomCreation ? { margin: "0" } : {}}
|
||||
labelVisible
|
||||
labelText={t("Account")}
|
||||
isVertical
|
||||
>
|
||||
<Button
|
||||
label={t("Reconnect")}
|
||||
size="normal"
|
||||
@ -295,6 +305,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
isVertical
|
||||
hasError={!isPasswordValid}
|
||||
errorMessage={t("Common:RequiredField")}
|
||||
style={roomCreation ? { margin: "0" } : {}}
|
||||
>
|
||||
<PasswordInput
|
||||
hasError={!isPasswordValid}
|
||||
@ -308,7 +319,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
</FieldContainer>
|
||||
</>
|
||||
)}
|
||||
{!isConnectionViaBackupModule && (
|
||||
{!(isConnectionViaBackupModule || roomCreation) && (
|
||||
<FieldContainer
|
||||
labelText={t("ConnectFolderTitle")}
|
||||
isRequired
|
||||
@ -327,7 +338,7 @@ const PureConnectDialogContainer = (props) => {
|
||||
</FieldContainer>
|
||||
)}
|
||||
|
||||
{!personal && !isConnectionViaBackupModule && (
|
||||
{!personal && !(isConnectionViaBackupModule || roomCreation) && (
|
||||
<Checkbox
|
||||
label={t("ConnectMakeShared")}
|
||||
isChecked={isCorporate}
|
||||
@ -354,7 +365,6 @@ const PureConnectDialogContainer = (props) => {
|
||||
scale={isAccount}
|
||||
onClick={onClose}
|
||||
isDisabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
@ -403,6 +413,8 @@ export default inject(
|
||||
connectDialogVisible: visible,
|
||||
setConnectDialogVisible,
|
||||
connectItem,
|
||||
roomCreation,
|
||||
setSaveThirdpartyResponse,
|
||||
} = dialogsStore;
|
||||
|
||||
const item = isConnectionViaBackupModule ? passedItem : connectItem;
|
||||
@ -416,6 +428,8 @@ export default inject(
|
||||
providers,
|
||||
visible,
|
||||
item,
|
||||
roomCreation,
|
||||
setSaveThirdpartyResponse,
|
||||
folderFormValidation,
|
||||
|
||||
getOAuthToken,
|
||||
|
@ -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) {
|
||||
|
@ -0,0 +1,158 @@
|
||||
import React, { useState } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Button from "@docspace/components/button";
|
||||
|
||||
import TagHandler from "./handlers/tagHandler";
|
||||
|
||||
import SetRoomParams from "./sub-components/SetRoomParams";
|
||||
import RoomTypeList from "./sub-components/RoomTypeList";
|
||||
import DialogHeader from "./sub-components/DialogHeader";
|
||||
|
||||
const StyledModalDialog = styled(ModalDialog)`
|
||||
.header-with-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isOauthWindowOpen &&
|
||||
css`
|
||||
#modal-dialog {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const CreateRoomDialog = ({
|
||||
t,
|
||||
visible,
|
||||
onClose,
|
||||
onCreate,
|
||||
|
||||
connectItems,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
openConnectWindow,
|
||||
setConnectItem,
|
||||
getOAuthToken,
|
||||
|
||||
fetchedTags,
|
||||
isLoading,
|
||||
folderFormValidation,
|
||||
}) => {
|
||||
const [isScrollLocked, setIsScrollLocked] = useState(false);
|
||||
const [isOauthWindowOpen, setIsOauthWindowOpen] = useState(false);
|
||||
|
||||
const startRoomParams = {
|
||||
title: "",
|
||||
type: undefined,
|
||||
tags: [],
|
||||
isPrivate: false,
|
||||
isThirdparty: false,
|
||||
storageLocation: {
|
||||
isConnected: false,
|
||||
provider: null,
|
||||
thirdpartyFolderId: "",
|
||||
storageFolderPath: "",
|
||||
},
|
||||
rememberThirdpartyStorage: false,
|
||||
icon: {
|
||||
uploadedFile: null,
|
||||
tmpFile: "",
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
zoom: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const [roomParams, setRoomParams] = useState({ ...startRoomParams });
|
||||
|
||||
const setRoomTags = (newTags) =>
|
||||
setRoomParams({ ...roomParams, tags: newTags });
|
||||
|
||||
const tagHandler = new TagHandler(roomParams.tags, setRoomTags, fetchedTags);
|
||||
|
||||
const setRoomType = (newRoomType) => {
|
||||
setRoomParams((prev) => ({
|
||||
...prev,
|
||||
type: newRoomType,
|
||||
}));
|
||||
};
|
||||
|
||||
const onCreateRoom = () => onCreate(roomParams);
|
||||
|
||||
const isChooseRoomType = roomParams.type === undefined;
|
||||
const goBack = () => {
|
||||
setRoomParams({ ...startRoomParams });
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledModalDialog
|
||||
displayType="aside"
|
||||
withBodyScroll
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
isScrollLocked={isScrollLocked}
|
||||
withFooterBorder
|
||||
isOauthWindowOpen={isOauthWindowOpen}
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<DialogHeader
|
||||
isChooseRoomType={isChooseRoomType}
|
||||
onArrowClick={goBack}
|
||||
/>
|
||||
</ModalDialog.Header>
|
||||
|
||||
<ModalDialog.Body>
|
||||
{isChooseRoomType ? (
|
||||
<RoomTypeList t={t} setRoomType={setRoomType} />
|
||||
) : (
|
||||
<SetRoomParams
|
||||
t={t}
|
||||
setIsOauthWindowOpen={setIsOauthWindowOpen}
|
||||
tagHandler={tagHandler}
|
||||
roomParams={roomParams}
|
||||
setRoomParams={setRoomParams}
|
||||
setRoomType={setRoomType}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
connectItems={connectItems}
|
||||
setConnectDialogVisible={setConnectDialogVisible}
|
||||
setRoomCreation={setRoomCreation}
|
||||
saveThirdpartyResponse={saveThirdpartyResponse}
|
||||
openConnectWindow={openConnectWindow}
|
||||
setConnectItem={setConnectItem}
|
||||
getOAuthToken={getOAuthToken}
|
||||
/>
|
||||
)}
|
||||
</ModalDialog.Body>
|
||||
|
||||
{!isChooseRoomType && (
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
tabIndex={5}
|
||||
label={t("Common:Create")}
|
||||
size="normal"
|
||||
primary
|
||||
scale
|
||||
onClick={onCreateRoom}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Button
|
||||
tabIndex={5}
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
onClick={onClose}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
)}
|
||||
</StyledModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateRoomDialog;
|
@ -0,0 +1,95 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import TagHandler from "./handlers/tagHandler";
|
||||
import SetRoomParams from "./sub-components/SetRoomParams";
|
||||
import DialogHeader from "./sub-components/DialogHeader";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Button from "@docspace/components/button";
|
||||
|
||||
const EditRoomDialog = ({
|
||||
t,
|
||||
visible,
|
||||
onClose,
|
||||
onSave,
|
||||
isLoading,
|
||||
fetchedRoomParams,
|
||||
fetchedTags,
|
||||
fetchedImage,
|
||||
folderFormValidation,
|
||||
}) => {
|
||||
const [isScrollLocked, setIsScrollLocked] = useState(false);
|
||||
|
||||
const [roomParams, setRoomParams] = useState({
|
||||
...fetchedRoomParams,
|
||||
});
|
||||
|
||||
const setRoomTags = (newTags) =>
|
||||
setRoomParams({ ...roomParams, tags: newTags });
|
||||
|
||||
const tagHandler = new TagHandler(roomParams.tags, setRoomTags, fetchedTags);
|
||||
|
||||
const setRoomType = (newRoomType) =>
|
||||
setRoomParams((prev) => ({
|
||||
...prev,
|
||||
type: newRoomType,
|
||||
}));
|
||||
|
||||
const onEditRoom = () => onSave(roomParams);
|
||||
|
||||
useEffect(async () => {
|
||||
if (fetchedImage)
|
||||
setRoomParams({
|
||||
...roomParams,
|
||||
icon: { ...roomParams.icon, uploadedFile: fetchedImage },
|
||||
});
|
||||
}, [fetchedImage]);
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
displayType="aside"
|
||||
withBodyScroll
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
isScrollLocked={isScrollLocked}
|
||||
withFooterBorder
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<DialogHeader isEdit />
|
||||
</ModalDialog.Header>
|
||||
|
||||
<ModalDialog.Body>
|
||||
<SetRoomParams
|
||||
t={t}
|
||||
tagHandler={tagHandler}
|
||||
roomParams={roomParams}
|
||||
setRoomParams={setRoomParams}
|
||||
setRoomType={setRoomType}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
isEdit
|
||||
/>
|
||||
</ModalDialog.Body>
|
||||
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
tabIndex={5}
|
||||
label={t("Common:SaveButton")}
|
||||
size="normal"
|
||||
primary
|
||||
scale
|
||||
onClick={onEditRoom}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Button
|
||||
tabIndex={5}
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
onClick={onClose}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditRoomDialog;
|
@ -0,0 +1,43 @@
|
||||
import { RoomsType } from "@docspace/common/constants";
|
||||
|
||||
export const roomTypes = [
|
||||
{
|
||||
type: RoomsType.FillingFormsRoom,
|
||||
title: "FillingFormsRoomTitle",
|
||||
description: "FillingFormsRoomDescription",
|
||||
defaultTag: "Files:FillingFormRooms",
|
||||
withSecondaryInfo: true,
|
||||
secondaryInfo: "FillingFormsRoomSecondaryInfo",
|
||||
},
|
||||
{
|
||||
type: RoomsType.EditingRoom,
|
||||
title: "CollaborationRoomTitle",
|
||||
description: "CollaborationRoomDescription",
|
||||
defaultTag: "Files:CollaborationRooms",
|
||||
withSecondaryInfo: true,
|
||||
secondaryInfo: "CollaborationRoomSecondaryInfo",
|
||||
},
|
||||
{
|
||||
type: RoomsType.ReviewRoom,
|
||||
title: "ReviewRoomTitle",
|
||||
description: "ReviewRoomDescription",
|
||||
defaultTag: "Files:ReviewRooms",
|
||||
withSecondaryInfo: true,
|
||||
secondaryInfo: "ReviewRoomSecondaryInfo",
|
||||
},
|
||||
{
|
||||
type: RoomsType.ReadOnlyRoom,
|
||||
title: "ViewOnlyRoomTitle",
|
||||
description: "ViewOnlyRoomDescription",
|
||||
defaultTag: "Files:ViewOnlyRooms",
|
||||
withSecondaryInfo: true,
|
||||
secondaryInfo: "ViewOnlyRoomSecondaryInfo",
|
||||
},
|
||||
{
|
||||
type: RoomsType.CustomRoom,
|
||||
title: "CustomRoomTitle",
|
||||
description: "CustomRoomDescription",
|
||||
defaultTag: "Files:CustomRooms",
|
||||
withSecondaryInfo: false,
|
||||
},
|
||||
];
|
@ -0,0 +1,49 @@
|
||||
class TagHandler {
|
||||
constructor(tags, setTags, fetchedTags) {
|
||||
this.tags = tags;
|
||||
this.setTags = setTags;
|
||||
this.fetchedTags = fetchedTags;
|
||||
}
|
||||
|
||||
createRandomTagId() {
|
||||
return "_" + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
|
||||
refreshDefaultTag(name) {
|
||||
let newTags = [...this.tags].filter((tag) => !tag.isDefault);
|
||||
newTags.unshift({
|
||||
id: this.createRandomTagId(),
|
||||
name,
|
||||
isDefault: true,
|
||||
});
|
||||
|
||||
this.setTags(newTags);
|
||||
}
|
||||
|
||||
addTag(name) {
|
||||
let newTags = [...this.tags];
|
||||
newTags.push({
|
||||
id: this.createRandomTagId(),
|
||||
name,
|
||||
});
|
||||
this.setTags(newTags);
|
||||
}
|
||||
|
||||
addNewTag(name) {
|
||||
let newTags = [...this.tags];
|
||||
newTags.push({
|
||||
id: this.createRandomTagId(),
|
||||
isNew: true,
|
||||
name,
|
||||
});
|
||||
this.setTags(newTags);
|
||||
}
|
||||
|
||||
deleteTag(id) {
|
||||
let newTags = [...this.tags];
|
||||
newTags = newTags.filter((tag) => tag.id !== id);
|
||||
this.setTags(newTags);
|
||||
}
|
||||
}
|
||||
|
||||
export default TagHandler;
|
@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
||||
import withLoader from "@docspace/client/src/HOCs/withLoader";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import { IconButton } from "@docspace/components";
|
||||
|
||||
const DialogHeader = ({ t, isEdit, isChooseRoomType, onArrowClick }) => {
|
||||
return (
|
||||
<>
|
||||
{isEdit ? (
|
||||
t("RoomEditing")
|
||||
) : isChooseRoomType ? (
|
||||
t("ChooseRoomType")
|
||||
) : (
|
||||
<div className="header-with-button">
|
||||
<IconButton
|
||||
size="15px"
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
className="sharing_panel-arrow"
|
||||
onClick={onArrowClick}
|
||||
/>
|
||||
<div>{t("CreateRoom")}</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation(["CreateEditRoomDialog"])(
|
||||
withLoader(DialogHeader)(<Loaders.CreateEditRoomDilogHeaderLoader />)
|
||||
);
|
@ -0,0 +1,115 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import resizeImage from "resize-image";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledDropzone = styled.div`
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
border: 2px dashed
|
||||
${(props) => props.theme.createEditRoomDialog.dropzone.borderColor};
|
||||
border-radius: 6px;
|
||||
|
||||
.dropzone {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
|
||||
user-select: none;
|
||||
|
||||
&-link {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
&-main {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.dropzone.linkMainColor};
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dashed;
|
||||
text-underline-offset: 1px;
|
||||
}
|
||||
&-secondary {
|
||||
font-weight: 400;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.dropzone.linkSecondaryColor};
|
||||
}
|
||||
}
|
||||
|
||||
&-exsts {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: ${(props) => props.theme.createEditRoomDialog.dropzone.exstsColor};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledDropzone.defaultProps = { theme: Base };
|
||||
|
||||
const Dropzone = ({ t, setUploadedFile }) => {
|
||||
const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
|
||||
maxFiles: 1,
|
||||
// maxSize: 1000000,
|
||||
accept: ["image/png", "image/jpeg"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (acceptedFiles.length) {
|
||||
const fr = new FileReader();
|
||||
fr.readAsDataURL(acceptedFiles[0]);
|
||||
|
||||
fr.onload = () => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const canvas = resizeImage.resize2Canvas(img, img.width, img.height);
|
||||
|
||||
const data = resizeImage.resize(
|
||||
canvas,
|
||||
img.width / 4,
|
||||
img.height / 4,
|
||||
resizeImage.JPEG
|
||||
);
|
||||
|
||||
fetch(data)
|
||||
.then((res) => res.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], "File name", {
|
||||
type: "image/jpg",
|
||||
});
|
||||
setUploadedFile(file);
|
||||
});
|
||||
};
|
||||
img.src = fr.result;
|
||||
};
|
||||
}
|
||||
}, [acceptedFiles]);
|
||||
|
||||
return (
|
||||
<StyledDropzone>
|
||||
<div {...getRootProps({ className: "dropzone" })}>
|
||||
<input {...getInputProps()} />
|
||||
<div className="dropzone-link">
|
||||
<span className="dropzone-link-main">{t("DropzoneTitleLink")}</span>
|
||||
<span className="dropzone-link-secondary">
|
||||
{t("DropzoneTitleSecondary")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="dropzone-exsts">{t("DropzoneTitleExsts")}</div>
|
||||
</div>
|
||||
</StyledDropzone>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dropzone;
|
@ -0,0 +1,206 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import throttle from "lodash/throttle";
|
||||
import AvatarEditor from "react-avatar-editor";
|
||||
|
||||
import Slider from "@docspace/components/slider";
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledIconCropper = styled.div`
|
||||
max-width: 216px;
|
||||
|
||||
.icon_cropper-crop_area {
|
||||
width: 216px;
|
||||
height: 216px;
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
.icon_cropper-grid {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 216px;
|
||||
height: 216px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
svg {
|
||||
opacity: 0.2;
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.gridColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon_cropper-delete_button {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 6px 0;
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.deleteButton.background};
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.deleteButton.borderColor};
|
||||
border-radius: 3px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
transition: all 0.2s ease;
|
||||
&:hover {
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.deleteButton
|
||||
.hoverBackground};
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.deleteButton
|
||||
.hoverBorderColor};
|
||||
}
|
||||
|
||||
&-text {
|
||||
user-select: none;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.deleteButton.color};
|
||||
}
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.iconCropper.deleteButton.iconColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon_cropper-zoom-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&-slider {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&-button {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledIconCropper.defaultProps = { theme: Base };
|
||||
|
||||
const IconCropper = ({
|
||||
t,
|
||||
icon,
|
||||
onChangeIcon,
|
||||
uploadedFile,
|
||||
setUploadedFile,
|
||||
setPreviewIcon,
|
||||
}) => {
|
||||
let editorRef = null;
|
||||
const setEditorRef = (editor) => (editorRef = editor);
|
||||
|
||||
const handlePositionChange = (position) =>
|
||||
onChangeIcon({ ...icon, x: position.x, y: position.y });
|
||||
|
||||
const handleSliderChange = (e, newZoom = null) =>
|
||||
onChangeIcon({ ...icon, zoom: newZoom ? newZoom : +e.target.value });
|
||||
|
||||
const handleZoomInClick = () =>
|
||||
handleSliderChange({}, icon.zoom <= 4.5 ? icon.zoom + 0.5 : 5);
|
||||
|
||||
const handleZoomOutClick = () =>
|
||||
handleSliderChange({}, icon.zoom >= 1.5 ? icon.zoom - 0.5 : 1);
|
||||
|
||||
const handleDeleteImage = () => setUploadedFile(null);
|
||||
|
||||
const handleImageChange = throttle(() => {
|
||||
if (editorRef) {
|
||||
const newPreveiwImage = editorRef.getImageScaledToCanvas()?.toDataURL();
|
||||
setPreviewIcon(newPreveiwImage);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
useEffect(() => {
|
||||
handleImageChange();
|
||||
return () => {
|
||||
setPreviewIcon("");
|
||||
};
|
||||
}, [icon]);
|
||||
|
||||
return (
|
||||
<StyledIconCropper className="icon_cropper">
|
||||
<div className="icon_cropper-crop_area">
|
||||
<ReactSVG
|
||||
className="icon_cropper-grid"
|
||||
src="images/icon-cropper-grid.svg"
|
||||
/>
|
||||
<AvatarEditor
|
||||
ref={setEditorRef}
|
||||
image={uploadedFile}
|
||||
width={216}
|
||||
height={216}
|
||||
position={{ x: icon.x, y: icon.y }}
|
||||
scale={icon.zoom}
|
||||
color={[6, 22, 38, 0.2]}
|
||||
border={0}
|
||||
rotate={0}
|
||||
borderRadius={108}
|
||||
onPositionChange={handlePositionChange}
|
||||
onImageReady={handleImageChange}
|
||||
disableHiDPIScaling
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="icon_cropper-delete_button"
|
||||
onClick={handleDeleteImage}
|
||||
title={t("Common:Delete")}
|
||||
>
|
||||
<ReactSVG src={"images/trash.react.svg"} />
|
||||
<div className="icon_cropper-delete_button-text">
|
||||
{t("Common:Delete")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="icon_cropper-zoom-container">
|
||||
<IconButton
|
||||
className="icon_cropper-zoom-container-button"
|
||||
size="16"
|
||||
onClick={handleZoomOutClick}
|
||||
iconName={"/static/images/zoom-minus.react.svg"}
|
||||
isFill={true}
|
||||
isClickable={false}
|
||||
/>
|
||||
<Slider
|
||||
className="icon_cropper-zoom-container-slider"
|
||||
max={5}
|
||||
min={1}
|
||||
onChange={handleSliderChange}
|
||||
step={0.01}
|
||||
value={icon.zoom}
|
||||
/>
|
||||
<IconButton
|
||||
className="icon_cropper-zoom-container-button"
|
||||
size="16"
|
||||
onClick={handleZoomInClick}
|
||||
iconName={"/static/images/zoom-plus.react.svg"}
|
||||
isFill={true}
|
||||
isClickable={false}
|
||||
/>
|
||||
</div>
|
||||
</StyledIconCropper>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconCropper;
|
@ -0,0 +1,99 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { smallTablet } from "@docspace/components/utils/device";
|
||||
|
||||
import Tags from "@docspace/common/components/Tags";
|
||||
import Tag from "@docspace/components/tag";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledPreviewTile = styled.div`
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.previewTile.background};
|
||||
width: 214px;
|
||||
border: 1px solid
|
||||
${(props) => props.theme.createEditRoomDialog.previewTile.borderColor};
|
||||
height: 120px;
|
||||
border-radius: 12px;
|
||||
|
||||
@media ${smallTablet} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tile-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid
|
||||
${(props) => props.theme.createEditRoomDialog.previewTile.borderColor};
|
||||
|
||||
&-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.theme.createEditRoomDialog.previewTile.iconBorderColor};
|
||||
border-radius: 6px;
|
||||
img {
|
||||
user-select: none;
|
||||
height: 32px;
|
||||
width: ${(props) => (props.isGeneratedPreview ? "32px" : "auto")};
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
&-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
.tile-tags {
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
padding: 15px;
|
||||
|
||||
.type_tag {
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
`;
|
||||
StyledPreviewTile.defaultProps = { theme: Base };
|
||||
|
||||
const PreviewTile = ({ t, title, previewIcon, tags, defaultTagLabel }) => {
|
||||
return (
|
||||
<StyledPreviewTile>
|
||||
<div className="tile-header">
|
||||
<img className="tile-header-icon" src={previewIcon} alt={title} />
|
||||
<div className="tile-header-title">{title}</div>
|
||||
</div>
|
||||
<div className="tile-tags">
|
||||
{tags.length ? (
|
||||
<Tags columnCount={2} tags={tags} />
|
||||
) : (
|
||||
<Tag
|
||||
className="type_tag"
|
||||
tag="script"
|
||||
label={defaultTagLabel}
|
||||
isDefault
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</StyledPreviewTile>
|
||||
);
|
||||
};
|
||||
|
||||
export default PreviewTile;
|
@ -0,0 +1,60 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Dropzone from "./Dropzone";
|
||||
|
||||
const StyledIconEditor = styled.div`
|
||||
.icon-editor {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: start;
|
||||
gap: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
import IconCropper from "./IconCropper";
|
||||
import PreviewTile from "./PreviewTile";
|
||||
|
||||
const IconEditor = ({
|
||||
t,
|
||||
isEdit,
|
||||
title,
|
||||
tags,
|
||||
currentRoomTypeData,
|
||||
icon,
|
||||
onChangeIcon,
|
||||
}) => {
|
||||
const [previewIcon, setPreviewIcon] = useState(null);
|
||||
|
||||
const setUploadedFile = (uploadedFile) =>
|
||||
onChangeIcon({ ...icon, uploadedFile });
|
||||
|
||||
return (
|
||||
<StyledIconEditor>
|
||||
{icon.uploadedFile && (
|
||||
<div className="icon-editor">
|
||||
<IconCropper
|
||||
t={t}
|
||||
icon={icon}
|
||||
onChangeIcon={onChangeIcon}
|
||||
uploadedFile={icon.uploadedFile}
|
||||
setUploadedFile={setUploadedFile}
|
||||
setPreviewIcon={setPreviewIcon}
|
||||
/>
|
||||
|
||||
<PreviewTile
|
||||
t={t}
|
||||
title={title || t("Files:NewRoom")}
|
||||
previewIcon={previewIcon}
|
||||
tags={tags.map((tag) => tag.name)}
|
||||
defaultTagLabel={t(currentRoomTypeData.defaultTag)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Dropzone t={t} setUploadedFile={setUploadedFile} />
|
||||
</StyledIconEditor>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconEditor;
|
@ -0,0 +1,87 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledPrivacyLimitationsWarning = styled.div`
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.isPrivate.limitations.background};
|
||||
border-radius: 6px;
|
||||
padding: 12px 8px;
|
||||
|
||||
.warning-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
&-icon-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.warning-title-icon {
|
||||
width: 16px;
|
||||
height: 14px;
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.isPrivate.limitations.iconColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-text {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.isPrivate.limitations.titleColor};
|
||||
}
|
||||
}
|
||||
|
||||
.warning-description {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.isPrivate.limitations.descriptionColor};
|
||||
}
|
||||
|
||||
.warning-link {
|
||||
cursor: pointer;
|
||||
margin-top: 2px;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.isPrivate.limitations.linkColor};
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 1px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledPrivacyLimitationsWarning.defaultProps = { theme: Base };
|
||||
|
||||
const PrivacyLimitationsWarning = ({ t }) => {
|
||||
return (
|
||||
<StyledPrivacyLimitationsWarning>
|
||||
<div className="warning-title">
|
||||
<div className="warning-title-icon-wrapper">
|
||||
<ReactSVG
|
||||
className="warning-title-icon"
|
||||
src={"/static/images/danger.alert.react.svg"}
|
||||
/>
|
||||
</div>
|
||||
<div className="warning-title-text">{t("Common:Warning")}</div>
|
||||
</div>
|
||||
<div className="warning-description">
|
||||
{t("MakeRoomPrivateLimitationsWarningDescription")}
|
||||
</div>
|
||||
<div className="warning-link">{t("Common:LearnMore")}</div>
|
||||
</StyledPrivacyLimitationsWarning>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrivacyLimitationsWarning;
|
@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import ToggleParam from "../Params/ToggleParam";
|
||||
import PrivacyLimitationsWarning from "./PrivacyLimitationsWarning";
|
||||
|
||||
const StyledIsPrivateParam = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
`;
|
||||
|
||||
const IsPrivateParam = ({ t, isPrivate, onChangeIsPrivate }) => {
|
||||
return (
|
||||
<StyledIsPrivateParam>
|
||||
<ToggleParam
|
||||
title={t("MakeRoomPrivateTitle")}
|
||||
description={t("MakeRoomPrivateDescription")}
|
||||
isChecked={isPrivate}
|
||||
onCheckedChange={onChangeIsPrivate}
|
||||
/>
|
||||
{isPrivate && <PrivacyLimitationsWarning t={t} />}
|
||||
</StyledIsPrivateParam>
|
||||
);
|
||||
};
|
||||
|
||||
export default IsPrivateParam;
|
@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { StyledParam } from "./StyledParam";
|
||||
|
||||
import Label from "@docspace/components/label";
|
||||
import TextInput from "@docspace/components/text-input";
|
||||
|
||||
const StyledInputParam = styled(StyledParam)`
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.input-label {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const InputParam = ({
|
||||
id,
|
||||
title,
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
}) => {
|
||||
return (
|
||||
<StyledInputParam>
|
||||
<Label
|
||||
title={title}
|
||||
className="input-label"
|
||||
display="display"
|
||||
htmlFor={id}
|
||||
text={title}
|
||||
/>
|
||||
<TextInput
|
||||
id={id}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
scale
|
||||
placeholder={placeholder}
|
||||
tabIndex={2}
|
||||
/>
|
||||
</StyledInputParam>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputParam;
|
@ -0,0 +1,57 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledParam = styled.div`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
${(props) =>
|
||||
props.storageLocation
|
||||
? css``
|
||||
: props.folderName
|
||||
? css`
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
`
|
||||
: ""}
|
||||
|
||||
.set_room_params-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.set_room_params-info-title {
|
||||
user-select: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.set_room_params-info-title-text {
|
||||
user-select: none;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.set_room_params-info-description {
|
||||
user-select: none;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.commonParam.descriptionColor};
|
||||
}
|
||||
}
|
||||
|
||||
.set_room_params-toggle {
|
||||
width: 28px;
|
||||
height: 16px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledParam.defaultProps = { theme: Base };
|
||||
|
||||
export { StyledParam };
|
@ -0,0 +1,42 @@
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { StyledParam } from "./StyledParam";
|
||||
|
||||
const StyledToggleParam = styled(StyledParam)`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
|
||||
.set_room_params-info-description {
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.set_room_params-toggle {
|
||||
width: 28px;
|
||||
min-width: 28px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ToggleParam = ({ title, description, isChecked, onCheckedChange }) => {
|
||||
return (
|
||||
<StyledToggleParam isPrivate>
|
||||
<div className="set_room_params-info">
|
||||
<div className="set_room_params-info-title">
|
||||
<div className="set_room_params-info-title-text">{title}</div>
|
||||
</div>
|
||||
<div className="set_room_params-info-description">{description}</div>
|
||||
</div>
|
||||
<ToggleButton
|
||||
className="set_room_params-toggle"
|
||||
isChecked={isChecked}
|
||||
onChange={onCheckedChange}
|
||||
/>
|
||||
</StyledToggleParam>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleParam;
|
@ -0,0 +1,104 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
import SecondaryInfoButton from "../SecondaryInfoButton";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledPermanentSetting = styled.div`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: ${(props) => (props.isFull ? "column" : "row")};
|
||||
align-items: ${(props) => (props.isFull ? "start" : "center")};
|
||||
justify-content: ${(props) => (props.isFull ? "center" : "start")};
|
||||
gap: 4px;
|
||||
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 12px 16px;
|
||||
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.permanentSettings.background};
|
||||
border-radius: 6px;
|
||||
|
||||
user-select: none;
|
||||
|
||||
.permanent_setting-main_info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
gap: 8px;
|
||||
|
||||
&-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: -2px;
|
||||
|
||||
svg {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
${(props) =>
|
||||
props.type === "privacy" &&
|
||||
css`
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.permanentSettings
|
||||
.isPrivateIcon};
|
||||
}
|
||||
`}
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.permanent_setting-help_button {
|
||||
margin-left: auto;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.permanent_setting-secondary-info {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.permanentSettings.descriptionColor};
|
||||
white-space: pre-line;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledPermanentSetting.defaultProps = { theme: Base };
|
||||
|
||||
const PermanentSetting = ({ isFull, type, icon, title, content }) => {
|
||||
return (
|
||||
<StyledPermanentSetting
|
||||
className="permanent_setting"
|
||||
isFull={isFull}
|
||||
type={type}
|
||||
>
|
||||
<div className="permanent_setting-main_info">
|
||||
<ReactSVG className="permanent_setting-main_info-icon" src={icon} />
|
||||
<div className="permanent_setting-main_info-title">{title}</div>
|
||||
</div>
|
||||
|
||||
{isFull ? (
|
||||
<div className="permanent_setting-secondary-info">{content}</div>
|
||||
) : (
|
||||
<div className="permanent_setting-help_button">
|
||||
<SecondaryInfoButton content={content} />
|
||||
</div>
|
||||
)}
|
||||
</StyledPermanentSetting>
|
||||
);
|
||||
};
|
||||
|
||||
export default PermanentSetting;
|
@ -0,0 +1,72 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
import {
|
||||
connectedCloudsTypeIcon as getProviderTypeIcon,
|
||||
connectedCloudsTypeTitleTranslation as getProviderTypeTitle,
|
||||
} from "@docspace/client/src/helpers/filesUtils";
|
||||
|
||||
import PermanentSetting from "./PermanentSetting";
|
||||
|
||||
const StyledPermanentSettings = styled.div`
|
||||
display: ${(props) => (props.displayNone ? "none" : "flex")};
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
margin-top: -12px;
|
||||
`;
|
||||
|
||||
const PermanentSettings = ({
|
||||
t,
|
||||
title,
|
||||
isThirdparty,
|
||||
storageLocation,
|
||||
isPrivate,
|
||||
}) => {
|
||||
const createThirdpartyPath = () => {
|
||||
const path = storageLocation.parentId.split("|");
|
||||
path.shift();
|
||||
path.unshift(thirdpartyTitle);
|
||||
path.push(thirdpartyFolderName);
|
||||
return `(${path.join("/")})`;
|
||||
};
|
||||
|
||||
const thirdpartyTitle = getProviderTypeTitle(storageLocation?.providerKey, t);
|
||||
const thirdpartyFolderName = isThirdparty ? storageLocation?.title : "";
|
||||
const thirdpartyPath = isThirdparty ? createThirdpartyPath() : "";
|
||||
|
||||
return (
|
||||
<StyledPermanentSettings displayNone={!(isPrivate || isThirdparty)}>
|
||||
{isThirdparty && (
|
||||
<PermanentSetting
|
||||
type="storageLocation"
|
||||
isFull={!isPrivate}
|
||||
icon={storageLocation.iconSrc}
|
||||
title={thirdpartyTitle}
|
||||
content={
|
||||
<Trans
|
||||
i18nKey="ThirdPartyStoragePermanentSettingDescription"
|
||||
ns="CreateEditRoomDialog"
|
||||
t={t}
|
||||
>
|
||||
Files are stored in a third-party {{ thirdpartyTitle }} storage in
|
||||
the \"{{ thirdpartyFolderName }}\" folder.{" "}
|
||||
<strong>{{ thirdpartyPath }}</strong>"
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{isPrivate && (
|
||||
<PermanentSetting
|
||||
type="privacy"
|
||||
isFull={!storageLocation}
|
||||
icon={"images/security.svg"}
|
||||
title={"Private room"}
|
||||
content={`All files in this room will be encrypted`}
|
||||
/>
|
||||
)}
|
||||
</StyledPermanentSettings>
|
||||
);
|
||||
};
|
||||
|
||||
export default PermanentSettings;
|
@ -0,0 +1,215 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
|
||||
import IconButton from "@docspace/components/icon-button";
|
||||
import Text from "@docspace/components/text";
|
||||
import RoomLogo from "@docspace/components/room-logo";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledRoomType = styled.div`
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
outline: 0;
|
||||
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
|
||||
.choose_room-logo_wrapper {
|
||||
width: 32px;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.choose_room-info_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
.choose_room-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
.choose_room-title-text {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
.choose_room-description {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.choose_room-forward_btn {
|
||||
margin-left: auto;
|
||||
max-width: 17px;
|
||||
max-height: 17px;
|
||||
min-width: 17px;
|
||||
min-height: 17px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledListItem = styled(StyledRoomType)`
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.listItem.background};
|
||||
border: 1px solid
|
||||
${(props) => props.theme.createEditRoomDialog.roomType.listItem.borderColor};
|
||||
border-radius: 6px;
|
||||
|
||||
.choose_room-description {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.listItem.descriptionText};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDropdownButton = styled(StyledRoomType)`
|
||||
border-radius: 6px;
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.dropdownButton.background};
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.isOpen
|
||||
? props.theme.createEditRoomDialog.roomType.dropdownButton
|
||||
.isOpenBorderColor
|
||||
: props.theme.createEditRoomDialog.roomType.dropdownButton.borderColor};
|
||||
|
||||
.choose_room-description {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.dropdownButton.descriptionText};
|
||||
}
|
||||
|
||||
.choose_room-forward_btn {
|
||||
&.dropdown-button {
|
||||
transform: ${(props) =>
|
||||
props.isOpen ? "rotate(-90deg)" : "rotate(90deg)"};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDropdownItem = styled(StyledRoomType)`
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.dropdownItem.background};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.dropdownItem.hoverBackground};
|
||||
}
|
||||
|
||||
.choose_room-description {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.dropdownItem.descriptionText};
|
||||
}
|
||||
|
||||
.choose_room-forward_btn {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDisplayItem = styled(StyledRoomType)`
|
||||
cursor: default;
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.displayItem.background};
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.displayItem.borderColor};
|
||||
border-radius: 6px;
|
||||
|
||||
.choose_room-description {
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomType.displayItem.descriptionText};
|
||||
}
|
||||
|
||||
.choose_room-forward_btn {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const RoomType = ({ t, room, onClick, type = "listItem", isOpen }) => {
|
||||
const arrowClassName =
|
||||
type === "dropdownButton"
|
||||
? "choose_room-forward_btn dropdown-button"
|
||||
: type === "dropdownItem"
|
||||
? "choose_room-forward_btn dropdown-item"
|
||||
: "choose_room-forward_btn";
|
||||
|
||||
const onSecondaryInfoClick = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<div className="choose_room-logo_wrapper">
|
||||
<RoomLogo type={room.type} />
|
||||
</div>
|
||||
|
||||
<div className="choose_room-info_wrapper">
|
||||
<div className="choose_room-title">
|
||||
<Text noSelect className="choose_room-title-text">
|
||||
{t(room.title)}
|
||||
</Text>
|
||||
{room.withSecondaryInfo && (
|
||||
<div onClick={onSecondaryInfoClick}>
|
||||
{/* <SecondaryInfoButton content={t(room.secondaryInfo)} /> */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Text noSelect className="choose_room-description">
|
||||
{t(room.description)}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<IconButton
|
||||
className={arrowClassName}
|
||||
iconName="images/arrow.react.svg"
|
||||
size={16}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return type === "listItem" ? (
|
||||
<StyledListItem title={t(room.title)} onClick={onClick}>
|
||||
{content}
|
||||
</StyledListItem>
|
||||
) : type === "dropdownButton" ? (
|
||||
<StyledDropdownButton
|
||||
title={t(room.title)}
|
||||
onClick={onClick}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
{content}
|
||||
</StyledDropdownButton>
|
||||
) : type === "dropdownItem" ? (
|
||||
<StyledDropdownItem title={t(room.title)} onClick={onClick} isOpen={isOpen}>
|
||||
{content}
|
||||
</StyledDropdownItem>
|
||||
) : (
|
||||
<StyledDisplayItem title={t(room.title)}>{content}</StyledDisplayItem>
|
||||
);
|
||||
};
|
||||
|
||||
StyledListItem.defaultProps = { theme: Base };
|
||||
StyledDropdownButton.defaultProps = { theme: Base };
|
||||
StyledDropdownItem.defaultProps = { theme: Base };
|
||||
StyledDisplayItem.defaultProps = { theme: Base };
|
||||
|
||||
RoomType.propTypes = {
|
||||
room: PropTypes.object,
|
||||
onClick: PropTypes.func,
|
||||
type: PropTypes.oneOf([
|
||||
"displayItem",
|
||||
"listItem",
|
||||
"dropdownButton",
|
||||
"dropdownItem",
|
||||
]),
|
||||
isOpen: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default RoomType;
|
@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import RoomType from "../RoomType";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledDropdownDesktop = styled.div`
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
|
||||
${(props) => !props.isOpen && "display: none"};
|
||||
|
||||
.dropdown-content {
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomTypeDropdown.desktop.background};
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.theme.createEditRoomDialog.roomTypeDropdown.desktop.borderColor};
|
||||
margin-top: 4px;
|
||||
overflow: visible;
|
||||
z-index: 400;
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 6px 0;
|
||||
box-shadow: 0px 12px 40px rgba(4, 15, 27, 0.12);
|
||||
border-radius: 6px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledDropdownDesktop.defaultProps = { theme: Base };
|
||||
|
||||
const DropdownDesktop = ({ t, open, roomTypes, chooseRoomType }) => {
|
||||
return (
|
||||
<StyledDropdownDesktop className="dropdown-content-wrapper" isOpen={open}>
|
||||
<div className="dropdown-content">
|
||||
{roomTypes.map((room) => (
|
||||
<RoomType
|
||||
t={t}
|
||||
key={room.type}
|
||||
room={room}
|
||||
type="dropdownItem"
|
||||
onClick={() => chooseRoomType(room.type)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</StyledDropdownDesktop>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownDesktop;
|
@ -0,0 +1,90 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import RoomType from "../RoomType";
|
||||
|
||||
import { Scrollbar } from "@docspace/components";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledDropdownMobile = styled.div`
|
||||
visibility: ${(props) => (props.isOpen ? "visible" : "hidden")};
|
||||
|
||||
& > .dropdown-mobile-backdrop {
|
||||
z-index: 999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
margin-top: -64px;
|
||||
|
||||
.dropdown-mobile-wrapper {
|
||||
border-radius: 6px 6px 0 0;
|
||||
padding: 6px 0;
|
||||
box-shadow: 0px -4px 60px rgba(4, 15, 27, 0.12);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: calc(100vh - 254px);
|
||||
|
||||
bottom: -100%;
|
||||
transition: all 0.2s ease-in-out;
|
||||
&-active {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-mobile-scrollbar {
|
||||
background: rgba(6, 22, 38, 0.2);
|
||||
.scroll-body {
|
||||
padding-right: 0 !important;
|
||||
* {
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-mobile-content {
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.roomTypeDropdown.mobile.background};
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledDropdownMobile.defaultProps = { theme: Base };
|
||||
|
||||
const DropdownMobile = ({ t, open, onClose, roomTypes, chooseRoomType }) => {
|
||||
return (
|
||||
<StyledDropdownMobile className="dropdown-mobile" isOpen={open}>
|
||||
<div className="dropdown-mobile-backdrop" onClick={onClose}>
|
||||
<div
|
||||
className={`dropdown-mobile-wrapper ${
|
||||
open && "dropdown-mobile-wrapper-active"
|
||||
}`}
|
||||
>
|
||||
<Scrollbar className="dropdown-mobile-scrollbar">
|
||||
<div className="dropdown-mobile-content">
|
||||
{roomTypes.map((room) => (
|
||||
<RoomType
|
||||
t={t}
|
||||
key={room.type}
|
||||
room={room}
|
||||
type="dropdownItem"
|
||||
onClick={() => chooseRoomType(room.type)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</StyledDropdownMobile>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropdownMobile;
|
@ -0,0 +1,67 @@
|
||||
import { isHugeMobile } from "@docspace/components/utils/device";
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { roomTypes } from "../../data";
|
||||
import RoomType from "../RoomType";
|
||||
import DropdownDesktop from "./DropdownDesktop";
|
||||
import DropdownMobile from "./DropdownMobile";
|
||||
|
||||
const StyledRoomTypeDropdown = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const RoomTypeDropdown = ({
|
||||
t,
|
||||
currentRoom,
|
||||
setRoomType,
|
||||
setIsScrollLocked,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const toggleDropdown = () => {
|
||||
if (isOpen) {
|
||||
setIsScrollLocked(false);
|
||||
setIsOpen(false);
|
||||
} else {
|
||||
setIsScrollLocked(true);
|
||||
setIsOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const chooseRoomType = (roomType) => {
|
||||
setRoomType(roomType);
|
||||
toggleDropdown();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledRoomTypeDropdown isOpen={isOpen}>
|
||||
<RoomType
|
||||
t={t}
|
||||
room={currentRoom}
|
||||
type="dropdownButton"
|
||||
isOpen={isOpen}
|
||||
onClick={toggleDropdown}
|
||||
/>
|
||||
{isHugeMobile() ? (
|
||||
<DropdownMobile
|
||||
t={t}
|
||||
open={isOpen}
|
||||
onClose={toggleDropdown}
|
||||
roomTypes={roomTypes}
|
||||
chooseRoomType={chooseRoomType}
|
||||
/>
|
||||
) : (
|
||||
<DropdownDesktop
|
||||
t={t}
|
||||
open={isOpen}
|
||||
roomTypes={roomTypes}
|
||||
chooseRoomType={chooseRoomType}
|
||||
/>
|
||||
)}
|
||||
</StyledRoomTypeDropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoomTypeDropdown;
|
@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { roomTypes } from "../data";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
||||
import RoomType from "./RoomType";
|
||||
|
||||
import withLoader from "@docspace/client/src/HOCs/withLoader";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
const StyledRoomTypeList = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
`;
|
||||
|
||||
const RoomTypeList = ({ t, setRoomType }) => {
|
||||
return (
|
||||
<StyledRoomTypeList>
|
||||
{roomTypes.map((room) => (
|
||||
<RoomType
|
||||
t={t}
|
||||
key={room.type}
|
||||
room={room}
|
||||
type={"listItem"}
|
||||
onClick={() => setRoomType(room.type)}
|
||||
/>
|
||||
))}
|
||||
</StyledRoomTypeList>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation(["CreateEditRoomDialog"])(
|
||||
withLoader(RoomTypeList)(<Loaders.RoomTypeListLoader />)
|
||||
);
|
@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import HelpButton from "@docspace/components/help-button";
|
||||
|
||||
const StyledHelpButton = styled(HelpButton)`
|
||||
border-radius: 50%;
|
||||
background-color: #a3a9ae;
|
||||
circle,
|
||||
rect {
|
||||
fill: #ffffff;
|
||||
}
|
||||
`;
|
||||
|
||||
const SecondaryInfoButton = ({ content }) => {
|
||||
return (
|
||||
<StyledHelpButton
|
||||
displayType="auto"
|
||||
className="set_room_params-info-title-help"
|
||||
iconName="/static/images/info.react.svg"
|
||||
tooltipProps={{ globalEventOff: "click" }}
|
||||
tooltipContent={content}
|
||||
offsetRight={0}
|
||||
size={12}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SecondaryInfoButton;
|
@ -0,0 +1,172 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
||||
import { roomTypes } from "../data";
|
||||
import RoomTypeDropdown from "./RoomTypeDropdown";
|
||||
import ThirdPartyStorage from "./ThirdPartyStorage";
|
||||
import TagInput from "./TagInput";
|
||||
import RoomType from "./RoomType";
|
||||
import IconEditor from "./IconEditor";
|
||||
import PermanentSettings from "./PermanentSettings";
|
||||
import InputParam from "./Params/InputParam";
|
||||
import IsPrivateParam from "./IsPrivateParam";
|
||||
|
||||
import withLoader from "@docspace/client/src/HOCs/withLoader";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
|
||||
const StyledSetRoomParams = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 20px;
|
||||
`;
|
||||
|
||||
const SetRoomParams = ({
|
||||
t,
|
||||
roomParams,
|
||||
setRoomParams,
|
||||
setIsOauthWindowOpen,
|
||||
setRoomType,
|
||||
tagHandler,
|
||||
setIsScrollLocked,
|
||||
isEdit,
|
||||
connectItems,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
openConnectWindow,
|
||||
setConnectItem,
|
||||
getOAuthToken,
|
||||
}) => {
|
||||
const onChangeName = (e) => {
|
||||
// let value = e.target.value;
|
||||
// value = value.replace("/", "");
|
||||
// value = value.replace("\\", "");
|
||||
|
||||
// const storageFolderPath = roomParams.storageLocation.storageFolderPath;
|
||||
// const pathArr = storageFolderPath.split("/");
|
||||
// const folderName = pathArr.pop();
|
||||
|
||||
// if (roomParams.title === folderName)
|
||||
// setRoomParams({
|
||||
// ...roomParams,
|
||||
// title: value,
|
||||
// storageLocation: {
|
||||
// ...roomParams.storageLocation,
|
||||
// storageFolderPath:
|
||||
// pathArr.join("/") + (!!pathArr.length ? "/" : "") + value,
|
||||
// },
|
||||
// });
|
||||
// else
|
||||
|
||||
setRoomParams({ ...roomParams, title: e.target.value });
|
||||
};
|
||||
|
||||
const onChangeIsPrivate = () =>
|
||||
setRoomParams({ ...roomParams, isPrivate: !roomParams.isPrivate });
|
||||
|
||||
// const onChangeThidpartyFolderName = (e) =>
|
||||
// setRoomParams({ ...roomParams, thirdpartyFolderName: e.target.value });
|
||||
|
||||
const onChangeIcon = (icon) => setRoomParams({ ...roomParams, icon: icon });
|
||||
|
||||
const onChangeIsThirdparty = () =>
|
||||
setRoomParams({ ...roomParams, isThirdparty: !roomParams.isThirdparty });
|
||||
|
||||
const setChangeStorageLocation = (storageLocation) =>
|
||||
setRoomParams({ ...roomParams, storageLocation });
|
||||
|
||||
const onChangeRememberThirdpartyStorage = () =>
|
||||
setRoomParams({
|
||||
...roomParams,
|
||||
rememberThirdpartyStorage: !roomParams.rememberThirdpartyStorage,
|
||||
});
|
||||
|
||||
const [currentRoomTypeData] = roomTypes.filter(
|
||||
(room) => room.type === roomParams.type
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledSetRoomParams>
|
||||
{isEdit ? (
|
||||
<RoomType t={t} room={currentRoomTypeData} type="displayItem" />
|
||||
) : (
|
||||
<RoomTypeDropdown
|
||||
t={t}
|
||||
currentRoom={currentRoomTypeData}
|
||||
setRoomType={setRoomType}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isEdit && (
|
||||
<PermanentSettings
|
||||
t={t}
|
||||
title={roomParams.title}
|
||||
isThirdparty={roomParams.isThirdparty}
|
||||
storageLocation={roomParams.storageLocation}
|
||||
isPrivate={roomParams.isPrivate}
|
||||
/>
|
||||
)}
|
||||
|
||||
<InputParam
|
||||
id={"room-name"}
|
||||
title={`${t("Common:Name")}:`}
|
||||
placeholder={t("NamePlaceholder")}
|
||||
value={roomParams.title}
|
||||
onChange={onChangeName}
|
||||
/>
|
||||
|
||||
<TagInput
|
||||
t={t}
|
||||
tagHandler={tagHandler}
|
||||
currentRoomTypeData={currentRoomTypeData}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
/>
|
||||
|
||||
{/* {!isEdit && (
|
||||
<IsPrivateParam
|
||||
t={t}
|
||||
isPrivate={roomParams.isPrivate}
|
||||
onChangeIsPrivate={onChangeIsPrivate}
|
||||
/>
|
||||
)} */}
|
||||
|
||||
{!isEdit && (
|
||||
<ThirdPartyStorage
|
||||
t={t}
|
||||
connectItems={connectItems}
|
||||
setConnectDialogVisible={setConnectDialogVisible}
|
||||
setRoomCreation={setRoomCreation}
|
||||
saveThirdpartyResponse={saveThirdpartyResponse}
|
||||
openConnectWindow={openConnectWindow}
|
||||
setConnectItem={setConnectItem}
|
||||
getOAuthToken={getOAuthToken}
|
||||
roomParams={roomParams}
|
||||
isThirdparty={roomParams.isThirdparty}
|
||||
onChangeIsThirdparty={onChangeIsThirdparty}
|
||||
storageLocation={roomParams.storageLocation}
|
||||
setChangeStorageLocation={setChangeStorageLocation}
|
||||
rememberThirdpartyStorage={roomParams.rememberThirdpartyStorage}
|
||||
onChangeRememberThirdpartyStorage={onChangeRememberThirdpartyStorage}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
setIsOauthWindowOpen={setIsOauthWindowOpen}
|
||||
/>
|
||||
)}
|
||||
|
||||
<IconEditor
|
||||
t={t}
|
||||
title={roomParams.title}
|
||||
tags={roomParams.tags}
|
||||
currentRoomTypeData={currentRoomTypeData}
|
||||
icon={roomParams.icon}
|
||||
onChangeIcon={onChangeIcon}
|
||||
/>
|
||||
</StyledSetRoomParams>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation(["CreateEditRoomDialog"])(
|
||||
withLoader(SetRoomParams)(<Loaders.SetRoomParamsLoader />)
|
||||
);
|
@ -0,0 +1,62 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
import { smallTablet } from "@docspace/components/utils/device";
|
||||
|
||||
import DropDown from "@docspace/components/drop-down";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledDropDownWrapper = styled.div`
|
||||
width: 100%;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledDropDown = styled(DropDown)`
|
||||
margin-top: ${(props) => (props.marginTop ? props.marginTop : "4px")};
|
||||
padding: 6px 0;
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.dropdown.background};
|
||||
border: 1px solid
|
||||
${(props) => props.theme.createEditRoomDialog.dropdown.borderColor};
|
||||
box-shadow: 0px 12px 40px rgba(4, 15, 27, 0.12);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
|
||||
width: 446px;
|
||||
max-width: 446px;
|
||||
div {
|
||||
max-width: 446px;
|
||||
}
|
||||
|
||||
@media ${smallTablet} {
|
||||
width: calc(100vw - 34px);
|
||||
max-width: calc(100vw - 34px);
|
||||
div {
|
||||
max-width: calc(100vw - 34px);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
height: 32px !important;
|
||||
max-height: 32px !important;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
&:hover {
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.dropdown.item.hoverBackground};
|
||||
}
|
||||
|
||||
&-separator {
|
||||
height: 7px !important;
|
||||
max-height: 7px !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledDropDown.defaultProps = { theme: Base };
|
||||
|
||||
export { StyledDropDownWrapper, StyledDropDown };
|
@ -0,0 +1,116 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
|
||||
import { StyledDropDown, StyledDropDownWrapper } from "../StyledDropdown";
|
||||
|
||||
import DropDownItem from "@docspace/components/drop-down-item";
|
||||
import { isHugeMobile } from "@docspace/components/utils/device";
|
||||
import DomHelpers from "@docspace/components/utils/domHelpers";
|
||||
|
||||
const TagDropdown = ({
|
||||
open,
|
||||
tagHandler,
|
||||
tagInputValue,
|
||||
setTagInputValue,
|
||||
createTagLabel,
|
||||
}) => {
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
const [dropdownMaxHeight, setDropdownMaxHeight] = useState(0);
|
||||
|
||||
const chosenTags = tagHandler.tags.map((tag) => tag.name);
|
||||
|
||||
const tagsForDropdown = tagHandler.fetchedTags.filter(
|
||||
(tag) =>
|
||||
tag.toLowerCase().includes(tagInputValue.toLowerCase()) &&
|
||||
!chosenTags.includes(tag)
|
||||
);
|
||||
|
||||
const preventDefault = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const onClickOutside = () => {
|
||||
document.getElementById("tags-input").blur();
|
||||
};
|
||||
|
||||
const addNewTag = () => {
|
||||
tagHandler.addNewTag(tagInputValue);
|
||||
setTagInputValue("");
|
||||
onClickOutside();
|
||||
};
|
||||
|
||||
const addFetchedTag = (name) => {
|
||||
tagHandler.addTag(name);
|
||||
setTagInputValue("");
|
||||
onClickOutside();
|
||||
};
|
||||
|
||||
const calcualateDisplayedDropdownItems = () => {
|
||||
let res = tagsForDropdown.map((tag, i) => (
|
||||
<DropDownItem
|
||||
className="dropdown-item"
|
||||
height={32}
|
||||
heightTablet={32}
|
||||
key={i}
|
||||
label={tag}
|
||||
onClick={() => addFetchedTag(tag)}
|
||||
/>
|
||||
));
|
||||
|
||||
if (
|
||||
tagInputValue &&
|
||||
![...tagsForDropdown, ...chosenTags].find((tag) => tagInputValue === tag)
|
||||
)
|
||||
res = [
|
||||
<DropDownItem
|
||||
key={-2}
|
||||
className="dropdown-item"
|
||||
onMouseDown={preventDefault}
|
||||
onClick={addNewTag}
|
||||
label={`${createTagLabel} “${tagInputValue}”`}
|
||||
height={32}
|
||||
heightTablet={32}
|
||||
/>,
|
||||
...res,
|
||||
];
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (dropdownRef && open) {
|
||||
const { top: offsetTop } = DomHelpers.getOffset(dropdownRef.current);
|
||||
const offsetBottom = window.innerHeight - offsetTop;
|
||||
const maxHeight = Math.floor((offsetBottom - 22) / 32) * 32 - 2;
|
||||
const result = isHugeMobile()
|
||||
? Math.min(maxHeight, 158)
|
||||
: Math.min(maxHeight, 382);
|
||||
setDropdownMaxHeight(result);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const dropdownItems = calcualateDisplayedDropdownItems();
|
||||
|
||||
return (
|
||||
<StyledDropDownWrapper
|
||||
ref={dropdownRef}
|
||||
className="dropdown-content-wrapper"
|
||||
onMouseDown={preventDefault}
|
||||
>
|
||||
{!!dropdownItems.length && (
|
||||
<StyledDropDown
|
||||
className="dropdown-content"
|
||||
open={open}
|
||||
forwardedRef={dropdownRef}
|
||||
clickOutsideAction={onClickOutside}
|
||||
maxHeight={dropdownMaxHeight}
|
||||
showDisabledItems={false}
|
||||
>
|
||||
{dropdownItems}
|
||||
</StyledDropDown>
|
||||
)}
|
||||
</StyledDropDownWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagDropdown;
|
@ -0,0 +1,54 @@
|
||||
import Tag from "@docspace/components/tag";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledTagList = styled.div`
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
|
||||
.set_room_params-tag_input-tag {
|
||||
padding: 6px 8px;
|
||||
border-radius: 3px;
|
||||
margin: 0;
|
||||
|
||||
.tag-icon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TagList = ({ defaultTagLabel, tagHandler }) => {
|
||||
const { tags } = tagHandler;
|
||||
|
||||
return (
|
||||
<StyledTagList className="set_room_params-tag_input-tag_list">
|
||||
{tags.length ? (
|
||||
tags.map((tag) => (
|
||||
<Tag
|
||||
key={tag.id}
|
||||
className="set_room_params-tag_input-tag"
|
||||
tag="script"
|
||||
label={tag.name}
|
||||
isNewTag
|
||||
onDelete={() => {
|
||||
tagHandler.deleteTag(tag.id);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Tag
|
||||
className="set_room_params-tag_input-tag"
|
||||
tag="script"
|
||||
label={defaultTagLabel}
|
||||
isDefault
|
||||
/>
|
||||
)}
|
||||
</StyledTagList>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagList;
|
@ -0,0 +1,76 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import TagList from "./TagList";
|
||||
|
||||
import InputParam from "../Params/InputParam";
|
||||
import TagDropdown from "./TagDropdown";
|
||||
|
||||
const StyledTagInput = styled.div`
|
||||
.set_room_params-tag_input {
|
||||
&-label_wrapper {
|
||||
&-label {
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-content-wrapper {
|
||||
margin-bottom: -4px;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
const TagInput = ({
|
||||
t,
|
||||
tagHandler,
|
||||
currentRoomTypeData,
|
||||
setIsScrollLocked,
|
||||
}) => {
|
||||
const [tagInput, setTagInput] = useState("");
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
|
||||
const onTagInputChange = (e) => setTagInput(e.target.value);
|
||||
|
||||
const openDropdown = () => {
|
||||
setIsScrollLocked(true);
|
||||
setIsDropdownOpen(true);
|
||||
};
|
||||
|
||||
const closeDropdown = () => {
|
||||
setIsScrollLocked(false);
|
||||
setIsDropdownOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTagInput className="set_room_params-input set_room_params-tag_input">
|
||||
<InputParam
|
||||
id={"tags-input"}
|
||||
title={`${t("Common:Tags")}:`}
|
||||
placeholder={t("TagsPlaceholder")}
|
||||
value={tagInput}
|
||||
onChange={onTagInputChange}
|
||||
onFocus={openDropdown}
|
||||
onBlur={closeDropdown}
|
||||
/>
|
||||
|
||||
<TagDropdown
|
||||
open={isDropdownOpen}
|
||||
tagHandler={tagHandler}
|
||||
tagInputValue={tagInput}
|
||||
setTagInputValue={setTagInput}
|
||||
createTagLabel={t("CreateTagOption")}
|
||||
/>
|
||||
|
||||
<TagList
|
||||
tagHandler={tagHandler}
|
||||
defaultTagLabel={t(currentRoomTypeData.defaultTag)}
|
||||
/>
|
||||
</StyledTagInput>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagInput;
|
@ -0,0 +1,117 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { IconButton, TextInput } from "@docspace/components";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledFolderInput = styled.div`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0px;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
|
||||
border-radius: 3px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&,
|
||||
.icon-wrapper {
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.isFocused
|
||||
? props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.focusBorderColor
|
||||
: props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.borderColor};
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:hover > .icon-wrapper {
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.isFocused
|
||||
? props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.focusBorderColor
|
||||
: props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.hoverBorderColor};
|
||||
}
|
||||
|
||||
.root_label {
|
||||
padding: 5px 2px 5px 7px;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.background};
|
||||
color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.rootLabelColor};
|
||||
}
|
||||
|
||||
.text_input {
|
||||
padding-left: 0;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
cursor: pointer;
|
||||
background-color: ${(props) =>
|
||||
props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.background};
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
width: 31px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
border-top: none !important;
|
||||
border-right: none !important;
|
||||
border-bottom: none !important;
|
||||
|
||||
&:hover {
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.thirdpartyStorage.folderInput
|
||||
.iconFill};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
StyledFolderInput.defaultProps = { theme: Base };
|
||||
|
||||
const FolderInput = ({ value, onChangeFolderPath }) => {
|
||||
const [isFocused, setIsFocused] = useState();
|
||||
|
||||
const onFocus = () => setIsFocused(true);
|
||||
const onBlur = () => setIsFocused(false);
|
||||
|
||||
return (
|
||||
<StyledFolderInput isFocused={isFocused}>
|
||||
<span className="root_label">ROOT/</span>
|
||||
<TextInput
|
||||
className="text_input"
|
||||
value={value}
|
||||
onChange={onChangeFolderPath}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<div className="icon-wrapper">
|
||||
<IconButton
|
||||
size={16}
|
||||
iconName="images/folder.react.svg"
|
||||
isClickable
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</StyledFolderInput>
|
||||
);
|
||||
};
|
||||
|
||||
export default FolderInput;
|
@ -0,0 +1,271 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import styled from "styled-components";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
import { StyledDropDown, StyledDropDownWrapper } from "../StyledDropdown";
|
||||
|
||||
import { isHugeMobile } from "@docspace/components/utils/device";
|
||||
import DomHelpers from "@docspace/components/utils/domHelpers";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import Button from "@docspace/components/button";
|
||||
import DropDownItem from "@docspace/components/drop-down-item";
|
||||
import { connectedCloudsTypeTitleTranslation } from "@docspace/client/src/helpers/filesUtils";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledStorageLocation = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.set_room_params-thirdparty {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
&-combobox {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 5px 7px;
|
||||
background: ${(props) =>
|
||||
props.theme.createEditRoomDialog.thirdpartyStorage.combobox.background};
|
||||
border-radius: 3px;
|
||||
max-height: 32px;
|
||||
|
||||
border: ${(props) =>
|
||||
`1px solid ${
|
||||
props.isOpen
|
||||
? props.theme.createEditRoomDialog.thirdpartyStorage.combobox
|
||||
.isOpenDropdownBorderColor
|
||||
: props.theme.createEditRoomDialog.thirdpartyStorage.combobox
|
||||
.dropdownBorderColor
|
||||
}`};
|
||||
|
||||
transition: all 0.2s ease;
|
||||
&:hover {
|
||||
border: ${(props) =>
|
||||
`1px solid ${
|
||||
props.isOpen
|
||||
? props.theme.createEditRoomDialog.thirdpartyStorage.combobox
|
||||
.isOpenDropdownBorderColor
|
||||
: props.theme.createEditRoomDialog.thirdpartyStorage.combobox
|
||||
.hoverDropdownBorderColor
|
||||
}`};
|
||||
}
|
||||
|
||||
&-text {
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&-expander {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 6.35px;
|
||||
svg {
|
||||
transform: ${(props) =>
|
||||
props.isOpen ? "rotate(180deg)" : "rotate(0)"};
|
||||
width: 6.35px;
|
||||
height: auto;
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.createEditRoomDialog.thirdpartyStorage.combobox
|
||||
.arrowFill};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-checkbox {
|
||||
margin-top: 8px;
|
||||
.checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.checkbox-text {
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledStorageLocation.defaultProps = { theme: Base };
|
||||
|
||||
const ThirpartyComboBox = ({
|
||||
t,
|
||||
storageLocation,
|
||||
|
||||
setChangeStorageLocation,
|
||||
|
||||
connectItems,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
openConnectWindow,
|
||||
setConnectItem,
|
||||
getOAuthToken,
|
||||
|
||||
setIsScrollLocked,
|
||||
setIsOauthWindowOpen,
|
||||
}) => {
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
const thirdparties = connectItems.map((item) => ({
|
||||
...item,
|
||||
title: connectedCloudsTypeTitleTranslation(item.providerName, t),
|
||||
}));
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [dropdownDirection, setDropdownDirection] = useState("bottom");
|
||||
|
||||
const toggleIsOpen = () => {
|
||||
if (isOpen) setIsScrollLocked(false);
|
||||
else {
|
||||
setIsScrollLocked(true);
|
||||
calculateDropdownDirection();
|
||||
}
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const setStorageLocaiton = (thirparty) => {
|
||||
setChangeStorageLocation({
|
||||
...storageLocation,
|
||||
provider: thirparty,
|
||||
});
|
||||
setIsOpen(false);
|
||||
setIsScrollLocked(false);
|
||||
};
|
||||
|
||||
const calculateDropdownDirection = () => {
|
||||
const { top: offsetTop } = DomHelpers.getOffset(dropdownRef.current);
|
||||
const offsetBottom = window.innerHeight - offsetTop;
|
||||
|
||||
const neededHeightDesktop = Math.min(thirdparties.length * 32 + 16, 404);
|
||||
const neededHeightMobile = Math.min(thirdparties.length * 32 + 16, 180);
|
||||
const neededheight = isHugeMobile()
|
||||
? neededHeightMobile
|
||||
: neededHeightDesktop;
|
||||
|
||||
setDropdownDirection(neededheight > offsetBottom ? "top" : "bottom");
|
||||
};
|
||||
|
||||
const onShowService = async () => {
|
||||
setRoomCreation(true);
|
||||
|
||||
const item = {
|
||||
title: connectedCloudsTypeTitleTranslation(
|
||||
storageLocation.provider.providerName,
|
||||
t
|
||||
),
|
||||
customer_title: "NOTITLE",
|
||||
provider_key: storageLocation.provider.providerName,
|
||||
link: storageLocation.provider.oauthHref,
|
||||
};
|
||||
|
||||
if (storageLocation.provider.isOauth) {
|
||||
setIsOauthWindowOpen(true);
|
||||
let authModal = window.open(
|
||||
"",
|
||||
"Authorization",
|
||||
"height=600, width=1020"
|
||||
);
|
||||
await openConnectWindow(storageLocation.provider.providerName, authModal)
|
||||
.then(getOAuthToken)
|
||||
.then((token) => {
|
||||
authModal.close();
|
||||
setConnectItem({
|
||||
...item,
|
||||
token,
|
||||
});
|
||||
setConnectDialogVisible(true);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!e) return;
|
||||
console.error(e);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsOauthWindowOpen(false);
|
||||
});
|
||||
} else {
|
||||
setConnectItem(item);
|
||||
setConnectDialogVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!saveThirdpartyResponse) return;
|
||||
|
||||
if (saveThirdpartyResponse.id) {
|
||||
setChangeStorageLocation({
|
||||
...storageLocation,
|
||||
isConnected: true,
|
||||
thirdpartyFolderId: saveThirdpartyResponse.id,
|
||||
});
|
||||
} else {
|
||||
setChangeStorageLocation({
|
||||
...storageLocation,
|
||||
isConnected: false,
|
||||
});
|
||||
}
|
||||
}, [saveThirdpartyResponse]);
|
||||
|
||||
return (
|
||||
<StyledStorageLocation isOpen={isOpen}>
|
||||
<div className="set_room_params-thirdparty">
|
||||
<div
|
||||
className="set_room_params-thirdparty-combobox"
|
||||
onClick={toggleIsOpen}
|
||||
>
|
||||
<Text className="set_room_params-thirdparty-combobox-text" noSelect>
|
||||
{storageLocation?.provider?.title ||
|
||||
t("ThirdPartyStorageComboBoxPlaceholder")}
|
||||
</Text>
|
||||
<ReactSVG
|
||||
className="set_room_params-thirdparty-combobox-expander"
|
||||
src={"/static/images/expander-down.react.svg"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
isDisabled={!storageLocation?.provider}
|
||||
className="set_room_params-thirdparty-connect"
|
||||
size="small"
|
||||
label={t("Common:Connect")}
|
||||
onClick={onShowService}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<StyledDropDownWrapper
|
||||
className="dropdown-content-wrapper"
|
||||
ref={dropdownRef}
|
||||
>
|
||||
<StyledDropDown
|
||||
className="dropdown-content"
|
||||
open={isOpen}
|
||||
forwardedRef={dropdownRef}
|
||||
clickOutsideAction={toggleIsOpen}
|
||||
maxHeight={isHugeMobile() ? 158 : 382}
|
||||
directionY={dropdownDirection}
|
||||
marginTop={dropdownDirection === "bottom" ? "4px" : "-36px"}
|
||||
>
|
||||
{thirdparties.map((thirdparty) => (
|
||||
<DropDownItem
|
||||
className="dropdown-item"
|
||||
label={thirdparty.title}
|
||||
key={thirdparty.id}
|
||||
height={32}
|
||||
heightTablet={32}
|
||||
onClick={() => setStorageLocaiton(thirdparty)}
|
||||
/>
|
||||
))}
|
||||
</StyledDropDown>
|
||||
</StyledDropDownWrapper>
|
||||
</StyledStorageLocation>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThirpartyComboBox;
|
@ -0,0 +1,117 @@
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { StyledParam } from "../Params/StyledParam";
|
||||
|
||||
import ToggleParam from "../Params/ToggleParam";
|
||||
import ThirpartyComboBox from "./ThirpartyComboBox";
|
||||
|
||||
import Toast from "@docspace/components/toast";
|
||||
import toastrHelper from "@docspace/client/src/helpers/toastr";
|
||||
import FolderInput from "./FolderInput";
|
||||
|
||||
const StyledThirdPartyStorage = styled(StyledParam)`
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
`;
|
||||
|
||||
const ThirdPartyStorage = ({
|
||||
t,
|
||||
connectItems,
|
||||
setConnectDialogVisible,
|
||||
setRoomCreation,
|
||||
saveThirdpartyResponse,
|
||||
openConnectWindow,
|
||||
setConnectItem,
|
||||
getOAuthToken,
|
||||
isThirdparty,
|
||||
onChangeIsThirdparty,
|
||||
storageLocation,
|
||||
setChangeStorageLocation,
|
||||
rememberThirdpartyStorage,
|
||||
onChangeRememberThirdpartyStorage,
|
||||
setIsScrollLocked,
|
||||
setIsOauthWindowOpen,
|
||||
}) => {
|
||||
const checkForProviders = () => {
|
||||
if (connectItems.length) onChangeIsThirdparty();
|
||||
else
|
||||
toastrHelper.warning(
|
||||
<div>
|
||||
<div>{t("ThirdPartyStorageNoStorageAlert")}</div>
|
||||
<a href="#">Third-party services</a>
|
||||
</div>,
|
||||
"Alert",
|
||||
5000,
|
||||
true,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
const onChangeProvider = (provider) =>
|
||||
setChangeStorageLocation({ ...storageLocation, provider });
|
||||
|
||||
const onChangeFolderPath = (e) =>
|
||||
setChangeStorageLocation({
|
||||
...storageLocation,
|
||||
storageFolderPath: e.target.value,
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledThirdPartyStorage>
|
||||
{/* <div className="set_room_params-info">
|
||||
<div className="set_room_params-info-title">
|
||||
<Text className="set_room_params-info-title-text">
|
||||
{t("ThirdPartyStorageTitle")}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="set_room_params-info-description">
|
||||
{t("ThirdPartyStorageDescription")}
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<ToggleParam
|
||||
title={t("ThirdPartyStorageTitle")}
|
||||
description={t("ThirdPartyStorageDescription")}
|
||||
isChecked={isThirdparty}
|
||||
onCheckedChange={checkForProviders}
|
||||
/>
|
||||
|
||||
{isThirdparty && (
|
||||
<ThirpartyComboBox
|
||||
t={t}
|
||||
connectItems={connectItems}
|
||||
setConnectDialogVisible={setConnectDialogVisible}
|
||||
setRoomCreation={setRoomCreation}
|
||||
saveThirdpartyResponse={saveThirdpartyResponse}
|
||||
openConnectWindow={openConnectWindow}
|
||||
setConnectItem={setConnectItem}
|
||||
getOAuthToken={getOAuthToken}
|
||||
storageLocation={storageLocation}
|
||||
onChangeProvider={onChangeProvider}
|
||||
setChangeStorageLocation={setChangeStorageLocation}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
setIsOauthWindowOpen={setIsOauthWindowOpen}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* {isThirdparty && storageLocation.isConnected && (
|
||||
<FolderInput
|
||||
value={storageLocation.storageFolderPath}
|
||||
onChangeFolderPath={onChangeFolderPath}
|
||||
/>
|
||||
)} */}
|
||||
|
||||
{/* {isThirdparty && storageLocation.isConnected && (
|
||||
<Checkbox
|
||||
className="thirdparty-checkbox"
|
||||
label={t("ThirdPartyStorageRememberChoice")}
|
||||
isChecked={rememberThirdpartyStorage}
|
||||
onChange={onChangeRememberThirdpartyStorage}
|
||||
/>
|
||||
)} */}
|
||||
</StyledThirdPartyStorage>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThirdPartyStorage;
|
@ -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
|
||||
|
@ -20,6 +20,8 @@ import ChangeUserTypeDialog from "./ChangeUserTypeDialog";
|
||||
import DataLossWarningDialog from "./DataLossWarningDialog";
|
||||
import ResetApplicationDialog from "./ResetApplicationDialog";
|
||||
import BackupCodesDialog from "./BackupCodesDialog";
|
||||
import CreateRoomDialog from "./CreateEditRoomDialog/CreateRoomDialog";
|
||||
import EditRoomDialog from "./CreateEditRoomDialog/EditRoomDialog";
|
||||
|
||||
export {
|
||||
EmptyTrashDialog,
|
||||
@ -44,4 +46,6 @@ export {
|
||||
DataLossWarningDialog,
|
||||
ResetApplicationDialog,
|
||||
BackupCodesDialog,
|
||||
CreateRoomDialog,
|
||||
EditRoomDialog,
|
||||
};
|
||||
|
@ -24,6 +24,8 @@ class SelectFolderInput extends React.PureComponent {
|
||||
resultingFolderTree: [],
|
||||
baseId: "",
|
||||
};
|
||||
|
||||
this._isMount = false;
|
||||
}
|
||||
|
||||
setBaseInfo = async () => {
|
||||
@ -52,20 +54,22 @@ class SelectFolderInput extends React.PureComponent {
|
||||
);
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
this._isMount &&
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isPathError: false,
|
||||
resultingFolderTree,
|
||||
baseId: resultingId,
|
||||
baseFolderPath: "",
|
||||
newFolderPath: "",
|
||||
...(withoutBasicSelection && { isLoading: false }),
|
||||
});
|
||||
this._isMount &&
|
||||
this.setState({
|
||||
isPathError: false,
|
||||
resultingFolderTree,
|
||||
baseId: resultingId,
|
||||
baseFolderPath: "",
|
||||
newFolderPath: "",
|
||||
...(withoutBasicSelection && { isLoading: false }),
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -140,7 +144,7 @@ class SelectFolderInput extends React.PureComponent {
|
||||
};
|
||||
onSetNewFolderPath = async (folderId) => {
|
||||
let timerId = setTimeout(() => {
|
||||
this.setState({ isLoading: true });
|
||||
this._isMount && this.setState({ isLoading: true });
|
||||
}, 500);
|
||||
try {
|
||||
const convertFoldersArray = await this.setFolderPath(folderId);
|
||||
@ -157,10 +161,11 @@ class SelectFolderInput extends React.PureComponent {
|
||||
toastr.error(e);
|
||||
clearTimeout(timerId);
|
||||
timerId = null;
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isPathError: true,
|
||||
});
|
||||
this._isMount &&
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isPathError: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
onSetBaseFolderPath = async (folderId) => {
|
||||
@ -175,14 +180,16 @@ class SelectFolderInput extends React.PureComponent {
|
||||
});
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isPathError: true,
|
||||
});
|
||||
this._isMount &&
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
isPathError: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
onSelectFolder = (folderId) => {
|
||||
const { onSelectFolder: onSelect } = this.props;
|
||||
if (!this._isMount) return;
|
||||
this.onSetFolderInfo(folderId);
|
||||
onSelect && onSelect(folderId);
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ export const Events = Object.freeze({
|
||||
CREATE: "create",
|
||||
RENAME: "rename",
|
||||
ROOM_CREATE: "create_room",
|
||||
ROOM_EDIT: "edit_room",
|
||||
});
|
||||
|
||||
export const FilterGroups = Object.freeze({
|
||||
|
@ -205,3 +205,29 @@ export const connectedCloudsTypeTitleTranslation = (key, t) => {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
export const connectedCloudsTypeIcon = (key) => {
|
||||
switch (key) {
|
||||
case "GoogleDrive":
|
||||
return "/static/images/cloud.services.google.drive.react.svg";
|
||||
case "Box":
|
||||
return "/static/images/cloud.services.box.react.svg";
|
||||
case "DropboxV2":
|
||||
return "/static/images/cloud.services.dropbox.react.svg";
|
||||
case "OneDrive":
|
||||
return "/static/images/cloud.services.onedrive.react.svg";
|
||||
case "SharePoint":
|
||||
return "/static/images/cloud.services.onedrive.react.svg";
|
||||
case "kDrive":
|
||||
return "/static/images/cloud.services.kdrive.react.svg";
|
||||
case "Yandex":
|
||||
return "/static/images/cloud.services.yandex.react.svg";
|
||||
case "NextCloud":
|
||||
return "/static/images/cloud.services.nextcloud.react.svg";
|
||||
case "OwnCloud":
|
||||
return "/static/images/catalog.folder.react.svg";
|
||||
case "WebDav":
|
||||
return "/static/images/cloud.services.webdav.react.svg";
|
||||
default:
|
||||
}
|
||||
};
|
||||
|
@ -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} />
|
||||
))}
|
||||
@ -51,8 +51,8 @@ const SectionBodyContent = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore }) => ({
|
||||
oformFiles: filesStore.oformFiles,
|
||||
hasGalleryFiles: filesStore.hasGalleryFiles,
|
||||
setGallerySelected: filesStore.setGallerySelected,
|
||||
export default inject(({ oformsStore }) => ({
|
||||
oformFiles: oformsStore.oformFiles,
|
||||
hasGalleryFiles: oformsStore.hasGalleryFiles,
|
||||
setGallerySelected: oformsStore.setGallerySelected,
|
||||
}))(withTranslation("FormGallery")(observer(SectionBodyContent)));
|
||||
|
@ -71,9 +71,10 @@ const SectionHeaderContent = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, filesStore }) => {
|
||||
export default inject(({ auth, filesStore, oformsStore }) => {
|
||||
const { toggleIsVisible, isVisible } = auth.infoPanelStore;
|
||||
const { setGallerySelected, categoryType } = filesStore;
|
||||
const { categoryType } = filesStore;
|
||||
const { setGallerySelected } = oformsStore;
|
||||
return {
|
||||
toggleInfoPanel: toggleIsVisible,
|
||||
isInfoPanelVisible: isVisible,
|
||||
|
@ -210,7 +210,6 @@ const SimpleFilesTileContent = styled(TileContent)`
|
||||
|
||||
const paddingCss = css`
|
||||
@media ${desktop} {
|
||||
margin-left: 1px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
@ -236,6 +235,7 @@ const StyledGridWrapper = styled.div`
|
||||
|
||||
const StyledTileContainer = styled.div`
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
.tile-item-wrapper {
|
||||
position: relative;
|
||||
@ -386,6 +386,25 @@ const MainContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
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};
|
||||
`;
|
||||
|
||||
export {
|
||||
StyledTile,
|
||||
StyledFileTileTop,
|
||||
@ -399,4 +418,6 @@ export {
|
||||
StyledTileContent,
|
||||
MainContainerWrapper,
|
||||
MainContainer,
|
||||
StyledCard,
|
||||
StyledItem,
|
||||
};
|
||||
|
@ -0,0 +1,129 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import InfiniteLoaderComponent from "@docspace/components/infinite-loader";
|
||||
import { StyledCard, StyledItem } from "../StyledTileView";
|
||||
import Loaders from "@docspace/common/components/Loaders";
|
||||
import uniqueid from "lodash/uniqueId";
|
||||
|
||||
const Card = ({ children, ...rest }) => {
|
||||
const horizontalGap = 16;
|
||||
const fileHeight = 220 + horizontalGap;
|
||||
const cardHeight = fileHeight;
|
||||
|
||||
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,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const countTilesInRow = getCountTilesInRow();
|
||||
|
||||
let cards = [];
|
||||
const list = [];
|
||||
|
||||
const addItemToList = (key, clear) => {
|
||||
list.push(
|
||||
<Item key={key} className="isFile">
|
||||
{cards}
|
||||
</Item>
|
||||
);
|
||||
if (clear) cards = [];
|
||||
};
|
||||
|
||||
React.Children.map(children, (child) => {
|
||||
if (child) {
|
||||
if (cards.length && cards.length === countTilesInRow) {
|
||||
const listKey = uniqueid("list-item_");
|
||||
addItemToList(listKey, true);
|
||||
}
|
||||
|
||||
const cardKey = uniqueid("card-item_");
|
||||
cards.push(<Card key={cardKey}>{child}</Card>);
|
||||
}
|
||||
});
|
||||
|
||||
if (hasMoreFiles) {
|
||||
// If cards elements are full, it will add the full line of loaders
|
||||
if (cards.length === countTilesInRow) {
|
||||
addItemToList("loaded-row", 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 isFile"}
|
||||
isFolder={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
addItemToList("loaded-row");
|
||||
} else if (cards.length) {
|
||||
// Adds loaders until the row is full
|
||||
const listKey = uniqueid("list-item_");
|
||||
addItemToList(listKey);
|
||||
}
|
||||
|
||||
// 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}`}
|
||||
{...rest}
|
||||
>
|
||||
{list}
|
||||
</InfiniteLoaderComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore, oformsStore }) => {
|
||||
const {
|
||||
oformFiles,
|
||||
hasMoreForms,
|
||||
oformsFilterTotal,
|
||||
loadMoreForms,
|
||||
} = oformsStore;
|
||||
|
||||
const { getCountTilesInRow } = filesStore;
|
||||
|
||||
const filesLength = oformFiles.length;
|
||||
|
||||
return {
|
||||
filesList: oformFiles,
|
||||
hasMoreFiles: hasMoreForms,
|
||||
filterTotal: oformsFilterTotal,
|
||||
fetchMoreFiles: loadMoreForms,
|
||||
filesLength,
|
||||
getCountTilesInRow,
|
||||
};
|
||||
})(observer(InfiniteGrid));
|
@ -210,19 +210,22 @@ Tile.defaultProps = {
|
||||
item: {},
|
||||
};
|
||||
|
||||
export default inject(({ filesStore, settingsStore, auth }, { item }) => {
|
||||
const { gallerySelected, setGallerySelected, categoryType } = filesStore;
|
||||
const { getIcon } = settingsStore;
|
||||
const { isVisible, setIsVisible } = auth.infoPanelStore;
|
||||
export default inject(
|
||||
({ filesStore, settingsStore, auth, oformsStore }, { item }) => {
|
||||
const { categoryType } = filesStore;
|
||||
const { gallerySelected, setGallerySelected } = oformsStore;
|
||||
const { getIcon } = settingsStore;
|
||||
const { isVisible, setIsVisible } = auth.infoPanelStore;
|
||||
|
||||
const isSelected = item.id === gallerySelected?.id;
|
||||
const isSelected = item.id === gallerySelected?.id;
|
||||
|
||||
return {
|
||||
isSelected,
|
||||
setGallerySelected,
|
||||
getIcon,
|
||||
setIsInfoPanelVisible: setIsVisible,
|
||||
isInfoPanelVisible: isVisible,
|
||||
categoryType,
|
||||
};
|
||||
})(withTranslation(["FormGallery", "Common"])(withRouter(observer(Tile))));
|
||||
return {
|
||||
isSelected,
|
||||
setGallerySelected,
|
||||
getIcon,
|
||||
setIsInfoPanelVisible: setIsVisible,
|
||||
isInfoPanelVisible: isVisible,
|
||||
categoryType,
|
||||
};
|
||||
}
|
||||
)(withTranslation(["FormGallery", "Common"])(withRouter(observer(Tile))));
|
||||
|
@ -1,49 +1,17 @@
|
||||
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";
|
||||
import InfiniteGrid from "./InfiniteGrid";
|
||||
|
||||
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, useReactWindow, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<StyledTileContainer
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
useReactWindow={useReactWindow}
|
||||
>
|
||||
<StyledTileContainer {...rest}>
|
||||
{useReactWindow ? (
|
||||
<AutoSizer>{renderList}</AutoSizer>
|
||||
<InfiniteGrid>{children}</InfiniteGrid>
|
||||
) : (
|
||||
<StyledGridWrapper>{children}</StyledGridWrapper>
|
||||
)}
|
||||
@ -53,19 +21,15 @@ class TileContainer extends React.PureComponent {
|
||||
}
|
||||
|
||||
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",
|
||||
id: "tileContainer",
|
||||
};
|
||||
|
||||
export default withTranslation(["Files", "Common"])(TileContainer);
|
||||
|
@ -1,16 +1,26 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import Section from "@docspace/common/components/Section";
|
||||
import { observer, inject } from "mobx-react";
|
||||
|
||||
import SectionHeaderContent from "./Header";
|
||||
import SectionBodyContent from "./Body";
|
||||
import { InfoPanelBodyContent } from "../Home/InfoPanel";
|
||||
import InfoPanelHeaderContent from "../Home/InfoPanel/Header";
|
||||
|
||||
const FormGallery = () => {
|
||||
const FormGallery = ({ getOforms, setOformFiles }) => {
|
||||
useEffect(() => {
|
||||
getOforms();
|
||||
|
||||
return () => {
|
||||
setOformFiles(null);
|
||||
};
|
||||
}, [getOforms, setOformFiles]);
|
||||
|
||||
return (
|
||||
<Section
|
||||
// withBodyScroll
|
||||
// withBodyAutoFocus={!isMobile}
|
||||
// withBodyScroll
|
||||
// withBodyAutoFocus={!isMobile}
|
||||
withPaging={false}
|
||||
>
|
||||
<Section.SectionHeader>
|
||||
<SectionHeaderContent />
|
||||
@ -28,4 +38,11 @@ const FormGallery = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default FormGallery;
|
||||
export default inject(({ oformsStore }) => {
|
||||
const { getOforms, setOformFiles } = oformsStore;
|
||||
|
||||
return {
|
||||
getOforms,
|
||||
setOformFiles,
|
||||
};
|
||||
})(observer(FormGallery));
|
||||
|
@ -55,7 +55,9 @@ const SingleItem = (props) => {
|
||||
|
||||
<StyledProperties>
|
||||
<div className="property">
|
||||
<Text className="property-title">{t("Home:ByLastModifiedDate")}</Text>
|
||||
<Text className="property-title">
|
||||
{t("Files:ByLastModifiedDate")}
|
||||
</Text>
|
||||
<Text className="property-content">
|
||||
{parseAndFormatDate(selectedItem.attributes.updatedAt)}
|
||||
</Text>
|
||||
|
@ -147,7 +147,7 @@ const SingleItem = (props) => {
|
||||
},
|
||||
{
|
||||
id: "ByLastModifiedDate",
|
||||
title: t("Home:ByLastModifiedDate"),
|
||||
title: t("Files:ByLastModifiedDate"),
|
||||
content: styledText(parseAndFormatDate(item.updated)),
|
||||
},
|
||||
{
|
||||
|
@ -118,6 +118,7 @@ export default inject(
|
||||
dialogsStore,
|
||||
treeFoldersStore,
|
||||
selectedFolderStore,
|
||||
oformsStore,
|
||||
}) => {
|
||||
const { personal, culture } = auth.settingsStore;
|
||||
|
||||
@ -126,9 +127,9 @@ export default inject(
|
||||
bufferSelection,
|
||||
getFolderInfo,
|
||||
getShareUsers,
|
||||
gallerySelected,
|
||||
createThumbnail,
|
||||
} = filesStore;
|
||||
const { gallerySelected } = oformsStore;
|
||||
const { getIcon, getFolderIcon } = settingsStore;
|
||||
const { onSelectItem } = filesActionsStore;
|
||||
const { setSharingPanelVisible } = dialogsStore;
|
||||
|
@ -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));
|
||||
|
@ -12,6 +12,7 @@ import RowContent from "@docspace/components/row-content";
|
||||
import withContent from "../../../../../HOCs/withContent";
|
||||
import withBadges from "../../../../../HOCs/withBadges";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
import { RoomsTypeTranslations } from "@docspace/common/constants";
|
||||
|
||||
const SimpleFilesRowContent = styled(RowContent)`
|
||||
.row-main-container-wrapper {
|
||||
@ -105,7 +106,7 @@ const FilesRowContent = ({
|
||||
if (item.tags.length > 0) {
|
||||
tags = item?.tags.join(" | ");
|
||||
} else {
|
||||
tags = t("NoTag");
|
||||
tags = t(RoomsTypeTranslations[item.roomType]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ const SimpleFilesRow = (props) => {
|
||||
const element = (
|
||||
<ItemIcon
|
||||
id={item.id}
|
||||
icon={item.icon}
|
||||
icon={item.isRoom && item.logo.big ? item.logo.big : item.icon}
|
||||
fileExst={item.fileExst}
|
||||
isRoom={item.isRoom}
|
||||
/>
|
||||
|
@ -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,42 @@ 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;
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -296,13 +296,14 @@ const FilesTableRow = (props) => {
|
||||
id,
|
||||
tagRef,
|
||||
isRooms,
|
||||
onSelectType,
|
||||
} = props;
|
||||
const { acceptBackground, background } = theme.dragAndDrop;
|
||||
|
||||
const element = (
|
||||
<ItemIcon
|
||||
id={item.id}
|
||||
icon={item.icon}
|
||||
icon={item.isRoom && item.logo.big ? item.logo.big : item.icon}
|
||||
fileExst={item.fileExst}
|
||||
isRoom={item.isRoom}
|
||||
/>
|
||||
|
@ -3,21 +3,28 @@ import React from "react";
|
||||
import Tags from "@docspace/common/components/Tags";
|
||||
|
||||
import Tag from "@docspace/components/tag";
|
||||
import { RoomsTypeTranslations } from "@docspace/common/constants";
|
||||
|
||||
const TagsCell = React.forwardRef(({ t, item, tagCount, onSelectTag }, ref) => {
|
||||
return (
|
||||
<div style={{ width: "100%", overflow: "hidden" }} ref={ref}>
|
||||
{item.tags.length > 0 ? (
|
||||
<Tags
|
||||
tags={item.tags}
|
||||
columnCount={tagCount}
|
||||
onSelectTag={onSelectTag}
|
||||
/>
|
||||
) : (
|
||||
<Tag label={t("NoTag")} onClick={onSelectTag} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const TagsCell = React.forwardRef(
|
||||
({ t, item, tagCount, onSelectTag, onSelectType }, ref) => {
|
||||
return (
|
||||
<div style={{ width: "100%", overflow: "hidden" }} ref={ref}>
|
||||
{item.tags.length > 0 ? (
|
||||
<Tags
|
||||
tags={item.tags}
|
||||
columnCount={tagCount}
|
||||
onSelectTag={onSelectTag}
|
||||
/>
|
||||
) : (
|
||||
<Tag
|
||||
isDefault
|
||||
label={t(RoomsTypeTranslations[item.roomType])}
|
||||
onClick={() => onSelectType(item.roomType)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default React.memo(TagsCell);
|
||||
|
@ -43,6 +43,7 @@ const FileTile = (props) => {
|
||||
setSelection,
|
||||
id,
|
||||
onSelectTag,
|
||||
onSelectType,
|
||||
columnCount,
|
||||
} = props;
|
||||
|
||||
@ -61,7 +62,7 @@ const FileTile = (props) => {
|
||||
const element = (
|
||||
<ItemIcon
|
||||
id={item.id}
|
||||
icon={item.icon}
|
||||
icon={item.isRoom && item.logo.big ? item.logo.big : item.icon}
|
||||
fileExst={item.fileExst}
|
||||
isRoom={item.isRoom}
|
||||
/>
|
||||
@ -110,6 +111,7 @@ const FileTile = (props) => {
|
||||
showHotkeyBorder={showHotkeyBorder}
|
||||
setSelection={setSelection}
|
||||
selectTag={onSelectTag}
|
||||
selectType={onSelectType}
|
||||
columnCount={columnCount}
|
||||
>
|
||||
<FilesTileContent
|
||||
|
@ -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,204 @@
|
||||
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;
|
||||
|
||||
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 };
|
@ -13,6 +13,7 @@ import Loader from "@docspace/components/loader";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
import Tags from "@docspace/common/components/Tags";
|
||||
import Tag from "@docspace/components/tag";
|
||||
import { RoomsTypeTranslations } from "@docspace/common/constants";
|
||||
|
||||
const svgLoader = () => <div style={{ width: "96px" }} />;
|
||||
|
||||
@ -466,6 +467,7 @@ class Tile extends React.PureComponent {
|
||||
t,
|
||||
columnCount,
|
||||
selectTag,
|
||||
selectType,
|
||||
} = this.props;
|
||||
const { isFolder, isRoom, id, fileExst } = item;
|
||||
|
||||
@ -596,7 +598,11 @@ class Tile extends React.PureComponent {
|
||||
tags={item.tags}
|
||||
/>
|
||||
) : (
|
||||
<Tag label={t("NoTag")} onClick={selectTag} />
|
||||
<Tag
|
||||
isDefault
|
||||
label={t(RoomsTypeTranslations[item.roomType])}
|
||||
onClick={() => selectType(item.roomType)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
@ -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)));
|
||||
|
@ -370,13 +370,13 @@ const SectionFilterContent = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (roomsFilter.withoutTags) {
|
||||
filterValues.push({
|
||||
key: [t("NoTag")],
|
||||
group: FilterGroups.roomFilterTags,
|
||||
isMultiSelect: true,
|
||||
});
|
||||
}
|
||||
// if (roomsFilter.withoutTags) {
|
||||
// filterValues.push({
|
||||
// key: [t("NoTag")],
|
||||
// group: FilterGroups.roomFilterTags,
|
||||
// isMultiSelect: true,
|
||||
// });
|
||||
// }
|
||||
|
||||
if (roomsFilter?.tags?.length > 0) {
|
||||
filterValues.push({
|
||||
@ -654,12 +654,12 @@ const SectionFilterContent = ({
|
||||
isMultiSelect: true,
|
||||
}));
|
||||
|
||||
tagsOptions.push({
|
||||
key: t("NoTag"),
|
||||
group: FilterGroups.roomFilterTags,
|
||||
label: t("NoTag"),
|
||||
isMultiSelect: true,
|
||||
});
|
||||
// tagsOptions.push({
|
||||
// key: t("NoTag"),
|
||||
// group: FilterGroups.roomFilterTags,
|
||||
// label: t("NoTag"),
|
||||
// isMultiSelect: true,
|
||||
// });
|
||||
|
||||
filterOptions.push({
|
||||
key: FilterGroups.roomFilterTags,
|
||||
|
@ -72,7 +72,6 @@ class SectionHeaderContent extends React.Component {
|
||||
|
||||
onCreateRoom = () => {
|
||||
const event = new Event(Events.ROOM_CREATE);
|
||||
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
@ -567,6 +566,7 @@ export default inject(
|
||||
setEmptyTrashDialogVisible,
|
||||
setSelectFileDialogVisible,
|
||||
setIsFolderActions,
|
||||
setCreateRoomDialogVisible,
|
||||
} = dialogsStore;
|
||||
|
||||
const {
|
||||
@ -636,6 +636,7 @@ export default inject(
|
||||
backToParentFolder,
|
||||
getCheckboxItemLabel,
|
||||
setSelectFileDialogVisible,
|
||||
setCreateRoomDialogVisible,
|
||||
|
||||
isRecycleBinFolder,
|
||||
setEmptyTrashDialogVisible,
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
@ -601,6 +605,7 @@ export default inject(
|
||||
mediaViewerDataStore,
|
||||
settingsStore,
|
||||
filesActionsStore,
|
||||
oformsStore,
|
||||
}) => {
|
||||
const {
|
||||
secondaryProgressDataStore,
|
||||
@ -622,7 +627,6 @@ export default inject(
|
||||
isLoading,
|
||||
viewAs,
|
||||
getFileInfo,
|
||||
gallerySelected,
|
||||
setIsUpdatingRowItem,
|
||||
|
||||
folders,
|
||||
@ -634,8 +638,11 @@ export default inject(
|
||||
createRoom,
|
||||
refreshFiles,
|
||||
setViewAs,
|
||||
withPaging,
|
||||
} = filesStore;
|
||||
|
||||
const { gallerySelected } = oformsStore;
|
||||
|
||||
const {
|
||||
isRecycleBinFolder,
|
||||
isPrivacyFolder,
|
||||
@ -770,6 +777,7 @@ export default inject(
|
||||
createRoom,
|
||||
refreshFiles,
|
||||
setViewAs,
|
||||
withPaging,
|
||||
};
|
||||
}
|
||||
)(withRouter(observer(Home)));
|
||||
|
@ -11,7 +11,7 @@ import ThirdPartyModule from "./sub-components/ThirdPartyModule";
|
||||
import RoomsModule from "./sub-components/RoomsModule";
|
||||
import ThirdPartyStorageModule from "./sub-components/ThirdPartyStorageModule";
|
||||
import { StyledModules, StyledManualBackup } from "./../StyledBackup";
|
||||
import { getFromLocalStorage } from "../../../../utils";
|
||||
import { getFromLocalStorage, saveToLocalStorage } from "../../../../utils";
|
||||
//import { getThirdPartyCommonFolderTree } from "@docspace/common/api/files";
|
||||
import DataBackupLoader from "@docspace/common/components/Loaders/DataBackupLoader";
|
||||
import {
|
||||
|
@ -22,12 +22,21 @@ class RoomsModule extends React.Component {
|
||||
selectedFolder: selectedFolder,
|
||||
isPanelVisible: false,
|
||||
};
|
||||
|
||||
this._isMount = false;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMount = true;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._isMount = false;
|
||||
}
|
||||
onSelectFolder = (folderId) => {
|
||||
this.setState({
|
||||
selectedFolder: folderId,
|
||||
});
|
||||
this._isMount &&
|
||||
this.setState({
|
||||
selectedFolder: folderId,
|
||||
});
|
||||
};
|
||||
|
||||
onClickInput = () => {
|
||||
|
@ -26,6 +26,15 @@ class ThirdPartyModule extends React.Component {
|
||||
isError: false,
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
this._isMount = false;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMount = true;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._isMount = false;
|
||||
}
|
||||
|
||||
onSetLoadingData = (isLoading) => {
|
||||
@ -37,9 +46,10 @@ class ThirdPartyModule extends React.Component {
|
||||
};
|
||||
|
||||
onSelectFolder = (folderId) => {
|
||||
this.setState({
|
||||
selectedFolder: folderId,
|
||||
});
|
||||
this._isMount &&
|
||||
this.setState({
|
||||
selectedFolder: folderId,
|
||||
});
|
||||
};
|
||||
|
||||
onClickInput = () => {
|
||||
|
@ -342,8 +342,10 @@ class ContextOptionsStore {
|
||||
setIsVisible(true);
|
||||
};
|
||||
|
||||
onClickEditRoom = () => {
|
||||
console.log("edit room");
|
||||
onClickEditRoom = (item) => {
|
||||
const event = new Event(Events.ROOM_EDIT);
|
||||
event.item = item;
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
onClickInviteUsers = () => {
|
||||
|
@ -27,6 +27,7 @@ class DialogsStore {
|
||||
selectFileDialogVisible = false;
|
||||
convertPasswordDialogVisible = false;
|
||||
isFolderActions = false;
|
||||
roomCreation = false;
|
||||
|
||||
removeItem = null;
|
||||
connectItem = null;
|
||||
@ -39,6 +40,7 @@ class DialogsStore {
|
||||
unsubscribe = null;
|
||||
convertItem = null;
|
||||
formCreationInfo = null;
|
||||
saveThirdpartyResponse = null;
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
@ -78,10 +80,18 @@ class DialogsStore {
|
||||
this.copyPanelVisible = copyPanelVisible;
|
||||
};
|
||||
|
||||
setRoomCreation = (roomCreation) => {
|
||||
this.roomCreation = roomCreation;
|
||||
};
|
||||
|
||||
setSaveThirdpartyResponse = (saveThirdpartyResponse) => {
|
||||
this.saveThirdpartyResponse = saveThirdpartyResponse;
|
||||
};
|
||||
|
||||
setConnectDialogVisible = (connectDialogVisible) => {
|
||||
if (!connectDialogVisible) this.setConnectItem(null);
|
||||
|
||||
this.connectDialogVisible = connectDialogVisible;
|
||||
if (!this.connectDialogVisible) this.setRoomCreation(false);
|
||||
};
|
||||
|
||||
setRemoveItem = (removeItem) => {
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -915,6 +928,19 @@ class FilesActionStore {
|
||||
fetchRooms(id, newFilter).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
selectType = (type) => {
|
||||
const { roomsFilter, fetchRooms, setIsLoading } = this.filesStore;
|
||||
const { id } = this.selectedFolderStore;
|
||||
|
||||
const newFilter = roomsFilter.clone();
|
||||
const tags = newFilter.tags ? [...newFilter.tags] : [];
|
||||
newFilter.tags = [...tags];
|
||||
newFilter.type = type;
|
||||
|
||||
setIsLoading(true);
|
||||
fetchRooms(id, newFilter).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
selectRowAction = (checked, file) => {
|
||||
const {
|
||||
selected,
|
||||
|
@ -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";
|
||||
@ -83,8 +84,6 @@ class FilesStore {
|
||||
headerBorder = false;
|
||||
|
||||
enabledHotkeys = true;
|
||||
oformFiles = null;
|
||||
gallerySelected = null;
|
||||
|
||||
createdItem = null;
|
||||
scrollToItem = null;
|
||||
@ -93,6 +92,8 @@ class FilesStore {
|
||||
pageItemsLength = null;
|
||||
isHidePagination = false;
|
||||
trashIsEmpty = false;
|
||||
filesIsLoading = false;
|
||||
withPaging = false;
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
@ -132,7 +133,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 +182,10 @@ class FilesStore {
|
||||
})
|
||||
);
|
||||
|
||||
const newFilter = this.filter.clone();
|
||||
newFilter.total -= 1;
|
||||
this.setFilter(newFilter);
|
||||
|
||||
// Hide pagination when deleting files
|
||||
runInAction(() => {
|
||||
this.isHidePagination = true;
|
||||
@ -383,28 +388,11 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
requests.push(getFilesSettings());
|
||||
requests.push(this.getOforms());
|
||||
requests.push(this.getIsEmptyTrash());
|
||||
|
||||
return Promise.all(requests).then(() => (this.isInit = true));
|
||||
};
|
||||
|
||||
getOforms = async () => {
|
||||
const oformData = await this.authStore.getOforms();
|
||||
|
||||
runInAction(() => {
|
||||
this.oformFiles = oformData?.data?.data ? oformData.data.data : [];
|
||||
});
|
||||
};
|
||||
|
||||
get hasGalleryFiles() {
|
||||
return this.oformFiles && !!this.oformFiles.length;
|
||||
}
|
||||
|
||||
setGallerySelected = (gallerySelected) => {
|
||||
this.gallerySelected = gallerySelected;
|
||||
};
|
||||
|
||||
setFirstLoad = (firstLoad) => {
|
||||
this.firstLoad = firstLoad;
|
||||
};
|
||||
@ -524,9 +512,7 @@ class FilesStore {
|
||||
};
|
||||
|
||||
setHotkeyCaret = (hotkeyCaret) => {
|
||||
if (hotkeyCaret) {
|
||||
this.hotkeyCaret = hotkeyCaret;
|
||||
} else if (this.hotkeyCaret) {
|
||||
if (hotkeyCaret || this.hotkeyCaret) {
|
||||
this.hotkeyCaret = hotkeyCaret;
|
||||
}
|
||||
};
|
||||
@ -570,6 +556,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 +575,7 @@ class FilesStore {
|
||||
};
|
||||
|
||||
setFilter = (filter) => {
|
||||
if (!this.withPaging) filter.pageCount = 100;
|
||||
this.filter = filter;
|
||||
};
|
||||
|
||||
@ -659,9 +648,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 +667,11 @@ class FilesStore {
|
||||
filterData.sortOrder = splitFilter[2];
|
||||
}
|
||||
|
||||
if (!this.withPaging) {
|
||||
filterData.page = 0;
|
||||
filterData.pageCount = 100;
|
||||
}
|
||||
|
||||
setSelectedNode([folderId + ""]);
|
||||
|
||||
//TODO: fix @my
|
||||
@ -841,6 +835,11 @@ class FilesStore {
|
||||
filterData.sortOrder = splitFilter[2];
|
||||
}
|
||||
|
||||
if (!this.withPaging) {
|
||||
filterData.page = 0;
|
||||
filterData.pageCount = 100;
|
||||
}
|
||||
|
||||
if (folderId) setSelectedNode([folderId + ""]);
|
||||
|
||||
const searchArea = folderId
|
||||
@ -1612,8 +1611,60 @@ class FilesStore {
|
||||
return api.files.createFolder(parentFolderId, title);
|
||||
}
|
||||
|
||||
createRoom(title, type) {
|
||||
return api.rooms.createRoom({ title, roomType: type });
|
||||
createRoom(roomParams) {
|
||||
return api.rooms.createRoom(roomParams);
|
||||
}
|
||||
|
||||
createRoomInThirdpary(thirpartyFolderId, roomParams) {
|
||||
console.log(thirpartyFolderId, roomParams);
|
||||
return api.rooms.createRoomInThirdpary(thirpartyFolderId, roomParams);
|
||||
}
|
||||
|
||||
editRoom(id, roomParams) {
|
||||
return api.rooms.editRoom(id, roomParams);
|
||||
}
|
||||
|
||||
addTagsToRoom(id, tagArray) {
|
||||
return api.rooms.addTagsToRoom(id, tagArray);
|
||||
}
|
||||
|
||||
removeTagsFromRoom(id, tagArray) {
|
||||
return api.rooms.removeTagsFromRoom(id, tagArray);
|
||||
}
|
||||
|
||||
calculateRoomLogoParams(img, x, y, zoom) {
|
||||
let imgWidth, imgHeight, dimensions;
|
||||
if (img.width > img.height) {
|
||||
imgWidth = Math.min(1280, img.width);
|
||||
imgHeight = Math.round(img.height / (img.width / imgWidth));
|
||||
dimensions = Math.round(imgHeight / zoom);
|
||||
} else {
|
||||
imgHeight = Math.min(1280, img.height);
|
||||
imgWidth = Math.round(img.width / (img.height / imgHeight));
|
||||
dimensions = Math.round(imgWidth / zoom);
|
||||
}
|
||||
|
||||
const croppedX = Math.round(x * imgWidth - dimensions / 2);
|
||||
const croppedY = Math.round(y * imgHeight - dimensions / 2);
|
||||
|
||||
return {
|
||||
x: croppedX,
|
||||
y: croppedY,
|
||||
width: dimensions,
|
||||
height: dimensions,
|
||||
};
|
||||
}
|
||||
|
||||
uploadRoomLogo(formData) {
|
||||
return api.rooms.uploadRoomLogo(formData);
|
||||
}
|
||||
|
||||
addLogoToRoom(id, icon) {
|
||||
return api.rooms.addLogoToRoom(id, icon);
|
||||
}
|
||||
|
||||
removeLogoFromRoom(id) {
|
||||
return api.rooms.removeLogoFromRoom(id);
|
||||
}
|
||||
|
||||
setFile = (file) => {
|
||||
@ -1655,6 +1706,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)
|
||||
@ -1856,6 +1964,7 @@ class FilesStore {
|
||||
folderId,
|
||||
foldersCount,
|
||||
id,
|
||||
logo,
|
||||
locked,
|
||||
parentId,
|
||||
pureContentLength,
|
||||
@ -1959,6 +2068,7 @@ class FilesStore {
|
||||
icon,
|
||||
id,
|
||||
isFolder,
|
||||
logo,
|
||||
locked,
|
||||
new: item.new,
|
||||
parentId,
|
||||
@ -2497,6 +2607,76 @@ class FilesStore {
|
||||
setTrashIsEmpty = (isEmpty) => {
|
||||
this.trashIsEmpty = isEmpty;
|
||||
};
|
||||
|
||||
get roomsFilterTotal() {
|
||||
return this.roomsFilter.total;
|
||||
}
|
||||
|
||||
get filterTotal() {
|
||||
return this.filter.total;
|
||||
}
|
||||
|
||||
get hasMoreFiles() {
|
||||
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
|
||||
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
|
||||
// const filterTotal = isRoom ? this.roomsFilterTotal : this.filterTotal;
|
||||
const filterTotal = isRooms ? this.roomsFilter.total : this.filter.total;
|
||||
|
||||
console.log("hasMoreFiles isRooms", isRooms);
|
||||
console.log("hasMoreFiles filesList", this.filesList.length);
|
||||
console.log("hasMoreFiles this.filterTotal", this.filterTotal);
|
||||
console.log("hasMoreFiles this.roomsFilterTotal", this.roomsFilterTotal);
|
||||
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 { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
|
||||
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
|
||||
this.setFilesIsLoading(true);
|
||||
// console.log("fetchMoreFiles");
|
||||
|
||||
const newFilter = isRooms ? this.roomsFilter.clone() : this.filter.clone();
|
||||
newFilter.page += 1;
|
||||
if (isRooms) this.setRoomsFilter(newFilter);
|
||||
else this.setFilter(newFilter);
|
||||
|
||||
const newFiles = isRooms
|
||||
? 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;
|
||||
|
90
packages/client/src/store/OformsStore.js
Normal file
90
packages/client/src/store/OformsStore.js
Normal file
@ -0,0 +1,90 @@
|
||||
import { makeAutoObservable, runInAction } from "mobx";
|
||||
import api from "@docspace/common/api";
|
||||
|
||||
const { OformsFilter } = api;
|
||||
|
||||
class OformsStore {
|
||||
authStore;
|
||||
|
||||
oformFiles = null;
|
||||
oformsFilter = OformsFilter.getDefault();
|
||||
gallerySelected = null;
|
||||
oformsIsLoading = false;
|
||||
|
||||
constructor(authStore) {
|
||||
makeAutoObservable(this);
|
||||
|
||||
this.authStore = authStore;
|
||||
}
|
||||
|
||||
getOforms = async (filter = OformsFilter.getDefault()) => {
|
||||
const oformData = await this.authStore.getOforms(filter);
|
||||
const oformsFilter = oformData?.data?.meta?.pagination;
|
||||
const newOformsFilter = this.oformsFilter.clone();
|
||||
|
||||
if (oformsFilter) {
|
||||
newOformsFilter.page = oformsFilter.page;
|
||||
newOformsFilter.total = oformsFilter.total;
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
this.setOformsFilter(newOformsFilter);
|
||||
this.setOformFiles(oformData?.data?.data ?? []);
|
||||
});
|
||||
};
|
||||
|
||||
setOformFiles = (oformFiles) => {
|
||||
this.oformFiles = oformFiles;
|
||||
};
|
||||
|
||||
setOformsFilter = (oformsFilter) => {
|
||||
this.oformsFilter = oformsFilter;
|
||||
};
|
||||
|
||||
setGallerySelected = (gallerySelected) => {
|
||||
this.gallerySelected = gallerySelected;
|
||||
};
|
||||
|
||||
setOformsIsLoading = (oformsIsLoading) => {
|
||||
this.oformsIsLoading = oformsIsLoading;
|
||||
};
|
||||
|
||||
loadMoreForms = async () => {
|
||||
if (!this.hasMoreForms || this.oformsIsLoading) return;
|
||||
|
||||
// console.log("loadMoreForms");
|
||||
|
||||
this.setOformsIsLoading(true);
|
||||
|
||||
const newOformsFilter = this.oformsFilter.clone();
|
||||
|
||||
newOformsFilter.page += 1;
|
||||
this.setOformsFilter(newOformsFilter);
|
||||
|
||||
const oformData = await this.authStore.getOforms(newOformsFilter);
|
||||
const newForms = oformData?.data?.data ?? [];
|
||||
|
||||
runInAction(() => {
|
||||
this.setOformFiles([...this.oformFiles, ...newForms]);
|
||||
this.setOformsIsLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
get hasGalleryFiles() {
|
||||
return this.oformFiles && !!this.oformFiles.length;
|
||||
}
|
||||
|
||||
// get oformFilesLength() {
|
||||
// return this.oformFiles.length;
|
||||
// }
|
||||
|
||||
get oformsFilterTotal() {
|
||||
return this.oformsFilter.total;
|
||||
}
|
||||
|
||||
get hasMoreForms() {
|
||||
return this.oformFiles.length < this.oformsFilterTotal;
|
||||
}
|
||||
}
|
||||
|
||||
export default OformsStore;
|
353
packages/client/src/store/RoomsStore.js
Normal file
353
packages/client/src/store/RoomsStore.js
Normal file
@ -0,0 +1,353 @@
|
||||
import { makeAutoObservable, runInAction } from "mobx";
|
||||
import api from "@appserver/common/api";
|
||||
import { AppServerConfig, RoomsType } from "@appserver/common/constants";
|
||||
|
||||
import toastr from "studio/toastr";
|
||||
|
||||
import history from "@appserver/common/history";
|
||||
|
||||
import { combineUrl } from "@appserver/common/utils";
|
||||
|
||||
import config from "../../package.json";
|
||||
|
||||
const { RoomsFilter } = api;
|
||||
|
||||
class RoomsStore {
|
||||
authStore;
|
||||
settingsStore;
|
||||
userStore;
|
||||
filesStore;
|
||||
selectedFolderStore;
|
||||
treeFoldersStore;
|
||||
filesSettingsStore;
|
||||
|
||||
rooms = [];
|
||||
tags = [];
|
||||
|
||||
selection = [];
|
||||
bufferSelection = [];
|
||||
|
||||
filter = RoomsFilter.getDefault();
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
settingsStore,
|
||||
userStore,
|
||||
filesStore,
|
||||
selectedFolderStore,
|
||||
treeFoldersStore,
|
||||
filesSettingsStore
|
||||
) {
|
||||
makeAutoObservable(this);
|
||||
|
||||
this.authStore = authStore;
|
||||
this.settingsStore = settingsStore;
|
||||
this.userStore = userStore;
|
||||
this.filesStore = filesStore;
|
||||
this.selectedFolderStore = selectedFolderStore;
|
||||
this.treeFoldersStore = treeFoldersStore;
|
||||
this.filesSettingsStore = filesSettingsStore;
|
||||
}
|
||||
|
||||
setRoom = (room) => {
|
||||
const idx = this.rooms.findIndex((item) => item.id === room.id);
|
||||
|
||||
this.rooms[idx] = room;
|
||||
};
|
||||
|
||||
setRooms = (rooms) => {
|
||||
this.rooms = rooms;
|
||||
};
|
||||
|
||||
setTags = (tags) => {
|
||||
this.tags = tags;
|
||||
};
|
||||
|
||||
setRoomsFilter = (filter) => {
|
||||
this.filter = filter;
|
||||
};
|
||||
|
||||
setFilterUrl = (filter) => {
|
||||
this.filter = filter;
|
||||
const urlFilter = filter.toUrlParams();
|
||||
history.push(
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/rooms?${urlFilter}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
setSelection = (items) => {
|
||||
this.selection = items;
|
||||
};
|
||||
|
||||
setBufferSelection = (item) => {
|
||||
this.bufferSelection = item;
|
||||
};
|
||||
|
||||
setHeaderVisible = (isHeaderVisible) => {
|
||||
this.isHeaderVisible = isHeaderVisible;
|
||||
};
|
||||
|
||||
sortRooms = (sortBy, sortOrder) => {
|
||||
const newFilter = this.filter.clone();
|
||||
|
||||
newFilter.page = 0;
|
||||
newFilter.sortBy = sortBy;
|
||||
newFilter.sortOrder = sortOrder;
|
||||
|
||||
return this.fetchRooms(newFilter.searchArea, newFilter);
|
||||
};
|
||||
|
||||
filterRooms = (types, subjectId, tags) => {
|
||||
const newFilter = this.filter.clone();
|
||||
|
||||
newFilter.page = 0;
|
||||
newFilter.types = types ? types : null;
|
||||
newFilter.subjectId = subjectId ? subjectId : null;
|
||||
newFilter.tags = tags ? tags : null;
|
||||
|
||||
return this.fetchRooms(newFilter.searchArea, newFilter);
|
||||
};
|
||||
|
||||
searchRooms = (filterValue) => {
|
||||
const newFilter = this.filter.clone();
|
||||
|
||||
newFilter.page = 0;
|
||||
newFilter.filterValue = filterValue;
|
||||
|
||||
return this.fetchRooms(newFilter.searchArea, newFilter);
|
||||
};
|
||||
|
||||
fetchRooms = (searchArea, filter) => {
|
||||
const { setSelectedNode } = this.treeFoldersStore;
|
||||
|
||||
const filterData = !!filter ? filter.clone() : RoomsFilter.getDefault();
|
||||
|
||||
if (searchArea && filterData.searchArea !== searchArea) {
|
||||
filterData.searchArea = searchArea;
|
||||
}
|
||||
|
||||
const request = () =>
|
||||
api.rooms
|
||||
.getRooms(filterData)
|
||||
.then(async (data) => {
|
||||
const folderId = data.current.id;
|
||||
|
||||
setSelectedNode([folderId + ""]);
|
||||
|
||||
filterData.total = data.total;
|
||||
|
||||
if (data.total > 0) {
|
||||
const lastPage = filterData.getLastPage();
|
||||
|
||||
if (filterData.page > lastPage) {
|
||||
filterData.page = lastPage;
|
||||
|
||||
return this.fetchFiles(searchArea, filterData);
|
||||
}
|
||||
}
|
||||
|
||||
this.setFilterUrl(filterData);
|
||||
|
||||
runInAction(() => {
|
||||
this.setRooms(data.folders);
|
||||
this.fetchTags();
|
||||
});
|
||||
|
||||
this.selectedFolderStore.setSelectedFolder({
|
||||
folders: data.folders,
|
||||
...data.current,
|
||||
pathParts: data.pathParts,
|
||||
navigationPath: [],
|
||||
...{ new: data.new },
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
toastr.error(err);
|
||||
});
|
||||
|
||||
return request();
|
||||
};
|
||||
|
||||
fetchRoomInfo = (id) => api.rooms.getRoom(id);
|
||||
|
||||
selectRoom = (checked, item) => {
|
||||
this.setBufferSelection(null);
|
||||
|
||||
if (checked) {
|
||||
this.selection.push(item);
|
||||
} else {
|
||||
const idx = this.selection.findIndex((room) => room.id === item.id);
|
||||
|
||||
this.selection.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
|
||||
setSelected = (selected) => {
|
||||
if (selected === "none") {
|
||||
this.setBufferSelection(null);
|
||||
}
|
||||
|
||||
const newSelection = [];
|
||||
|
||||
this.rooms.forEach((room) => {
|
||||
const checked = this.getRoomChecked(room, selected);
|
||||
|
||||
if (checked) newSelection.push(room);
|
||||
});
|
||||
|
||||
this.selection = newSelection;
|
||||
};
|
||||
|
||||
openContextMenu = (item) => {
|
||||
if (this.selection.length > 0) return;
|
||||
|
||||
this.setBufferSelection(item);
|
||||
};
|
||||
|
||||
closeContextMenu = () => {
|
||||
this.bufferSelection = null;
|
||||
};
|
||||
|
||||
createRoom = (data) => {
|
||||
const isInThirdparty = !!data.storageLocation;
|
||||
return isInThirdparty
|
||||
? api.rooms.createRoomInThirdpary(data.storageLocation.id, data)
|
||||
: api.rooms.createRoom(data);
|
||||
};
|
||||
|
||||
deleteRoom = (room) => {
|
||||
const selectedRoom = room
|
||||
? room
|
||||
: this.selection.length > 0
|
||||
? this.selection[0]
|
||||
: this.bufferSelection;
|
||||
|
||||
return api.rooms.deleteRoom(selectedRoom.id);
|
||||
};
|
||||
|
||||
pinRoom = (id) => {
|
||||
return api.rooms.pinRoom(id);
|
||||
};
|
||||
|
||||
unpinRoom = (id) => {
|
||||
return api.rooms.unpinRoom(id);
|
||||
};
|
||||
|
||||
moveToArchive = (room) => {
|
||||
const selectedRoom = room
|
||||
? room
|
||||
: this.selection.length > 0
|
||||
? this.selection[0]
|
||||
: this.bufferSelection;
|
||||
|
||||
return api.rooms.archiveRoom(selectedRoom.id);
|
||||
};
|
||||
|
||||
moveFromArchive = (room) => {
|
||||
const selectedRoom = room
|
||||
? room
|
||||
: this.selection.length > 0
|
||||
? this.selection[0]
|
||||
: this.bufferSelection;
|
||||
|
||||
return api.rooms.unarchiveRoom(selectedRoom.id);
|
||||
};
|
||||
|
||||
fetchTags = () => {
|
||||
const request = () =>
|
||||
api.rooms.getTags().then((res) => {
|
||||
this.setTags(res);
|
||||
return res;
|
||||
});
|
||||
|
||||
return request();
|
||||
};
|
||||
|
||||
getRoomCheckboxTitle = (t, key) => {
|
||||
switch (key) {
|
||||
case "all":
|
||||
return t("All");
|
||||
case RoomsType.FillingFormsRoom:
|
||||
return "Filling form rooms";
|
||||
case RoomsType.CustomRoom:
|
||||
return "Custom rooms";
|
||||
case RoomsType.EditingRoom:
|
||||
return "Editing rooms";
|
||||
case RoomsType.ReviewRoom:
|
||||
return "Review rooms";
|
||||
case RoomsType.ReadOnlyRoom:
|
||||
return "Read-only rooms";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
getRoomChecked = (room, selected) => {
|
||||
const type = room.roomType;
|
||||
|
||||
switch (selected) {
|
||||
case "all":
|
||||
return true;
|
||||
case RoomsType.FillingFormsRoom:
|
||||
return type === RoomsType.FillingFormsRoom;
|
||||
case RoomsType.CustomRoom:
|
||||
return type === RoomsType.CustomRoom;
|
||||
case RoomsType.EditingRoom:
|
||||
return type === RoomsType.EditingRoom;
|
||||
case RoomsType.ReviewRoom:
|
||||
return type === RoomsType.ReviewRoom;
|
||||
case RoomsType.ReadOnlyRoom:
|
||||
return type === RoomsType.ReadOnlyRoom;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
get isHeaderVisible() {
|
||||
return this.selection.length > 0;
|
||||
}
|
||||
|
||||
get isHeaderIndeterminate() {
|
||||
return this.isHeaderVisible && this.selection.length
|
||||
? this.selection.length < this.rooms.length
|
||||
: false;
|
||||
}
|
||||
|
||||
get isHeaderChecked() {
|
||||
return this.isHeaderVisible && this.selection.length === this.rooms.length;
|
||||
}
|
||||
|
||||
get checkboxMenuItems() {
|
||||
let cbMenu = ["all"];
|
||||
|
||||
for (const item of this.rooms) {
|
||||
switch (item.roomType) {
|
||||
case RoomsType.FillingFormsRoom:
|
||||
cbMenu.push(RoomsType.FillingFormsRoom);
|
||||
break;
|
||||
case RoomsType.CustomRoom:
|
||||
cbMenu.push(RoomsType.CustomRoom);
|
||||
break;
|
||||
case RoomsType.EditingRoom:
|
||||
cbMenu.push(RoomsType.EditingRoom);
|
||||
break;
|
||||
case RoomsType.ReviewRoom:
|
||||
cbMenu.push(RoomsType.ReviewRoom);
|
||||
break;
|
||||
case RoomsType.ReadOnlyRoom:
|
||||
cbMenu.push(RoomsType.ReadOnlyRoom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cbMenu = cbMenu.filter((item, index) => cbMenu.indexOf(item) === index);
|
||||
|
||||
return cbMenu;
|
||||
}
|
||||
}
|
||||
|
||||
export default RoomsStore;
|
@ -13,6 +13,10 @@ class TagsStore {
|
||||
this.tags = tags;
|
||||
};
|
||||
|
||||
createTag = (name) => {
|
||||
return api.rooms.createTag(name);
|
||||
};
|
||||
|
||||
fetchTags = () => {
|
||||
const request = () =>
|
||||
api.rooms.getTags().then((res) => {
|
||||
|
@ -32,7 +32,8 @@ class ThirdPartyStore {
|
||||
isCorporate,
|
||||
customerTitle,
|
||||
providerKey,
|
||||
providerId
|
||||
providerId,
|
||||
isRoomsStorage
|
||||
) => {
|
||||
return api.files.saveThirdParty(
|
||||
url,
|
||||
@ -42,7 +43,8 @@ class ThirdPartyStore {
|
||||
isCorporate,
|
||||
customerTitle,
|
||||
providerKey,
|
||||
providerId
|
||||
providerId,
|
||||
isRoomsStorage
|
||||
);
|
||||
};
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user