Compare commits
215 Commits
master
...
feature/VD
Author | SHA1 | Date | |
---|---|---|---|
|
02d26df0a2 | ||
|
085f780bfa | ||
|
fe010d398b | ||
|
49b78e1e15 | ||
1bfa73ac4b | |||
9b360fa048 | |||
93a04c0392 | |||
2a30838dc1 | |||
2dd89e262b | |||
46654ecca2 | |||
d68eb46c9b | |||
8f2f8defa5 | |||
52855d4593 | |||
0b15f5bf91 | |||
d950c5557f | |||
046972b8f5 | |||
e28930bf00 | |||
bc47505b07 | |||
09d4c8a4f4 | |||
24d7b695ab | |||
0f8994bd52 | |||
d40881ec94 | |||
82de90538e | |||
a1f37ebc04 | |||
14a766bbb1 | |||
d2d7d535d1 | |||
61290de23c | |||
72f7e3ea5d | |||
ea56dece9e | |||
3fb4684e51 | |||
3ff194fc66 | |||
ce8aebc6a4 | |||
523bf28fd9 | |||
123665d3c9 | |||
bb7a9f3292 | |||
7e88a1567f | |||
746094957b | |||
796d90aa9f | |||
b220f57954 | |||
e5dd58ac08 | |||
ee89a8a40b | |||
3e3d15f715 | |||
16f7f3d3d4 | |||
643b92a49f | |||
df2d67c1df | |||
1b0425216c | |||
ef264c7755 | |||
efbeb95641 | |||
1788aacf93 | |||
c67f5ad7e3 | |||
4dfce3d3d6 | |||
e4dbd0df7a | |||
d2fdbecefe | |||
b73a9526d2 | |||
4e77bdf335 | |||
33145f2a3f | |||
92c4622217 | |||
03f23b0be3 | |||
d032fff28d | |||
f32561fa5f | |||
eceba70e98 | |||
afcd61e64e | |||
bda0c950a1 | |||
d90c1a1c74 | |||
d9b50962cf | |||
178da67a3c | |||
4a8ee6e20c | |||
b8f4ec5fbb | |||
365fcbbb9c | |||
a7307c94da | |||
5b687d6f8c | |||
4b96f5c99e | |||
0bfe24d0cb | |||
47d70ea8f2 | |||
cc644180a5 | |||
179a6c42f2 | |||
fc8e894362 | |||
02772b660b | |||
d083d108c8 | |||
86f4555c1b | |||
d15445301c | |||
0bcbd98a32 | |||
480f46a599 | |||
0eccb51598 | |||
c1ea56d06b | |||
2733e61aaa | |||
3af4f321cb | |||
c8c724e63b | |||
afd8251e27 | |||
ff514b79d3 | |||
8a8ed799fb | |||
410b2814c7 | |||
b7517458df | |||
49d9c7c3d9 | |||
377779d9d3 | |||
1b2ff05cd8 | |||
eeb29a7cee | |||
8f69a3cf60 | |||
a9e075ee51 | |||
f441cd4546 | |||
4c349f5d14 | |||
131d99d84c | |||
328df23d4a | |||
d2b6d23d9a | |||
3d9ffaf309 | |||
a0a4abadae | |||
e14319dce2 | |||
fb712b916a | |||
68c06ac91a | |||
a3c9c55ad4 | |||
6fb0edff91 | |||
5feca2b857 | |||
2c50e66e94 | |||
75abe1a629 | |||
02fb8738ce | |||
ea78cff437 | |||
5476c53716 | |||
dfaa8f9d4e | |||
df808d2e15 | |||
80b943c0e7 | |||
5be75c1745 | |||
f312ffe5e5 | |||
3e390d8e5c | |||
aa17569192 | |||
b26186c8cf | |||
60bf65af7d | |||
4784395b02 | |||
c3334f0695 | |||
029e6534af | |||
3ffb98a9df | |||
be153f7d2c | |||
059717eac7 | |||
e4178cc974 | |||
97bca2e6ec | |||
9ca89604ac | |||
06586acd73 | |||
17c7a4216c | |||
8bac9a7e0b | |||
c07d7218b3 | |||
b36b3e9648 | |||
b5debea181 | |||
0dfab5563b | |||
000efcfac6 | |||
6936cf92df | |||
f803ba930b | |||
9ee552e0d8 | |||
5cf2d32c24 | |||
06fee9c6b4 | |||
e917242293 | |||
06e124a422 | |||
00e4b09c7b | |||
f5416db8e0 | |||
e1f2b377d8 | |||
2472f28d78 | |||
758aa39d8f | |||
a0f290205d | |||
e8eb0db251 | |||
33c09cbcd3 | |||
c1ab48720d | |||
98de3ea7e7 | |||
aa83bae8c6 | |||
83325a89df | |||
dc7f45f7f6 | |||
5eb6f951cb | |||
54dc154567 | |||
8c3fe05203 | |||
bf1bd01793 | |||
e27e7dbc7a | |||
b6d67adc3a | |||
6ab5d32801 | |||
94ad3b879f | |||
bcba14cc8a | |||
5174a80282 | |||
0471b801ec | |||
8c4e888350 | |||
a88958efbb | |||
5a166b1c58 | |||
de668ed15e | |||
04368e7953 | |||
8f58a7bc9f | |||
f15252317e | |||
8c2a3f1216 | |||
83e1180c37 | |||
8cb87da1d5 | |||
90fddc25ab | |||
1568b4954f | |||
d47c6beb31 | |||
4585f388e6 | |||
da02a34b89 | |||
df56be32e6 | |||
457d5993e1 | |||
119ba72c1a | |||
d98a127963 | |||
e299804a58 | |||
99bd0055cc | |||
0e61a9028a | |||
7ee7f5bf29 | |||
02ff6d65f6 | |||
ec75beee04 | |||
e125c70d65 | |||
365a756dfd | |||
0eba7d8c22 | |||
88e39b44b6 | |||
98d7a86a0a | |||
f720d08874 | |||
be63e1be5b | |||
|
54df145654 | ||
f537c7baf6 | |||
|
6ed7eacf8c | ||
be954b594c | |||
7a3cb28683 | |||
920e634322 | |||
3ab8e3972c | |||
e92b3b1485 | |||
a508444e6e |
@ -1,25 +1,49 @@
|
||||
{
|
||||
"ActivationRequired": "activation required",
|
||||
"AddWatermarkElements": "Add watermark elements",
|
||||
"AddStaticText": "Add static text",
|
||||
"Diagonal": "Diagonal",
|
||||
"ChooseRoomType": "Choose room type",
|
||||
"CreateRoomConfirmation": "Continue without connecting the storage?\nYou have selected a third-party storage option that is not connected yet. If you proceed without connecting the service, this option will not be added.",
|
||||
"CreateRoomWatermarksConfirmation": "You have not set a watermark to be applied to documents in this room. You can always add a watermark in the room editing settings. Continue without a watermark?",
|
||||
"CreateTagOption": "Create tag",
|
||||
"DisableRoomQuota": "Disable quota for this room",
|
||||
"FormRoomBarDescription": "This room is available to anyone with the link. External users will have Form Filler permission for all the files.",
|
||||
"Center": "Center",
|
||||
"Horizontal": "Horizontal",
|
||||
"Icon": "Icon",
|
||||
"MakeRoomPrivateDescription": "All files in this room will be encrypted.",
|
||||
"MakeRoomPrivateLimitationsWarningDescription": "With this feature, you can invite only existing {{productName}} users. After creating a room, you will not be able to change the list of users.",
|
||||
"MakeRoomPrivateTitle": "Make the Room Private",
|
||||
"PeopleSelectorInfo": "Only a room admin or a {{productName}} admin can become the owner of the room",
|
||||
"PublicRoomBarDescription": "This room is available to anyone with the link. External users will have View Only permission for all the files.",
|
||||
"ViewerInfo": "Viewer info",
|
||||
"Position": "Position",
|
||||
"PublicRoomSystemFoldersDescription": "System folders store copies of forms at different stages of completion. Forms that are being filled are stored in the In progress folder, and completed forms are stored in the Complete folder.",
|
||||
"PublicRoomSystemFoldersTitle": "System Folders",
|
||||
"RoomEditing": "Room editing",
|
||||
"RootFolderLabel": "Root folder",
|
||||
"StorageDescription": "Storage quota set per room. You can change this value or turn off storage limit.",
|
||||
"TagsPlaceholder": "Add a tag",
|
||||
"Text": "Text",
|
||||
"ThirdPartyStorageComboBoxPlaceholder": "Select storage",
|
||||
"ThirdPartyStorageDescription": "Use third-party services as data storage for this room. You can create a new folder or select the existing one in the connected storage.",
|
||||
"ThirdPartyStorageNoStorageAlert": "Before, you need to connect the corresponding service in the “Integration” section. Otherwise, the connection will not be possible.",
|
||||
"ThirdPartyStoragePermanentSettingDescription": "Files are stored in a third-party {{thirdpartyTitle}} storage in the \"{{thirdpartyFolderName}}\" folder.\n<strong>{{thirdpartyPath}}</strong>",
|
||||
"ThirdPartyStorageRoomAdminNoStorageAlert": "To connect a third-party storage, you need to add the corresponding service in the Integration section of {{productName}} settings. Contact {{productName}} owner or administrator to enable the integration."
|
||||
"ThirdPartyStorageRoomAdminNoStorageAlert": "To connect a third-party storage, you need to add the corresponding service in the Integration section of {{productName}} settings. Contact {{productName}} owner or administrator to enable the integration.",
|
||||
"UserName": "User Name",
|
||||
"UserEmail": "User Email",
|
||||
"UserIPAddress": "User IP Address",
|
||||
"ViewOnlyRoomDescription": "Share any ready documents, reports, documentation, and other files for viewing.",
|
||||
"ViewOnlyRoomTitle": "View-only room",
|
||||
"WatermarkPreview": "Watermark Preview",
|
||||
"WatermarkPreviewHelp": "This image preview roughly shows how the watermark will be displayed in your files.",
|
||||
"AutomaticIndexing": "Automatic indexing",
|
||||
"AutomaticIndexingDescription": "Enable automatic indexing to index files and folders by serial number. Sorting by number will be set as default for all users.",
|
||||
"FileLifetime": "File lifetime",
|
||||
"FileLifetimeDescription": "Set file lifetime to automatically delete the files in this room after a defined period. Lifetime begins on the date of upload/creation of the file.",
|
||||
"RestrictCopyAndDownload": "Restrict copy and download",
|
||||
"RestrictCopyAndDownloadDescription": "Enable this setting to disable downloads and content copying for users with the \"{{role}}\" role.",
|
||||
"AddWatermarksToDocuments": "Add watermarks to documents",
|
||||
"AddWatermarksToDocumentsDescription": "Protect all documents in this room with watermarks. If a document already contains one, it will not be replaced.",
|
||||
"FilesOlderThan": "Files older than:"
|
||||
}
|
||||
|
@ -66,10 +66,14 @@
|
||||
"EmptyScreenFolder": "No docs here yet",
|
||||
"EnableLink": "Enable link",
|
||||
"EnableNotifications": "Enable notifications",
|
||||
"ErrorChangeIndex": "Error when changing index. The problem may be caused on the server side. Reload the page or check your Internet connection settings.",
|
||||
"ExcludeSubfolders": "Exclude subfolders",
|
||||
"ExportRoomIndex": "Export room index",
|
||||
"ExportRoomIndexAlreadyInProgressError": "Room index export is already in progress. Please wait until the current export is completed to start the new one.",
|
||||
"FavoritesEmptyContainerDescription": "To mark files as favorites or remove them from this list, use the context menu.",
|
||||
"FileContents": "File contents",
|
||||
"FileDownloadingIsRestricted": "File downloading is restricted in this room.",
|
||||
"FileExportedToMyDocuments": "file exported to My Documents",
|
||||
"FileRemoved": "File moved to Trash",
|
||||
"FileRenamed": "The document '{{oldTitle}}' is renamed to '{{newTitle}}'",
|
||||
"FilesWillAppearHere": "Files and folders added to the room will appear here.",
|
||||
@ -85,6 +89,7 @@
|
||||
"GoToPersonal": "Go to Documents",
|
||||
"Images": "Images",
|
||||
"InviteUsersInRoom": "Invite users in room",
|
||||
"Index": "Index",
|
||||
"LeaveRoomDescription": "You are the owner of this room. Before you leave the room, you must transfer the owner's role to another user.",
|
||||
"LeaveTheRoom": "Leave the room",
|
||||
"LeftAndAppointNewOwner": "You have left the room and appointed a new owner",
|
||||
@ -151,6 +156,8 @@
|
||||
"RoomsRemoved": "Rooms removed",
|
||||
"RoomsUnpinned": "Rooms unpinned: {{count}}",
|
||||
"RoomUnpinned": "Room unpinned",
|
||||
"ReorderIndex": "The Reorder action will remove spaces in the indexes. The files will be re-indexed in order with offset to fill in missing indexes.",
|
||||
"Reorder": "Reorder",
|
||||
"SearchByContent": "Search by file contents",
|
||||
"SendByEmail": "Send by email",
|
||||
"ShareFolder": "Share folder",
|
||||
@ -181,6 +188,10 @@
|
||||
"WantToRestoreTheRooms": "All shared links in restored rooms will become active, and their contents will be available to everyone with the room links. Do you want to restore the rooms?",
|
||||
"WithSubfolders": "With subfolders",
|
||||
"YouLeftTheRoom": "You have left the room",
|
||||
"RoomFilesLifetime": "The file lifetime is set to {{days}} {{period}} in this room",
|
||||
"FileWillBeDeleted": "The file will be deleted {{date}}",
|
||||
"LifetimeDialogDescription": "Lifetime countdown begins at the file creation date. Some files in this room exceed the proposed lifetime and will be deleted once you enable the setting.",
|
||||
"LifetimeDialogDescriptionHeader": "Older files with exceeded lifetime will be deleted",
|
||||
"Protected": "protected",
|
||||
"Embed": "Embed"
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
"CreationDate": "Creation date",
|
||||
"Data": "Data",
|
||||
"DateModified": "Date modified",
|
||||
"LifetimeEnds": "Lifetime ends",
|
||||
"DeletedRoomTags": "Tags removed.",
|
||||
"ExpectUsers": "Expect users",
|
||||
"FeedLinkWasDeleted": "Link was deleted",
|
||||
|
@ -65,6 +65,7 @@
|
||||
"EmptyScreenFolder": "Здесь пока нет документов",
|
||||
"EnableLink": "Активировать ссылку",
|
||||
"EnableNotifications": "Включить уведомления",
|
||||
"ErrorChangeIndex": "Ошибка при изменении индекса. Проблема может быть вызвана на стороне сервера. Перезагрузите страницу или проверьте настройки подключения к Интернету.",
|
||||
"ExcludeSubfolders": "Исключить вложенные папки",
|
||||
"FavoritesEmptyContainerDescription": "Чтобы добавить файлы в избранное или удалить их из этого списка, используйте контекстное меню.",
|
||||
"FileContents": "Содержимое файла",
|
||||
@ -84,6 +85,7 @@
|
||||
"GoToPersonal": "Перейти к Документам",
|
||||
"Images": "Изображения",
|
||||
"InviteUsersInRoom": "Пригласить пользователей в комнату",
|
||||
"Index": "Индекс",
|
||||
"LeaveRoomDescription": "Вы являетесь новым владельцем комнаты. Перед тем, как покинуть комнату, вы должны передать роль владельца другому пользователю.",
|
||||
"LeaveTheRoom": "Покинуть комнату",
|
||||
"LeftAndAppointNewOwner": "Вы покинули комнату и назначили нового владельца",
|
||||
@ -135,6 +137,8 @@
|
||||
"RemoveFromList": "Убрать из списка",
|
||||
"RestoreAll": "Восстановить все",
|
||||
"RevokeLink": "Отозвать ссылку",
|
||||
"ReorderIndex": "Действие «Изменить порядок» удалит пробелы в индексах. Файлы будут переиндексированы по порядку со смещением для заполнения недостающих индексов.",
|
||||
"Reorder": "Изменить порядок",
|
||||
"RoomAvailableViaExternalLink": "Комната доступна по внешней ссылке",
|
||||
"RoomCreated": "Комната создана",
|
||||
"RoomEmptyAtTheMoment": "В данный момент эта комната пуста.",
|
||||
|
@ -276,7 +276,9 @@ export default function withFileActions(WrappedFileItem) {
|
||||
isDisabledDropItem,
|
||||
isRecentTab,
|
||||
canDrag,
|
||||
isIndexUpdated,
|
||||
} = this.props;
|
||||
|
||||
const { access, id } = item;
|
||||
|
||||
const isDragging =
|
||||
@ -336,6 +338,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
value={value}
|
||||
displayShareButton={displayShareButton}
|
||||
isPrivacy={isPrivacy}
|
||||
isIndexUpdated={isIndexUpdated}
|
||||
checkedProps={checkedProps}
|
||||
dragging={dragging}
|
||||
getContextModel={this.getContextModel}
|
||||
@ -361,6 +364,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
filesStore,
|
||||
uploadDataStore,
|
||||
contextOptionsStore,
|
||||
indexingStore,
|
||||
},
|
||||
{ item, t },
|
||||
) => {
|
||||
@ -374,6 +378,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
uploadEmptyFolders,
|
||||
} = filesActionsStore;
|
||||
const { setSharingPanelVisible } = dialogsStore;
|
||||
const { updateSelection } = indexingStore;
|
||||
const {
|
||||
isPrivacyFolder,
|
||||
isRecycleBinFolder,
|
||||
@ -408,6 +413,10 @@ export default function withFileActions(WrappedFileItem) {
|
||||
(x) => x.id === item.id && x.fileExst === item.fileExst,
|
||||
);
|
||||
|
||||
const isIndexUpdated = !!updateSelection.find(
|
||||
(x) => x.id === item.id && x.fileExst === item?.fileExst,
|
||||
);
|
||||
|
||||
const isDisabledDropItem = item.security?.Create === false;
|
||||
|
||||
const draggable =
|
||||
@ -505,6 +514,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
currentDeviceType: settingsStore.currentDeviceType,
|
||||
isDisabledDropItem,
|
||||
isRecentTab,
|
||||
isIndexUpdated,
|
||||
|
||||
canDrag: !dragIsDisabled,
|
||||
};
|
||||
|
@ -93,6 +93,7 @@ const withHotkeys = (Component) => {
|
||||
isGroupMenuBlocked,
|
||||
isFormRoom,
|
||||
isParentFolderFormRoom,
|
||||
isIndexEditingMode,
|
||||
} = props;
|
||||
|
||||
const navigate = useNavigate();
|
||||
@ -178,7 +179,8 @@ const withHotkeys = (Component) => {
|
||||
(e) => {
|
||||
const someDialogIsOpen = checkDialogsOpen();
|
||||
|
||||
if (e.shiftKey || e.ctrlKey || someDialogIsOpen) return;
|
||||
if (e.shiftKey || e.ctrlKey || someDialogIsOpen || isIndexEditingMode)
|
||||
return;
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowDown":
|
||||
@ -423,6 +425,7 @@ const withHotkeys = (Component) => {
|
||||
selectedFolderStore,
|
||||
userStore,
|
||||
currentTariffStatusStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const {
|
||||
setSelected,
|
||||
@ -530,6 +533,7 @@ const withHotkeys = (Component) => {
|
||||
isTrashFolder,
|
||||
isArchiveFolder,
|
||||
isRoomsFolder,
|
||||
isIndexEditingMode: indexingStore.isIndexEditingMode,
|
||||
|
||||
selection,
|
||||
setFavoriteAction,
|
||||
|
@ -26,9 +26,12 @@
|
||||
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import moment from "moment";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { copyShareLink } from "@docspace/shared/utils/copy";
|
||||
import QuickButtons from "../components/QuickButtons";
|
||||
import { LANGUAGE } from "@docspace/shared/constants";
|
||||
import { getCookie, getCorrectDate } from "@docspace/shared/utils";
|
||||
|
||||
export default function withQuickButtons(WrappedComponent) {
|
||||
class WithQuickButtons extends React.Component {
|
||||
@ -100,6 +103,39 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
}
|
||||
};
|
||||
|
||||
getStartDate = () => {
|
||||
const { period, value } = this.props.roomLifetime;
|
||||
const date = new Date(this.props.item.expired);
|
||||
|
||||
switch (period) {
|
||||
case 0:
|
||||
return new Date(date.setDate(date.getDate() - value));
|
||||
case 1:
|
||||
return new Date(date.setMonth(date.getMonth() - value));
|
||||
case 2:
|
||||
return new Date(date.setFullYear(date.getFullYear() - value));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
getShowLifetimeIcon = () => {
|
||||
const { item } = this.props;
|
||||
|
||||
const startDate = this.getStartDate();
|
||||
const dateDiff = moment(startDate).diff(item.expired) * 0.1;
|
||||
const showDate = moment(item.expired).add(dateDiff, "milliseconds");
|
||||
|
||||
return moment().valueOf() >= showDate.valueOf();
|
||||
};
|
||||
|
||||
getItemExpiredDate = () => {
|
||||
const { culture, item } = this.props;
|
||||
|
||||
const locale = getCookie(LANGUAGE) || culture;
|
||||
return getCorrectDate(locale, item.expired);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isLoading } = this.state;
|
||||
|
||||
@ -114,9 +150,16 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
isPublicRoom,
|
||||
isPersonalRoom,
|
||||
isArchiveFolder,
|
||||
isIndexEditingMode,
|
||||
currentDeviceType,
|
||||
roomLifetime,
|
||||
} = this.props;
|
||||
|
||||
const showLifetimeIcon =
|
||||
item.expired && roomLifetime ? this.getShowLifetimeIcon() : false;
|
||||
const expiredDate =
|
||||
item.expired && roomLifetime ? this.getItemExpiredDate() : null;
|
||||
|
||||
const quickButtonsComponent = (
|
||||
<QuickButtons
|
||||
t={t}
|
||||
@ -135,7 +178,10 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
folderCategory={folderCategory}
|
||||
onCopyPrimaryLink={this.onCopyPrimaryLink}
|
||||
isArchiveFolder={isArchiveFolder}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
currentDeviceType={currentDeviceType}
|
||||
showLifetimeIcon={showLifetimeIcon}
|
||||
expiredDate={expiredDate}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -158,6 +204,7 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
treeFoldersStore,
|
||||
filesStore,
|
||||
infoPanelStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const { lockFileAction, setFavoriteAction, onSelectItem } =
|
||||
filesActionsStore;
|
||||
@ -169,16 +216,20 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
isArchiveFolder,
|
||||
} = treeFoldersStore;
|
||||
|
||||
const { isIndexEditingMode } = indexingStore;
|
||||
|
||||
const { setSharingPanelVisible } = dialogsStore;
|
||||
|
||||
const folderCategory =
|
||||
isTrashFolder || isArchiveFolderRoot || isPersonalFolderRoot;
|
||||
|
||||
const { isPublicRoom } = publicRoomStore;
|
||||
const { getPrimaryFileLink, setShareChanged } = infoPanelStore;
|
||||
const { getPrimaryFileLink, setShareChanged, infoPanelRoom } =
|
||||
infoPanelStore;
|
||||
|
||||
return {
|
||||
theme: settingsStore.theme,
|
||||
culture: settingsStore.culture,
|
||||
currentDeviceType: settingsStore.currentDeviceType,
|
||||
isAdmin: authStore.isAdmin,
|
||||
lockFileAction,
|
||||
@ -192,6 +243,8 @@ export default function withQuickButtons(WrappedComponent) {
|
||||
isArchiveFolder,
|
||||
getPrimaryFileLink,
|
||||
setShareChanged,
|
||||
isIndexEditingMode,
|
||||
roomLifetime: infoPanelRoom?.lifetime,
|
||||
};
|
||||
},
|
||||
)(observer(WithQuickButtons));
|
||||
|
@ -75,10 +75,12 @@ const Item = ({
|
||||
iconBadge,
|
||||
folderId,
|
||||
currentColorScheme,
|
||||
isIndexEditingMode,
|
||||
}) => {
|
||||
const [isDragActive, setIsDragActive] = useState(false);
|
||||
|
||||
const isDragging = dragging ? showDragItems(item) : false;
|
||||
const isDragging =
|
||||
dragging && !isIndexEditingMode ? showDragItems(item) : false;
|
||||
|
||||
let value = "";
|
||||
if (isDragging) value = `${item.id} dragging`;
|
||||
@ -221,6 +223,7 @@ const Items = ({
|
||||
currentDeviceType,
|
||||
folderAccess,
|
||||
currentColorScheme,
|
||||
isIndexEditingMode,
|
||||
}) => {
|
||||
const getEndOfBlock = React.useCallback((item) => {
|
||||
switch (item.key) {
|
||||
@ -343,6 +346,7 @@ const Items = ({
|
||||
iconBadge={iconBadge}
|
||||
folderId={`document_catalog-${FOLDER_NAMES[item.rootFolderType]}`}
|
||||
currentColorScheme={currentColorScheme}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@ -395,6 +399,7 @@ const Items = ({
|
||||
firstLoad,
|
||||
activeItemId,
|
||||
emptyTrashInProgress,
|
||||
isIndexEditingMode,
|
||||
],
|
||||
);
|
||||
|
||||
@ -421,6 +426,7 @@ export default inject(
|
||||
clientLoadingStore,
|
||||
userStore,
|
||||
settingsStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const { isCommunity, isPaymentPageAvailable, currentDeviceType } =
|
||||
authStore;
|
||||
@ -437,6 +443,8 @@ export default inject(
|
||||
startDrag,
|
||||
} = filesStore;
|
||||
|
||||
const { isIndexEditingMode } = indexingStore;
|
||||
|
||||
const { firstLoad } = clientLoadingStore;
|
||||
|
||||
const { startUpload } = uploadDataStore;
|
||||
@ -488,6 +496,7 @@ export default inject(
|
||||
currentDeviceType,
|
||||
folderAccess,
|
||||
currentColorScheme,
|
||||
isIndexEditingMode,
|
||||
};
|
||||
},
|
||||
)(withTranslation(["Files", "Common", "Translations"])(observer(Items)));
|
||||
|
@ -76,6 +76,8 @@ import LeaveRoomDialog from "../dialogs/LeaveRoomDialog";
|
||||
import ChangeRoomOwnerPanel from "../panels/ChangeRoomOwnerPanel";
|
||||
import { CreatedPDFFormDialog } from "../dialogs/CreatedPDFFormDialog";
|
||||
import { PDFFormEditingDialog } from "../dialogs/PDFFormEditingDialog";
|
||||
import ReorderIndexDialog from "../dialogs/ReorderIndexDialog";
|
||||
import LifetimeDialog from "../dialogs/LifetimeDialog";
|
||||
import { SharePDFFormDialog } from "../dialogs/SharePDFFormDialog";
|
||||
|
||||
const Panels = (props) => {
|
||||
@ -90,6 +92,7 @@ const Panels = (props) => {
|
||||
deleteThirdPartyDialogVisible,
|
||||
versionHistoryPanelVisible,
|
||||
deleteDialogVisible,
|
||||
lifetimeDialogVisible,
|
||||
downloadDialogVisible,
|
||||
emptyTrashDialogVisible,
|
||||
newFilesPanelVisible,
|
||||
@ -131,6 +134,7 @@ const Panels = (props) => {
|
||||
shareFolderDialogVisible,
|
||||
pdfFormEditVisible,
|
||||
selectFileFormRoomOpenRoot,
|
||||
reorderDialogVisible,
|
||||
} = props;
|
||||
|
||||
const [createPDFFormFile, setCreatePDFFormFile] = useState({
|
||||
@ -269,6 +273,7 @@ const Panels = (props) => {
|
||||
<VersionHistoryPanel key="version-history-panel" />
|
||||
),
|
||||
deleteDialogVisible && <DeleteDialog key="delete-dialog" />,
|
||||
lifetimeDialogVisible && <LifetimeDialog key="delete-dialog" />,
|
||||
emptyTrashDialogVisible && <EmptyTrashDialog key="empty-trash-dialog" />,
|
||||
downloadDialogVisible && <DownloadDialog key="download-dialog" />,
|
||||
|
||||
@ -344,6 +349,7 @@ const Panels = (props) => {
|
||||
<ChangeRoomOwnerPanel key="change-room-owner" />
|
||||
),
|
||||
shareFolderDialogVisible && <ShareFolderDialog key="share-folder-dialog" />,
|
||||
reorderDialogVisible && <ReorderIndexDialog key="reorder-index-dialog" />,
|
||||
createPDFFormFile.visible && (
|
||||
<CreatedPDFFormDialog
|
||||
key="created-pdf-form-dialog"
|
||||
@ -378,6 +384,7 @@ export default inject(
|
||||
connectDialogVisible,
|
||||
deleteThirdPartyDialogVisible,
|
||||
deleteDialogVisible,
|
||||
lifetimeDialogVisible,
|
||||
downloadDialogVisible,
|
||||
emptyTrashDialogVisible,
|
||||
newFilesPanelVisible,
|
||||
@ -414,6 +421,7 @@ export default inject(
|
||||
shareFolderDialogVisible,
|
||||
pdfFormEditVisible,
|
||||
selectFileFormRoomOpenRoot,
|
||||
reorderDialogVisible,
|
||||
} = dialogsStore;
|
||||
|
||||
const { preparationPortalDialogVisible } = backup;
|
||||
@ -442,6 +450,7 @@ export default inject(
|
||||
deleteThirdPartyDialogVisible,
|
||||
versionHistoryPanelVisible,
|
||||
deleteDialogVisible,
|
||||
lifetimeDialogVisible,
|
||||
downloadDialogVisible,
|
||||
emptyTrashDialogVisible,
|
||||
newFilesPanelVisible,
|
||||
@ -482,6 +491,7 @@ export default inject(
|
||||
shareFolderDialogVisible,
|
||||
pdfFormEditVisible,
|
||||
selectFileFormRoomOpenRoot,
|
||||
reorderDialogVisible,
|
||||
};
|
||||
},
|
||||
)(observer(Panels));
|
||||
|
@ -51,6 +51,7 @@ const CreateRoomEvent = ({
|
||||
enableThirdParty,
|
||||
deleteThirdParty,
|
||||
startRoomType,
|
||||
isNotWatermarkSet,
|
||||
}) => {
|
||||
const { t } = useTranslation(["CreateEditRoomDialog", "Common", "Files"]);
|
||||
const [fetchedTags, setFetchedTags] = useState([]);
|
||||
@ -59,14 +60,17 @@ const CreateRoomEvent = ({
|
||||
setRoomParams(roomParams);
|
||||
setOnClose(onClose);
|
||||
|
||||
if (
|
||||
const notConnectedThirdparty =
|
||||
roomParams.storageLocation.isThirdparty &&
|
||||
!roomParams.storageLocation.storageFolderId
|
||||
) {
|
||||
!roomParams.storageLocation.storageFolderId;
|
||||
|
||||
if (notConnectedThirdparty || isNotWatermarkSet()) {
|
||||
setCreateRoomConfirmDialogVisible(true);
|
||||
} else {
|
||||
onCreateRoom(false, t);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
onCreateRoom(false, t);
|
||||
};
|
||||
|
||||
const fetchTagsAction = useCallback(async () => {
|
||||
@ -134,6 +138,8 @@ export default inject(
|
||||
setIsLoading,
|
||||
setOnClose,
|
||||
confirmDialogIsLoading,
|
||||
|
||||
isNotWatermarkSet,
|
||||
} = createEditRoomStore;
|
||||
|
||||
return {
|
||||
@ -151,6 +157,8 @@ export default inject(
|
||||
fetchThirdPartyProviders,
|
||||
enableThirdParty,
|
||||
deleteThirdParty,
|
||||
|
||||
isNotWatermarkSet,
|
||||
};
|
||||
},
|
||||
)(observer(CreateRoomEvent));
|
||||
|
@ -27,11 +27,17 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { EditRoomDialog } from "../dialogs";
|
||||
import { Encoder } from "@docspace/shared/utils/encoder";
|
||||
import api from "@docspace/shared/api";
|
||||
import { getRoomInfo } from "@docspace/shared/api/rooms";
|
||||
import {
|
||||
deleteWatermarkSettings,
|
||||
getRoomInfo,
|
||||
getWatermarkSettings,
|
||||
} from "@docspace/shared/api/rooms";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { setWatermarkSettings } from "@docspace/shared/api/rooms";
|
||||
|
||||
const EditRoomEvent = ({
|
||||
addActiveItems,
|
||||
@ -75,12 +81,18 @@ const EditRoomEvent = ({
|
||||
|
||||
defaultRoomsQuota,
|
||||
isDefaultRoomsQuotaSet,
|
||||
changeRoomLifetime,
|
||||
setInitialWatermarks,
|
||||
getWatermarkRequest,
|
||||
watermarksSettings,
|
||||
isNotWatermarkSet,
|
||||
}) => {
|
||||
const { t } = useTranslation(["CreateEditRoomDialog", "Common", "Files"]);
|
||||
|
||||
const [fetchedTags, setFetchedTags] = useState([]);
|
||||
const [fetchedImage, setFetchedImage] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isInitLoading, setIsInitLoading] = useState(false);
|
||||
|
||||
const startTags = Object.values(item.tags);
|
||||
const startObjTags = startTags.map((tag, i) => ({ id: i, name: tag }));
|
||||
@ -105,6 +117,8 @@ const EditRoomEvent = ({
|
||||
zoom: 1,
|
||||
},
|
||||
roomOwner: item.createdBy,
|
||||
indexing: item.indexing,
|
||||
lifetime: item.lifetime,
|
||||
|
||||
...(isDefaultRoomsQuotaSet && {
|
||||
quota: item.quotaLimit,
|
||||
@ -139,6 +153,7 @@ const EditRoomEvent = ({
|
||||
const isTitleChanged = roomParams?.title !== item.title;
|
||||
const isQuotaChanged = quotaLimit !== item.quotaLimit;
|
||||
const isOwnerChanged = roomParams?.roomOwner?.id !== item.createdBy.id;
|
||||
const lifetimeChanged = !isEqual(roomParams.lifetime, item.lifetime);
|
||||
|
||||
const tags = roomParams.tags.map((tag) => tag.name);
|
||||
const newTags = roomParams.tags.filter((t) => t.isNew).map((t) => t.name);
|
||||
@ -175,6 +190,12 @@ const EditRoomEvent = ({
|
||||
displayName: roomParams.roomOwner.label,
|
||||
};
|
||||
}
|
||||
|
||||
if (lifetimeChanged) {
|
||||
actions.push(changeRoomLifetime(room.id, roomParams.lifetime));
|
||||
room.lifetime = roomParams.lifetime;
|
||||
}
|
||||
|
||||
if (tags.length) {
|
||||
actions.push(addTagsToRoom(room.id, newTags));
|
||||
room.tags = tags;
|
||||
@ -182,6 +203,15 @@ const EditRoomEvent = ({
|
||||
if (removedTags.length)
|
||||
actions.push(removeTagsFromRoom(room.id, removedTags));
|
||||
|
||||
|
||||
|
||||
if (watermarksSettings && !isNotWatermarkSet()) {
|
||||
|
||||
const request = getWatermarkRequest(room, watermarksSettings);
|
||||
|
||||
actions.push(request);
|
||||
}
|
||||
|
||||
await Promise.all(actions);
|
||||
|
||||
if (!!item.logo.original && !roomParams.icon.uploadedFile) {
|
||||
@ -237,7 +267,11 @@ const EditRoomEvent = ({
|
||||
if (withPaging) await updateCurrentFolder(null, currentFolderId);
|
||||
|
||||
if (item.id === currentFolderId) {
|
||||
updateEditedSelectedRoom(editRoomParams.title, tags);
|
||||
updateEditedSelectedRoom(
|
||||
editRoomParams.title,
|
||||
tags,
|
||||
roomParams.lifetime,
|
||||
);
|
||||
if (item.logo.original && !roomParams.icon.uploadedFile) {
|
||||
removeLogoPaths();
|
||||
// updateInfoPanelSelection();
|
||||
@ -266,24 +300,28 @@ const EditRoomEvent = ({
|
||||
setFetchedImage(file);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const logo = item?.logo?.original ? item.logo.original : "";
|
||||
if (logo) {
|
||||
fetchLogoAction(logo);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchTagsAction = useCallback(async () => {
|
||||
const tags = await fetchTags();
|
||||
setFetchedTags(tags);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTagsAction();
|
||||
}, [fetchTagsAction]);
|
||||
|
||||
useEffect(() => {
|
||||
setCreateRoomDialogVisible(true);
|
||||
setIsInitLoading(true);
|
||||
|
||||
const logo = item?.logo?.original ? item.logo.original : "";
|
||||
|
||||
const requests = [fetchTags(), getWatermarkSettings(item.id)];
|
||||
|
||||
if (logo) requests.push(fetchLogoAction);
|
||||
|
||||
const fetchInfo = async () => {
|
||||
const [tags, watermarks] = await Promise.all(requests);
|
||||
|
||||
setFetchedTags(tags);
|
||||
|
||||
setInitialWatermarks(watermarks);
|
||||
|
||||
setIsInitLoading(false);
|
||||
};
|
||||
|
||||
fetchInfo();
|
||||
|
||||
return () => setCreateRoomDialogVisible(false);
|
||||
}, []);
|
||||
|
||||
@ -297,6 +335,7 @@ const EditRoomEvent = ({
|
||||
fetchedTags={fetchedTags}
|
||||
fetchedImage={fetchedImage}
|
||||
isLoading={isLoading}
|
||||
isInitLoading={isInitLoading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -312,6 +351,7 @@ export default inject(
|
||||
filesSettingsStore,
|
||||
infoPanelStore,
|
||||
currentQuotaStore,
|
||||
createEditRoomStore,
|
||||
}) => {
|
||||
const {
|
||||
editRoom,
|
||||
@ -336,13 +376,20 @@ export default inject(
|
||||
removeLogoPaths,
|
||||
updateLogoPathsCacheBreaker,
|
||||
} = selectedFolderStore;
|
||||
const { updateCurrentFolder, changeRoomOwner } = filesActionsStore;
|
||||
const { updateCurrentFolder, changeRoomOwner, changeRoomLifetime } =
|
||||
filesActionsStore;
|
||||
const { getThirdPartyIcon } = filesSettingsStore.thirdPartyStore;
|
||||
const { setCreateRoomDialogVisible } = dialogsStore;
|
||||
const { withPaging } = settingsStore;
|
||||
const { updateInfoPanelSelection } = infoPanelStore;
|
||||
|
||||
const { defaultRoomsQuota, isDefaultRoomsQuotaSet } = currentQuotaStore;
|
||||
const {
|
||||
setInitialWatermarks,
|
||||
watermarksSettings,
|
||||
isNotWatermarkSet,
|
||||
getWatermarkRequest,
|
||||
} = createEditRoomStore;
|
||||
|
||||
return {
|
||||
defaultRoomsQuota,
|
||||
@ -380,6 +427,11 @@ export default inject(
|
||||
|
||||
updateInfoPanelSelection,
|
||||
changeRoomOwner,
|
||||
changeRoomLifetime,
|
||||
setInitialWatermarks,
|
||||
watermarksSettings,
|
||||
isNotWatermarkSet,
|
||||
getWatermarkRequest,
|
||||
};
|
||||
},
|
||||
)(observer(EditRoomEvent));
|
||||
|
@ -32,21 +32,38 @@ import LinkReactSvgUrl from "PUBLIC_DIR/images/link.react.svg?url";
|
||||
import LockedReactSvgUrl from "PUBLIC_DIR/images/locked.react.svg?url";
|
||||
import FileActionsFavoriteReactSvgUrl from "PUBLIC_DIR/images/file.actions.favorite.react.svg?url";
|
||||
import FavoriteReactSvgUrl from "PUBLIC_DIR/images/favorite.react.svg?url";
|
||||
import LifetimeReactSvgUrl from "PUBLIC_DIR/images/lifetime.react.svg?url";
|
||||
import LockedReact12SvgUrl from "PUBLIC_DIR/images/icons/12/lock.react.svg?url";
|
||||
|
||||
import React, { useMemo } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { isTablet, isMobile, commonIconsStyles } from "@docspace/shared/utils";
|
||||
import { isTablet } from "@docspace/shared/utils";
|
||||
import {
|
||||
DeviceType,
|
||||
FileStatus,
|
||||
RoomsType,
|
||||
ShareAccessRights,
|
||||
} from "@docspace/shared/enums";
|
||||
import { Tooltip } from "@docspace/shared/components/tooltip";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
|
||||
import { ColorTheme, ThemeId } from "@docspace/shared/components/color-theme";
|
||||
|
||||
const StyledQuickButtons = styled.div`
|
||||
.file-lifetime {
|
||||
svg {
|
||||
rect {
|
||||
fill: ${({ theme }) => theme.filesQuickButtons.lifeTimeColor};
|
||||
}
|
||||
|
||||
circle,
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.filesQuickButtons.lifeTimeColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const QuickButtons = (props) => {
|
||||
const {
|
||||
t,
|
||||
@ -64,7 +81,10 @@ const QuickButtons = (props) => {
|
||||
onClickShare,
|
||||
isPersonalRoom,
|
||||
isArchiveFolder,
|
||||
isIndexEditingMode,
|
||||
currentDeviceType,
|
||||
showLifetimeIcon,
|
||||
expiredDate,
|
||||
} = props;
|
||||
|
||||
const isMobile = currentDeviceType === DeviceType.mobile;
|
||||
@ -136,8 +156,38 @@ const QuickButtons = (props) => {
|
||||
!isArchiveFolder &&
|
||||
!isTile;
|
||||
|
||||
const getTooltipContent = () => (
|
||||
<Text fontSize="12px" fontWeight={400} noSelect>
|
||||
{t("Files:FileWillBeDeleted", { date: expiredDate })}.
|
||||
</Text>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="badges additional-badges badges__quickButtons">
|
||||
<StyledQuickButtons className="badges additional-badges badges__quickButtons">
|
||||
{!isIndexEditingMode && (
|
||||
<>
|
||||
{showLifetimeIcon && (
|
||||
<>
|
||||
<ColorTheme
|
||||
themeId={ThemeId.IconButton}
|
||||
iconName={LifetimeReactSvgUrl}
|
||||
className="badge file-lifetime icons-group"
|
||||
size={sizeQuickButton}
|
||||
isClickable
|
||||
isDisabled={isDisabled}
|
||||
data-tooltip-id="lifetimeTooltip"
|
||||
color={theme.filesQuickButtons.lifeTimeColor}
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
id="lifetimeTooltip"
|
||||
place="bottom"
|
||||
getContent={getTooltipContent}
|
||||
maxWidth="300px"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isAvailableLockFile && (
|
||||
<ColorTheme
|
||||
themeId={ThemeId.IconButton}
|
||||
@ -206,7 +256,9 @@ const QuickButtons = (props) => {
|
||||
hoverColor={theme.filesQuickButtons.hoverColor}
|
||||
/>
|
||||
)} */}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</StyledQuickButtons>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -41,10 +41,12 @@ export default inject(
|
||||
settingsStore,
|
||||
dialogsStore,
|
||||
infoPanelStore,
|
||||
indexingStore,
|
||||
}: {
|
||||
settingsStore: any;
|
||||
dialogsStore: any;
|
||||
infoPanelStore: any;
|
||||
indexingStore: any;
|
||||
}) => {
|
||||
const {
|
||||
isDesktopClient: isDesktop,
|
||||
@ -59,6 +61,8 @@ export default inject(
|
||||
const { isVisible, isMobileHidden, setIsVisible, getCanDisplay } =
|
||||
infoPanelStore;
|
||||
|
||||
const { isIndexEditingMode } = indexingStore;
|
||||
|
||||
const { createRoomDialogVisible, invitePanelOptions } = dialogsStore;
|
||||
|
||||
const canDisplay = getCanDisplay();
|
||||
@ -71,7 +75,7 @@ export default inject(
|
||||
return {
|
||||
isDesktop,
|
||||
currentDeviceType,
|
||||
isInfoPanelVisible: isVisible,
|
||||
isInfoPanelVisible: isVisible && !isIndexEditingMode,
|
||||
isMobileHidden,
|
||||
setIsInfoPanelVisible: setIsVisible,
|
||||
canDisplay,
|
||||
|
@ -220,13 +220,13 @@ const ConflictResolveDialog = (props: ConflictResolveDialogProps) => {
|
||||
isLoading={!ready}
|
||||
onSubmit={isUploadConflict ? onAcceptUploadType : onAcceptType}
|
||||
onClose={onCloseDialog}
|
||||
cancelButtonLabel={t("Common:CancelButton")}
|
||||
submitButtonLabel={t("Common:OKButton")}
|
||||
cancelButtonLabel={t("CancelButton")}
|
||||
submitButtonLabel={t("OKButton")}
|
||||
messageText={messageText}
|
||||
selectActionText={t("Common:ConflictResolveSelectAction")}
|
||||
overwriteTitle={t("Common:OverwriteTitle")}
|
||||
overwriteDescription={t("Common:OverwriteDescription")}
|
||||
duplicateTitle={t("Common:CreateFileCopy")}
|
||||
duplicateTitle={t("CreateFileCopy")}
|
||||
duplicateDescription={t("Common:CreateDescription")}
|
||||
skipTitle={t("Common:SkipTitle")}
|
||||
skipDescription={t("Common:SkipDescription")}
|
||||
|
@ -103,6 +103,7 @@ const CreateRoomDialog = ({
|
||||
y: 0.5,
|
||||
zoom: 1,
|
||||
},
|
||||
indexing: false, // VDR Automatic indexing
|
||||
};
|
||||
|
||||
const [roomParams, setRoomParams] = useState({ ...startRoomParams });
|
||||
|
@ -25,7 +25,9 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import isEqual from "lodash/isEqual";
|
||||
import TagHandler from "./handlers/TagHandler";
|
||||
import SetRoomParams from "./sub-components/SetRoomParams";
|
||||
import DialogHeader from "./sub-components/DialogHeader";
|
||||
@ -42,6 +44,8 @@ const EditRoomDialog = ({
|
||||
fetchedRoomParams,
|
||||
fetchedTags,
|
||||
fetchedImage,
|
||||
isInitLoading,
|
||||
isEqualWatermarkChanges,
|
||||
}) => {
|
||||
const [isScrollLocked, setIsScrollLocked] = useState(false);
|
||||
const [isValidTitle, setIsValidTitle] = useState(true);
|
||||
@ -75,7 +79,10 @@ const EditRoomDialog = ({
|
||||
(currentParams.icon.uploadedFile === null ||
|
||||
currentParams.icon.uploadedFile === undefined)) ||
|
||||
prevParams.icon.uploadedFile === currentParams.icon.uploadedFile) &&
|
||||
prevParams.quota === currentParams.quota
|
||||
prevParams.quota === currentParams.quota &&
|
||||
prevParams.indexing === currentParams.indexing &&
|
||||
isEqual(prevParams.lifetime, currentParams.lifetime) &&
|
||||
isEqualWatermarkChanges()
|
||||
);
|
||||
};
|
||||
|
||||
@ -130,6 +137,7 @@ const EditRoomDialog = ({
|
||||
visible={visible}
|
||||
onClose={onCloseAction}
|
||||
isScrollLocked={isScrollLocked}
|
||||
isLoading={isInitLoading}
|
||||
withFooterBorder
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
@ -181,4 +189,10 @@ const EditRoomDialog = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default EditRoomDialog;
|
||||
export default inject(({ createEditRoomStore }) => {
|
||||
const { isEqualWatermarkChanges } = createEditRoomStore;
|
||||
|
||||
return {
|
||||
isEqualWatermarkChanges,
|
||||
};
|
||||
})(observer(EditRoomDialog));
|
||||
|
@ -40,6 +40,8 @@ export const getRoomTypeDefaultTagTranslation = (roomType = 1, t) => {
|
||||
return t("Common:CustomRooms");
|
||||
case RoomsType.PublicRoom:
|
||||
return t("Common:PublicRoom");
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return t("Common:VirtualDataRoom");
|
||||
case RoomsType.FormRoom:
|
||||
return t("Common:FormRoom");
|
||||
}
|
||||
|
@ -0,0 +1,179 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { capitalize } from "lodash";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { TextInput } from "@docspace/shared/components/text-input";
|
||||
import { ComboBox } from "@docspace/shared/components/combobox";
|
||||
import { mobile } from "@docspace/shared/utils/device";
|
||||
|
||||
const StyledFileLifetime = styled.div`
|
||||
margin-top: 12px;
|
||||
|
||||
.virtual-data-room_file-lifetime_body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@media ${mobile} {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.virtual-data-room_file-lifetime_date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.virtual-data-room_file-lifetime_input {
|
||||
min-width: 150px;
|
||||
margin-right: 4px;
|
||||
|
||||
@media ${mobile} {
|
||||
margin-right: 8px;
|
||||
width: 165px;
|
||||
}
|
||||
}
|
||||
|
||||
.virtual-data-room_file-lifetime_combo-box {
|
||||
margin-right: 16px;
|
||||
width: 92px;
|
||||
min-width: 92px;
|
||||
|
||||
@media ${mobile} {
|
||||
margin-right: 0px;
|
||||
width: 165px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const FileLifetime = ({ t, roomParams, setRoomParams }) => {
|
||||
const lifetime = roomParams.lifetime ?? {
|
||||
value: 12,
|
||||
deletePermanently: false,
|
||||
period: 0,
|
||||
};
|
||||
|
||||
const dateOptions = [
|
||||
{
|
||||
key: 1,
|
||||
label: capitalize(t("Common:Days")),
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
label: t("Common:Months"),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
key: 3,
|
||||
label: t("Common:Years"),
|
||||
value: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const deleteOptions = [
|
||||
{
|
||||
key: 1,
|
||||
label: t("Common:MoveToTrash"),
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
label: t("Common:DeletePermanently"),
|
||||
value: true,
|
||||
},
|
||||
];
|
||||
|
||||
const selectedInputValue = lifetime.value + "";
|
||||
const selectedDateOption = dateOptions.find(
|
||||
(o) => o.value === lifetime.period,
|
||||
);
|
||||
const selectedDeleteOptions = lifetime.deletePermanently
|
||||
? deleteOptions[1]
|
||||
: deleteOptions[0];
|
||||
|
||||
const [inputValue, setInputValue] = useState(selectedInputValue);
|
||||
const [selectedDate, setSelectedDate] = useState(selectedDateOption);
|
||||
const [selectedDelete, setSelectedDelete] = useState(selectedDeleteOptions);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roomParams.lifetime) {
|
||||
setRoomParams({
|
||||
...roomParams,
|
||||
lifetime,
|
||||
});
|
||||
}
|
||||
}, [roomParams.lifetime]);
|
||||
|
||||
const onChange = (e) => {
|
||||
// /^(?:[1-9][0-9]*|0)$/
|
||||
if (e.target.value && !/^(?:[1-9][0-9]*)$/.test(e.target.value)) return;
|
||||
|
||||
setInputValue(e.target.value);
|
||||
|
||||
setRoomParams({
|
||||
...roomParams,
|
||||
lifetime: { ...lifetime, value: +e.target.value },
|
||||
});
|
||||
};
|
||||
|
||||
const isLoading = false;
|
||||
|
||||
const onSelectDate = (option) => {
|
||||
setSelectedDate(option);
|
||||
|
||||
setRoomParams({
|
||||
...roomParams,
|
||||
lifetime: { ...lifetime, period: option.value },
|
||||
});
|
||||
};
|
||||
|
||||
const onSelectDelete = (option) => {
|
||||
setSelectedDelete(option);
|
||||
|
||||
setRoomParams({
|
||||
...roomParams,
|
||||
lifetime: { ...lifetime, deletePermanently: option.value },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledFileLifetime className="virtual-data-room_file-lifetime">
|
||||
<Text fontWeight={600} fontSize="13px">
|
||||
{t("FilesOlderThan")}
|
||||
</Text>
|
||||
|
||||
<div className="virtual-data-room_file-lifetime_body">
|
||||
<div className="virtual-data-room_file-lifetime_date">
|
||||
<TextInput
|
||||
className="virtual-data-room_file-lifetime_input"
|
||||
isAutoFocussed={true}
|
||||
isDisabled={isLoading}
|
||||
tabIndex={1}
|
||||
value={inputValue}
|
||||
onChange={onChange}
|
||||
scale
|
||||
/>
|
||||
<ComboBox
|
||||
className="virtual-data-room_file-lifetime_combo-box"
|
||||
options={dateOptions}
|
||||
isDisabled={isLoading}
|
||||
showDisabledItems
|
||||
selectedOption={selectedDate}
|
||||
scaledOptions={true}
|
||||
onSelect={onSelectDate}
|
||||
/>
|
||||
</div>
|
||||
<ComboBox
|
||||
options={deleteOptions}
|
||||
isDisabled={isLoading}
|
||||
showDisabledItems
|
||||
selectedOption={selectedDelete}
|
||||
scale
|
||||
onSelect={onSelectDelete}
|
||||
/>
|
||||
</div>
|
||||
</StyledFileLifetime>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileLifetime;
|
@ -44,6 +44,7 @@ import { getRoomTypeDefaultTagTranslation } from "../data";
|
||||
import { ImageEditor } from "@docspace/shared/components/image-editor";
|
||||
import PreviewTile from "@docspace/shared/components/image-editor/PreviewTile";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import VirtualDataRoomBlock from "./VirtualDataRoomBlock";
|
||||
|
||||
import ChangeRoomOwner from "./ChangeRoomOwner";
|
||||
import RoomQuota from "./RoomQuota";
|
||||
@ -105,6 +106,8 @@ const SetRoomParams = ({
|
||||
const [forceHideRoomTypeDropdown, setForceHideRoomTypeDropdown] =
|
||||
useState(false);
|
||||
|
||||
const isVDRRoom = roomParams.type === RoomsType.VirtualDataRoom;
|
||||
|
||||
const isFormRoom = roomParams.type === RoomsType.FormRoom;
|
||||
const isPublicRoom = roomParams.type === RoomsType.PublicRoom;
|
||||
|
||||
@ -215,6 +218,15 @@ const SetRoomParams = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{isVDRRoom && (
|
||||
<VirtualDataRoomBlock
|
||||
t={t}
|
||||
roomParams={roomParams}
|
||||
setRoomParams={setRoomParams}
|
||||
isEdit={isEdit}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isDefaultRoomsQuotaSet && !roomParams.storageLocation.providerKey && (
|
||||
<RoomQuota
|
||||
setRoomParams={setRoomParams}
|
||||
|
@ -0,0 +1,132 @@
|
||||
import { useState } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { ToggleButton } from "@docspace/shared/components/toggle-button";
|
||||
|
||||
import FileLifetime from "./FileLifetime";
|
||||
import WatermarkBlock from "./Watermarks/WatermarkBlock";
|
||||
|
||||
const StyledVirtualDataRoomBlock = styled.div`
|
||||
.virtual-data-room-block {
|
||||
margin-bottom: 18px;
|
||||
|
||||
.virtual-data-room-block_header {
|
||||
display: flex;
|
||||
|
||||
.virtual-data-room-block_toggle {
|
||||
margin-left: auto;
|
||||
margin-right: 28px;
|
||||
}
|
||||
}
|
||||
.virtual-data-room-block_description {
|
||||
max-width: 420px;
|
||||
margin-right: 28px;
|
||||
|
||||
color: ${({ theme }) => theme.editLink.text.color};
|
||||
}
|
||||
.virtual-data-room-block_content {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Block = ({
|
||||
headerText,
|
||||
bodyText,
|
||||
onChange,
|
||||
isDisabled,
|
||||
isChecked,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className="virtual-data-room-block">
|
||||
<div className="virtual-data-room-block_header">
|
||||
<Text fontWeight={600} fontSize="13px">
|
||||
{headerText}
|
||||
</Text>
|
||||
<ToggleButton
|
||||
isDisabled={isDisabled}
|
||||
isChecked={isChecked}
|
||||
onChange={onChange}
|
||||
className="virtual-data-room-block_toggle"
|
||||
/>
|
||||
</div>
|
||||
<Text
|
||||
fontWeight={400}
|
||||
fontSize="12px"
|
||||
className="virtual-data-room-block_description"
|
||||
>
|
||||
{bodyText}
|
||||
</Text>
|
||||
{isChecked && (
|
||||
<div className="virtual-data-room-block_content">{children}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const VirtualDataRoomBlock = ({ t, roomParams, setRoomParams, isEdit }) => {
|
||||
const role = t("Translations:RoleViewer");
|
||||
|
||||
const [fileLifetimeChecked, setFileLifetimeChecked] = useState(
|
||||
!!roomParams?.lifetime,
|
||||
);
|
||||
const [copyAndDownloadChecked, setCopyAndDownloadChecked] = useState(false);
|
||||
|
||||
const onChangeAutomaticIndexing = () => {
|
||||
setRoomParams({ ...roomParams, indexing: !roomParams.indexing });
|
||||
};
|
||||
|
||||
const onChangeFileLifetime = () => {
|
||||
if (fileLifetimeChecked) setRoomParams({ ...roomParams, lifetime: null });
|
||||
setFileLifetimeChecked(!fileLifetimeChecked);
|
||||
};
|
||||
|
||||
const onChangeRestrictCopyAndDownload = () => {
|
||||
setCopyAndDownloadChecked(!copyAndDownloadChecked);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledVirtualDataRoomBlock>
|
||||
<Block
|
||||
headerText={t("AutomaticIndexing")}
|
||||
bodyText={t("AutomaticIndexingDescription")}
|
||||
onChange={onChangeAutomaticIndexing}
|
||||
isDisabled={false}
|
||||
isChecked={roomParams.indexing}
|
||||
></Block>
|
||||
<Block
|
||||
headerText={t("FileLifetime")}
|
||||
bodyText={t("FileLifetimeDescription")}
|
||||
onChange={onChangeFileLifetime}
|
||||
isDisabled={false}
|
||||
isChecked={fileLifetimeChecked}
|
||||
>
|
||||
<FileLifetime
|
||||
t={t}
|
||||
roomParams={roomParams}
|
||||
setRoomParams={setRoomParams}
|
||||
/>
|
||||
</Block>
|
||||
<Block
|
||||
headerText={t("RestrictCopyAndDownload")}
|
||||
bodyText={
|
||||
<Trans t={t} i18nKey="RestrictCopyAndDownloadDescription">
|
||||
Enable this setting to disable downloads and content copying for
|
||||
users with the {{ role }} role.
|
||||
</Trans>
|
||||
}
|
||||
onChange={onChangeRestrictCopyAndDownload}
|
||||
isDisabled={false}
|
||||
isChecked={copyAndDownloadChecked}
|
||||
></Block>
|
||||
|
||||
<WatermarkBlock BlockComponent={Block} t={t} isEdit={isEdit} />
|
||||
</StyledVirtualDataRoomBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default VirtualDataRoomBlock;
|
@ -0,0 +1,334 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { ComboBox } from "@docspace/shared/components/combobox";
|
||||
import { DropDownItem } from "@docspace/shared/components/drop-down-item";
|
||||
import { FileInput } from "@docspace/shared/components/file-input";
|
||||
import { imageProcessing } from "@docspace/shared/utils/common";
|
||||
import { ButtonDelete } from "@docspace/shared/components/image-editor";
|
||||
import { HelpButton } from "@docspace/shared/components/help-button";
|
||||
|
||||
import { StyledWatermark } from "./StyledComponent";
|
||||
|
||||
const scaleOptions = [
|
||||
{ key: 100, label: "100" },
|
||||
{ key: 200, label: "200" },
|
||||
{ key: 300, label: "300" },
|
||||
{ key: 400, label: "400" },
|
||||
{ key: 500, label: "500" },
|
||||
];
|
||||
|
||||
const rotateOptions = [
|
||||
{ key: 0, label: "0" },
|
||||
{ key: 30, label: "30" },
|
||||
{ key: 45, label: "45" },
|
||||
{ key: 60, label: "60" },
|
||||
{ key: 90, label: "90" },
|
||||
];
|
||||
const getInitialScale = (scale, isEdit) => {
|
||||
if (!isEdit || !scale) return scaleOptions[0];
|
||||
|
||||
return scaleOptions.find((item) => {
|
||||
return item.key === scale;
|
||||
});
|
||||
};
|
||||
|
||||
const getInitialRotate = (rotate, isEdit) => {
|
||||
if (!isEdit) return rotateOptions[0];
|
||||
|
||||
const item = rotateOptions.find((item) => {
|
||||
return item.key === rotate;
|
||||
});
|
||||
|
||||
return !item ? rotateOptions[0] : item;
|
||||
};
|
||||
|
||||
const ImageWatermark = ({
|
||||
isEdit,
|
||||
setWatermarks,
|
||||
initialWatermarksSettings,
|
||||
imageUrl,
|
||||
}) => {
|
||||
const { t } = useTranslation(["CreateEditRoomDialog", "Common"]);
|
||||
|
||||
const initialInfo = useRef(null);
|
||||
const previewRef = useRef(null);
|
||||
|
||||
if (initialInfo.current === null) {
|
||||
initialInfo.current = {
|
||||
rotate: getInitialRotate(initialWatermarksSettings?.rotate, isEdit),
|
||||
scale: getInitialScale(initialWatermarksSettings?.imageScale, isEdit),
|
||||
};
|
||||
}
|
||||
|
||||
const initialInfoRef = initialInfo.current;
|
||||
|
||||
useEffect(() => {
|
||||
const { enabled, isImage } = initialWatermarksSettings;
|
||||
|
||||
if (isEdit && enabled && isImage) {
|
||||
setWatermarks(initialWatermarksSettings);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setWatermarks({
|
||||
rotate: initialInfoRef.rotate.key,
|
||||
scale: initialInfoRef.scale.key,
|
||||
additions: 0,
|
||||
isImage: true,
|
||||
enabled: true,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
URL.revokeObjectURL(previewRef.current);
|
||||
previewRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [selectedRotate, setRotate] = useState(initialInfoRef.rotate);
|
||||
const [selectedScale, setScale] = useState(initialInfoRef.scale);
|
||||
const [selectedImageUrl, setImageUrl] = useState(imageUrl);
|
||||
|
||||
const onInput = (file) => {
|
||||
imageProcessing(file)
|
||||
.then((f) => {
|
||||
if (f instanceof File) {
|
||||
setWatermarks({ image: f });
|
||||
|
||||
const img = new Image();
|
||||
|
||||
previewRef.current = URL.createObjectURL(f);
|
||||
img.src = previewRef.current;
|
||||
|
||||
img.onload = () => {
|
||||
setImageUrl(previewRef.current);
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message === "recursion depth exceeded"
|
||||
) {
|
||||
toastr.error(t("Common:SizeImageLarge"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onScaleChange = (item) => {
|
||||
setScale(item);
|
||||
|
||||
setWatermarks({ imageScale: item.key });
|
||||
};
|
||||
|
||||
const onRotateChange = (item) => {
|
||||
setRotate(item);
|
||||
|
||||
setWatermarks({ rotate: item.key });
|
||||
};
|
||||
|
||||
const onButtonClick = () => {
|
||||
if (previewRef.current) {
|
||||
URL.revokeObjectURL(previewRef.current);
|
||||
previewRef.current = null;
|
||||
}
|
||||
|
||||
setWatermarks({ image: null, imageUrl: null });
|
||||
setImageUrl("");
|
||||
};
|
||||
|
||||
const rotateItems = () => {
|
||||
const items = rotateOptions.map((item) => {
|
||||
return (
|
||||
<DropDownItem
|
||||
className="access-right-item"
|
||||
key={item.key}
|
||||
data-key={item.key}
|
||||
onClick={() => onRotateChange(item)}
|
||||
>
|
||||
{item.label}°
|
||||
</DropDownItem>
|
||||
);
|
||||
});
|
||||
|
||||
return <div style={{ display: "contents" }}>{items}</div>;
|
||||
};
|
||||
|
||||
const scaleItems = () => {
|
||||
const items = scaleOptions.map((item) => {
|
||||
return (
|
||||
<DropDownItem
|
||||
className="access-right-item"
|
||||
key={item.key}
|
||||
data-key={item.key}
|
||||
onClick={() => onScaleChange(item)}
|
||||
>
|
||||
{item.label}%
|
||||
</DropDownItem>
|
||||
);
|
||||
});
|
||||
|
||||
return <div style={{ display: "contents" }}>{items}</div>;
|
||||
};
|
||||
|
||||
// const onSelectFile = (fileInfo) => {
|
||||
// setWatermarks({ image: fileInfo });
|
||||
// };
|
||||
|
||||
console.log("selectedRotate", selectedRotate.key, selectedScale.key);
|
||||
return (
|
||||
<StyledWatermark
|
||||
rotate={selectedRotate.key}
|
||||
scale={selectedScale.key / 100}
|
||||
mainHeight={50}
|
||||
>
|
||||
{!selectedImageUrl && (
|
||||
<FileInput
|
||||
accept={["image/png", "image/jpeg"]}
|
||||
onInput={onInput}
|
||||
scale
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* <FilesSelectorInput
|
||||
onSelectFile={onSelectFile}
|
||||
filterParam={FilesSelectorFilterTypes.IMG}
|
||||
isSelect
|
||||
scale
|
||||
/> */}
|
||||
|
||||
{selectedImageUrl && (
|
||||
<div className="image-wrapper">
|
||||
<div>
|
||||
<div className="image-description">
|
||||
<Text fontWeight={600} className="image-watermark_text">
|
||||
{t("WatermarkPreview")}
|
||||
</Text>
|
||||
<HelpButton
|
||||
tooltipContent={
|
||||
<Text fontSize="12px">{t("WatermarkPreviewHelp")}</Text>
|
||||
}
|
||||
offsetRight={0}
|
||||
className="settings_unavailable"
|
||||
/>
|
||||
</div>
|
||||
<div className="image-watermark_wrapper">
|
||||
<img
|
||||
alt="logo"
|
||||
src={selectedImageUrl}
|
||||
className="header-logo-icon"
|
||||
/>
|
||||
</div>
|
||||
<ButtonDelete t={t} onClick={onButtonClick} />
|
||||
</div>
|
||||
|
||||
<div className="options-wrapper">
|
||||
<div>
|
||||
<Text fontWeight={600} lineHeight="20px">
|
||||
{t("Scale")}
|
||||
</Text>
|
||||
<ComboBox
|
||||
onSelect={onScaleChange}
|
||||
scaled
|
||||
scaledOptions
|
||||
advancedOptions={scaleItems()}
|
||||
options={[]}
|
||||
selectedOption={{}}
|
||||
>
|
||||
<div>{selectedScale.label}%</div>
|
||||
</ComboBox>
|
||||
</div>
|
||||
<div>
|
||||
<Text fontWeight={600} lineHeight="20px">
|
||||
{t("Rotate")}
|
||||
</Text>
|
||||
|
||||
<ComboBox
|
||||
onSelect={onRotateChange}
|
||||
scaled
|
||||
scaledOptions
|
||||
advancedOptions={rotateItems()}
|
||||
options={[]}
|
||||
selectedOption={{}}
|
||||
advancedOptionsCount={rotateOptions.length}
|
||||
fillIcon={false}
|
||||
>
|
||||
<div>{selectedRotate.label}°</div>
|
||||
</ComboBox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</StyledWatermark>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ createEditRoomStore }) => {
|
||||
const { setWatermarks, initialWatermarksSettings, watermarksSettings } =
|
||||
createEditRoomStore;
|
||||
|
||||
const { imageUrl } = watermarksSettings;
|
||||
|
||||
return {
|
||||
setWatermarks,
|
||||
initialWatermarksSettings,
|
||||
imageUrl,
|
||||
};
|
||||
})(observer(ImageWatermark));
|
@ -0,0 +1,90 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
const StyledWatermark = styled.div`
|
||||
margin-top: 16px;
|
||||
|
||||
.watermark-title {
|
||||
margin: 16px 0 8px 0;
|
||||
}
|
||||
.title-without-top {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.watermark-checkbox {
|
||||
margin: 18px 0 0 0;
|
||||
}
|
||||
|
||||
.options-wrapper {
|
||||
display: grid;
|
||||
grid-template-rows: 56px 56px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 216px auto;
|
||||
gap: 16px;
|
||||
|
||||
.image-description {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: baseline;
|
||||
|
||||
.image-watermark_text {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.image-watermark_wrapper {
|
||||
width: 216px;
|
||||
height: 216px;
|
||||
border: 1px solid #eceef1;
|
||||
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 88%;
|
||||
height: 88%;
|
||||
transform: ${(props) =>
|
||||
`rotate(${props.rotate}deg) scale(${props.scale})`};
|
||||
|
||||
opacity: 0.4;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const StyledBody = styled.div`
|
||||
.types-content {
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledWatermark, StyledBody };
|
@ -0,0 +1,240 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { TextInput } from "@docspace/shared/components/text-input";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { ComboBox } from "@docspace/shared/components/combobox";
|
||||
import { WatermarkAdditions } from "@docspace/shared/enums";
|
||||
|
||||
import { StyledWatermark } from "./StyledComponent";
|
||||
import { Tabs, TabsTypes } from "@docspace/shared/components/tabs";
|
||||
|
||||
const tabsOptions = (t) => [
|
||||
{
|
||||
id: "UserName",
|
||||
name: t("UserName"),
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
id: "UserEmail",
|
||||
name: t("UserEmail"),
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
id: "UserIpAdress",
|
||||
name: t("UserIPAddress"),
|
||||
index: 2,
|
||||
},
|
||||
{
|
||||
id: "CurrentDate",
|
||||
name: t("Common:CurrentDate"),
|
||||
index: 3,
|
||||
},
|
||||
{
|
||||
id: "RoomName",
|
||||
name: t("Common:RoomName"),
|
||||
index: 4,
|
||||
},
|
||||
];
|
||||
|
||||
const getInitialState = (initialTab) => {
|
||||
const state = {
|
||||
UserName: false,
|
||||
UserEmail: false,
|
||||
UserIpAdress: false,
|
||||
CurrentDate: false,
|
||||
RoomName: false,
|
||||
};
|
||||
|
||||
initialTab.map((item) => {
|
||||
state[item.id] = true;
|
||||
});
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
const getInitialText = (text, isEdit) => {
|
||||
return isEdit && text ? text : "";
|
||||
};
|
||||
|
||||
const getInitialTabs = (additions, isEdit, t) => {
|
||||
const dataTabs = tabsOptions(t);
|
||||
|
||||
if (!isEdit || !additions) return [dataTabs[0]];
|
||||
|
||||
return dataTabs.filter((item) => additions & WatermarkAdditions[item.id]);
|
||||
};
|
||||
|
||||
const rotateOptions = (t) => [
|
||||
{ key: -45, label: t("Diagonal") },
|
||||
{ key: 0, label: t("Horizontal") },
|
||||
];
|
||||
|
||||
const getInitialRotate = (rotate, isEdit, t) => {
|
||||
const dataRotate = rotateOptions(t);
|
||||
|
||||
if (!isEdit) return dataRotate[0];
|
||||
|
||||
const item = dataRotate.find((item) => {
|
||||
return item.key === rotate;
|
||||
});
|
||||
|
||||
return !item ? dataRotate[0] : item;
|
||||
};
|
||||
|
||||
const ViewerInfoWatermark = ({
|
||||
isEdit,
|
||||
|
||||
setWatermarks,
|
||||
|
||||
initialWatermarksSettings,
|
||||
}) => {
|
||||
const { t } = useTranslation(["CreateEditRoomDialog", "Common"]);
|
||||
|
||||
const elements = useRef(null);
|
||||
const initialInfo = useRef(null);
|
||||
|
||||
if (initialInfo.current === null) {
|
||||
initialInfo.current = {
|
||||
dataRotate: rotateOptions(t),
|
||||
dataTabs: tabsOptions(t),
|
||||
tabs: getInitialTabs(initialWatermarksSettings?.additions, isEdit, t),
|
||||
rotate: getInitialRotate(initialWatermarksSettings?.rotate, isEdit, t),
|
||||
text: getInitialText(initialWatermarksSettings?.text, isEdit),
|
||||
};
|
||||
|
||||
elements.current = getInitialState(initialInfo.current.tabs);
|
||||
}
|
||||
|
||||
const initialInfoRef = initialInfo.current;
|
||||
|
||||
useEffect(() => {
|
||||
const { enabled, isImage } = initialWatermarksSettings;
|
||||
|
||||
if (isEdit && enabled && !isImage) {
|
||||
setWatermarks(initialWatermarksSettings);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setWatermarks({
|
||||
rotate: initialInfoRef.rotate.key,
|
||||
additions: WatermarkAdditions.UserName,
|
||||
isImage: false,
|
||||
enabled: true,
|
||||
image: "",
|
||||
imageWidth: 0,
|
||||
imageHeight: 0,
|
||||
imageScale: 0,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [selectedPosition, setSelectedPosition] = useState(
|
||||
initialInfoRef.rotate,
|
||||
);
|
||||
const [textValue, setTextValue] = useState(initialInfoRef.text);
|
||||
|
||||
const onSelect = (item) => {
|
||||
let elementsData = elements.current;
|
||||
let flagsCount = 0;
|
||||
|
||||
const key = item.id;
|
||||
|
||||
elementsData[key] = !elementsData[item.id];
|
||||
|
||||
for (const key in elementsData) {
|
||||
const value = elementsData[key];
|
||||
|
||||
if (value) {
|
||||
flagsCount += WatermarkAdditions[key];
|
||||
}
|
||||
}
|
||||
|
||||
setWatermarks({ additions: flagsCount });
|
||||
};
|
||||
|
||||
const onPositionChange = (item) => {
|
||||
setSelectedPosition(item);
|
||||
|
||||
setWatermarks({ rotate: item.key });
|
||||
};
|
||||
|
||||
const onTextChange = (e) => {
|
||||
const { value } = e.target;
|
||||
setTextValue(value);
|
||||
|
||||
setWatermarks({ text: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWatermark>
|
||||
<Text className="watermark-title" fontWeight={600} lineHeight="20px">
|
||||
{t("AddWatermarkElements")}
|
||||
</Text>
|
||||
|
||||
<Tabs
|
||||
items={initialInfoRef.dataTabs}
|
||||
selectedItems={initialInfoRef.tabs.map((item) => item.index)}
|
||||
onSelect={onSelect}
|
||||
type={TabsTypes.Secondary}
|
||||
multiple
|
||||
/>
|
||||
|
||||
<Text
|
||||
className="watermark-title title-without-top"
|
||||
fontWeight={600}
|
||||
lineHeight="20px"
|
||||
>
|
||||
{t("AddStaticText")}
|
||||
</Text>
|
||||
<TextInput scale value={textValue} tabIndex={1} onChange={onTextChange} />
|
||||
|
||||
<Text className="watermark-title" fontWeight={600} lineHeight="20px">
|
||||
{t("Position")}
|
||||
</Text>
|
||||
<ComboBox
|
||||
selectedOption={selectedPosition}
|
||||
options={initialInfoRef.dataRotate}
|
||||
onSelect={onPositionChange}
|
||||
scaled
|
||||
scaledOptions
|
||||
/>
|
||||
</StyledWatermark>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ createEditRoomStore }) => {
|
||||
const { setWatermarks, initialWatermarksSettings } = createEditRoomStore;
|
||||
|
||||
return {
|
||||
setWatermarks,
|
||||
initialWatermarksSettings,
|
||||
};
|
||||
})(observer(ViewerInfoWatermark));
|
@ -0,0 +1,102 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import Watermarks from "./index";
|
||||
|
||||
const WatermarkBlock = ({
|
||||
BlockComponent,
|
||||
setWatermarks,
|
||||
isEdit = false,
|
||||
isWatermarks = false,
|
||||
resetWatermarks,
|
||||
t,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
return () => resetWatermarks();
|
||||
}, []);
|
||||
|
||||
const [watermarksChecked, setWatermarksChecked] = useState(
|
||||
isWatermarks && isEdit,
|
||||
);
|
||||
|
||||
const onChangeAddWatermarksToDocuments = () => {
|
||||
setWatermarksChecked(!watermarksChecked);
|
||||
|
||||
setWatermarks({ enabled: !watermarksChecked });
|
||||
};
|
||||
|
||||
return (
|
||||
<BlockComponent
|
||||
headerText={t("AddWatermarksToDocuments")}
|
||||
bodyText={t("AddWatermarksToDocumentsDescription")}
|
||||
onChange={onChangeAddWatermarksToDocuments}
|
||||
isDisabled={false}
|
||||
isChecked={watermarksChecked}
|
||||
>
|
||||
<Watermarks isEdit={isEdit} />
|
||||
</BlockComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ createEditRoomStore }) => {
|
||||
const { setWatermarks, initialWatermarksSettings, resetWatermarks } =
|
||||
createEditRoomStore;
|
||||
|
||||
return {
|
||||
setWatermarks,
|
||||
isWatermarks: initialWatermarksSettings?.enabled,
|
||||
resetWatermarks,
|
||||
};
|
||||
})(observer(WatermarkBlock));
|
@ -0,0 +1,102 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { RadioButtonGroup } from "@docspace/shared/components/radio-button-group";
|
||||
|
||||
import ViewerInfoWatermark from "./ViewerInfo";
|
||||
import { StyledBody } from "./StyledComponent";
|
||||
import ImageWatermark from "./ImageWatermark";
|
||||
|
||||
const imageWatermark = "image",
|
||||
viewerInfoWatermark = "viewerInfo";
|
||||
|
||||
const options = (t) => [
|
||||
{
|
||||
label: t("ViewerInfo"),
|
||||
value: viewerInfoWatermark,
|
||||
},
|
||||
{
|
||||
label: t("Common:Image"),
|
||||
value: imageWatermark,
|
||||
},
|
||||
];
|
||||
|
||||
const getOptionType = (additions, isEdit) => {
|
||||
if (isEdit) {
|
||||
return additions === 0 ? imageWatermark : viewerInfoWatermark;
|
||||
}
|
||||
|
||||
return viewerInfoWatermark;
|
||||
};
|
||||
const Watermarks = ({ isEdit, setWatermarks, initialWatermarksSettings }) => {
|
||||
const { t } = useTranslation(["CreateEditRoomDialog", "Common"]);
|
||||
const [type, setType] = useState(
|
||||
getOptionType(initialWatermarksSettings?.additions, isEdit),
|
||||
);
|
||||
|
||||
const onSelectType = (e) => {
|
||||
const { value } = e.target;
|
||||
|
||||
setType(value);
|
||||
setWatermarks({
|
||||
isImage: type === imageWatermark,
|
||||
});
|
||||
};
|
||||
|
||||
const typeOptions = options(t);
|
||||
|
||||
return (
|
||||
<StyledBody>
|
||||
<RadioButtonGroup
|
||||
name="watermarks-radiobutton"
|
||||
fontSize="13px"
|
||||
fontWeight="400"
|
||||
spacing="8px"
|
||||
options={typeOptions}
|
||||
selected={type}
|
||||
onClick={onSelectType}
|
||||
/>
|
||||
|
||||
{type === imageWatermark ? (
|
||||
<ImageWatermark isEdit={isEdit} />
|
||||
) : (
|
||||
<ViewerInfoWatermark isEdit={isEdit} />
|
||||
)}
|
||||
</StyledBody>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ createEditRoomStore }) => {
|
||||
const { setWatermarks, initialWatermarksSettings } = createEditRoomStore;
|
||||
return {
|
||||
setWatermarks,
|
||||
initialWatermarksSettings,
|
||||
};
|
||||
})(observer(Watermarks));
|
@ -29,6 +29,7 @@ import { ModalDialog } from "@docspace/shared/components/modal-dialog";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { RoomsType } from "@docspace/shared/enums";
|
||||
|
||||
const CreateRoomConfirmDialog = ({
|
||||
t,
|
||||
@ -46,6 +47,10 @@ const CreateRoomConfirmDialog = ({
|
||||
|
||||
const onClose = () => setVisible(false);
|
||||
|
||||
const bodyText = RoomsType.VirtualDataRoom
|
||||
? t("CreateEditRoomDialog:CreateRoomWatermarksConfirmation")
|
||||
: t("CreateEditRoomDialog:CreateRoomConfirmation");
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
visible={visible || confirmDialogIsLoading}
|
||||
@ -54,9 +59,7 @@ const CreateRoomConfirmDialog = ({
|
||||
zIndex={310}
|
||||
>
|
||||
<ModalDialog.Header>{t("Common:Warning")}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
{t("CreateEditRoomDialog:CreateRoomConfirmation")}
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Body>{bodyText}</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
label={t("Common:ContinueButton")}
|
||||
|
@ -0,0 +1,46 @@
|
||||
import { ModalDialog } from "@docspace/shared/components/modal-dialog";
|
||||
import { tablet } from "@docspace/shared/utils";
|
||||
import styled from "styled-components";
|
||||
import { getCorrectFourValuesStyle } from "@docspace/shared/utils";
|
||||
|
||||
const StyledLifetimeDialog = styled(ModalDialog)`
|
||||
.modal-dialog-content-body {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.modal-dialog-aside-header {
|
||||
margin: ${({ theme }) =>
|
||||
getCorrectFourValuesStyle("0 -24px 0 -16px", theme.interfaceDirection)};
|
||||
padding: ${({ theme }) =>
|
||||
getCorrectFourValuesStyle("0 0 0 16px", theme.interfaceDirection)};
|
||||
}
|
||||
|
||||
.modal-dialog-aside-footer {
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
|
||||
${({ theme }) =>
|
||||
theme.interfaceDirection === "rtl"
|
||||
? `padding-left: 32px;`
|
||||
: `padding-right: 32px;`}
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.button-dialog-accept {
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.button-dialog {
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
display: inline-block;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledLifetimeDialog };
|
@ -0,0 +1,91 @@
|
||||
import { useEffect } from "react";
|
||||
import { ModalDialog } from "@docspace/shared/components/modal-dialog";
|
||||
import { StyledLifetimeDialog } from "./StyledLifetimeDialog";
|
||||
import { Button } from "@docspace/shared/components/button";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const LifetimeDialogComponent = (props) => {
|
||||
const { t, setLifetimeDialogVisible, visible, tReady } = props;
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keyup", onKeyUp, false);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keyup", onKeyUp, false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onKeyUp = (e) => {
|
||||
if (e.keyCode === 27) onClose();
|
||||
if (e.keyCode === 13 || e.which === 13) onDeleteAction();
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
setLifetimeDialogVisible(false);
|
||||
};
|
||||
|
||||
const onAcceptClick = () => {
|
||||
console.log("onAcceptClick");
|
||||
onClose();
|
||||
};
|
||||
|
||||
const onDeleteAction = () => {
|
||||
onAcceptClick();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledLifetimeDialog
|
||||
isLoading={!tReady}
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalDialog.Header>{t("Common:Warning")}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<div className="modal-dialog-content-body">
|
||||
<Text fontWeight={600} fontSize="13px" noSelect>
|
||||
{t("Files:LifetimeDialogDescriptionHeader")}
|
||||
</Text>
|
||||
<Text fontSize="13px" noSelect>
|
||||
{t("Files:LifetimeDialogDescription")}
|
||||
</Text>
|
||||
</div>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
id="delete-file-modal_submit"
|
||||
key="OkButton"
|
||||
label={t("Common:OKButton")}
|
||||
size="normal"
|
||||
primary
|
||||
scale
|
||||
onClick={onDeleteAction}
|
||||
// isDisabled={!selection.length}
|
||||
/>
|
||||
<Button
|
||||
id="delete-file-modal_cancel"
|
||||
key="CancelButton"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
onClick={onClose}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</StyledLifetimeDialog>
|
||||
);
|
||||
};
|
||||
|
||||
const LifetimeDialog = withTranslation(["Common", "Files"])(
|
||||
LifetimeDialogComponent,
|
||||
);
|
||||
|
||||
export default inject(({ dialogsStore }) => {
|
||||
const { lifetimeDialogVisible: visible, setLifetimeDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
visible,
|
||||
setLifetimeDialogVisible,
|
||||
};
|
||||
})(observer(LifetimeDialog));
|
@ -0,0 +1,128 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TTranslation } from "@docspace/shared/types";
|
||||
|
||||
import {
|
||||
ModalDialog,
|
||||
ModalDialogType,
|
||||
} from "@docspace/shared/components/modal-dialog";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Button, ButtonSize } from "@docspace/shared/components/button";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import DialogsStore from "SRC_DIR/store/DialogsStore";
|
||||
import SelectedFolderStore from "SRC_DIR/store/SelectedFolderStore";
|
||||
import FilesActionsStore from "SRC_DIR/store/FilesActionsStore";
|
||||
import { TSelectedFolder } from "@docspace/client/src/store/SelectedFolderStore";
|
||||
|
||||
export interface ReorderIndexDialogProps {
|
||||
reorder: (id: number | string | null, t: TTranslation) => void;
|
||||
setIsVisible: (visible: boolean) => void;
|
||||
visible: boolean;
|
||||
selectedFolder: TSelectedFolder;
|
||||
}
|
||||
|
||||
const ReorderIndexDialog = ({
|
||||
visible,
|
||||
setIsVisible,
|
||||
reorder,
|
||||
selectedFolder,
|
||||
}: ReorderIndexDialogProps) => {
|
||||
const { t, ready } = useTranslation(["Files", "Common"]);
|
||||
|
||||
const onClose = () => {
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
const onReorder = () => {
|
||||
reorder(selectedFolder?.id, t);
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
displayType={ModalDialogType.modal}
|
||||
isLarge
|
||||
isLoading={!ready}
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalDialog.Header>{t("Common:Warning")}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<Text fontSize="13px" fontWeight={400} noSelect>
|
||||
{t("Files:ReorderIndex")}
|
||||
</Text>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
id="create-room"
|
||||
key="OkButton"
|
||||
label={t("Files:Reorder")}
|
||||
size={ButtonSize.normal}
|
||||
primary
|
||||
onClick={onReorder}
|
||||
scale={isMobile}
|
||||
/>
|
||||
<Button
|
||||
id="cancel-share-folder"
|
||||
key="CancelButton"
|
||||
label={t("Common:CancelButton")}
|
||||
size={ButtonSize.normal}
|
||||
onClick={onClose}
|
||||
scale={isMobile}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({
|
||||
dialogsStore,
|
||||
filesActionsStore,
|
||||
selectedFolderStore,
|
||||
}: {
|
||||
dialogsStore: DialogsStore;
|
||||
filesActionsStore: FilesActionsStore;
|
||||
selectedFolderStore: SelectedFolderStore;
|
||||
}) => {
|
||||
const { reorderDialogVisible, setReorderDialogVisible } = dialogsStore;
|
||||
const selectedFolder = selectedFolderStore.getSelectedFolder();
|
||||
const { reorder } = filesActionsStore;
|
||||
|
||||
return {
|
||||
visible: reorderDialogVisible,
|
||||
setIsVisible: setReorderDialogVisible,
|
||||
reorder,
|
||||
selectedFolder,
|
||||
};
|
||||
},
|
||||
)(observer(ReorderIndexDialog));
|
@ -86,6 +86,9 @@ export const getDefaultRoomName = (room, t) => {
|
||||
|
||||
case RoomsType.PublicRoom:
|
||||
return t("Common:PublicRoom");
|
||||
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return t("Common:VirtualDataRoom");
|
||||
case RoomsType.FormRoom:
|
||||
return t("Common:FormRoom");
|
||||
}
|
||||
|
57
packages/client/src/helpers/toast-helpers.tsx
Normal file
57
packages/client/src/helpers/toast-helpers.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { TFunction } from "i18next";
|
||||
|
||||
import { Link, LinkTarget } from "@docspace/shared/components/link";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
|
||||
export const showSuccessExportRoomIndexToast = (
|
||||
t: TFunction,
|
||||
fileName: string,
|
||||
fileUrl: string,
|
||||
openOnNewPage: boolean,
|
||||
) => {
|
||||
const toastMessage = (
|
||||
<>
|
||||
<Link
|
||||
color="#5299E0"
|
||||
fontSize="12px"
|
||||
target={openOnNewPage ? LinkTarget.blank : LinkTarget.self}
|
||||
href={fileUrl}
|
||||
>
|
||||
{fileName}
|
||||
</Link>
|
||||
|
||||
<Text as="span" fontSize="12px">
|
||||
{t<string>("Files:FileExportedToMyDocuments")}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
toastr.success(toastMessage);
|
||||
};
|
@ -144,6 +144,8 @@ class DetailsHelper {
|
||||
return "info_details_comments";
|
||||
case "Tags":
|
||||
return "info_details_tags";
|
||||
case "Lifetime ends":
|
||||
return "info_details_lifetime";
|
||||
case "Storage":
|
||||
return "info_details_storage";
|
||||
}
|
||||
@ -172,6 +174,7 @@ class DetailsHelper {
|
||||
"Date modified",
|
||||
"Last modified by",
|
||||
"Creation date",
|
||||
this.item.order && "Index",
|
||||
]
|
||||
: [
|
||||
"Owner",
|
||||
@ -182,7 +185,9 @@ class DetailsHelper {
|
||||
"Date modified",
|
||||
"Last modified by",
|
||||
"Creation date",
|
||||
this.item.expired && "Lifetime ends",
|
||||
"Versions",
|
||||
this.item.order && "Index",
|
||||
"Comments",
|
||||
]
|
||||
).filter((nP) => !!nP);
|
||||
@ -214,6 +219,11 @@ class DetailsHelper {
|
||||
return this.t("LastModifiedBy");
|
||||
case "Creation date":
|
||||
return this.t("CreationDate");
|
||||
case "Lifetime ends":
|
||||
return this.t("LifetimeEnds");
|
||||
|
||||
case "Index":
|
||||
return this.t("Files:Index");
|
||||
|
||||
case "Versions":
|
||||
return this.t("InfoPanel:Versions");
|
||||
@ -240,6 +250,9 @@ class DetailsHelper {
|
||||
case "Location":
|
||||
return this.getItemLocation();
|
||||
|
||||
case "Index":
|
||||
return this.getItemIndex();
|
||||
|
||||
case "Type":
|
||||
return this.getItemType();
|
||||
case "Storage Type":
|
||||
@ -261,6 +274,8 @@ class DetailsHelper {
|
||||
return this.getAuthorDecoration("updatedBy");
|
||||
case "Creation date":
|
||||
return this.getItemCreationDate();
|
||||
case "Lifetime ends":
|
||||
return this.getItemExpiredDate();
|
||||
|
||||
case "Versions":
|
||||
return this.getItemVersions();
|
||||
@ -330,6 +345,10 @@ class DetailsHelper {
|
||||
return text(this.item.contentLength);
|
||||
};
|
||||
|
||||
getItemIndex = () => {
|
||||
return text(this.item.order);
|
||||
};
|
||||
|
||||
getItemDateModified = () => {
|
||||
return text(parseAndFormatDate(this.item.updated, this.culture));
|
||||
};
|
||||
@ -338,6 +357,10 @@ class DetailsHelper {
|
||||
return text(parseAndFormatDate(this.item.created, this.culture));
|
||||
};
|
||||
|
||||
getItemExpiredDate = () => {
|
||||
return text(parseAndFormatDate(this.item.expired, this.culture));
|
||||
};
|
||||
|
||||
getItemVersions = () => {
|
||||
return text(this.item.version);
|
||||
};
|
||||
|
@ -360,8 +360,22 @@ const StyledProperties = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledItemOptions = styled.div`
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: auto;
|
||||
`
|
||||
: css`
|
||||
margin-left: auto;
|
||||
`}
|
||||
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
StyledInfoPanelBody.defaultProps = { theme: Base };
|
||||
StyledTitle.defaultProps = { theme: Base };
|
||||
StyledItemOptions.defaultProps = { theme: Base };
|
||||
|
||||
export {
|
||||
StyledInfoPanelBody,
|
||||
|
@ -0,0 +1,152 @@
|
||||
import IconCalendar from "PUBLIC_DIR/images/calendar.info.panel.react.svg?url";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import moment from "moment";
|
||||
import { Calendar } from "@docspace/shared/components/calendar";
|
||||
import { isMobile } from "@docspace/shared/utils";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
const heightCalendar = 376;
|
||||
const heightCalendarMobile = 420;
|
||||
|
||||
const StyledCalendarComponent = styled.div`
|
||||
position: relative;
|
||||
|
||||
.icon-calendar {
|
||||
padding-right: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCalendar = styled(Calendar)<{
|
||||
height: number;
|
||||
}>`
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: -30px;
|
||||
|
||||
height: ${(props) => `${props.height}px`};
|
||||
|
||||
${(props) =>
|
||||
props.isMobile &&
|
||||
css`
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
top: auto;
|
||||
right: auto;
|
||||
left: 0;
|
||||
height: ${`${heightCalendarMobile}px`};
|
||||
`}
|
||||
|
||||
.track-vertical {
|
||||
height: 100% !important;
|
||||
}
|
||||
`;
|
||||
|
||||
interface CalendarProps {
|
||||
roomCreationDate: string;
|
||||
setCalendarDay: (value: null | string) => void;
|
||||
setIsScrollLocked: (value: boolean) => void;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
const CalendarComponent = ({
|
||||
roomCreationDate,
|
||||
setCalendarDay,
|
||||
setIsScrollLocked,
|
||||
locale,
|
||||
}: CalendarProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedDate, setSelectedDate] = useState<moment.Moment>();
|
||||
|
||||
const [height, setHeight] = useState(heightCalendar);
|
||||
|
||||
const calendarRef = useRef<HTMLDivElement | null>(null);
|
||||
const calendarButtonRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
if (!calendarButtonRef?.current || !calendarRef?.current) return;
|
||||
|
||||
const calendarButtonElem = calendarButtonRef.current;
|
||||
const calendarElem = calendarRef.current as HTMLElement;
|
||||
|
||||
if (
|
||||
!calendarButtonElem.contains(e.target as Node) &&
|
||||
!calendarElem.contains(e.target as Node)
|
||||
)
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const onChangeHeight = () => {
|
||||
if (!calendarButtonRef?.current) return;
|
||||
|
||||
const calendarButtonElem = calendarButtonRef.current as HTMLElement;
|
||||
|
||||
const hightTop = calendarButtonElem.getBoundingClientRect().top;
|
||||
const hightIconCalendar = 20;
|
||||
const hightWindow = document.documentElement.clientHeight;
|
||||
const hightScroll = hightWindow - hightIconCalendar - hightTop;
|
||||
|
||||
if (hightScroll !== heightCalendar && hightScroll > heightCalendar) {
|
||||
setHeight(heightCalendar);
|
||||
} else setHeight(hightScroll);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("click", handleClick, { capture: true });
|
||||
window.addEventListener("resize", onChangeHeight);
|
||||
onChangeHeight();
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("click", handleClick, { capture: true });
|
||||
window.removeEventListener("resize", onChangeHeight);
|
||||
setCalendarDay(null);
|
||||
};
|
||||
}, [setCalendarDay]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && height < heightCalendar) setIsScrollLocked(true);
|
||||
if (!isOpen) setIsScrollLocked(false);
|
||||
}, [isOpen, height, setIsScrollLocked]);
|
||||
|
||||
const toggleCalendar = () => setIsOpen((open) => !open);
|
||||
|
||||
const onDateSet = (date: moment.Moment) => {
|
||||
if (!date) return;
|
||||
const formattedDate = moment(date.format("YYYY-MM-DD"));
|
||||
setSelectedDate(date);
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
setCalendarDay(formattedDate._i);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
const formattedRoomCreationDate =
|
||||
moment(roomCreationDate).format("YYYY/MM/DD");
|
||||
|
||||
return (
|
||||
<StyledCalendarComponent>
|
||||
<div ref={calendarButtonRef}>
|
||||
<ReactSVG
|
||||
className="icon-calendar"
|
||||
src={IconCalendar}
|
||||
onClick={toggleCalendar}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<StyledCalendar
|
||||
height={height}
|
||||
setSelectedDate={onDateSet}
|
||||
selectedDate={selectedDate ?? moment()}
|
||||
minDate={new Date(formattedRoomCreationDate)}
|
||||
maxDate={new Date()}
|
||||
forwardedRef={calendarRef}
|
||||
isMobile={isMobile()}
|
||||
isScroll={!isMobile()}
|
||||
locale={locale}
|
||||
/>
|
||||
)}
|
||||
</StyledCalendarComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default CalendarComponent;
|
@ -36,8 +36,13 @@ import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { StyledTitle } from "../../../styles/common";
|
||||
import { RoomIcon } from "@docspace/shared/components/room-icon";
|
||||
import RoomsContextBtn from "./context-btn";
|
||||
import { FolderType, RoomsType } from "@docspace/shared/enums";
|
||||
import { getDefaultAccessUser } from "@docspace/shared/utils/getDefaultAccessUser";
|
||||
import CalendarComponent from "../Calendar";
|
||||
import {
|
||||
FolderType,
|
||||
RoomsType,
|
||||
ShareAccessRights,
|
||||
} from "@docspace/shared/enums";
|
||||
import Search from "../../Search";
|
||||
|
||||
const RoomsItemHeader = ({
|
||||
@ -54,8 +59,12 @@ const RoomsItemHeader = ({
|
||||
isArchive,
|
||||
hasLinks,
|
||||
showSearchBlock,
|
||||
setCalendarDay,
|
||||
openHistory,
|
||||
setShowSearchBlock,
|
||||
roomType,
|
||||
setIsScrollLocked,
|
||||
i18n,
|
||||
}) => {
|
||||
const itemTitleRef = useRef();
|
||||
|
||||
@ -141,7 +150,15 @@ const RoomsItemHeader = ({
|
||||
size={16}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* TODO: Add a condition so that there is a vdr room when the correct room type is returned from the backend for Calendar */}
|
||||
{openHistory && (
|
||||
<CalendarComponent
|
||||
setCalendarDay={setCalendarDay}
|
||||
roomCreationDate={selection.created}
|
||||
setIsScrollLocked={setIsScrollLocked}
|
||||
locale={i18n.language}
|
||||
/>
|
||||
)}
|
||||
<RoomsContextBtn
|
||||
selection={selection}
|
||||
itemTitleRef={itemTitleRef}
|
||||
@ -167,6 +184,8 @@ export default inject(
|
||||
setIsMobileHidden,
|
||||
showSearchBlock,
|
||||
setShowSearchBlock,
|
||||
setCalendarDay,
|
||||
setIsScrollLocked,
|
||||
} = infoPanelStore;
|
||||
const { externalLinks } = publicRoomStore;
|
||||
|
||||
@ -195,7 +214,9 @@ export default inject(
|
||||
setBufferSelection: filesStore.setBufferSelection,
|
||||
isArchive,
|
||||
hasLinks: externalLinks.length,
|
||||
setCalendarDay,
|
||||
roomType,
|
||||
setIsScrollLocked,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -43,6 +43,8 @@ const ItemTitle = ({
|
||||
currentColorScheme,
|
||||
getIcon,
|
||||
getUserContextOptions,
|
||||
setCalendarDay,
|
||||
roomsView,
|
||||
getGroupContextOptions,
|
||||
}) => {
|
||||
if (!infoPanelSelection) return null;
|
||||
@ -58,6 +60,7 @@ const ItemTitle = ({
|
||||
/>
|
||||
);
|
||||
|
||||
const openHistory = roomsView === "info_history";
|
||||
if (isGroups)
|
||||
return (
|
||||
<GroupsItemTitle
|
||||
@ -77,16 +80,28 @@ const ItemTitle = ({
|
||||
/>
|
||||
);
|
||||
|
||||
return <RoomsItemHeader />;
|
||||
return (
|
||||
<RoomsItemHeader
|
||||
openHistory={openHistory}
|
||||
setCalendarDay={setCalendarDay}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ settingsStore, filesSettingsStore, peopleStore, oformsStore }) => {
|
||||
({
|
||||
settingsStore,
|
||||
filesSettingsStore,
|
||||
peopleStore,
|
||||
oformsStore,
|
||||
infoPanelStore,
|
||||
}) => {
|
||||
const { currentColorScheme } = settingsStore;
|
||||
const { getIcon } = filesSettingsStore;
|
||||
const { getUserContextOptions } = peopleStore.contextOptionsStore;
|
||||
const { getGroupContextOptions } = peopleStore.groupsStore;
|
||||
const { gallerySelected } = oformsStore;
|
||||
const { roomsView, setCalendarDay } = infoPanelStore;
|
||||
|
||||
return {
|
||||
currentColorScheme,
|
||||
@ -94,6 +109,8 @@ export default inject(
|
||||
getUserContextOptions,
|
||||
getGroupContextOptions,
|
||||
getIcon,
|
||||
roomsView,
|
||||
setCalendarDay,
|
||||
};
|
||||
},
|
||||
)(observer(ItemTitle));
|
||||
|
@ -44,6 +44,7 @@ const HistoryBlock = ({ t, feed, isLastEntity }) => {
|
||||
|
||||
return (
|
||||
<StyledHistoryBlock
|
||||
className={date}
|
||||
withBottomDivider={!isLastEntity}
|
||||
isUserAction={isUserAction}
|
||||
>
|
||||
|
@ -47,6 +47,8 @@ const History = ({
|
||||
openUser,
|
||||
isVisitor,
|
||||
isCollaborator,
|
||||
calendarDay,
|
||||
setCalendarDay,
|
||||
}) => {
|
||||
const isMount = useRef(true);
|
||||
const abortControllerRef = useRef(new AbortController());
|
||||
@ -76,8 +78,86 @@ const History = ({
|
||||
infoPanelSelection.isFolder || infoPanelSelection.isRoom,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!calendarDay) return;
|
||||
|
||||
const heightTitleRoom = 80;
|
||||
const heightDayWeek = 40;
|
||||
|
||||
const historyListNode = document.getElementById("history-list-info-panel");
|
||||
if (!historyListNode) return;
|
||||
|
||||
const scroll = historyListNode.closest(".scroller");
|
||||
if (!scroll) return;
|
||||
|
||||
let dateCoincidingWithCalendarDay = null;
|
||||
|
||||
selectionHistory.every((item) => {
|
||||
if (dateCoincidingWithCalendarDay) return false;
|
||||
|
||||
item.feeds.every((feed) => {
|
||||
if (feed.date.slice(0, 10) === calendarDay) {
|
||||
dateCoincidingWithCalendarDay = feed.date;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (dateCoincidingWithCalendarDay) {
|
||||
const dayNode = historyListNode.getElementsByClassName(
|
||||
dateCoincidingWithCalendarDay,
|
||||
);
|
||||
if (!dayNode[0]) return;
|
||||
|
||||
const y = dayNode[0].offsetTop - heightTitleRoom - heightDayWeek;
|
||||
scroll.scrollTo(0, y);
|
||||
setCalendarDay(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//If there are no entries in the history for the selected day
|
||||
const calendarDayModified = new Date(calendarDay);
|
||||
let nearestNewerDate = null;
|
||||
|
||||
selectionHistory.every((item, indexItem) => {
|
||||
if (nearestNewerDate) return false;
|
||||
|
||||
item.feeds.every((feed) => {
|
||||
const date = new Date(feed.date);
|
||||
|
||||
//Stop checking all entries for one day
|
||||
if (date > calendarDayModified) return false;
|
||||
|
||||
//Looking for the nearest new date
|
||||
if (date < calendarDayModified) {
|
||||
//If there are no nearby new entries in the post history, then scroll to the last one
|
||||
if (indexItem === 0) {
|
||||
nearestNewerDate = feed.date;
|
||||
return false;
|
||||
}
|
||||
|
||||
nearestNewerDate = selectionHistory[indexItem - 1].feeds[0].date;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!nearestNewerDate) return;
|
||||
|
||||
const dayNode = historyListNode.getElementsByClassName(nearestNewerDate);
|
||||
if (!dayNode[0]) return;
|
||||
|
||||
const y = dayNode[0].offsetTop - heightTitleRoom - heightDayWeek;
|
||||
scroll.scrollTo(0, y);
|
||||
setCalendarDay(null);
|
||||
}, [calendarDay]);
|
||||
|
||||
useEffect(() => {
|
||||
const showLoaderTimer = setTimeout(() => setIsShowLoader(true), 500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(showLoaderTimer);
|
||||
abortControllerRef.current?.abort();
|
||||
@ -92,7 +172,7 @@ const History = ({
|
||||
if (!selectionHistory?.length) return <NoHistory t={t} />;
|
||||
|
||||
return (
|
||||
<StyledHistoryList>
|
||||
<StyledHistoryList id="history-list-info-panel">
|
||||
{selectionHistory.map(({ day, feeds }) => [
|
||||
<StyledHistorySubtitle key={day}>
|
||||
{getRelativeDateDay(t, feeds[0].date)}
|
||||
@ -127,6 +207,8 @@ export default inject(
|
||||
historyWithFileList,
|
||||
getInfoPanelItemIcon,
|
||||
openUser,
|
||||
calendarDay,
|
||||
setCalendarDay,
|
||||
} = infoPanelStore;
|
||||
const { culture } = settingsStore;
|
||||
|
||||
@ -147,6 +229,8 @@ export default inject(
|
||||
openUser,
|
||||
isVisitor,
|
||||
isCollaborator,
|
||||
calendarDay,
|
||||
setCalendarDay,
|
||||
};
|
||||
},
|
||||
)(withTranslation(["InfoPanel", "Common", "Translations"])(observer(History)));
|
||||
|
@ -74,6 +74,8 @@ const FilesRowContainer = ({
|
||||
withPaging,
|
||||
highlightFile,
|
||||
currentDeviceType,
|
||||
isIndexEditingMode,
|
||||
changeIndex,
|
||||
}) => {
|
||||
useViewEffect({
|
||||
view: viewAs,
|
||||
@ -93,9 +95,11 @@ const FilesRowContainer = ({
|
||||
sectionWidth={sectionWidth}
|
||||
isRooms={isRooms}
|
||||
isTrashFolder={isTrashFolder}
|
||||
changeIndex={changeIndex}
|
||||
isHighlight={
|
||||
highlightFile.id == item.id && highlightFile.isExst === !item.fileExst
|
||||
}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
/>
|
||||
));
|
||||
}, [
|
||||
@ -124,7 +128,14 @@ const FilesRowContainer = ({
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ filesStore, settingsStore, infoPanelStore, treeFoldersStore }) => {
|
||||
({
|
||||
filesStore,
|
||||
settingsStore,
|
||||
infoPanelStore,
|
||||
treeFoldersStore,
|
||||
indexingStore,
|
||||
filesActionsStore,
|
||||
}) => {
|
||||
const {
|
||||
filesList,
|
||||
viewAs,
|
||||
@ -138,6 +149,7 @@ export default inject(
|
||||
const { isVisible: infoPanelVisible } = infoPanelStore;
|
||||
const { isRoomsFolder, isArchiveFolder, isTrashFolder } = treeFoldersStore;
|
||||
const { withPaging, currentDeviceType } = settingsStore;
|
||||
const { isIndexEditingMode } = indexingStore;
|
||||
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
|
||||
@ -154,6 +166,8 @@ export default inject(
|
||||
withPaging,
|
||||
highlightFile,
|
||||
currentDeviceType,
|
||||
isIndexEditingMode,
|
||||
changeIndex: filesActionsStore.changeIndex,
|
||||
};
|
||||
},
|
||||
)(observer(FilesRowContainer));
|
||||
|
@ -216,6 +216,7 @@ const FilesRowContent = ({
|
||||
isDefaultRoomsQuotaSet,
|
||||
isStatisticsAvailable,
|
||||
showStorageInfo,
|
||||
isIndexing,
|
||||
}) => {
|
||||
const {
|
||||
contentLength,
|
||||
@ -230,6 +231,7 @@ const FilesRowContent = ({
|
||||
tags,
|
||||
quotaLimit,
|
||||
usedSpace,
|
||||
order,
|
||||
} = item;
|
||||
|
||||
const contentComponent = () => {
|
||||
@ -323,6 +325,17 @@ const FilesRowContent = ({
|
||||
{!isRoom && !isRooms && quickButtons}
|
||||
</div>
|
||||
|
||||
{isIndexing && (
|
||||
<Text
|
||||
containerMinWidth="200px"
|
||||
containerWidth="15%"
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
className="row_update-text"
|
||||
>
|
||||
{`${t("Files:Index")} ${order}`}
|
||||
</Text>
|
||||
)}
|
||||
{mainInfo && (
|
||||
<Text
|
||||
containerMinWidth="200px"
|
||||
@ -352,7 +365,13 @@ const FilesRowContent = ({
|
||||
};
|
||||
|
||||
export default inject(
|
||||
({ currentQuotaStore, settingsStore, treeFoldersStore, filesStore }) => {
|
||||
({
|
||||
currentQuotaStore,
|
||||
settingsStore,
|
||||
treeFoldersStore,
|
||||
filesStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const { filter, roomsFilter } = filesStore;
|
||||
const { isRecycleBinFolder, isRoomsFolder, isArchiveFolder } =
|
||||
treeFoldersStore;
|
||||
@ -360,6 +379,8 @@ export default inject(
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const filterSortBy = isRooms ? roomsFilter.sortBy : filter.sortBy;
|
||||
|
||||
const { isIndexing } = indexingStore;
|
||||
|
||||
const { isDefaultRoomsQuotaSet, isStatisticsAvailable, showStorageInfo } =
|
||||
currentQuotaStore;
|
||||
return {
|
||||
@ -369,6 +390,7 @@ export default inject(
|
||||
isDefaultRoomsQuotaSet,
|
||||
isStatisticsAvailable,
|
||||
showStorageInfo,
|
||||
isIndexing,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -68,7 +68,10 @@ const StyledWrapper = styled.div`
|
||||
`1px ${props.theme.filesSection.tableView.row.borderColor} solid`};
|
||||
margin-top: -1px;
|
||||
|
||||
${(props) => (props.checked || props.isActive) && checkedStyle};
|
||||
${(props) =>
|
||||
(props.checked || props.isActive) &&
|
||||
!props.isIndexEditingMode &&
|
||||
checkedStyle};
|
||||
${(props) =>
|
||||
(props.checked || props.isActive) &&
|
||||
props.isFirstElem &&
|
||||
@ -77,9 +80,20 @@ const StyledWrapper = styled.div`
|
||||
`${props.theme.filesSection.tableView.row.borderColor} !important`};
|
||||
`};
|
||||
|
||||
${(props) =>
|
||||
props.isIndexUpdated &&
|
||||
css`
|
||||
background: ${(props) =>
|
||||
props.isIndexEditingMode
|
||||
? `${props.theme.filesSection.tableView.row.indexUpdate} !important`
|
||||
: `${props.theme.filesSection.tableView.row.backgroundActive} !important`};
|
||||
${marginStyles}
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
!isMobile &&
|
||||
!props.isDragging &&
|
||||
!props.isIndexEditingMode &&
|
||||
css`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
@ -87,6 +101,18 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
`};
|
||||
|
||||
${(props) =>
|
||||
!isMobile &&
|
||||
props.isIndexEditingMode &&
|
||||
css`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
background: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.indexActive};
|
||||
${marginStyles}
|
||||
}
|
||||
`};
|
||||
|
||||
${(props) =>
|
||||
props.showHotkeyBorder &&
|
||||
css`
|
||||
@ -335,6 +361,7 @@ StyledSimpleFilesRow.defaultProps = { theme: Base };
|
||||
|
||||
const SimpleFilesRow = (props) => {
|
||||
const {
|
||||
t,
|
||||
item,
|
||||
sectionWidth,
|
||||
dragging,
|
||||
@ -368,6 +395,9 @@ const SimpleFilesRow = (props) => {
|
||||
itemIndex,
|
||||
badgeUrl,
|
||||
canDrag,
|
||||
isIndexEditingMode,
|
||||
changeIndex,
|
||||
isIndexUpdated,
|
||||
} = props;
|
||||
|
||||
const isMobileDevice = isMobileUtile();
|
||||
@ -377,6 +407,10 @@ const SimpleFilesRow = (props) => {
|
||||
const withAccess = item.security?.Lock;
|
||||
const isSmallContainer = sectionWidth <= 500;
|
||||
|
||||
const onChangeIndex = (action) => {
|
||||
return changeIndex(action, item, t);
|
||||
};
|
||||
|
||||
const element = (
|
||||
<ItemIcon
|
||||
id={item.id}
|
||||
@ -433,6 +467,8 @@ const SimpleFilesRow = (props) => {
|
||||
checked={checkedProps}
|
||||
isActive={isActive}
|
||||
showHotkeyBorder={showHotkeyBorder}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
isIndexUpdated={isIndexUpdated}
|
||||
isFirstElem={itemIndex === 0}
|
||||
isHighlight={isHighlight}
|
||||
>
|
||||
@ -468,6 +504,8 @@ const SimpleFilesRow = (props) => {
|
||||
contextButtonSpacerWidth={displayShareButton}
|
||||
dragging={dragging && isDragging}
|
||||
isDragging={dragging}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
onChangeIndex={onChangeIndex}
|
||||
isActive={isActive}
|
||||
inProgress={inProgress}
|
||||
isThirdPartyFolder={item.isThirdPartyFolder}
|
||||
|
@ -74,11 +74,19 @@ const contextMenuWrapperDraggingStyle = css`
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
.table-container_cell:not(.table-container_element-wrapper) {
|
||||
border-top: ${(props) =>
|
||||
!props.isIndexEditingMode &&
|
||||
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
|
||||
margin-top: -1px;
|
||||
border-left: 0; //for Safari
|
||||
border-right: 0; //for Safari
|
||||
}
|
||||
${(props) =>
|
||||
props.isIndexEditingMode &&
|
||||
css`
|
||||
.table-container_element {
|
||||
display: flex !important;
|
||||
}
|
||||
`}
|
||||
|
||||
.table-container_cell:not(.table-container_element-wrapper) {
|
||||
height: auto;
|
||||
@ -107,14 +115,18 @@ const StyledTableRow = styled(TableRow)`
|
||||
`}
|
||||
${(props) =>
|
||||
!props.isDragging &&
|
||||
!props.isIndexUpdated &&
|
||||
css`
|
||||
:hover {
|
||||
.table-container_cell {
|
||||
cursor: pointer;
|
||||
background: ${(props) =>
|
||||
`${props.theme.filesSection.tableView.row.backgroundActive} !important`};
|
||||
props.isIndexEditingMode
|
||||
? `${props.theme.filesSection.tableView.row.indexActive} !important`
|
||||
: `${props.theme.filesSection.tableView.row.backgroundActive} !important`};
|
||||
}
|
||||
.table-container_file-name-cell {
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
@ -126,6 +138,7 @@ const StyledTableRow = styled(TableRow)`
|
||||
padding-left: 24px;
|
||||
`}
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
@ -140,9 +153,49 @@ const StyledTableRow = styled(TableRow)`
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.isIndexUpdated &&
|
||||
css`
|
||||
.table-container_cell {
|
||||
cursor: pointer;
|
||||
background: ${(props) =>
|
||||
props.isIndexEditingMode
|
||||
? `${props.theme.filesSection.tableView.row.indexUpdate} !important`
|
||||
: `${props.theme.filesSection.tableView.row.backgroundActive} !important`};
|
||||
}
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -24px;
|
||||
padding-right: 24px;
|
||||
`
|
||||
: css`
|
||||
margin-left: -24px;
|
||||
padding-left: 24px;
|
||||
`}
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-left: -20px;
|
||||
padding-left: 20px !important;
|
||||
`
|
||||
: css`
|
||||
margin-right: -20px;
|
||||
padding-right: 20px !important;
|
||||
`}
|
||||
}
|
||||
`}
|
||||
.table-container_cell {
|
||||
background: ${(props) =>
|
||||
(props.checked || props.isActive) &&
|
||||
!props.isIndexUpdated &&
|
||||
!props.isIndexEditingMode &&
|
||||
`${props.theme.filesSection.tableView.row.backgroundActive} !important`};
|
||||
cursor: ${(props) =>
|
||||
!props.isThirdPartyFolder &&
|
||||
@ -219,7 +272,8 @@ const StyledTableRow = styled(TableRow)`
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.table-container_file-name-cell {
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell {
|
||||
${(props) =>
|
||||
props.showHotkeyBorder &&
|
||||
css`
|
||||
@ -239,6 +293,30 @@ const StyledTableRow = styled(TableRow)`
|
||||
${(props) => props.dragging && rowCheckboxDraggingStyle};
|
||||
}
|
||||
|
||||
.table-container_element-wrapper {
|
||||
${(props) =>
|
||||
props.isIndexing &&
|
||||
css`
|
||||
margin-left: 0px;
|
||||
padding-left: 0px;
|
||||
`}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isIndexing &&
|
||||
css`
|
||||
.table-container_file-name-cell {
|
||||
margin-left: 0px !important;
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
&:hover {
|
||||
.table-container_file-name-cell {
|
||||
margin-left: 0px !important;
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
@ -281,6 +359,10 @@ const StyledTableRow = styled(TableRow)`
|
||||
padding-inline: 0 8px;
|
||||
}
|
||||
|
||||
.item-file-name-index {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isHighlight &&
|
||||
css`
|
||||
@ -298,7 +380,8 @@ const StyledTableRow = styled(TableRow)`
|
||||
}
|
||||
}
|
||||
|
||||
.table-container_file-name-cell {
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
|
@ -70,19 +70,28 @@ const StyledTableContainer = styled(TableContainer)`
|
||||
.table-container_file-name-cell {
|
||||
${fileNameCss}
|
||||
}
|
||||
.table-container_index-cell {
|
||||
${fileNameCss}
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
${contextCss}
|
||||
}
|
||||
}
|
||||
.table-container_index-cell {
|
||||
margin-right: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.table-row-selected + .table-row-selected {
|
||||
.table-row {
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell,
|
||||
.table-container_row-context-menu-wrapper {
|
||||
border-image-slice: 1;
|
||||
}
|
||||
.table-container_file-name-cell {
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell {
|
||||
${fileNameCss}
|
||||
border-left: 0; //for Safari macOS
|
||||
border-right: 0; //for Safari macOS
|
||||
@ -101,7 +110,8 @@ const StyledTableContainer = styled(TableContainer)`
|
||||
|
||||
.files-item:not(.table-row-selected) + .table-row-selected {
|
||||
.table-row {
|
||||
.table-container_file-name-cell {
|
||||
.table-container_file-name-cell,
|
||||
.table-container_index-cell {
|
||||
${fileNameCss}
|
||||
}
|
||||
|
||||
@ -133,11 +143,14 @@ const Table = ({
|
||||
filterTotal,
|
||||
isRooms,
|
||||
isTrashFolder,
|
||||
isIndexEditingMode,
|
||||
withPaging,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
highlightFile,
|
||||
currentDeviceType,
|
||||
onEditIndex,
|
||||
isIndexing,
|
||||
}) => {
|
||||
const [tagCount, setTagCount] = React.useState(null);
|
||||
const [hideColumns, setHideColumns] = React.useState(false);
|
||||
@ -200,6 +213,9 @@ const Table = ({
|
||||
item={item}
|
||||
itemIndex={index}
|
||||
index={index}
|
||||
onEditIndex={onEditIndex}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
isIndexing={isIndexing}
|
||||
setFirsElemChecked={setFirsElemChecked}
|
||||
setHeaderBorder={setHeaderBorder}
|
||||
theme={theme}
|
||||
@ -223,6 +239,8 @@ const Table = ({
|
||||
highlightFile.id,
|
||||
highlightFile.isExst,
|
||||
isTrashFolder,
|
||||
isIndexEditingMode,
|
||||
isIndexing,
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -235,6 +253,7 @@ const Table = ({
|
||||
navigate={navigate}
|
||||
location={location}
|
||||
isRooms={isRooms}
|
||||
isIndexing={isIndexing}
|
||||
filesList={filesList}
|
||||
/>
|
||||
|
||||
@ -246,6 +265,7 @@ const Table = ({
|
||||
itemCount={filterTotal}
|
||||
useReactWindow={!withPaging}
|
||||
infoPanelVisible={infoPanelVisible}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
itemHeight={48}
|
||||
>
|
||||
@ -264,6 +284,9 @@ export default inject(
|
||||
tableStore,
|
||||
userStore,
|
||||
settingsStore,
|
||||
|
||||
indexingStore,
|
||||
filesActionsStore,
|
||||
}) => {
|
||||
const { isVisible: infoPanelVisible } = infoPanelStore;
|
||||
|
||||
@ -285,6 +308,9 @@ export default inject(
|
||||
highlightFile,
|
||||
} = filesStore;
|
||||
|
||||
const { isIndexEditingMode, isIndexing } = indexingStore;
|
||||
const { changeIndex } = filesActionsStore;
|
||||
|
||||
const { withPaging, theme, currentDeviceType } = settingsStore;
|
||||
|
||||
return {
|
||||
@ -301,11 +327,14 @@ export default inject(
|
||||
filterTotal: isRooms ? roomsFilterTotal : filterTotal,
|
||||
isRooms,
|
||||
isTrashFolder,
|
||||
isIndexEditingMode,
|
||||
isIndexing,
|
||||
withPaging,
|
||||
columnStorageName,
|
||||
columnInfoPanelStorageName,
|
||||
highlightFile,
|
||||
currentDeviceType,
|
||||
onEditIndex: changeIndex,
|
||||
};
|
||||
},
|
||||
)(observer(Table));
|
||||
|
@ -54,6 +54,8 @@ class FilesTableHeader extends React.Component {
|
||||
showStorageInfo,
|
||||
isArchiveFolder,
|
||||
tableStorageName,
|
||||
isIndexing,
|
||||
indexColumnSize,
|
||||
} = this.props;
|
||||
|
||||
const defaultColumns = [];
|
||||
@ -357,6 +359,17 @@ class FilesTableHeader extends React.Component {
|
||||
defaultColumns.push(...columns);
|
||||
}
|
||||
|
||||
if (isIndexing) {
|
||||
defaultColumns.unshift({
|
||||
key: "Index",
|
||||
title: "#",
|
||||
enable: this.props.indexColumnIsEnabled,
|
||||
minWidth: indexColumnSize,
|
||||
resizable: false,
|
||||
isShort: true,
|
||||
});
|
||||
}
|
||||
|
||||
let columns = getColumns(defaultColumns);
|
||||
const storageColumns = localStorage.getItem(tableStorageName);
|
||||
const splitColumns = storageColumns && storageColumns.split(",");
|
||||
@ -426,11 +439,15 @@ class FilesTableHeader extends React.Component {
|
||||
columnInfoPanelStorageName,
|
||||
isRecentTab,
|
||||
isArchiveFolder,
|
||||
isIndexEditingMode,
|
||||
showStorageInfo,
|
||||
indexColumnSize,
|
||||
} = this.props;
|
||||
|
||||
if (
|
||||
isArchiveFolder !== prevProps.isArchiveFolder ||
|
||||
indexColumnSize !== prevProps.indexColumnSize ||
|
||||
isIndexEditingMode !== prevProps.isIndexEditingMode ||
|
||||
isRooms !== prevProps.isRooms ||
|
||||
isTrashFolder !== prevProps.isTrashFolder ||
|
||||
columnStorageName !== prevProps.columnStorageName ||
|
||||
@ -543,6 +560,9 @@ class FilesTableHeader extends React.Component {
|
||||
setHideColumns,
|
||||
isFrame,
|
||||
showSettings,
|
||||
|
||||
isIndexing,
|
||||
isIndexEditingMode,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@ -568,7 +588,9 @@ class FilesTableHeader extends React.Component {
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
sectionWidth={sectionWidth}
|
||||
resetColumnsSize={resetColumnsSize}
|
||||
sortingVisible={sortingVisible}
|
||||
isIndexing={isIndexing}
|
||||
sortingVisible={isIndexing ? false : sortingVisible}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
infoPanelVisible={infoPanelVisible}
|
||||
useReactWindow={!withPaging}
|
||||
tagRef={tagRef}
|
||||
@ -592,11 +614,14 @@ export default inject(
|
||||
clientLoadingStore,
|
||||
infoPanelStore,
|
||||
currentQuotaStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const { isVisible: infoPanelVisible } = infoPanelStore;
|
||||
|
||||
const { isDefaultRoomsQuotaSet, showStorageInfo } = currentQuotaStore;
|
||||
|
||||
const { isIndexEditingMode, isIndexing } = indexingStore;
|
||||
|
||||
const {
|
||||
isHeaderChecked,
|
||||
|
||||
@ -607,6 +632,7 @@ export default inject(
|
||||
headerBorder,
|
||||
roomsFilter,
|
||||
setRoomsFilter,
|
||||
indexColumnSize,
|
||||
} = filesStore;
|
||||
const { isRecentTab, isArchiveFolder, isTrashFolder } = treeFoldersStore;
|
||||
const withContent = canShare;
|
||||
@ -627,6 +653,7 @@ export default inject(
|
||||
roomColumnIsEnabled,
|
||||
erasureColumnIsEnabled,
|
||||
sizeColumnIsEnabled,
|
||||
indexColumnIsEnabled,
|
||||
sizeTrashColumnIsEnabled,
|
||||
typeColumnIsEnabled,
|
||||
typeTrashColumnIsEnabled,
|
||||
@ -655,6 +682,8 @@ export default inject(
|
||||
withContent,
|
||||
sortingVisible,
|
||||
|
||||
isIndexing,
|
||||
|
||||
setIsLoading: clientLoadingStore.setIsSectionBodyLoading,
|
||||
|
||||
roomsFilter,
|
||||
@ -678,6 +707,7 @@ export default inject(
|
||||
roomColumnIsEnabled,
|
||||
erasureColumnIsEnabled,
|
||||
sizeColumnIsEnabled,
|
||||
indexColumnIsEnabled,
|
||||
sizeTrashColumnIsEnabled,
|
||||
typeColumnIsEnabled,
|
||||
typeTrashColumnIsEnabled,
|
||||
@ -704,6 +734,9 @@ export default inject(
|
||||
isDefaultRoomsQuotaSet,
|
||||
showStorageInfo,
|
||||
isArchiveFolder,
|
||||
isIndexEditingMode,
|
||||
|
||||
indexColumnSize,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -65,6 +65,8 @@ const FilesTableRow = (props) => {
|
||||
id,
|
||||
isRooms,
|
||||
isTrashFolder,
|
||||
isIndexEditingMode,
|
||||
isIndexing,
|
||||
isHighlight,
|
||||
hideColumns,
|
||||
onDragOver,
|
||||
@ -72,7 +74,10 @@ const FilesTableRow = (props) => {
|
||||
badgeUrl,
|
||||
isRecentTab,
|
||||
canDrag,
|
||||
onEditIndex,
|
||||
isIndexUpdated,
|
||||
} = props;
|
||||
|
||||
const { acceptBackground, background } = theme.dragAndDrop;
|
||||
|
||||
const element = (
|
||||
@ -99,7 +104,7 @@ const FilesTableRow = (props) => {
|
||||
const dragStyles = {
|
||||
style: {
|
||||
background:
|
||||
dragging && isDragging
|
||||
dragging && isDragging && !isIndexEditingMode
|
||||
? isDragActive
|
||||
? acceptBackground
|
||||
: background
|
||||
@ -107,6 +112,10 @@ const FilesTableRow = (props) => {
|
||||
},
|
||||
};
|
||||
|
||||
const onChangeIndex = (action) => {
|
||||
return onEditIndex(action, item, t);
|
||||
};
|
||||
|
||||
const onDragOverEvent = (dragActive, e) => {
|
||||
onDragOver && onDragOver(e);
|
||||
|
||||
@ -140,6 +149,13 @@ const FilesTableRow = (props) => {
|
||||
? `${item.id}_${item.fileExst}`
|
||||
: item.id ?? "";
|
||||
|
||||
const contextOptionProps = isIndexEditingMode
|
||||
? {}
|
||||
: {
|
||||
contextOptions: item.contextOptions,
|
||||
getContextModel,
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledDragAndDrop
|
||||
id={id}
|
||||
@ -163,16 +179,18 @@ const FilesTableRow = (props) => {
|
||||
selectionProp={selectionProp}
|
||||
key={item.id}
|
||||
fileContextClick={fileContextClick}
|
||||
onClick={onMouseClick}
|
||||
onClick={isIndexEditingMode ? () => {} : onMouseClick}
|
||||
onChangeIndex={onChangeIndex}
|
||||
isActive={isActive}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
inProgress={inProgress}
|
||||
isFolder={item.isFolder}
|
||||
onHideContextMenu={onHideContextMenu}
|
||||
isThirdPartyFolder={item.isThirdPartyFolder}
|
||||
onDoubleClick={onDoubleClick}
|
||||
checked={checkedProps}
|
||||
contextOptions={item.contextOptions}
|
||||
getContextModel={getContextModel}
|
||||
onDoubleClick={isIndexEditingMode ? () => {} : onDoubleClick}
|
||||
checked={checkedProps || isIndexUpdated}
|
||||
isIndexing={isIndexing}
|
||||
isIndexUpdated={isIndexUpdated}
|
||||
showHotkeyBorder={showHotkeyBorder}
|
||||
title={
|
||||
item.isFolder
|
||||
@ -184,6 +202,7 @@ const FilesTableRow = (props) => {
|
||||
hideColumns={hideColumns}
|
||||
badgeUrl={badgeUrl}
|
||||
canDrag={canDrag}
|
||||
{...contextOptionProps}
|
||||
>
|
||||
{isRooms ? (
|
||||
<RoomsRowDataComponent
|
||||
|
@ -27,6 +27,7 @@
|
||||
import React from "react";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Checkbox } from "@docspace/shared/components/checkbox";
|
||||
import { classNames } from "@docspace/shared/utils";
|
||||
import { TableCell } from "@docspace/shared/components/table";
|
||||
import { Loader } from "@docspace/shared/components/loader";
|
||||
|
||||
@ -40,6 +41,7 @@ const FileNameCell = ({
|
||||
theme,
|
||||
t,
|
||||
inProgress,
|
||||
isIndexEditingMode,
|
||||
}) => {
|
||||
const { title, viewAccessibility } = item;
|
||||
|
||||
@ -49,6 +51,9 @@ const FileNameCell = ({
|
||||
|
||||
const isMedia = viewAccessibility?.ImageView || viewAccessibility?.MediaView;
|
||||
|
||||
const indexingClass = isIndexEditingMode ? "item-file-name-index" : "";
|
||||
const linkProps = isIndexEditingMode ? null : { ...linkStyles };
|
||||
|
||||
return (
|
||||
<>
|
||||
{inProgress ? (
|
||||
@ -59,31 +64,35 @@ const FileNameCell = ({
|
||||
/>
|
||||
) : (
|
||||
<TableCell
|
||||
className="table-container_element-wrapper"
|
||||
className={classNames("table-container_element-wrapper", {
|
||||
["table-container-index"]: isIndexEditingMode,
|
||||
})}
|
||||
style={{background: "none !important"}}
|
||||
hasAccess={true}
|
||||
checked={checked}
|
||||
>
|
||||
<div className="table-container_element-container">
|
||||
<div className="table-container_element">{element}</div>
|
||||
{!isIndexEditingMode && (
|
||||
<Checkbox
|
||||
className="table-container_row-checkbox"
|
||||
onChange={onChange}
|
||||
isChecked={checked}
|
||||
title={t("Common:TitleSelectFile")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
)}
|
||||
|
||||
<Link
|
||||
type="page"
|
||||
title={title}
|
||||
fontWeight="600"
|
||||
fontSize="13px"
|
||||
{...linkStyles}
|
||||
color={theme.filesSection.tableView.fileName.linkColor}
|
||||
isTextOverflow
|
||||
className="item-file-name"
|
||||
{...linkProps}
|
||||
className={`item-file-name ${indexingClass}`}
|
||||
dir="auto"
|
||||
>
|
||||
{titleWithoutExt}
|
||||
|
@ -0,0 +1,46 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import React from "react";
|
||||
import { StyledText } from "./CellStyles";
|
||||
|
||||
const IndexCell = ({ t, item, sideColor }) => {
|
||||
const { order } = item;
|
||||
|
||||
return (
|
||||
<StyledText
|
||||
color={sideColor}
|
||||
fontSize="12px"
|
||||
fontWeight={600}
|
||||
title={order}
|
||||
style={{ marginRight: 0 }}
|
||||
>
|
||||
{order}
|
||||
</StyledText>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexCell;
|
@ -32,7 +32,9 @@ import TypeCell from "./TypeCell";
|
||||
import AuthorCell from "./AuthorCell";
|
||||
import DateCell from "./DateCell";
|
||||
import SizeCell from "./SizeCell";
|
||||
import IndexCell from "./IndexCell";
|
||||
import { classNames, getLastColumn } from "@docspace/shared/utils";
|
||||
import { RoomsType } from "@docspace/shared/enums";
|
||||
import {
|
||||
StyledBadgesContainer,
|
||||
StyledQuickButtonsContainer,
|
||||
@ -45,6 +47,7 @@ const RowDataComponent = (props) => {
|
||||
modifiedColumnIsEnabled,
|
||||
sizeColumnIsEnabled,
|
||||
typeColumnIsEnabled,
|
||||
indexColumnIsEnabled,
|
||||
quickButtonsColumnIsEnabled,
|
||||
|
||||
dragStyles,
|
||||
@ -58,6 +61,8 @@ const RowDataComponent = (props) => {
|
||||
showHotkeyBorder,
|
||||
badgesComponent,
|
||||
quickButtonsComponent,
|
||||
|
||||
isIndexing,
|
||||
tableStorageName,
|
||||
} = props;
|
||||
|
||||
@ -65,6 +70,24 @@ const RowDataComponent = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{indexColumnIsEnabled && isIndexing && (
|
||||
<TableCell
|
||||
className={classNames(
|
||||
selectionProp?.className,
|
||||
"table-container_index-cell",
|
||||
)}
|
||||
style={
|
||||
!indexColumnIsEnabled ? { background: "none" } : dragStyles.style
|
||||
}
|
||||
value={value}
|
||||
>
|
||||
<IndexCell
|
||||
sideColor={theme.filesSection.tableView.row.sideColor}
|
||||
{...props}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
|
||||
<TableCell
|
||||
{...dragStyles}
|
||||
className={classNames(
|
||||
@ -214,24 +237,30 @@ const RowDataComponent = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ tableStore }) => {
|
||||
export default inject(({ tableStore, indexingStore }) => {
|
||||
const {
|
||||
authorColumnIsEnabled,
|
||||
createdColumnIsEnabled,
|
||||
modifiedColumnIsEnabled,
|
||||
sizeColumnIsEnabled,
|
||||
indexColumnIsEnabled,
|
||||
typeColumnIsEnabled,
|
||||
quickButtonsColumnIsEnabled,
|
||||
tableStorageName,
|
||||
} = tableStore;
|
||||
|
||||
const { isIndexing } = indexingStore;
|
||||
|
||||
return {
|
||||
authorColumnIsEnabled,
|
||||
createdColumnIsEnabled,
|
||||
modifiedColumnIsEnabled,
|
||||
sizeColumnIsEnabled,
|
||||
indexColumnIsEnabled,
|
||||
typeColumnIsEnabled,
|
||||
quickButtonsColumnIsEnabled,
|
||||
|
||||
isIndexing,
|
||||
tableStorageName,
|
||||
};
|
||||
})(observer(RowDataComponent));
|
||||
|
@ -38,9 +38,13 @@ import withHotkeys from "../../../../HOCs/withHotkeys";
|
||||
import { Consumer, isMobile, isTablet } from "@docspace/shared/utils";
|
||||
import { isElementInViewport } from "@docspace/shared/utils/common";
|
||||
|
||||
import { DeviceType } from "@docspace/shared/enums";
|
||||
import { DeviceType, VDRIndexingAction } from "@docspace/shared/enums";
|
||||
|
||||
const separatorStyles = `width: 100vw; position: absolute; height: 3px; z-index: 1;`;
|
||||
const sectionClass = "section-wrapper-content";
|
||||
|
||||
let currentDroppable = null;
|
||||
let droppableSeparator = null;
|
||||
let isDragActive = false;
|
||||
|
||||
const SectionBodyContent = (props) => {
|
||||
@ -71,6 +75,8 @@ const SectionBodyContent = (props) => {
|
||||
isEmptyPage,
|
||||
movingInProgress,
|
||||
currentDeviceType,
|
||||
isIndexEditingMode,
|
||||
changeIndex,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
@ -203,7 +209,23 @@ const SectionBodyContent = (props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
droppableSeparator && droppableSeparator.remove();
|
||||
|
||||
const droppable = wrapperElement.closest(".droppable");
|
||||
const tableItem = wrapperElement.closest(".table-list-item");
|
||||
const styles = tableItem && window.getComputedStyle(tableItem);
|
||||
const indexSeparatorNode = document.createElement("div");
|
||||
indexSeparatorNode.classList.add("indexing-separator");
|
||||
|
||||
const parent = document.querySelector(
|
||||
".ReactVirtualized__Grid__innerScrollContainer",
|
||||
);
|
||||
|
||||
if (styles) {
|
||||
indexSeparatorNode.setAttribute("style", separatorStyles);
|
||||
indexSeparatorNode.style.top = styles.top;
|
||||
}
|
||||
|
||||
if (currentDroppable !== droppable) {
|
||||
if (currentDroppable) {
|
||||
if (viewAs === "table") {
|
||||
@ -213,12 +235,15 @@ const SectionBodyContent = (props) => {
|
||||
for (let cl of classElements) {
|
||||
cl.classList.remove("droppable-hover");
|
||||
}
|
||||
if (isIndexEditingMode) {
|
||||
droppableSeparator.remove();
|
||||
}
|
||||
} else {
|
||||
currentDroppable.classList.remove("droppable-hover");
|
||||
}
|
||||
}
|
||||
currentDroppable = droppable;
|
||||
|
||||
droppableSeparator = indexSeparatorNode;
|
||||
if (currentDroppable) {
|
||||
if (viewAs === "table") {
|
||||
const value = currentDroppable.getAttribute("value");
|
||||
@ -231,17 +256,38 @@ const SectionBodyContent = (props) => {
|
||||
cl.classList.add("droppable-hover");
|
||||
}
|
||||
}
|
||||
if (isIndexEditingMode) {
|
||||
parent.insertBefore(indexSeparatorNode, tableItem);
|
||||
}
|
||||
} else {
|
||||
currentDroppable.classList.add("droppable-hover");
|
||||
currentDroppable = droppable;
|
||||
droppableSeparator = indexSeparatorNode;
|
||||
}
|
||||
}
|
||||
} else if (isIndexEditingMode) {
|
||||
droppableSeparator && droppableSeparator.remove();
|
||||
|
||||
const wrappedClass = wrapperElement && wrapperElement.className;
|
||||
droppableSeparator = indexSeparatorNode;
|
||||
|
||||
if (wrappedClass === sectionClass) {
|
||||
indexSeparatorNode.setAttribute(
|
||||
"style",
|
||||
separatorStyles + "bottom: 0px;",
|
||||
);
|
||||
return parent.append(indexSeparatorNode);
|
||||
}
|
||||
|
||||
parent.insertBefore(indexSeparatorNode, tableItem);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (e) => {
|
||||
setStartDrag(false);
|
||||
|
||||
droppableSeparator && droppableSeparator.remove();
|
||||
|
||||
setTimeout(() => {
|
||||
isDragActive = false;
|
||||
setDragging(false);
|
||||
@ -254,17 +300,48 @@ const SectionBodyContent = (props) => {
|
||||
const isDragging = splitValue && splitValue.includes("dragging");
|
||||
const treeValue = isDragging ? splitValue[0] : null;
|
||||
|
||||
const elem = e.target.closest(".droppable");
|
||||
const elem = isIndexEditingMode
|
||||
? e.target.closest(".files-item") || e.target.closest(`.${sectionClass}`)
|
||||
: e.target.closest(".droppable");
|
||||
|
||||
const title = elem && elem.dataset.title;
|
||||
const value = elem && elem.getAttribute("value");
|
||||
if ((!value && !treeValue) || isRecycleBinFolder || !isDragActive) {
|
||||
if (
|
||||
((!value && !treeValue) || isRecycleBinFolder || !isDragActive) &&
|
||||
!isIndexEditingMode
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const folderId = value
|
||||
? value.split("_").slice(1, -3).join("_")
|
||||
: treeValue;
|
||||
onMoveTo(folderId, title);
|
||||
|
||||
if (!isIndexEditingMode) return onMoveTo(folderId, title);
|
||||
if (filesList.length === 1) return;
|
||||
|
||||
const replaceableItemId = isNaN(+folderId) ? folderId : +folderId;
|
||||
|
||||
const replaceableItemType = value && value.split("_").slice(0, 1).join("_");
|
||||
const isSectionTarget = elem && elem.className === sectionClass;
|
||||
|
||||
let replaceable;
|
||||
const item = isSectionTarget
|
||||
? filesList[filesList.length - 1]
|
||||
: filesList.find((i) =>
|
||||
replaceableItemType === "file"
|
||||
? i.id === replaceableItemId && i.fileExst
|
||||
: i.id === replaceableItemId,
|
||||
);
|
||||
|
||||
replaceable = item;
|
||||
if (item === filesList[filesList.length - 1] && !isSectionTarget) {
|
||||
replaceable = filesList[filesList.length - 2];
|
||||
}
|
||||
|
||||
if (!replaceable) return;
|
||||
|
||||
changeIndex(VDRIndexingAction.MoveIndex, replaceable, t);
|
||||
return;
|
||||
};
|
||||
|
||||
@ -340,6 +417,7 @@ export default inject(
|
||||
treeFoldersStore,
|
||||
filesActionsStore,
|
||||
uploadDataStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const {
|
||||
isEmptyFilesList,
|
||||
@ -372,6 +450,7 @@ export default inject(
|
||||
setTooltipPosition,
|
||||
isRecycleBinFolder: treeFoldersStore.isRecycleBinFolder,
|
||||
moveDragItems: filesActionsStore.moveDragItems,
|
||||
changeIndex: filesActionsStore.changeIndex,
|
||||
viewAs,
|
||||
setSelection,
|
||||
setBufferSelection,
|
||||
@ -387,6 +466,7 @@ export default inject(
|
||||
movingInProgress,
|
||||
currentDeviceType: settingsStore.currentDeviceType,
|
||||
isEmptyPage,
|
||||
isIndexEditingMode: indexingStore.isIndexEditingMode,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -316,6 +316,8 @@ const SectionFilterContent = ({
|
||||
isTrash,
|
||||
userId,
|
||||
isPersonalRoom,
|
||||
isIndexing,
|
||||
isIndexEditingMode,
|
||||
|
||||
providers,
|
||||
|
||||
@ -1618,6 +1620,13 @@ const SectionFilterContent = ({
|
||||
group: FilterGroups.roomFilterType,
|
||||
label: t("Common:PublicRoom"),
|
||||
};
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return {
|
||||
id: "filter_type-virtual-data",
|
||||
key: RoomsType.VirtualDataRoom,
|
||||
group: FilterGroups.roomFilterType,
|
||||
label: t("Common:VirtualDataRoom"),
|
||||
};
|
||||
case RoomsType.CustomRoom:
|
||||
default:
|
||||
return {
|
||||
@ -2656,6 +2665,8 @@ const SectionFilterContent = ({
|
||||
isPeopleAccounts={isPeopleAccounts}
|
||||
isGroupsAccounts={isGroupsAccounts}
|
||||
isInsideGroup={isInsideGroup}
|
||||
isIndexing={isIndexing}
|
||||
isIndexEditingMode={isIndexEditingMode}
|
||||
disableThirdParty={isTrash}
|
||||
/>
|
||||
);
|
||||
@ -2674,6 +2685,7 @@ export default inject(
|
||||
userStore,
|
||||
settingsStore,
|
||||
currentQuotaStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const {
|
||||
filter,
|
||||
@ -2713,6 +2725,7 @@ export default inject(
|
||||
const { isVisible: infoPanelVisible } = infoPanelStore;
|
||||
const { showStorageInfo, isDefaultRoomsQuotaSet } = currentQuotaStore;
|
||||
|
||||
const { isIndexing, isIndexEditingMode } = indexingStore;
|
||||
const {
|
||||
filterStore,
|
||||
|
||||
@ -2752,6 +2765,8 @@ export default inject(
|
||||
isRooms,
|
||||
isTrash,
|
||||
isArchiveFolder,
|
||||
isIndexing,
|
||||
isIndexEditingMode,
|
||||
|
||||
setIsLoading: clientLoadingStore.setIsSectionBodyLoading,
|
||||
showFilterLoader: clientLoadingStore.showFilterLoader,
|
||||
|
@ -25,6 +25,7 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import PublicRoomIconUrl from "PUBLIC_DIR/images/public-room.react.svg?url";
|
||||
import LifetimeRoomIconUrl from "PUBLIC_DIR/images/lifetime-room.react.svg?url";
|
||||
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
@ -50,6 +51,8 @@ import {
|
||||
getCategoryUrl,
|
||||
} from "SRC_DIR/helpers/utils";
|
||||
import TariffBar from "SRC_DIR/components/TariffBar";
|
||||
import IndexMenu from "../IndexHeader";
|
||||
import { getLifetimePeriodTranslation } from "@docspace/shared/utils/common";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
@ -145,6 +148,21 @@ const StyledContainer = styled.div`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isVirtualDataRoomType &&
|
||||
css`
|
||||
.title-icon {
|
||||
svg {
|
||||
path {
|
||||
fill: ${({ theme }) =>
|
||||
theme.navigation.lifetimeIconFill} !important;
|
||||
stroke: ${({ theme }) =>
|
||||
theme.navigation.lifetimeIconStroke} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = (props) => {
|
||||
@ -155,6 +173,7 @@ const SectionHeaderContent = (props) => {
|
||||
t,
|
||||
isRoomsFolder,
|
||||
security,
|
||||
setIsIndexEditingMode,
|
||||
tReady,
|
||||
isInfoPanelVisible,
|
||||
isRootFolder,
|
||||
@ -168,6 +187,7 @@ const SectionHeaderContent = (props) => {
|
||||
isArchiveFolder,
|
||||
isEmptyFilesList,
|
||||
isHeaderVisible,
|
||||
isIndexEditingMode,
|
||||
isHeaderChecked,
|
||||
isHeaderIndeterminate,
|
||||
showText,
|
||||
@ -211,6 +231,8 @@ const SectionHeaderContent = (props) => {
|
||||
isPublicRoom,
|
||||
theme,
|
||||
isPublicRoomType,
|
||||
isVirtualDataRoomType,
|
||||
|
||||
moveToPublicRoom,
|
||||
currentDeviceType,
|
||||
isFrame,
|
||||
@ -219,11 +241,13 @@ const SectionHeaderContent = (props) => {
|
||||
onCreateAndCopySharedLink,
|
||||
showNavigationButton,
|
||||
startUpload,
|
||||
reorder,
|
||||
getFolderModel,
|
||||
onCreateRoom,
|
||||
onEmptyTrashAction,
|
||||
getHeaderOptions,
|
||||
setBufferSelection,
|
||||
setReorderDialogVisible,
|
||||
} = props;
|
||||
|
||||
const location = useLocation();
|
||||
@ -388,6 +412,12 @@ const SectionHeaderContent = (props) => {
|
||||
isMobileView: currentDeviceType === DeviceType.mobile,
|
||||
};
|
||||
|
||||
const indexMenuProps = {
|
||||
setIsIndexEditingMode,
|
||||
t,
|
||||
setReorderDialogVisible,
|
||||
};
|
||||
|
||||
if (isAccountsPage && !(isGroupsPage && isRoomAdmin)) {
|
||||
tableGroupMenuVisible =
|
||||
(!isGroupsPage ? isAccountsHeaderVisible : isGroupsHeaderVisible) &&
|
||||
@ -474,6 +504,17 @@ const SectionHeaderContent = (props) => {
|
||||
const logo = getLogoUrl(WhiteLabelLogoType.LightSmall, !theme.isBase);
|
||||
const burgerLogo = getLogoUrl(WhiteLabelLogoType.LeftMenu, !theme.isBase);
|
||||
|
||||
const titleIcon =
|
||||
(isPublicRoomType && !isPublicRoom && PublicRoomIconUrl) ||
|
||||
(isVirtualDataRoomType && selectedFolder.lifetime && LifetimeRoomIconUrl);
|
||||
|
||||
const titleIconTooltip = selectedFolder.lifetime
|
||||
? t("Files:RoomFilesLifetime", {
|
||||
days: selectedFolder.lifetime.value,
|
||||
period: getLifetimePeriodTranslation(selectedFolder.lifetime.period, t),
|
||||
})
|
||||
: null;
|
||||
|
||||
const navigationButtonLabel = showNavigationButton
|
||||
? t("Files:ShareRoom")
|
||||
: null;
|
||||
@ -481,9 +522,14 @@ const SectionHeaderContent = (props) => {
|
||||
return (
|
||||
<Consumer key="header">
|
||||
{(context) => (
|
||||
<StyledContainer isRecycleBinFolder={isRecycleBinFolder}>
|
||||
<StyledContainer
|
||||
isRecycleBinFolder={isRecycleBinFolder}
|
||||
isVirtualDataRoomType={isVirtualDataRoomType}
|
||||
>
|
||||
{tableGroupMenuVisible ? (
|
||||
<TableGroupMenu {...tableGroupMenuProps} withComboBox />
|
||||
) : isIndexEditingMode ? (
|
||||
<IndexMenu {...indexMenuProps} />
|
||||
) : (
|
||||
<div className="header-container">
|
||||
<Navigation
|
||||
@ -534,9 +580,8 @@ const SectionHeaderContent = (props) => {
|
||||
withLogo={isPublicRoom && logo}
|
||||
burgerLogo={isPublicRoom && burgerLogo}
|
||||
isPublicRoom={isPublicRoom}
|
||||
titleIcon={
|
||||
currentIsPublicRoomType && !isPublicRoom && PublicRoomIconUrl
|
||||
}
|
||||
titleIcon={titleIcon}
|
||||
titleIconTooltip={titleIconTooltip}
|
||||
showRootFolderTitle={insideTheRoom || isInsideGroup}
|
||||
currentDeviceType={currentDeviceType}
|
||||
isFrame={isFrame}
|
||||
@ -592,6 +637,8 @@ export default inject(
|
||||
userStore,
|
||||
settingsStore,
|
||||
uploadDataStore,
|
||||
indexingStore,
|
||||
dialogsStore,
|
||||
}) => {
|
||||
const { startUpload } = uploadDataStore;
|
||||
const isRoomAdmin = userStore.user?.isRoomAdmin;
|
||||
@ -630,12 +677,15 @@ export default inject(
|
||||
const { isRecycleBinFolder, isRoomsFolder, isArchiveFolder } =
|
||||
treeFoldersStore;
|
||||
|
||||
const { setReorderDialogVisible } = dialogsStore;
|
||||
|
||||
const {
|
||||
getHeaderMenu,
|
||||
isGroupMenuBlocked,
|
||||
moveToRoomsPage,
|
||||
onClickBack,
|
||||
moveToPublicRoom,
|
||||
reorder,
|
||||
} = filesActionsStore;
|
||||
|
||||
const { setIsVisible, isVisible } = infoPanelStore;
|
||||
@ -663,6 +713,7 @@ export default inject(
|
||||
|
||||
const isRoom = !!roomType;
|
||||
const isPublicRoomType = roomType === RoomsType.PublicRoom;
|
||||
const isVirtualDataRoomType = roomType === RoomsType.VirtualDataRoom;
|
||||
const isCustomRoomType = roomType === RoomsType.CustomRoom;
|
||||
const isFormRoomType = roomType === RoomsType.FormRoom;
|
||||
|
||||
@ -698,6 +749,7 @@ export default inject(
|
||||
getCheckboxItemLabel: getAccountsCheckboxItemLabel,
|
||||
} = headerMenuStore;
|
||||
|
||||
const { isIndexEditingMode, setIsIndexEditingMode } = indexingStore;
|
||||
const { setSelected: setAccountsSelected } = selectionStore;
|
||||
const { isPublicRoom } = publicRoomStore;
|
||||
|
||||
@ -740,6 +792,8 @@ export default inject(
|
||||
setIsInfoPanelVisible: setIsVisible,
|
||||
isInfoPanelVisible: isVisible,
|
||||
isHeaderVisible,
|
||||
isIndexEditingMode,
|
||||
setIsIndexEditingMode,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
isTabletView: settingsStore.isTabletView,
|
||||
@ -771,6 +825,7 @@ export default inject(
|
||||
moveToRoomsPage,
|
||||
onClickBack,
|
||||
isPublicRoomType,
|
||||
isVirtualDataRoomType,
|
||||
isPublicRoom,
|
||||
|
||||
moveToPublicRoom,
|
||||
@ -801,11 +856,13 @@ export default inject(
|
||||
onCreateAndCopySharedLink,
|
||||
showNavigationButton,
|
||||
startUpload,
|
||||
reorder,
|
||||
getFolderModel,
|
||||
onCreateRoom,
|
||||
onEmptyTrashAction,
|
||||
getHeaderOptions,
|
||||
setBufferSelection,
|
||||
setReorderDialogVisible,
|
||||
};
|
||||
},
|
||||
)(
|
||||
|
@ -0,0 +1,142 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
import styled from "styled-components";
|
||||
import {
|
||||
mobile,
|
||||
tablet,
|
||||
getCorrectBorderRadius,
|
||||
getCorrectFourValuesStyle,
|
||||
} from "@docspace/shared/utils";
|
||||
|
||||
const StyledTableIndexMenu = styled.div`
|
||||
position: relative;
|
||||
|
||||
background: ${(props) => props.theme.tableContainer.groupMenu.background};
|
||||
border-bottom: ${(props) =>
|
||||
props.theme.tableContainer.groupMenu.borderBottom};
|
||||
box-shadow: ${(props) => props.theme.tableContainer.groupMenu.boxShadow};
|
||||
border-radius: ${({ theme }) =>
|
||||
getCorrectBorderRadius("0px 0px 6px 6px", theme.interfaceDirection)};
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 100%;
|
||||
margin: 0px 0px 0px -20px;
|
||||
padding: 0 20px 0 20px;
|
||||
height: 68px;
|
||||
|
||||
z-index: 199;
|
||||
|
||||
@media ${tablet} {
|
||||
height: 60px;
|
||||
}
|
||||
@media ${mobile} {
|
||||
height: 48px;
|
||||
padding: 0px 18px;
|
||||
}
|
||||
|
||||
.table-header_index-separator {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? `border-left: ${props.theme.tableContainer.groupMenu.borderRight};`
|
||||
: `border-right: ${props.theme.tableContainer.groupMenu.borderRight};`}
|
||||
width: 1px;
|
||||
height: 21px;
|
||||
margin: ${({ theme }) =>
|
||||
getCorrectFourValuesStyle("0 16px 0 20px", theme.interfaceDirection)};
|
||||
|
||||
@media ${tablet} {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-header_reorder-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.table-header_index-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-header_reorder-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.table-header_reorder-container {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.table-header_reorder-icon {
|
||||
svg {
|
||||
path[fill] {
|
||||
fill: ${(props) => props.theme.button.color.base};
|
||||
}
|
||||
|
||||
path[stroke] {
|
||||
stroke: ${(props) => props.theme.button.color.base};
|
||||
}
|
||||
}
|
||||
|
||||
:hover {
|
||||
svg {
|
||||
path[fill] {
|
||||
fill: ${(props) => props.theme.button.color.baseHover};
|
||||
}
|
||||
|
||||
path[stroke] {
|
||||
stroke: ${(props) => props.theme.button.color.baseHover};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:active {
|
||||
svg {
|
||||
path[fill] {
|
||||
fill: ${(props) => props.theme.button.color.baseActive};
|
||||
}
|
||||
|
||||
path[stroke] {
|
||||
stroke: ${(props) => props.theme.button.color.baseActive};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledTableIndexMenu };
|
82
packages/client/src/pages/Home/Section/IndexHeader/index.tsx
Normal file
82
packages/client/src/pages/Home/Section/IndexHeader/index.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { TTranslation } from "@docspace/shared/types";
|
||||
import RoundedArrowSvgUrl from "PUBLIC_DIR/images/rounded arrow.react.svg?url";
|
||||
import CrossIconSvgUrl from "PUBLIC_DIR/images/cross.react.svg?url";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { StyledTableIndexMenu } from "./StyledIndexHeader";
|
||||
|
||||
export interface IndexMenuProps {
|
||||
setIsIndexEditingMode: (mode: boolean) => void;
|
||||
setReorderDialogVisible: (visible: boolean) => void;
|
||||
t: TTranslation;
|
||||
}
|
||||
|
||||
const IndexMenu = ({
|
||||
setIsIndexEditingMode,
|
||||
t,
|
||||
setReorderDialogVisible,
|
||||
}: IndexMenuProps) => {
|
||||
return (
|
||||
<StyledTableIndexMenu>
|
||||
<div className="table-header_index-container">
|
||||
<Text fontSize="14px" lineHeight="16px" fontWeight={600}>
|
||||
{t("Common:SortingIndex")}
|
||||
</Text>
|
||||
|
||||
<div className="table-header_index-separator" />
|
||||
<div
|
||||
className="table-header_reorder-container"
|
||||
onClick={() => setReorderDialogVisible(true)}
|
||||
>
|
||||
<IconButton
|
||||
className="table-header_reorder-icon"
|
||||
size={16}
|
||||
onClick={() => {}}
|
||||
iconName={RoundedArrowSvgUrl}
|
||||
isFill
|
||||
isClickable={false}
|
||||
/>
|
||||
<Text fontSize="12px" lineHeight="16px" fontWeight={600}>
|
||||
{t("Files:Reorder")}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<IconButton
|
||||
className="table-header_cross-icon"
|
||||
size={16}
|
||||
onClick={() => setIsIndexEditingMode(false)}
|
||||
iconName={CrossIconSvgUrl}
|
||||
isFill
|
||||
isClickable={false}
|
||||
/>
|
||||
</StyledTableIndexMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexMenu;
|
@ -39,6 +39,7 @@ const SelectionArea = (props) => {
|
||||
foldersLength,
|
||||
filesLength,
|
||||
isInfoPanelVisible,
|
||||
isIndexEditingMode,
|
||||
} = props;
|
||||
|
||||
const [countTilesInRow, setCountTilesInRow] = useState(getCountTilesInRow());
|
||||
@ -87,7 +88,7 @@ const SelectionArea = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
return isMobile || dragging ? (
|
||||
return isMobile || dragging || isIndexEditingMode ? (
|
||||
<></>
|
||||
) : (
|
||||
<SelectionAreaComponent
|
||||
@ -107,7 +108,8 @@ const SelectionArea = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore, treeFoldersStore, infoPanelStore }) => {
|
||||
export default inject(
|
||||
({ filesStore, treeFoldersStore, infoPanelStore, indexingStore }) => {
|
||||
const {
|
||||
dragging,
|
||||
viewAs,
|
||||
@ -118,6 +120,7 @@ export default inject(({ filesStore, treeFoldersStore, infoPanelStore }) => {
|
||||
} = filesStore;
|
||||
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
|
||||
const { isVisible: isInfoPanelVisible } = infoPanelStore;
|
||||
const { isIndexEditingMode } = indexingStore;
|
||||
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
|
||||
@ -130,5 +133,7 @@ export default inject(({ filesStore, treeFoldersStore, infoPanelStore }) => {
|
||||
foldersLength: folders.length,
|
||||
filesLength: files.length,
|
||||
isInfoPanelVisible,
|
||||
isIndexEditingMode,
|
||||
};
|
||||
})(observer(SelectionArea));
|
||||
},
|
||||
)(observer(SelectionArea));
|
||||
|
@ -115,6 +115,7 @@ const PureHome = (props) => {
|
||||
isPrivacyFolder,
|
||||
isRecycleBinFolder,
|
||||
isErrorRoomNotAvailable,
|
||||
isIndexEditingMode,
|
||||
|
||||
primaryProgressDataPercent,
|
||||
primaryProgressDataIcon,
|
||||
@ -351,6 +352,7 @@ const PureHome = (props) => {
|
||||
sectionProps.secondaryProgressBarIcon = secondaryProgressDataStoreIcon;
|
||||
sectionProps.showSecondaryButtonAlert = secondaryProgressDataStoreAlert;
|
||||
sectionProps.getContextModel = getContextModel;
|
||||
sectionProps.isIndexEditingMode = isIndexEditingMode;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -439,6 +441,7 @@ export default inject(
|
||||
currentTariffStatusStore,
|
||||
settingsStore,
|
||||
contextOptionsStore,
|
||||
indexingStore,
|
||||
}) => {
|
||||
const { setSelectedFolder, security: folderSecurity } = selectedFolderStore;
|
||||
const {
|
||||
@ -605,6 +608,7 @@ export default inject(
|
||||
isErrorRoomNotAvailable,
|
||||
isRoomsFolder,
|
||||
isArchiveFolder,
|
||||
isIndexEditingMode: indexingStore.isIndexEditingMode,
|
||||
|
||||
disableDrag,
|
||||
|
||||
|
@ -56,6 +56,7 @@ import UnmuteReactSvgUrl from "PUBLIC_DIR/images/unmute.react.svg?url";
|
||||
import MuteReactSvgUrl from "PUBLIC_DIR/images/icons/16/mute.react.svg?url";
|
||||
import ShareReactSvgUrl from "PUBLIC_DIR/images/share.react.svg?url";
|
||||
import InvitationLinkReactSvgUrl from "PUBLIC_DIR/images/invitation.link.react.svg?url";
|
||||
import EditIndexReactSvgUrl from "PUBLIC_DIR/images/edit.index.react.svg?url";
|
||||
import TabletLinkReactSvgUrl from "PUBLIC_DIR/images/tablet-link.react.svg?url";
|
||||
import MailReactSvgUrl from "PUBLIC_DIR/images/mail.react.svg?url";
|
||||
import RoomArchiveSvgUrl from "PUBLIC_DIR/images/room.archive.svg?url";
|
||||
@ -80,6 +81,7 @@ import ActionsUploadReactSvgUrl from "PUBLIC_DIR/images/actions.upload.react.svg
|
||||
import PluginMoreReactSvgUrl from "PUBLIC_DIR/images/plugin.more.react.svg?url";
|
||||
import CodeReactSvgUrl from "PUBLIC_DIR/images/code.react.svg?url";
|
||||
import ClearTrashReactSvgUrl from "PUBLIC_DIR/images/clear.trash.react.svg?url";
|
||||
import ExportRoomIndexSvgUrl from "PUBLIC_DIR/images/icons/16/export-room-index.react.svg?url";
|
||||
|
||||
import { getCategoryUrl } from "@docspace/client/src/helpers/utils";
|
||||
|
||||
@ -132,6 +134,7 @@ class ContextOptionsStore {
|
||||
infoPanelStore;
|
||||
currentTariffStatusStore;
|
||||
userStore;
|
||||
indexingStore;
|
||||
clientLoadingStore;
|
||||
|
||||
linksIsLoading = false;
|
||||
@ -153,6 +156,7 @@ class ContextOptionsStore {
|
||||
infoPanelStore,
|
||||
currentTariffStatusStore,
|
||||
userStore,
|
||||
indexingStore,
|
||||
clientLoadingStore,
|
||||
) {
|
||||
makeAutoObservable(this);
|
||||
@ -172,6 +176,7 @@ class ContextOptionsStore {
|
||||
this.infoPanelStore = infoPanelStore;
|
||||
this.currentTariffStatusStore = currentTariffStatusStore;
|
||||
this.userStore = userStore;
|
||||
this.indexingStore = indexingStore;
|
||||
this.clientLoadingStore = clientLoadingStore;
|
||||
}
|
||||
|
||||
@ -913,6 +918,14 @@ class ContextOptionsStore {
|
||||
this.filesActionsStore.setMuteAction(action, item, t);
|
||||
};
|
||||
|
||||
onExportRoomIndex = (t, roomId) => {
|
||||
this.filesActionsStore.exportRoomIndex(t, roomId);
|
||||
};
|
||||
|
||||
onEditIndex = () => {
|
||||
this.indexingStore.setIsIndexEditingMode(true);
|
||||
};
|
||||
|
||||
onClickRemoveFromRecent = (item) => {
|
||||
this.filesActionsStore.removeFilesFromRecent([item.id]);
|
||||
};
|
||||
@ -1390,6 +1403,7 @@ class ContextOptionsStore {
|
||||
item.roomType === RoomsType.PublicRoom ||
|
||||
item.roomType === RoomsType.FormRoom ||
|
||||
item.roomType === RoomsType.CustomRoom;
|
||||
const isVDRRoomType = item.roomType === RoomsType.VirtualDataRoom;
|
||||
|
||||
const { shared, navigationPath } = this.selectedFolderStore;
|
||||
|
||||
@ -1400,6 +1414,17 @@ class ContextOptionsStore {
|
||||
const isArchive = item.rootFolderType === FolderType.Archive;
|
||||
const isShared = shared || navigationPath.findIndex((r) => r.shared) > -1;
|
||||
|
||||
const { isIndexing } = this.indexingStore;
|
||||
|
||||
const indexOptions = {
|
||||
id: "option_edit-index",
|
||||
key: "edit-index",
|
||||
label: t("Common:EditIndex"),
|
||||
icon: EditIndexReactSvgUrl,
|
||||
onClick: () => this.onEditIndex(),
|
||||
disabled: !isIndexing && item.security?.Edit,
|
||||
};
|
||||
|
||||
const optionsModel = [
|
||||
{
|
||||
id: "option_select",
|
||||
@ -1582,6 +1607,14 @@ class ContextOptionsStore {
|
||||
},
|
||||
...pinOptions,
|
||||
...muteOptions,
|
||||
{
|
||||
id: "option_export-room-index",
|
||||
key: "export-room-index",
|
||||
label: t("Files:ExportRoomIndex"),
|
||||
icon: ExportRoomIndexSvgUrl,
|
||||
onClick: () => this.onExportRoomIndex(t, item.id),
|
||||
disabled: !isVDRRoomType || !item.indexing,
|
||||
},
|
||||
{
|
||||
id: "option_owner-change",
|
||||
key: "owner-change",
|
||||
@ -1702,6 +1735,7 @@ class ContextOptionsStore {
|
||||
onClick: this.onRestoreAction,
|
||||
disabled: false,
|
||||
},
|
||||
indexOptions,
|
||||
{
|
||||
id: "option_rename",
|
||||
key: "rename",
|
||||
@ -1783,7 +1817,6 @@ class ContextOptionsStore {
|
||||
disabled: !this.treeFoldersStore.isRecentTab,
|
||||
},
|
||||
];
|
||||
|
||||
const options = this.filterModel(optionsModel, contextOptions);
|
||||
|
||||
const pluginItems = this.onLoadPlugins(item);
|
||||
|
@ -25,12 +25,15 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import isEqual from "lodash/isEqual";
|
||||
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { isDesktop } from "@docspace/shared/utils";
|
||||
import FilesFilter from "@docspace/shared/api/files/filter";
|
||||
import { getCategoryUrl } from "SRC_DIR/helpers/utils";
|
||||
import { CategoryType } from "SRC_DIR/helpers/constants";
|
||||
import { RoomsType } from "@docspace/shared/enums";
|
||||
import { setWatermarkSettings } from "@docspace/shared/api/rooms";
|
||||
|
||||
class CreateEditRoomStore {
|
||||
roomParams = null;
|
||||
@ -46,6 +49,9 @@ class CreateEditRoomStore {
|
||||
settingsStore = null;
|
||||
infoPanelStore = null;
|
||||
currentQuotaStore = null;
|
||||
watermarksSettings = {};
|
||||
initialWatermarksSettings = {};
|
||||
isImageType = false;
|
||||
|
||||
constructor(
|
||||
filesStore,
|
||||
@ -91,6 +97,113 @@ class CreateEditRoomStore {
|
||||
this.onClose = onClose;
|
||||
};
|
||||
|
||||
setInitialWatermarks = (watermarksSettings) => {
|
||||
this.initialWatermarksSettings = !watermarksSettings
|
||||
? { enabled: false }
|
||||
: watermarksSettings;
|
||||
|
||||
this.initialWatermarksSettings.isImage =
|
||||
!!this.initialWatermarksSettings.imageUrl;
|
||||
|
||||
this.setWatermarks(this.initialWatermarksSettings);
|
||||
};
|
||||
|
||||
setWatermarks = (object) => {
|
||||
for (const [key, value] of Object.entries(object)) {
|
||||
this.watermarksSettings[key] = value;
|
||||
}
|
||||
};
|
||||
|
||||
resetWatermarks = () => {
|
||||
this.watermarksSettings = {};
|
||||
this.initialWatermarksSettings = {};
|
||||
};
|
||||
|
||||
isEqualWatermarkChanges = () => {
|
||||
return isEqual(this.watermarksSettings, this.initialWatermarksSettings);
|
||||
};
|
||||
|
||||
isNotWatermarkSet = () => {
|
||||
if (
|
||||
this.watermarksSettings.isImage &&
|
||||
!this.watermarksSettings.image &&
|
||||
!this.watermarksSettings.imageUrl
|
||||
)
|
||||
return true;
|
||||
|
||||
if (
|
||||
!this.watermarksSettings.isImage &&
|
||||
this.watermarksSettings.additions === 0
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
getWatermarkRequest = async (room) => {
|
||||
if (!this.watermarksSettings.isImage) {
|
||||
return setWatermarkSettings(room.id, {
|
||||
enabled: this.watermarksSettings.enabled,
|
||||
rotate: this.watermarksSettings.rotate,
|
||||
text: this.watermarksSettings.text,
|
||||
additions: this.watermarksSettings.additions,
|
||||
});
|
||||
}
|
||||
|
||||
const watermarkImage = this.watermarksSettings.image;
|
||||
const watermarksSettings = this.watermarksSettings;
|
||||
|
||||
const getMeta = (url, onSetInfo) => {
|
||||
//url for this.watermarksSettings.image.viewUrl
|
||||
const img = new Image();
|
||||
const imgUrl = url ?? URL.createObjectURL(watermarkImage);
|
||||
img.src = imgUrl;
|
||||
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(imgUrl);
|
||||
onSetInfo(null, img);
|
||||
};
|
||||
|
||||
img.onerror = (err) => onSetInfo(err);
|
||||
};
|
||||
|
||||
if (!watermarkImage && this.watermarksSettings.imageUrl) {
|
||||
return setWatermarkSettings(room.id, {
|
||||
enabled: watermarksSettings.enabled,
|
||||
imageScale: watermarksSettings.imageScale,
|
||||
rotate: watermarksSettings.rotate,
|
||||
imageUrl: watermarksSettings.imageUrl,
|
||||
// imageId: watermarksSettings.image.id,
|
||||
imageWidth: watermarksSettings.imageWidth,
|
||||
imageHeight: watermarksSettings.imageHeight,
|
||||
});
|
||||
}
|
||||
|
||||
const { uploadRoomLogo } = this.filesStore;
|
||||
|
||||
const uploadWatermarkData = new FormData();
|
||||
uploadWatermarkData.append(0, watermarkImage);
|
||||
|
||||
const response = await uploadRoomLogo(uploadWatermarkData);
|
||||
|
||||
getMeta(null, (err, img) => {
|
||||
if (err) {
|
||||
toastr.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
return setWatermarkSettings(room.id, {
|
||||
enabled: watermarksSettings.enabled,
|
||||
imageScale: watermarksSettings.imageScale,
|
||||
rotate: watermarksSettings.rotate,
|
||||
imageUrl: response.data,
|
||||
// imageId: watermarksSettings.image.id,
|
||||
imageWidth: img.naturalWidth,
|
||||
imageHeight: img.naturalHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onCreateRoom = async (withConfirm = false, t) => {
|
||||
const roomParams = this.roomParams;
|
||||
|
||||
@ -125,6 +238,8 @@ class CreateEditRoomStore {
|
||||
const createRoomData = {
|
||||
roomType: roomParams.type,
|
||||
title: roomParams.title || t("Common:NewRoom"),
|
||||
indexing: roomParams.indexing,
|
||||
lifetime: roomParams.lifetime,
|
||||
createAsNewFolder: roomParams.createAsNewFolder ?? true,
|
||||
...(quotaLimit && {
|
||||
quota: +quotaLimit,
|
||||
@ -156,10 +271,17 @@ class CreateEditRoomStore {
|
||||
|
||||
const actions = [];
|
||||
|
||||
const requests = [];
|
||||
|
||||
if (this.watermarksSettings.enabled && !this.isNotWatermarkSet()) {
|
||||
requests.push(this.getWatermarkRequest(room));
|
||||
}
|
||||
|
||||
// delete thirdparty account if not needed
|
||||
if (!isThirdparty && storageFolderId)
|
||||
await deleteThirdParty(thirdpartyAccount.providerId);
|
||||
requests.push(deleteThirdParty(thirdpartyAccount.providerId));
|
||||
|
||||
await Promise.all(requests);
|
||||
// create new tags
|
||||
for (let i = 0; i < createTagsData.length; i++) {
|
||||
actions.push(createTag(createTagsData[i]));
|
||||
@ -174,6 +296,7 @@ class CreateEditRoomStore {
|
||||
await uploadRoomLogo(uploadLogoData).then(async (response) => {
|
||||
const url = URL.createObjectURL(roomParams.icon.uploadedFile);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = async () => {
|
||||
const { x, y, zoom } = roomParams.icon;
|
||||
try {
|
||||
|
@ -43,11 +43,13 @@ class DialogsStore {
|
||||
ownerPanelVisible = false;
|
||||
moveToPanelVisible = false;
|
||||
restorePanelVisible = false;
|
||||
reorderDialogVisible = false;
|
||||
copyPanelVisible = false;
|
||||
deleteThirdPartyDialogVisible = false;
|
||||
connectDialogVisible = false;
|
||||
thirdPartyMoveDialogVisible = false;
|
||||
deleteDialogVisible = false;
|
||||
lifetimeDialogVisible = false;
|
||||
downloadDialogVisible = false;
|
||||
emptyTrashDialogVisible = false;
|
||||
newFilesPanelVisible = false;
|
||||
@ -232,6 +234,10 @@ class DialogsStore {
|
||||
this.deleteDialogVisible = deleteDialogVisible;
|
||||
};
|
||||
|
||||
setLifetimeDialogVisible = (lifetimeDialogVisible) => {
|
||||
this.lifetimeDialogVisible = lifetimeDialogVisible;
|
||||
};
|
||||
|
||||
setEventDialogVisible = (eventDialogVisible) => {
|
||||
this.eventDialogVisible = eventDialogVisible;
|
||||
};
|
||||
@ -514,6 +520,10 @@ class DialogsStore {
|
||||
this.pdfFormEditVisible = visible;
|
||||
this.pdfFormEditData = data;
|
||||
};
|
||||
|
||||
setReorderDialogVisible = (visible) => {
|
||||
this.reorderDialogVisible = visible;
|
||||
};
|
||||
}
|
||||
|
||||
export default DialogsStore;
|
||||
|
@ -54,15 +54,19 @@ import {
|
||||
moveToFolder,
|
||||
getFolder,
|
||||
deleteFilesFromRecent,
|
||||
changeIndex,
|
||||
reorder,
|
||||
} from "@docspace/shared/api/files";
|
||||
import {
|
||||
ConflictResolveType,
|
||||
Events,
|
||||
ExportRoomIndexTaskStatus,
|
||||
FileAction,
|
||||
FileStatus,
|
||||
FolderType,
|
||||
RoomsType,
|
||||
ShareAccessRights,
|
||||
VDRIndexingAction,
|
||||
} from "@docspace/shared/enums";
|
||||
import { makeAutoObservable } from "mobx";
|
||||
|
||||
@ -87,6 +91,9 @@ import {
|
||||
} from "SRC_DIR/helpers/utils";
|
||||
import { MEDIA_VIEW_URL } from "@docspace/shared/constants";
|
||||
import { openingNewTab } from "@docspace/shared/utils/openingNewTab";
|
||||
import { changeRoomLifetime } from "@docspace/shared/api/rooms";
|
||||
import api from "@docspace/shared/api";
|
||||
import { showSuccessExportRoomIndexToast } from "SRC_DIR/helpers/toast-helpers";
|
||||
|
||||
class FilesActionStore {
|
||||
settingsStore;
|
||||
@ -102,6 +109,7 @@ class FilesActionStore {
|
||||
publicRoomStore;
|
||||
infoPanelStore;
|
||||
peopleStore;
|
||||
indexingStore;
|
||||
userStore = null;
|
||||
currentTariffStatusStore = null;
|
||||
currentQuotaStore = null;
|
||||
@ -110,6 +118,7 @@ class FilesActionStore {
|
||||
isGroupMenuBlocked = false;
|
||||
emptyTrashInProgress = false;
|
||||
processCreatingRoomFromData = false;
|
||||
alreadyExportingRoomIndex = false;
|
||||
|
||||
constructor(
|
||||
settingsStore,
|
||||
@ -129,6 +138,7 @@ class FilesActionStore {
|
||||
currentTariffStatusStore,
|
||||
peopleStore,
|
||||
currentQuotaStore,
|
||||
indexingStore,
|
||||
) {
|
||||
makeAutoObservable(this);
|
||||
this.settingsStore = settingsStore;
|
||||
@ -148,6 +158,7 @@ class FilesActionStore {
|
||||
this.currentTariffStatusStore = currentTariffStatusStore;
|
||||
this.peopleStore = peopleStore;
|
||||
this.currentQuotaStore = currentQuotaStore;
|
||||
this.indexingStore = indexingStore;
|
||||
}
|
||||
|
||||
setIsBulkDownload = (isBulkDownload) => {
|
||||
@ -2718,6 +2729,10 @@ class FilesActionStore {
|
||||
await refreshFiles();
|
||||
};
|
||||
|
||||
changeRoomLifetime = (roomId, lifetime) => {
|
||||
return changeRoomLifetime(roomId, lifetime);
|
||||
};
|
||||
|
||||
copyFromTemplateForm = async (fileInfo, t) => {
|
||||
const selectedItemId = this.selectedFolderStore.id;
|
||||
const fileIds = [fileInfo.id];
|
||||
@ -2748,6 +2763,183 @@ class FilesActionStore {
|
||||
.itemOperationToFolder(operationData)
|
||||
.catch((error) => toastr.error(error));
|
||||
};
|
||||
changeIndex = async (action, item, t) => {
|
||||
const { filesList } = this.filesStore;
|
||||
|
||||
const index = filesList.findIndex(
|
||||
(elem) => elem.id === item?.id && elem.fileExst === item?.fileExst,
|
||||
);
|
||||
|
||||
if (
|
||||
(action === VDRIndexingAction.HigherIndex && index === 0) ||
|
||||
(action === VDRIndexingAction.LowerIndex &&
|
||||
index === filesList.length - 1)
|
||||
)
|
||||
return;
|
||||
|
||||
const { bufferSelection } = this.filesStore;
|
||||
|
||||
let selection = this.filesStore.selection.length
|
||||
? this.filesStore.selection
|
||||
: [bufferSelection];
|
||||
|
||||
const { id } = this.selectedFolderStore;
|
||||
const { setUpdateItems } = this.indexingStore;
|
||||
|
||||
let replaceable;
|
||||
let current = item;
|
||||
|
||||
switch (action) {
|
||||
case VDRIndexingAction.HigherIndex:
|
||||
replaceable = filesList[index - 1];
|
||||
break;
|
||||
|
||||
case VDRIndexingAction.LowerIndex:
|
||||
replaceable = filesList[index + 1];
|
||||
break;
|
||||
|
||||
default:
|
||||
current = selection[0];
|
||||
replaceable = item;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!replaceable) return;
|
||||
|
||||
try {
|
||||
await changeIndex(current?.id, replaceable.order, current?.isFolder);
|
||||
|
||||
const items = [current, replaceable];
|
||||
setUpdateItems(items);
|
||||
|
||||
const operationId = uniqueid("operation_");
|
||||
this.updateCurrentFolder(null, [id], true, operationId);
|
||||
} catch (e) {
|
||||
toastr.error(t("Files:ErrorChangeIndex"));
|
||||
}
|
||||
};
|
||||
|
||||
reorder = async (id, t) => {
|
||||
try {
|
||||
const operationId = uniqueid("operation_");
|
||||
await reorder(id);
|
||||
this.updateCurrentFolder(null, [id], true, operationId);
|
||||
} catch (e) {
|
||||
toastr.error(t("Files:ErrorChangeIndex"));
|
||||
}
|
||||
};
|
||||
|
||||
checkExportRoomIndexProgress = async () => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const res = await api.rooms.getExportRoomIndexProgress();
|
||||
|
||||
resolve(res);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
loopExportRoomIndexStatusChecking = async (pbData) => {
|
||||
const { setSecondaryProgressBarData } =
|
||||
this.uploadDataStore.secondaryProgressDataStore;
|
||||
|
||||
let isCompleted = false;
|
||||
let res;
|
||||
|
||||
while (!isCompleted) {
|
||||
res = await this.checkExportRoomIndexProgress();
|
||||
|
||||
if (res?.isCompleted) {
|
||||
isCompleted = true;
|
||||
}
|
||||
|
||||
if (res?.percentage) {
|
||||
setSecondaryProgressBarData({
|
||||
icon: pbData.icon,
|
||||
visible: true,
|
||||
percent: res.percentage,
|
||||
label: "",
|
||||
alert: false,
|
||||
operationId: pbData.operationId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
checkPreviousExportRoomIndexInProgress = async () => {
|
||||
try {
|
||||
if (this.alreadyExportingRoomIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const previousExport = await api.rooms.getExportRoomIndexProgress();
|
||||
|
||||
return previousExport && !previousExport.isCompleted;
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
onSuccessExportRoomIndex = (t, fileName, fileUrl) => {
|
||||
const { openOnNewPage } = this.filesSettingsStore;
|
||||
const urlWithProxy = combineUrl(window.ClientConfig?.proxy?.url, fileUrl);
|
||||
|
||||
showSuccessExportRoomIndexToast(t, fileName, urlWithProxy, openOnNewPage);
|
||||
};
|
||||
|
||||
exportRoomIndex = async (t, roomId) => {
|
||||
const previousExportInProgress =
|
||||
await this.checkPreviousExportRoomIndexInProgress();
|
||||
|
||||
if (previousExportInProgress) {
|
||||
return toastr.error(t("Files:ExportRoomIndexAlreadyInProgressError"));
|
||||
}
|
||||
|
||||
const { setSecondaryProgressBarData, clearSecondaryProgressData } =
|
||||
this.uploadDataStore.secondaryProgressDataStore;
|
||||
|
||||
const pbData = { icon: "exportIndex", operationId: uniqueid("operation_") };
|
||||
|
||||
setSecondaryProgressBarData({
|
||||
icon: pbData.icon,
|
||||
visible: true,
|
||||
percent: 0,
|
||||
label: "",
|
||||
alert: false,
|
||||
operationId: pbData.operationId,
|
||||
});
|
||||
|
||||
this.alreadyExportingRoomIndex = true;
|
||||
|
||||
try {
|
||||
let res = await api.rooms.exportRoomIndex(roomId);
|
||||
|
||||
if (!res.isCompleted) {
|
||||
res = await this.loopExportRoomIndexStatusChecking(pbData);
|
||||
}
|
||||
|
||||
if (res.status === ExportRoomIndexTaskStatus.Failed) {
|
||||
toastr.error(res.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === ExportRoomIndexTaskStatus.Completed) {
|
||||
this.onSuccessExportRoomIndex(t, res.resultFileName, res.resultFileUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
toastr.error(e);
|
||||
} finally {
|
||||
this.alreadyExportingRoomIndex = false;
|
||||
|
||||
setTimeout(() => clearSecondaryProgressData(pbData.operationId), TIMEOUT);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default FilesActionStore;
|
||||
|
@ -395,6 +395,9 @@ class FilesSettingsStore {
|
||||
case RoomsType.PublicRoom:
|
||||
path = "public.svg";
|
||||
break;
|
||||
case RoomsType.VirtualDataRoom:
|
||||
path = "virtual.data.svg";
|
||||
break;
|
||||
case RoomsType.FormRoom:
|
||||
path = "form.svg";
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ class FilesStore {
|
||||
publicRoomStore;
|
||||
settingsStore;
|
||||
currentQuotaStore;
|
||||
indexingStore;
|
||||
|
||||
pluginStore;
|
||||
|
||||
@ -200,6 +201,7 @@ class FilesStore {
|
||||
userStore,
|
||||
currentTariffStatusStore,
|
||||
settingsStore,
|
||||
indexingStore,
|
||||
) {
|
||||
const pathname = window.location.pathname.toLowerCase();
|
||||
this.isEditor = pathname.indexOf("doceditor") !== -1;
|
||||
@ -218,6 +220,7 @@ class FilesStore {
|
||||
this.infoPanelStore = infoPanelStore;
|
||||
this.currentTariffStatusStore = currentTariffStatusStore;
|
||||
this.settingsStore = settingsStore;
|
||||
this.indexingStore = indexingStore;
|
||||
|
||||
this.roomsController = new AbortController();
|
||||
this.filesController = new AbortController();
|
||||
@ -1440,7 +1443,6 @@ class FilesStore {
|
||||
`${url}?${RoomsFilter.getDefault().toUrlParams()}`,
|
||||
);
|
||||
}
|
||||
|
||||
this.setIsErrorRoomNotAvailable(false);
|
||||
this.setIsLoadedFetchFiles(false);
|
||||
|
||||
@ -1450,7 +1452,6 @@ class FilesStore {
|
||||
|
||||
if (filterStorageItem && !filter) {
|
||||
const splitFilter = filterStorageItem.split(",");
|
||||
|
||||
filterData.sortBy = splitFilter[0];
|
||||
filterData.pageCount = +splitFilter[1];
|
||||
filterData.sortOrder = splitFilter[2];
|
||||
@ -1501,12 +1502,17 @@ class FilesStore {
|
||||
await this.publicRoomStore.getExternalLinks(data.current.id);
|
||||
}
|
||||
|
||||
if (data.current.indexing || data.current.order) {
|
||||
this.indexingStore.setIsIndexing(true);
|
||||
} else if (data.current.rootFolderId !== FolderType.COMMON) {
|
||||
this.indexingStore.setIsIndexing(false);
|
||||
}
|
||||
|
||||
if (newTotal > 0) {
|
||||
const lastPage = filterData.getLastPage();
|
||||
|
||||
if (filterData.page > lastPage) {
|
||||
filterData.page = lastPage;
|
||||
|
||||
return this.fetchFiles(
|
||||
folderId,
|
||||
filterData,
|
||||
@ -1748,6 +1754,9 @@ class FilesStore {
|
||||
withFilterLocalStorage = false,
|
||||
) => {
|
||||
const { setSelectedNode, roomsFolderId } = this.treeFoldersStore;
|
||||
const { setIsIndexing, isIndexing } = this.indexingStore;
|
||||
|
||||
if (isIndexing) setIsIndexing(false);
|
||||
|
||||
if (this.clientLoadingStore.isLoading) {
|
||||
this.abortAllFetch();
|
||||
@ -2093,6 +2102,7 @@ class FilesStore {
|
||||
"copy",
|
||||
"restore",
|
||||
"rename",
|
||||
"edit-index",
|
||||
"separator2",
|
||||
// "unsubscribe",
|
||||
"delete",
|
||||
@ -2384,6 +2394,7 @@ class FilesStore {
|
||||
"unpin-room",
|
||||
"mute-room",
|
||||
"unmute-room",
|
||||
"export-room-index",
|
||||
"separator1",
|
||||
"download",
|
||||
"archive-room",
|
||||
@ -2500,6 +2511,7 @@ class FilesStore {
|
||||
"copy-to",
|
||||
"mark-read",
|
||||
"restore",
|
||||
"edit-index",
|
||||
"rename",
|
||||
// "change-thirdparty-info",
|
||||
"separator2",
|
||||
@ -3235,13 +3247,17 @@ class FilesStore {
|
||||
mute,
|
||||
inRoom,
|
||||
requestToken,
|
||||
indexing,
|
||||
lifetime,
|
||||
lastOpened,
|
||||
quotaLimit,
|
||||
usedSpace,
|
||||
isCustomQuota,
|
||||
providerId,
|
||||
order,
|
||||
startFilling,
|
||||
draftLocation,
|
||||
expired,
|
||||
} = item;
|
||||
|
||||
const thirdPartyIcon = this.thirdPartyStore.getThirdPartyIcon(
|
||||
@ -3403,6 +3419,8 @@ class FilesStore {
|
||||
viewAccessibility,
|
||||
...pluginOptions,
|
||||
inRoom,
|
||||
indexing,
|
||||
lifetime,
|
||||
type,
|
||||
hasDraft,
|
||||
isForm,
|
||||
@ -3413,8 +3431,10 @@ class FilesStore {
|
||||
usedSpace,
|
||||
isCustomQuota,
|
||||
providerId,
|
||||
order,
|
||||
startFilling,
|
||||
draftLocation,
|
||||
expired,
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -3422,6 +3442,22 @@ class FilesStore {
|
||||
//return [...this.folders, ...this.files];
|
||||
|
||||
const newFolders = [...this.folders];
|
||||
const orderItems = [...this.folders, ...this.files].filter((x) => x.order);
|
||||
|
||||
if (orderItems.length > 0) {
|
||||
orderItems.sort((a, b) => {
|
||||
if (a.order.includes(".")) {
|
||||
return (
|
||||
Number(a.order.split(".").at(-1)) -
|
||||
Number(b.order.split(".").at(-1))
|
||||
);
|
||||
}
|
||||
|
||||
return Number(a.order) - Number(b.order);
|
||||
});
|
||||
|
||||
return this.getFilesListItems(orderItems);
|
||||
}
|
||||
|
||||
newFolders.sort((a, b) => {
|
||||
const firstValue = a.roomType ? 1 : 0;
|
||||
@ -4043,6 +4079,15 @@ class FilesStore {
|
||||
return this.filter.total;
|
||||
}
|
||||
|
||||
get indexColumnSize() {
|
||||
if (!this.indexingStore.isIndexing) return;
|
||||
|
||||
let minWidth = 36;
|
||||
const lastFile = this.filesList[this.filesList.length - 1];
|
||||
|
||||
return minWidth + lastFile?.order?.length * 3;
|
||||
}
|
||||
|
||||
get hasMoreFiles() {
|
||||
const { isRoomsFolder, isArchiveFolder } = this.treeFoldersStore;
|
||||
|
||||
|
85
packages/client/src/store/IndexingStore.ts
Normal file
85
packages/client/src/store/IndexingStore.ts
Normal file
@ -0,0 +1,85 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import InfoPanelStore from "SRC_DIR/store/InfoPanelStore";
|
||||
|
||||
class IndexingStore {
|
||||
infoPanelStore;
|
||||
|
||||
isIndexEditingMode: boolean = false;
|
||||
|
||||
isIndexing: boolean = false;
|
||||
|
||||
updateSelection: any[] = [];
|
||||
|
||||
constructor(infoPanelStore: InfoPanelStore) {
|
||||
this.infoPanelStore = infoPanelStore;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
setIsIndexing = (indexing: boolean) => {
|
||||
// turn off the mode if we are no longer in indexed folders
|
||||
const { setIsVisible } = this.infoPanelStore;
|
||||
if (!indexing && this.isIndexEditingMode) this.setIsIndexEditingMode(false);
|
||||
if (!indexing) setIsVisible(false);
|
||||
|
||||
this.isIndexing = indexing;
|
||||
};
|
||||
|
||||
setUpdateSelection = (selection: any[]) => {
|
||||
this.updateSelection = selection;
|
||||
};
|
||||
|
||||
setUpdateItems = (items: any) => {
|
||||
const newSelection = [...this.updateSelection];
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const item of items) {
|
||||
const exist = this.updateSelection.find(
|
||||
(selectionItem) =>
|
||||
selectionItem.id === item.id &&
|
||||
selectionItem.fileExst === item.fileExst,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-continue
|
||||
if (exist) continue;
|
||||
newSelection.push(item);
|
||||
}
|
||||
this.setUpdateSelection(newSelection);
|
||||
};
|
||||
|
||||
setIsIndexEditingMode = (mode: boolean) => {
|
||||
if (!mode) {
|
||||
this.setUpdateSelection([]);
|
||||
}
|
||||
const { setIsVisible } = this.infoPanelStore;
|
||||
setIsVisible(false);
|
||||
this.isIndexEditingMode = mode;
|
||||
};
|
||||
}
|
||||
|
||||
export default IndexingStore;
|
@ -100,6 +100,7 @@ class InfoPanelStore {
|
||||
isMembersPanelUpdating = false;
|
||||
|
||||
shareChanged = false;
|
||||
calendarDay = null;
|
||||
|
||||
showSearchBlock = false;
|
||||
searchValue = "";
|
||||
@ -831,6 +832,10 @@ class InfoPanelStore {
|
||||
setShareChanged = (shareChanged) => {
|
||||
this.shareChanged = shareChanged;
|
||||
};
|
||||
|
||||
setCalendarDay = (calendarDay) => {
|
||||
this.calendarDay = calendarDay;
|
||||
};
|
||||
}
|
||||
|
||||
export default InfoPanelStore;
|
||||
|
@ -40,7 +40,11 @@ import type {
|
||||
TPathParts,
|
||||
} from "@docspace/shared/types";
|
||||
import { TFolder, TFolderSecurity } from "@docspace/shared/api/files/types";
|
||||
import { TLogo, TRoomSecurity } from "@docspace/shared/api/rooms/types";
|
||||
import {
|
||||
TLogo,
|
||||
TRoomLifetime,
|
||||
TRoomSecurity,
|
||||
} from "@docspace/shared/api/rooms/types";
|
||||
|
||||
import { setDocumentTitle } from "../helpers/utils";
|
||||
|
||||
@ -139,8 +143,14 @@ class SelectedFolderStore {
|
||||
|
||||
canShare = false;
|
||||
|
||||
indexing = false;
|
||||
|
||||
parentRoomType: Nullable<FolderType> = null;
|
||||
|
||||
lifetime: TRoomLifetime | null = null;
|
||||
|
||||
indexing = false;
|
||||
|
||||
constructor(settingsStore: SettingsStore) {
|
||||
makeAutoObservable(this);
|
||||
this.settingsStore = settingsStore;
|
||||
@ -185,6 +195,8 @@ class SelectedFolderStore {
|
||||
type: this.type,
|
||||
isRootFolder: this.isRootFolder,
|
||||
parentRoomType: this.parentRoomType,
|
||||
lifetime: this.lifetime,
|
||||
indexing: this.indexing,
|
||||
};
|
||||
};
|
||||
|
||||
@ -230,6 +242,8 @@ class SelectedFolderStore {
|
||||
this.type = null;
|
||||
this.inRoom = false;
|
||||
this.parentRoomType = null;
|
||||
this.lifetime = null;
|
||||
this.indexing = false;
|
||||
};
|
||||
|
||||
setParentId = (parentId: number) => {
|
||||
@ -252,9 +266,14 @@ class SelectedFolderStore {
|
||||
this.shared = shared;
|
||||
};
|
||||
|
||||
updateEditedSelectedRoom = (title = this.title, tags = this.tags) => {
|
||||
updateEditedSelectedRoom = (
|
||||
title = this.title,
|
||||
tags = this.tags,
|
||||
lifetime = this.lifetime,
|
||||
) => {
|
||||
this.title = title;
|
||||
this.tags = tags;
|
||||
this.lifetime = lifetime;
|
||||
};
|
||||
|
||||
setInRoom = (inRoom: boolean) => {
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import { TableVersions } from "SRC_DIR/helpers/constants";
|
||||
import { RoomsType } from "@docspace/shared/enums";
|
||||
|
||||
const TABLE_COLUMNS = `filesTableColumns_ver-${TableVersions.Files}`;
|
||||
const TABLE_ACCOUNTS_PEOPLE_COLUMNS = `peopleTableColumns_ver-${TableVersions.People}`;
|
||||
@ -34,11 +35,13 @@ const TABLE_ACCOUNTS_INSIDE_GROUP_COLUMNS = `insideGroupTableColumns_ver-${Table
|
||||
const TABLE_ROOMS_COLUMNS = `roomsTableColumns_ver-${TableVersions.Rooms}`;
|
||||
const TABLE_TRASH_COLUMNS = `trashTableColumns_ver-${TableVersions.Trash}`;
|
||||
const TABLE_RECENT_COLUMNS = `recentTableColumns_ver-${TableVersions.Recent}`;
|
||||
const TABLE_VDR_INDEXING_COLUMNS = `vdrIndexingColumns_ver-${TableVersions.Rooms}`;
|
||||
|
||||
const COLUMNS_SIZE = `filesColumnsSize_ver-${TableVersions.Files}`;
|
||||
const COLUMNS_ROOMS_SIZE = `roomsColumnsSize_ver-${TableVersions.Rooms}`;
|
||||
const COLUMNS_TRASH_SIZE = `trashColumnsSize_ver-${TableVersions.Trash}`;
|
||||
const COLUMNS_RECENT_SIZE = `recentColumnsSize_ver-${TableVersions.Recent}`;
|
||||
const COLUMNS_VDR_INDEXING_SIZE = `vdrIndexingColumnsSize_ver-${TableVersions.Rooms}`;
|
||||
|
||||
const COLUMNS_SIZE_INFO_PANEL = `filesColumnsSizeInfoPanel_ver-${TableVersions.Files}`;
|
||||
const COLUMNS_ROOMS_SIZE_INFO_PANEL = `roomsColumnsSizeInfoPanel_ver-${TableVersions.Rooms}`;
|
||||
@ -50,6 +53,7 @@ class TableStore {
|
||||
treeFoldersStore;
|
||||
userStore;
|
||||
settingsStore;
|
||||
selectedFolderStore;
|
||||
|
||||
roomColumnNameIsEnabled = true; // always true
|
||||
roomColumnTypeIsEnabled = false;
|
||||
@ -66,6 +70,7 @@ class TableStore {
|
||||
createdColumnIsEnabled = true;
|
||||
modifiedColumnIsEnabled = true;
|
||||
sizeColumnIsEnabled = true;
|
||||
indexColumnIsEnabled = true;
|
||||
typeColumnIsEnabled = true;
|
||||
quickButtonsColumnIsEnabled = true;
|
||||
lastOpenedColumnIsEnabled = true;
|
||||
@ -86,13 +91,20 @@ class TableStore {
|
||||
groupAccountsInsideGroupColumnIsEnabled = true;
|
||||
emailAccountsInsideGroupColumnIsEnabled = true;
|
||||
|
||||
constructor(authStore, treeFoldersStore, userStore, settingsStore) {
|
||||
constructor(
|
||||
authStore,
|
||||
treeFoldersStore,
|
||||
userStore,
|
||||
settingsStore,
|
||||
indexingStore,
|
||||
) {
|
||||
makeAutoObservable(this);
|
||||
|
||||
this.authStore = authStore;
|
||||
this.treeFoldersStore = treeFoldersStore;
|
||||
this.userStore = userStore;
|
||||
this.settingsStore = settingsStore;
|
||||
this.indexingStore = indexingStore;
|
||||
}
|
||||
|
||||
setRoomColumnType = (enable) => {
|
||||
@ -139,6 +151,10 @@ class TableStore {
|
||||
this.sizeColumnIsEnabled = enable;
|
||||
};
|
||||
|
||||
setIndexColumn = (enable) => {
|
||||
this.indexColumnIsEnabled = enable;
|
||||
};
|
||||
|
||||
setTypeColumn = (enable) => {
|
||||
this.typeColumnIsEnabled = enable;
|
||||
};
|
||||
@ -240,6 +256,7 @@ class TableStore {
|
||||
this.setAuthorColumn(splitColumns.includes("Author"));
|
||||
this.setCreatedColumn(splitColumns.includes("Created"));
|
||||
this.setSizeColumn(splitColumns.includes("Size"));
|
||||
this.setIndexColumn(splitColumns.includes("Index"));
|
||||
this.setTypeColumn(splitColumns.includes("Type"));
|
||||
this.setQuickButtonsColumn(splitColumns.includes("QuickButtons"));
|
||||
this.setLastOpenedColumn(splitColumns.includes("LastOpened"));
|
||||
@ -292,6 +309,10 @@ class TableStore {
|
||||
this.setErasureColumn(!this.erasureColumnIsEnabled);
|
||||
return;
|
||||
|
||||
case "Index":
|
||||
this.setIndexColumn(!this.indexColumnIsEnabled);
|
||||
return;
|
||||
|
||||
case "Size":
|
||||
this.setSizeColumn(!this.sizeColumnIsEnabled);
|
||||
return;
|
||||
@ -403,6 +424,7 @@ class TableStore {
|
||||
getIsAccountsInsideGroup,
|
||||
} = this.treeFoldersStore;
|
||||
|
||||
const { isIndexing } = this.indexingStore;
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const userId = this.userStore.user?.id;
|
||||
const isFrame = this.settingsStore.isFrame;
|
||||
@ -419,6 +441,8 @@ class TableStore {
|
||||
? `${TABLE_TRASH_COLUMNS}=${userId}`
|
||||
: isRecentTab
|
||||
? `${TABLE_RECENT_COLUMNS}=${userId}`
|
||||
: isIndexing
|
||||
? `${TABLE_VDR_INDEXING_COLUMNS}=${userId}`
|
||||
: `${TABLE_COLUMNS}=${userId}`;
|
||||
|
||||
return isFrame ? `SDK_${tableStorageName}` : tableStorageName;
|
||||
@ -428,6 +452,7 @@ class TableStore {
|
||||
get columnStorageName() {
|
||||
const { isRoomsFolder, isArchiveFolder, isTrashFolder, isRecentTab } =
|
||||
this.treeFoldersStore;
|
||||
const { isIndexing } = this.indexingStore;
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const userId = this.userStore.user?.id;
|
||||
const isFrame = this.settingsStore.isFrame;
|
||||
@ -438,6 +463,8 @@ class TableStore {
|
||||
? `${COLUMNS_TRASH_SIZE}=${userId}`
|
||||
: isRecentTab
|
||||
? `${COLUMNS_RECENT_SIZE}=${userId}`
|
||||
: isIndexing
|
||||
? `${COLUMNS_VDR_INDEXING_SIZE}=${userId}`
|
||||
: `${COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
return isFrame ? `SDK_${columnStorageName}` : columnStorageName;
|
||||
|
@ -80,6 +80,7 @@ import ImportAccountsStore from "./ImportAccountsStore";
|
||||
import PluginStore from "./PluginStore";
|
||||
import InfoPanelStore from "./InfoPanelStore";
|
||||
import CampaignsStore from "./CampaignsStore";
|
||||
import IndexingStore from "./IndexingStore";
|
||||
|
||||
const selectedFolderStore = new SelectedFolderStore(settingsStore);
|
||||
|
||||
@ -115,6 +116,7 @@ const clientLoadingStore = new ClientLoadingStore();
|
||||
const publicRoomStore = new PublicRoomStore(clientLoadingStore);
|
||||
|
||||
const infoPanelStore = new InfoPanelStore(userStore);
|
||||
const indexingStore = new IndexingStore(infoPanelStore);
|
||||
|
||||
const treeFoldersStore = new TreeFoldersStore(
|
||||
selectedFolderStore,
|
||||
@ -151,6 +153,7 @@ const filesStore = new FilesStore(
|
||||
userStore,
|
||||
currentTariffStatusStore,
|
||||
settingsStore,
|
||||
indexingStore,
|
||||
);
|
||||
|
||||
const mediaViewerDataStore = new MediaViewerDataStore(
|
||||
@ -214,6 +217,7 @@ const filesActionsStore = new FilesActionsStore(
|
||||
currentTariffStatusStore,
|
||||
peopleStore,
|
||||
currentQuotaStore,
|
||||
indexingStore,
|
||||
);
|
||||
|
||||
const contextOptionsStore = new ContextOptionsStore(
|
||||
@ -233,6 +237,7 @@ const contextOptionsStore = new ContextOptionsStore(
|
||||
infoPanelStore,
|
||||
currentTariffStatusStore,
|
||||
userStore,
|
||||
indexingStore,
|
||||
clientLoadingStore,
|
||||
);
|
||||
|
||||
@ -264,6 +269,7 @@ const tableStore = new TableStore(
|
||||
treeFoldersStore,
|
||||
userStore,
|
||||
settingsStore,
|
||||
indexingStore,
|
||||
);
|
||||
|
||||
infoPanelStore.filesSettingsStore = filesSettingsStore;
|
||||
@ -351,6 +357,7 @@ const store = {
|
||||
pluginStore,
|
||||
storageManagement,
|
||||
campaignsStore,
|
||||
indexingStore,
|
||||
};
|
||||
|
||||
export default store;
|
||||
|
117
packages/common/components/Watermarks/index.js
Normal file
117
packages/common/components/Watermarks/index.js
Normal file
@ -0,0 +1,117 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
const Watermark = ({
|
||||
text,
|
||||
rotate,
|
||||
image,
|
||||
color,
|
||||
|
||||
isSemitransparent,
|
||||
|
||||
children,
|
||||
}) => {
|
||||
const setCtxText = (ctx) => {
|
||||
const getColor = () => {
|
||||
if (color)
|
||||
return Array.isArray(color)
|
||||
? `rgb(${color[0]}, ${color[1]}, ${color[2]}, 1)`
|
||||
: color;
|
||||
|
||||
if (isSemitransparent) return "rgba(223, 226, 227, 1)";
|
||||
|
||||
return "rgba(208, 213, 218, 1)";
|
||||
};
|
||||
|
||||
ctx.fillStyle = getColor();
|
||||
ctx.textAlign = "center";
|
||||
ctx.font = `${13}px Arial`;
|
||||
};
|
||||
|
||||
const setCtxCenteredText = (imgContent, ctx) => {
|
||||
ctx.translate(imgContent.width / 2, imgContent.height / 2);
|
||||
};
|
||||
|
||||
const setCtxRotate = (ctx) => {
|
||||
const angle = (Math.PI / 180) * Number(rotate);
|
||||
ctx.rotate(angle);
|
||||
};
|
||||
|
||||
const setCtxTextWrap = (ctx, canvas) => {
|
||||
let line = "",
|
||||
marginTop = 0,
|
||||
marginLeft = 0,
|
||||
lineHeight = 15;
|
||||
|
||||
for (var n = 0; n < text.length; n++) {
|
||||
let testLine = line + text[n];
|
||||
let testWidth = ctx.measureText(testLine).width;
|
||||
|
||||
const percentWidth = ((canvas.width - testWidth) * 100) / canvas.width;
|
||||
|
||||
if (
|
||||
(percentWidth < 32 && text[n] === " ") ||
|
||||
testWidth > canvas.width - 4
|
||||
) {
|
||||
ctx.fillText(line, marginLeft, marginTop);
|
||||
line = text[n];
|
||||
marginTop += lineHeight;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillText(line, marginLeft, marginTop);
|
||||
};
|
||||
|
||||
const getContent = (canvas, ctx, imgContent, imgWidth, imgHeight) => {
|
||||
setCtxText(ctx);
|
||||
|
||||
ctx.drawImage(imgContent, 0, 0, imgWidth, imgHeight);
|
||||
|
||||
setCtxCenteredText(imgContent, ctx);
|
||||
setCtxRotate(ctx);
|
||||
setCtxTextWrap(ctx, canvas);
|
||||
};
|
||||
|
||||
const drawCanvas = (drawContent, ctx, canvas) => {
|
||||
canvas.width = drawContent.naturalWidth;
|
||||
canvas.height = drawContent.naturalHeight;
|
||||
|
||||
getContent(
|
||||
canvas,
|
||||
ctx,
|
||||
drawContent || "",
|
||||
drawContent.naturalWidth,
|
||||
drawContent.naturalHeight
|
||||
);
|
||||
};
|
||||
|
||||
const renderWatermark = () => {
|
||||
const canvas = document.querySelector("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
drawCanvas(img, ctx, canvas);
|
||||
};
|
||||
img.onerror = () => {
|
||||
drawCanvas(text);
|
||||
};
|
||||
img.crossOrigin = "anonymous";
|
||||
img.referrerPolicy = "no-referrer";
|
||||
|
||||
img.src = image;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
renderWatermark();
|
||||
}, [text, rotate, isSemitransparent]);
|
||||
|
||||
return (
|
||||
<canvas>
|
||||
<div>{children}</div>
|
||||
</canvas>
|
||||
);
|
||||
};
|
||||
export default Watermark;
|
@ -1435,6 +1435,26 @@ export async function startFilling(fileId: string | number): Promise<void> {
|
||||
await request(options);
|
||||
}
|
||||
|
||||
export async function changeIndex(
|
||||
id: number,
|
||||
order: number,
|
||||
isFolder: boolean,
|
||||
) {
|
||||
const url = isFolder ? `/files/folder/${id}/order` : `/files/${id}/order`;
|
||||
return request({
|
||||
method: "put",
|
||||
url,
|
||||
data: { order },
|
||||
});
|
||||
}
|
||||
|
||||
export async function reorder(id: number) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: `/files/rooms/${id}/reorder`,
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkIsPDFForm(fileId: string | number) {
|
||||
return request({
|
||||
method: "get",
|
||||
|
@ -35,7 +35,7 @@ import {
|
||||
toUrlParams,
|
||||
} from "../../utils/common";
|
||||
import RoomsFilter from "./filter";
|
||||
import { TGetRooms } from "./types";
|
||||
import { TGetRooms, TRoomLifetime, TExportRoomIndexTask } from "./types";
|
||||
|
||||
export async function getRooms(filter: RoomsFilter, signal?: AbortSignal) {
|
||||
let params;
|
||||
@ -478,3 +478,67 @@ export function resetRoomQuota(roomIds) {
|
||||
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function changeRoomLifetime(
|
||||
roomId: string | number,
|
||||
lifetime: TRoomLifetime | null,
|
||||
) {
|
||||
const data = lifetime ? { ...lifetime } : null;
|
||||
const options = {
|
||||
method: "put",
|
||||
url: `files/rooms/${roomId}/lifetime`,
|
||||
data,
|
||||
};
|
||||
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function exportRoomIndex(roomId: number) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: `files/rooms/${roomId}/indexexport`,
|
||||
}) as Promise<TExportRoomIndexTask>;
|
||||
}
|
||||
|
||||
export function getExportRoomIndexProgress() {
|
||||
return request({
|
||||
method: "get",
|
||||
url: `files/rooms/indexexport`,
|
||||
}) as Promise<TExportRoomIndexTask>;
|
||||
}
|
||||
|
||||
export function setWatermarkSettings(
|
||||
roomId: number | string,
|
||||
data: {
|
||||
enabled: boolean;
|
||||
rotate: number;
|
||||
text: string;
|
||||
additions: number;
|
||||
imageScale: number;
|
||||
imageUrl: string;
|
||||
imageWidth: string;
|
||||
imageHeight: string;
|
||||
},
|
||||
) {
|
||||
const options = {
|
||||
method: "put",
|
||||
url: `files/rooms/${roomId}/watermark`,
|
||||
data,
|
||||
};
|
||||
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function getWatermarkSettings(roomId: number | string) {
|
||||
return request({
|
||||
method: "get",
|
||||
url: `files/rooms/${roomId}/watermark`,
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteWatermarkSettings(roomId: number | string) {
|
||||
return request({
|
||||
method: "delete",
|
||||
url: `files/rooms/${roomId}/watermark`,
|
||||
});
|
||||
}
|
||||
|
@ -25,7 +25,12 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import { TFile, TFolder } from "../files/types";
|
||||
import { FolderType, RoomsType, ShareAccessRights } from "../../enums";
|
||||
import {
|
||||
ExportRoomIndexTaskStatus,
|
||||
FolderType,
|
||||
RoomsType,
|
||||
ShareAccessRights,
|
||||
} from "../../enums";
|
||||
import { TCreatedBy, TPathParts } from "../../types";
|
||||
|
||||
export type TLogo = {
|
||||
@ -54,6 +59,12 @@ export type TRoomSecurity = {
|
||||
CopySharedLink: boolean;
|
||||
};
|
||||
|
||||
export type TRoomLifetime = {
|
||||
deletePermanently: boolean;
|
||||
period: number;
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type TRoom = {
|
||||
parentId: number;
|
||||
filesCount: number;
|
||||
@ -79,6 +90,7 @@ export type TRoom = {
|
||||
updatedBy: TCreatedBy;
|
||||
isArchive?: boolean;
|
||||
security: TRoomSecurity;
|
||||
lifetime: TRoomLifetime;
|
||||
};
|
||||
|
||||
export type TGetRooms = {
|
||||
@ -91,3 +103,14 @@ export type TGetRooms = {
|
||||
total: number;
|
||||
new: number;
|
||||
};
|
||||
|
||||
export type TExportRoomIndexTask = {
|
||||
id: string;
|
||||
error: string;
|
||||
percentage: number;
|
||||
isCompleted: boolean;
|
||||
status: ExportRoomIndexTaskStatus;
|
||||
resultFileId: number;
|
||||
resultFileName: string;
|
||||
resultFileUrl: string;
|
||||
};
|
||||
|
@ -78,6 +78,7 @@ ButtonsContainer.displayName = "ButtonsContainer";
|
||||
const CalendarContainer = styled.div<{
|
||||
isMobile?: boolean;
|
||||
big?: boolean;
|
||||
isScroll?: boolean;
|
||||
}>`
|
||||
${(props) =>
|
||||
!props.isMobile &&
|
||||
@ -109,19 +110,37 @@ const CalendarContainer = styled.div<{
|
||||
props.big ? "repeat(4, 1fr)" : "repeat(7, 1fr)"};
|
||||
box-sizing: border-box;
|
||||
padding: ${(props) => (props.big ? "14px 6px 6px 6px" : "0 6px")};
|
||||
${(props) =>
|
||||
props.isScroll &&
|
||||
css`
|
||||
margin-bottom: 28px;
|
||||
`};
|
||||
`;
|
||||
|
||||
CalendarContainer.defaultProps = { theme: Base };
|
||||
|
||||
const Container = styled.div<{ isMobile?: boolean }>`
|
||||
const Container = styled.div<{ isMobile?: boolean; isScroll?: boolean }>`
|
||||
box-sizing: border-box;
|
||||
width: ${(props) => (props.isMobile ? "100%" : "362px")};
|
||||
height: ${(props) => (props.isMobile ? "420px" : "376px")};
|
||||
padding: ${(props) => (props.isMobile ? "16px" : "30px 28px 28px 28px")};
|
||||
padding: ${(props) =>
|
||||
props.isMobile
|
||||
? "16px"
|
||||
: props.isScroll
|
||||
? "0px 0px 0px 28px"
|
||||
: "30px 28px 28px 28px"};
|
||||
box-shadow: ${(props) => props.theme.calendar.boxShadow};
|
||||
border-radius: 6px;
|
||||
z-index: 320;
|
||||
background-color: ${(props) => props.theme.backgroundColor};
|
||||
|
||||
${(props) =>
|
||||
props.isScroll &&
|
||||
css`
|
||||
header {
|
||||
padding: 30px 23px 0 12px !important;
|
||||
}
|
||||
`};
|
||||
`;
|
||||
|
||||
Container.defaultProps = { theme: Base };
|
||||
@ -317,6 +336,7 @@ Weekday.defaultProps = { theme: Base };
|
||||
const StyledContainerTheme = styled(Container)<{
|
||||
$currentColorScheme?: TColorScheme;
|
||||
isMobile?: boolean;
|
||||
isScroll?: boolean;
|
||||
}>`
|
||||
${HeaderActionIcon} {
|
||||
border-color: ${(props) => props.$currentColorScheme?.main?.accent};
|
||||
|
@ -30,6 +30,7 @@ import { useTheme } from "styled-components";
|
||||
import moment from "moment";
|
||||
import "moment/locale/ar-sa";
|
||||
|
||||
import { Scrollbar } from "@docspace/shared/components/scrollbar";
|
||||
import { Days, Months, Years } from "./sub-components";
|
||||
|
||||
import { getValidDates } from "./utils";
|
||||
@ -49,6 +50,7 @@ const Calendar = ({
|
||||
onChange,
|
||||
isMobile,
|
||||
forwardedRef,
|
||||
isScroll = false,
|
||||
}: CalendarProps) => {
|
||||
moment.locale(locale);
|
||||
|
||||
@ -105,17 +107,8 @@ const Calendar = ({
|
||||
setObservedDate(date);
|
||||
}, [initialDate, maxDate, minDate]);
|
||||
|
||||
return (
|
||||
<StyledContainerTheme
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
isMobile={isMobile}
|
||||
ref={forwardedRef}
|
||||
$currentColorScheme={theme?.currentColorScheme}
|
||||
data-testid="calendar"
|
||||
>
|
||||
{selectedScene === 0 ? (
|
||||
const CalendarBodyNode =
|
||||
selectedScene === 0 ? (
|
||||
<Days
|
||||
observedDate={observedDate}
|
||||
setObservedDate={setObservedDate}
|
||||
@ -125,6 +118,7 @@ const Calendar = ({
|
||||
minDate={resultMinDate}
|
||||
maxDate={resultMaxDate}
|
||||
isMobile={isMobile || false}
|
||||
isScroll={isScroll}
|
||||
/>
|
||||
) : selectedScene === 1 ? (
|
||||
<Months
|
||||
@ -135,6 +129,7 @@ const Calendar = ({
|
||||
minDate={resultMinDate}
|
||||
maxDate={resultMaxDate}
|
||||
isMobile={isMobile || false}
|
||||
isScroll={isScroll}
|
||||
/>
|
||||
) : (
|
||||
<Years
|
||||
@ -145,8 +140,28 @@ const Calendar = ({
|
||||
minDate={resultMinDate}
|
||||
maxDate={resultMaxDate}
|
||||
isMobile={isMobile || false}
|
||||
isScroll={isScroll}
|
||||
/>
|
||||
)}
|
||||
);
|
||||
|
||||
const CalendarNode = isScroll ? (
|
||||
<Scrollbar>{CalendarBodyNode}</Scrollbar>
|
||||
) : (
|
||||
CalendarBodyNode
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainerTheme
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
isMobile={isMobile}
|
||||
ref={forwardedRef}
|
||||
$currentColorScheme={theme?.currentColorScheme}
|
||||
data-testid="calendar"
|
||||
isScroll={isScroll}
|
||||
>
|
||||
{CalendarNode}
|
||||
</StyledContainerTheme>
|
||||
);
|
||||
};
|
||||
|
@ -49,6 +49,7 @@ export interface CalendarProps {
|
||||
initialDate?: moment.Moment | Date;
|
||||
isMobile?: boolean;
|
||||
forwardedRef?: React.RefObject<HTMLDivElement>;
|
||||
isScroll?: boolean;
|
||||
}
|
||||
|
||||
export interface DaysProps {
|
||||
@ -60,6 +61,7 @@ export interface DaysProps {
|
||||
minDate: moment.Moment;
|
||||
maxDate: moment.Moment;
|
||||
isMobile: boolean;
|
||||
isScroll?: boolean;
|
||||
}
|
||||
|
||||
export interface DaysHeaderProps {
|
||||
@ -78,6 +80,7 @@ export interface DaysBodyProps {
|
||||
minDate: moment.Moment;
|
||||
maxDate: moment.Moment;
|
||||
isMobile: boolean;
|
||||
isScroll?: boolean;
|
||||
}
|
||||
|
||||
export interface HeaderButtonsProps {
|
||||
@ -96,6 +99,7 @@ export interface MonthsProps {
|
||||
minDate: moment.Moment;
|
||||
maxDate: moment.Moment;
|
||||
isMobile: boolean;
|
||||
isScroll?: boolean;
|
||||
}
|
||||
|
||||
export interface MonthsBodyProps {
|
||||
@ -106,6 +110,7 @@ export interface MonthsBodyProps {
|
||||
minDate: moment.Moment;
|
||||
maxDate: moment.Moment;
|
||||
isMobile: boolean;
|
||||
isScroll?: boolean;
|
||||
}
|
||||
|
||||
export interface MonthsHeaderProps {
|
||||
@ -125,6 +130,7 @@ export interface YearsProps {
|
||||
minDate: moment.Moment;
|
||||
maxDate: moment.Moment;
|
||||
isMobile: boolean;
|
||||
isScroll?: boolean;
|
||||
}
|
||||
|
||||
export interface YearsHeaderProps {
|
||||
|
@ -38,6 +38,7 @@ export const Days = ({
|
||||
minDate,
|
||||
maxDate,
|
||||
isMobile,
|
||||
isScroll,
|
||||
}: DaysProps) => {
|
||||
return (
|
||||
<>
|
||||
@ -56,6 +57,7 @@ export const Days = ({
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
isMobile={isMobile}
|
||||
isScroll={isScroll}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -37,6 +37,7 @@ export const DaysBody = ({
|
||||
minDate,
|
||||
maxDate,
|
||||
isMobile,
|
||||
isScroll,
|
||||
}: DaysBodyProps) => {
|
||||
const daysElements = getDayElements(
|
||||
observedDate,
|
||||
@ -49,7 +50,7 @@ export const DaysBody = ({
|
||||
const weekdayElements = getWeekdayElements(isMobile);
|
||||
|
||||
return (
|
||||
<CalendarContainer isMobile={isMobile}>
|
||||
<CalendarContainer isMobile={isMobile} isScroll={isScroll}>
|
||||
{weekdayElements} {daysElements}
|
||||
</CalendarContainer>
|
||||
);
|
||||
|
@ -38,6 +38,7 @@ export const Months = ({
|
||||
minDate,
|
||||
maxDate,
|
||||
isMobile,
|
||||
isScroll,
|
||||
}: MonthsProps) => {
|
||||
return (
|
||||
<>
|
||||
@ -57,6 +58,7 @@ export const Months = ({
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
isMobile={isMobile}
|
||||
isScroll={isScroll}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -37,6 +37,7 @@ export const MonthsBody = ({
|
||||
minDate,
|
||||
maxDate,
|
||||
isMobile,
|
||||
isScroll,
|
||||
}: MonthsBodyProps) => {
|
||||
const months = getCalendarMonths(observedDate);
|
||||
const monthsElements = getMonthElements(
|
||||
@ -50,7 +51,7 @@ export const MonthsBody = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<CalendarContainer big isMobile={isMobile}>
|
||||
<CalendarContainer big isMobile={isMobile} isScroll={isScroll}>
|
||||
{monthsElements}
|
||||
</CalendarContainer>
|
||||
);
|
||||
|
@ -37,6 +37,7 @@ export const Years = ({
|
||||
minDate,
|
||||
maxDate,
|
||||
isMobile,
|
||||
isScroll,
|
||||
}: YearsProps) => {
|
||||
return (
|
||||
<>
|
||||
@ -55,6 +56,7 @@ export const Years = ({
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
isMobile={isMobile}
|
||||
isScroll={isScroll}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -38,6 +38,7 @@ export const YearsBody = ({
|
||||
minDate,
|
||||
maxDate,
|
||||
isMobile,
|
||||
isScroll,
|
||||
}: YearsProps) => {
|
||||
const years = getCalendarYears(observedDate);
|
||||
const yearElements = getYearElements(
|
||||
@ -51,7 +52,7 @@ export const YearsBody = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<CalendarContainer big isMobile={isMobile}>
|
||||
<CalendarContainer big isMobile={isMobile} isScroll={isScroll}>
|
||||
{yearElements}
|
||||
</CalendarContainer>
|
||||
);
|
||||
|
@ -39,4 +39,5 @@ export const enum ThemeId {
|
||||
IndicatorLoader = "indicatorLoader",
|
||||
Progress = "progress",
|
||||
SubmenuText = "submenuText",
|
||||
IndexIconButton = "indexIconButton",
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import LoadingButton from "./styled-components/loadingButton";
|
||||
import ProgressColorTheme from "./styled-components/progress";
|
||||
import VersionBadgeTheme from "./styled-components/versionBadge";
|
||||
import SubmenuTextTheme from "./styled-components/submenuText";
|
||||
import StyledIndexWrapper from "./sub-components/StyledIndexWrapper";
|
||||
|
||||
const ColorTheme = forwardRef<
|
||||
HTMLDivElement,
|
||||
@ -76,6 +77,20 @@ const ColorTheme = forwardRef<
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ThemeId.IndexIconButton: {
|
||||
return (
|
||||
<StyledIndexWrapper
|
||||
$currentColorScheme={currentColorScheme}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<IconButtonTheme
|
||||
{...props}
|
||||
themeId={themeId}
|
||||
$currentColorScheme={currentColorScheme}
|
||||
/>
|
||||
</StyledIndexWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
case ThemeId.IconButtonMute: {
|
||||
return (
|
||||
|
@ -77,6 +77,10 @@ export interface IndicatorFilterButtonColorTheme
|
||||
themeId: ThemeId.IndicatorFilterButton;
|
||||
}
|
||||
|
||||
export interface IndexIconButton extends DefaultColorThemeProps {
|
||||
themeId: ThemeId.IndexIconButton;
|
||||
}
|
||||
|
||||
export interface IndicatorLoaderColorTheme extends DefaultColorThemeProps {
|
||||
themeId: ThemeId.IndicatorLoader;
|
||||
}
|
||||
@ -132,4 +136,5 @@ export type ColorThemeProps =
|
||||
| ProgressColorTheme
|
||||
| VersionBadgeTheme
|
||||
| LinkColorTheme
|
||||
| IndexIconButton
|
||||
| SubmenuTextTheme;
|
||||
|
@ -0,0 +1,73 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Base, TColorScheme } from "../../../themes";
|
||||
|
||||
const StyledIndexWrapper = styled.div<{
|
||||
$currentColorScheme: TColorScheme | undefined;
|
||||
}>`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 3px;
|
||||
|
||||
.index-up-icon {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.index-down-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.indexBackgroundButtonHover};
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
|
||||
path {
|
||||
fill: ${(props) =>
|
||||
props.theme.filesSection.tableView.row
|
||||
.indexArrowButtonHover} !important;
|
||||
}
|
||||
|
||||
circle {
|
||||
stroke: ${(props) =>
|
||||
props.theme.filesSection.tableView.row
|
||||
.indexArrowButtonHover} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledIndexWrapper.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledIndexWrapper;
|
@ -233,6 +233,9 @@ export interface FilterProps {
|
||||
isPeopleAccounts: boolean;
|
||||
isGroupsAccounts: boolean;
|
||||
isInsideGroup: boolean;
|
||||
isIndexing: boolean;
|
||||
isIndexEditingMode: boolean;
|
||||
|
||||
filterTitle: string;
|
||||
sortByTitle: string;
|
||||
|
||||
|
@ -71,6 +71,9 @@ const FilterInput = React.memo(
|
||||
isPeopleAccounts,
|
||||
isGroupsAccounts,
|
||||
isInsideGroup,
|
||||
isIndexing,
|
||||
isIndexEditingMode,
|
||||
|
||||
filterTitle,
|
||||
sortByTitle,
|
||||
|
||||
@ -208,8 +211,10 @@ const FilterInput = React.memo(
|
||||
onClearSearch={onClearSearch}
|
||||
id="filter_search-input"
|
||||
size={InputSize.base}
|
||||
isDisabled={isIndexEditingMode}
|
||||
onFocus={onInputFocus}
|
||||
/>
|
||||
{!isIndexEditingMode && (
|
||||
<FilterButton
|
||||
id="filter-button"
|
||||
onFilter={onFilter}
|
||||
@ -226,6 +231,9 @@ const FilterInput = React.memo(
|
||||
userId={userId}
|
||||
disableThirdParty={disableThirdParty}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isIndexing && (
|
||||
<SortButton
|
||||
id="sort-by-button"
|
||||
onSort={onSort}
|
||||
@ -243,8 +251,9 @@ const FilterInput = React.memo(
|
||||
}
|
||||
title={sortByTitle}
|
||||
/>
|
||||
|
||||
)}
|
||||
{viewSettings &&
|
||||
!isIndexing &&
|
||||
currentDeviceType === DeviceType.desktop &&
|
||||
viewSelectorVisible && (
|
||||
<ViewSelector
|
||||
|
@ -33,4 +33,5 @@ export const enum FloatingButtonIcons {
|
||||
plus = "plus",
|
||||
minus = "minus",
|
||||
refresh = "refresh",
|
||||
exportIndex = "exportIndex",
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import ButtonPlusIcon from "PUBLIC_DIR/images/icons/16/button.plus.react.svg";
|
||||
import ButtonMinusIcon from "PUBLIC_DIR/images/icons/16/button.minus.react.svg";
|
||||
import RefreshIcon from "PUBLIC_DIR/images/refresh.react.svg";
|
||||
import CloseIcon from "PUBLIC_DIR/images/close-icon.react.svg";
|
||||
import ExportRoomIndexIcon from "PUBLIC_DIR/images/icons/16/export-room-index.react.svg";
|
||||
|
||||
import { FloatingButtonTheme } from "./FloatingButton.theme";
|
||||
|
||||
@ -67,6 +68,7 @@ const icons = {
|
||||
minus: <ButtonMinusIcon />,
|
||||
refresh: <RefreshIcon />,
|
||||
duplicate: <ButtonDuplicateIcon />,
|
||||
exportIndex: <ExportRoomIndexIcon />,
|
||||
};
|
||||
|
||||
const FloatingButton = (props: FloatingButtonProps) => {
|
||||
|
123
packages/shared/components/image-editor/ButtonDelete/index.tsx
Normal file
123
packages/shared/components/image-editor/ButtonDelete/index.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
// (c) Copyright Ascensio System SIA 2009-2024
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// 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
|
||||
|
||||
import { ReactSVG } from "react-svg";
|
||||
import styled from "styled-components";
|
||||
|
||||
import TrashReactSvgUrl from "PUBLIC_DIR/images/trash.react.svg?url";
|
||||
import { TTranslation } from "../../../types";
|
||||
|
||||
const StyledButton = styled.div`
|
||||
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};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonDelete = ({
|
||||
onClick,
|
||||
t,
|
||||
}: {
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
t: TTranslation;
|
||||
}) => {
|
||||
return (
|
||||
<StyledButton
|
||||
className="icon_cropper-delete_button"
|
||||
onClick={onClick}
|
||||
title={t("Common:Delete")}
|
||||
>
|
||||
<ReactSVG src={TrashReactSvgUrl} />
|
||||
<div className="icon_cropper-delete_button-text">
|
||||
{t("Common:Delete")}
|
||||
</div>
|
||||
</StyledButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonDelete;
|
@ -25,15 +25,11 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import resizeImage from "resize-image";
|
||||
import { imageProcessing } from "@docspace/shared/utils/common";
|
||||
|
||||
import DropzoneComponent from "../../dropzone";
|
||||
import { toastr } from "../../toast";
|
||||
|
||||
const ONE_MEGABYTE = 1024 * 1024;
|
||||
const COMPRESSION_RATIO = 2;
|
||||
const NO_COMPRESSION_RATIO = 1;
|
||||
|
||||
const Dropzone = ({
|
||||
t,
|
||||
setUploadedFile,
|
||||
@ -57,70 +53,12 @@ const Dropzone = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
async function resizeRecursiveAsync(
|
||||
img: { width: number; height: number },
|
||||
canvas: HTMLCanvasElement,
|
||||
compressionRatio = COMPRESSION_RATIO,
|
||||
depth = 0,
|
||||
): Promise<unknown> {
|
||||
const data = resizeImage.resize(
|
||||
// @ts-expect-error canvas
|
||||
canvas,
|
||||
img.width / compressionRatio,
|
||||
img.height / compressionRatio,
|
||||
resizeImage.JPEG,
|
||||
);
|
||||
|
||||
const file = await fetch(data)
|
||||
.then((res) => res.blob())
|
||||
.then((blob) => {
|
||||
const f = new File([blob], "File name", {
|
||||
type: "image/jpg",
|
||||
});
|
||||
return f;
|
||||
});
|
||||
|
||||
// const stepMessage = `Step ${depth + 1}`;
|
||||
// const sizeMessage = `size = ${file.size} bytes`;
|
||||
// const compressionRatioMessage = `compressionRatio = ${compressionRatio}`;
|
||||
|
||||
// console.log(`${stepMessage} ${sizeMessage} ${compressionRatioMessage}`);
|
||||
|
||||
if (file.size < maxImageSize) {
|
||||
return file;
|
||||
}
|
||||
|
||||
if (depth > 5) {
|
||||
// console.log("start");
|
||||
throw new Error("recursion depth exceeded");
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
return resolve(file);
|
||||
}).then(() =>
|
||||
resizeRecursiveAsync(img, canvas, compressionRatio + 1, depth + 1),
|
||||
);
|
||||
}
|
||||
|
||||
const onDrop = async ([file]: File[]) => {
|
||||
timer.current = setTimeout(() => {
|
||||
setLoadingFile(true);
|
||||
}, 50);
|
||||
try {
|
||||
const imageBitMap = await createImageBitmap(file);
|
||||
|
||||
const width = imageBitMap.width;
|
||||
const height = imageBitMap.height;
|
||||
|
||||
// @ts-expect-error imageBitMap
|
||||
const canvas = resizeImage.resize2Canvas(imageBitMap, width, height);
|
||||
|
||||
resizeRecursiveAsync(
|
||||
{ width, height },
|
||||
canvas,
|
||||
file.size > maxImageSize ? COMPRESSION_RATIO : NO_COMPRESSION_RATIO,
|
||||
)
|
||||
imageProcessing(file)
|
||||
.then((f) => {
|
||||
if (mount.current) {
|
||||
if (f instanceof File) setUploadedFile(f);
|
||||
|
@ -32,12 +32,12 @@ import AvatarEditor, { Position } from "react-avatar-editor";
|
||||
import ZoomMinusReactSvgUrl from "PUBLIC_DIR/images/zoom-minus.react.svg?url";
|
||||
import ZoomPlusReactSvgUrl from "PUBLIC_DIR/images/zoom-plus.react.svg?url";
|
||||
import IconCropperGridSvgUrl from "PUBLIC_DIR/images/icon-cropper-grid.svg?url";
|
||||
import TrashReactSvgUrl from "PUBLIC_DIR/images/trash.react.svg?url";
|
||||
|
||||
import { Slider } from "../../slider";
|
||||
import { IconButton } from "../../icon-button";
|
||||
import { StyledImageCropper } from "../ImageEditor.styled";
|
||||
import { ImageCropperProps } from "../ImageEditor.types";
|
||||
import ButtonDelete from "../ButtonDelete";
|
||||
|
||||
const ImageCropper = ({
|
||||
t,
|
||||
@ -132,16 +132,8 @@ const ImageCropper = ({
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="icon_cropper-delete_button"
|
||||
onClick={handleDeleteImage}
|
||||
title={t("Common:Delete")}
|
||||
>
|
||||
<ReactSVG src={TrashReactSvgUrl} />
|
||||
<div className="icon_cropper-delete_button-text">
|
||||
{t("Common:Delete")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ButtonDelete onClick={handleDeleteImage} t={t} />
|
||||
|
||||
{typeof uploadedFile !== "string" &&
|
||||
uploadedFile?.name &&
|
||||
|
@ -63,50 +63,6 @@ const StyledImageCropper = styled.div<{ disableImageRescaling?: boolean }>`
|
||||
`};
|
||||
}
|
||||
|
||||
.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;
|
||||
|
@ -27,6 +27,7 @@
|
||||
import React from "react";
|
||||
import Dropzone from "./Dropzone";
|
||||
import ImageCropper from "./ImageCropper";
|
||||
import ButtonDelete from "./ButtonDelete";
|
||||
import { ImageEditorProps } from "./ImageEditor.types";
|
||||
import AvatarPreview from "./AvatarPreview";
|
||||
|
||||
@ -77,4 +78,4 @@ const ImageEditor = ({
|
||||
);
|
||||
};
|
||||
|
||||
export { ImageEditor, AvatarPreview };
|
||||
export { ImageEditor, AvatarPreview, Dropzone, ButtonDelete };
|
||||
|
@ -26,19 +26,20 @@
|
||||
|
||||
import React, { useCallback } from "react";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import NavigationText from "./sub-components/Text";
|
||||
|
||||
import { Consumer, DomHelpers } from "../../utils";
|
||||
import { DeviceType } from "../../enums";
|
||||
|
||||
import { Backdrop } from "../backdrop";
|
||||
|
||||
import ArrowButton from "./sub-components/ArrowBtn";
|
||||
import Text from "./sub-components/Text";
|
||||
import ControlButtons from "./sub-components/ControlBtn";
|
||||
import ToggleInfoPanelButton from "./sub-components/ToggleInfoPanelBtn";
|
||||
import NavigationLogo from "./sub-components/LogoBlock";
|
||||
import DropBox from "./sub-components/DropBox";
|
||||
|
||||
import { Tooltip } from "../tooltip";
|
||||
import { Text } from "../text";
|
||||
import { DeviceType } from "../../enums";
|
||||
import { StyledContainer } from "./Navigation.styled";
|
||||
import { INavigationProps } from "./Navigation.types";
|
||||
|
||||
@ -75,6 +76,7 @@ const Navigation = ({
|
||||
burgerLogo,
|
||||
isPublicRoom,
|
||||
titleIcon,
|
||||
titleIconTooltip,
|
||||
currentDeviceType,
|
||||
rootRoomTitle,
|
||||
showTitle,
|
||||
@ -168,13 +170,35 @@ const Navigation = ({
|
||||
((navigationItems && navigationItems.length > 1) || rootRoomTitle) &&
|
||||
currentDeviceType !== DeviceType.mobile;
|
||||
|
||||
const getContent = () => (
|
||||
<Text fontSize="12px" fontWeight={400} noSelect>
|
||||
{titleIconTooltip}
|
||||
</Text>
|
||||
);
|
||||
|
||||
const navigationTitleNode = (
|
||||
<div className="title-block">
|
||||
{titleIcon && <ReactSVG className="title-icon" src={titleIcon} />}
|
||||
<Text
|
||||
{titleIcon && (
|
||||
<ReactSVG
|
||||
data-tooltip-id="iconTooltip"
|
||||
className="title-icon"
|
||||
src={titleIcon}
|
||||
/>
|
||||
)}
|
||||
|
||||
{titleIconTooltip && (
|
||||
<Tooltip
|
||||
id="iconTooltip"
|
||||
place="bottom"
|
||||
getContent={getContent}
|
||||
maxWidth="300px"
|
||||
/>
|
||||
)}
|
||||
|
||||
<NavigationText
|
||||
className="title-block-text"
|
||||
title={title}
|
||||
isOpen={isOpen}
|
||||
isOpen={false}
|
||||
isRootFolder={isRootFolder}
|
||||
onClick={toggleDropBox}
|
||||
isRootFolderTitle={false}
|
||||
@ -189,7 +213,7 @@ const Navigation = ({
|
||||
|
||||
const navigationTitleContainerNode = showRootFolderNavigation ? (
|
||||
<div className="title-container">
|
||||
<Text
|
||||
<NavigationText
|
||||
className="room-title"
|
||||
title={
|
||||
rootRoomTitle || navigationItems[navigationItems.length - 2].title
|
||||
@ -241,6 +265,7 @@ const Navigation = ({
|
||||
currentDeviceType={currentDeviceType}
|
||||
navigationTitleContainerNode={navigationTitleContainerNode}
|
||||
onCloseDropBox={onCloseDropBox}
|
||||
titleIconTooltip={titleIconTooltip}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
@ -203,5 +203,6 @@ export interface INavigationProps {
|
||||
onNavigationButtonClick?: () => void;
|
||||
tariffBar: React.ReactElement;
|
||||
showNavigationButton: boolean;
|
||||
titleIconTooltip?: string;
|
||||
onContextOptionsClick?: () => void;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import EditingSvg32Url from "PUBLIC_DIR/images/icons/32/room/editing.svg?url";
|
||||
// import ViewOnlySvg32Url from "PUBLIC_DIR/images/icons/32/room/view.only.svg?url";
|
||||
import PublicRoomSvg32Url from "PUBLIC_DIR/images/icons/32/room/public.svg?url";
|
||||
import FormRoomSvg32Url from "PUBLIC_DIR/images/icons/32/room/form.svg?url";
|
||||
import VirtualDataRoomRoomSvg32Url from "PUBLIC_DIR/images/icons/32/room/virtual-data.svg?url";
|
||||
|
||||
import { RoomsType } from "../../enums";
|
||||
|
||||
@ -74,6 +75,8 @@ const RoomLogoPure = ({
|
||||
return CustomSvg32Url;
|
||||
case RoomsType.PublicRoom:
|
||||
return PublicRoomSvg32Url;
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return VirtualDataRoomRoomSvg32Url;
|
||||
case RoomsType.FormRoom:
|
||||
return FormRoomSvg32Url;
|
||||
default:
|
||||
|
@ -14,10 +14,14 @@ export const getRoomTypeTitleTranslation = (
|
||||
// return t("Common:ReviewRoomTitle");
|
||||
// case RoomsType.ReadOnlyRoom:
|
||||
// return t("Common:ViewOnlyRoomTitle");
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return t("Common:VirtualDataRoom");
|
||||
case RoomsType.CustomRoom:
|
||||
return t("Common:CustomRoomTitle");
|
||||
case RoomsType.PublicRoom:
|
||||
return t("Common:PublicRoom");
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return t("Common:VirtualDataRoom");
|
||||
case RoomsType.FormRoom:
|
||||
return t("Common:FormFilingRoomTitle");
|
||||
default:
|
||||
@ -38,10 +42,14 @@ export const getRoomTypeDescriptionTranslation = (
|
||||
// return t("Common:ReviewRoomDescription");
|
||||
// case RoomsType.ReadOnlyRoom:
|
||||
// return t("Common:ViewOnlyRoomDescription");
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return t("Common:VirtualDataRoomDescription");
|
||||
case RoomsType.CustomRoom:
|
||||
return t("Common:CustomRoomDescription");
|
||||
case RoomsType.PublicRoom:
|
||||
return t("Common:PublicRoomDescription");
|
||||
case RoomsType.VirtualDataRoom:
|
||||
return t("Common:VirtualDataRoomDescription");
|
||||
case RoomsType.FormRoom:
|
||||
return t("Common:FormFilingRoomDescription");
|
||||
default:
|
||||
|
@ -25,11 +25,13 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
import React, { useRef } from "react";
|
||||
|
||||
import ArrowReactSvgUrl from "PUBLIC_DIR/images/arrow2.react.svg?url";
|
||||
import { isMobile } from "react-device-detect"; // TODO: isDesktop=true for IOS(Firefox & Safari)
|
||||
import { VDRIndexingAction } from "../../enums";
|
||||
import { isMobile as isMobileUtils } from "../../utils/device";
|
||||
|
||||
import { Checkbox } from "../checkbox";
|
||||
import { ColorTheme, ThemeId } from "../color-theme";
|
||||
import {
|
||||
ContextMenuButton,
|
||||
ContextMenuButtonDisplayType,
|
||||
@ -58,6 +60,7 @@ const Row = (props: RowProps) => {
|
||||
onSelect,
|
||||
onRowClick,
|
||||
onContextClick,
|
||||
onChangeIndex,
|
||||
|
||||
getContextModel,
|
||||
isRoom,
|
||||
@ -71,6 +74,7 @@ const Row = (props: RowProps) => {
|
||||
className,
|
||||
badgeUrl,
|
||||
isDisabled,
|
||||
isIndexEditingMode,
|
||||
} = props;
|
||||
|
||||
const cm = useRef<null | {
|
||||
@ -143,6 +147,11 @@ const Row = (props: RowProps) => {
|
||||
onSelect?.(true, data);
|
||||
};
|
||||
|
||||
const changeIndex = (e, action) => {
|
||||
e.stopPropagation();
|
||||
onChangeIndex(action);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledRow
|
||||
ref={row}
|
||||
@ -213,6 +222,25 @@ const Row = (props: RowProps) => {
|
||||
{renderContentElement && (
|
||||
<StyledContentElement>{contentElement}</StyledContentElement>
|
||||
)}
|
||||
{isIndexEditingMode ? (
|
||||
<>
|
||||
<ColorTheme
|
||||
themeId={ThemeId.IndexIconButton}
|
||||
iconName={ArrowReactSvgUrl}
|
||||
className="index-up-icon"
|
||||
size="small"
|
||||
onClick={(e) => changeIndex(e, VDRIndexingAction.HigherIndex)}
|
||||
/>
|
||||
<ColorTheme
|
||||
themeId={ThemeId.IndexIconButton}
|
||||
iconName={ArrowReactSvgUrl}
|
||||
className="index-down-icon"
|
||||
size="small"
|
||||
onClick={(e) => changeIndex(e, VDRIndexingAction.LowerIndex)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{renderContext ? (
|
||||
<ContextMenuButton
|
||||
isFill
|
||||
@ -237,6 +265,8 @@ const Row = (props: RowProps) => {
|
||||
isArchive={isArchive}
|
||||
badgeUrl={badgeUrl}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</StyledOptionButton>
|
||||
</StyledRow>
|
||||
);
|
||||
|
@ -69,6 +69,7 @@ export interface RowProps {
|
||||
mode?: TMode;
|
||||
/** Removes the borders */
|
||||
withoutBorder?: boolean;
|
||||
isIndexEditingMode: boolean;
|
||||
isRoom?: boolean;
|
||||
contextTitle?: string;
|
||||
badgesComponent?: React.ReactNode;
|
||||
|
@ -49,7 +49,7 @@ const StyledScrollbar = styled(Scrollbar)<{ $isScrollLocked?: boolean }>`
|
||||
${({ $isScrollLocked }) =>
|
||||
$isScrollLocked &&
|
||||
css`
|
||||
& .scroll-wrapper > .scroller {
|
||||
&:first-child > .scroll-wrapper > .scroller {
|
||||
overflow: hidden !important;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
@ -62,7 +62,7 @@ const StyledScrollbar = styled(Scrollbar)<{ $isScrollLocked?: boolean }>`
|
||||
}
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
& .scroll-wrapper > .scroller {
|
||||
&:first-child > .scroll-wrapper > .scroller {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
|
@ -66,6 +66,7 @@ export interface SectionBodyProps {
|
||||
settingsStudio: boolean;
|
||||
isFormGallery?: boolean;
|
||||
isDesktop?: boolean;
|
||||
isIndexEditingMode?: boolean;
|
||||
currentDeviceType?: DeviceType;
|
||||
getContextModel?: () => ContextMenuModel[];
|
||||
}
|
||||
@ -144,4 +145,5 @@ export interface SectionProps {
|
||||
anotherDialogOpen?: boolean;
|
||||
isDesktop?: boolean;
|
||||
getContextModel?: () => ContextMenuModel[];
|
||||
isIndexEditingMode?: boolean;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user