Compare commits

...

215 Commits

Author SHA1 Message Date
Vlada Gazizova
02d26df0a2 Client:Calendar:The component has been rewritten in typescript. 2024-08-30 15:23:42 +03:00
Vlada Gazizova
085f780bfa Client:Calendar:Fixed crash when clicking on a day in the calendar. 2024-08-29 15:49:23 +03:00
Vlada Gazizova
fe010d398b Shared:InfoPanelScroll:Fixed scroll blocking in the info panel for the general panel only. Removed scroll blocking in components inside the info panel. 2024-08-29 15:14:00 +03:00
Vlada Gazizova
49b78e1e15 Shared:Calendar:Add scroll to calendar. 2024-08-23 16:53:42 +03:00
1bfa73ac4b
Merge pull request #536 from ONLYOFFICE/feature/VDR-indexing
Feature/vdr indexing
2024-07-11 11:45:53 +04:00
9b360fa048 Client: QuickButtons: corrected the is index editing mode condition 2024-07-11 12:43:46 +05:00
93a04c0392 Merge branch 'feature/VDR-room' into feature/VDR-indexing
# Conflicts:
#	packages/client/src/HOCs/withQuickButtons.js
#	packages/client/src/components/FilesPanels/index.js
#	packages/client/src/components/QuickButtons.js
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/client/src/store/ContextOptionsStore.js
#	packages/client/src/store/FilesActionsStore.js
#	packages/client/src/store/SelectedFolderStore.ts
2024-07-11 12:35:06 +05:00
2a30838dc1
Merge pull request #534 from ONLYOFFICE/feature/VDR-watermarks
Feature/vdr-watermarks
2024-07-11 11:21:51 +04:00
2dd89e262b Merge branch 'feature/VDR-room' into feature/VDR-watermarks
# Conflicts:
#	packages/client/src/components/GlobalEvents/EditRoomEvent.js
#	packages/client/src/components/dialogs/CreateEditRoomDialog/EditRoomDialog.js
#	packages/shared/api/rooms/index.ts
2024-07-11 10:19:57 +03:00
46654ecca2
Merge pull request #526 from ONLYOFFICE/feature/vdr-export-room-index
Feature/vdr export room index
2024-07-10 20:13:53 +04:00
d68eb46c9b Merge branch 'refs/heads/feature/VDR-room' into feature/vdr-export-room-index
# Conflicts:
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/client/src/store/ContextOptionsStore.js
#	packages/client/src/store/FilesActionsStore.js
#	packages/client/src/store/SelectedFolderStore.ts
#	packages/shared/api/rooms/index.ts
2024-07-09 17:53:35 +02:00
8f2f8defa5
Merge pull request #509 from ONLYOFFICE/feature/VDR-lifetime
Feature/vdr lifetime
2024-07-09 17:29:59 +04:00
52855d4593 Shared: Table: fix after merge 2024-07-01 14:55:42 +05:00
0b15f5bf91 Client: Header: fix after merge 2024-07-01 14:22:19 +05:00
d950c5557f Merge branch 'feature/VDR-room' into feature/VDR-indexing
# Conflicts:
#	packages/client/src/pages/Home/Section/Body/RowsView/FilesRowContent.js
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/client/src/store/ContextOptionsStore.js
2024-07-01 14:18:13 +05:00
046972b8f5 Client: fixed visible edit index context option 2024-07-01 13:50:28 +05:00
e28930bf00 Fixed after merge. 2024-06-28 10:39:03 +03:00
bc47505b07 Fixed after merge. 2024-06-28 10:34:55 +03:00
09d4c8a4f4 Shared: Components: Tabs: Deleted useless code. 2024-06-28 10:34:41 +03:00
24d7b695ab Shared: Components: Tabs: Added multiple mode for the component. 2024-06-28 10:33:38 +03:00
0f8994bd52 Merge branch 'feature/VDR-room' into feature/VDR-lifetime
# Conflicts:
#	packages/client/public/locales/en/Files.json
#	packages/client/public/locales/en/InfoPanel.json
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/shared/components/navigation/Navigation.types.ts
2024-06-27 17:08:52 +03:00
d40881ec94 Merge branch 'develop' into feature/VDR-room 2024-06-27 17:03:34 +03:00
82de90538e Merge branch 'feature/VDR-room' of https://github.com/ONLYOFFICE/DocSpace-client into feature/VDR-room 2024-06-27 17:02:46 +03:00
a1f37ebc04 Merge branch 'feature/VDR-room' into feature/VDR-indexing
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/public/locales/en/Files.json
#	packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js
#	packages/client/src/helpers/filesUtils.js
#	packages/client/src/pages/Home/Section/Body/TableView/sub-components/RowData.js
#	packages/shared/components/table/TableHeader.tsx
#	public/locales/en/Common.json
2024-06-27 15:48:33 +05:00
14a766bbb1 Merge branch 'develop' into feature/VDR-room 2024-06-27 15:13:15 +05:00
d2d7d535d1 Fixed after merge. 2024-06-27 12:39:43 +03:00
61290de23c Merge branch 'feature/VDR-room' into feature/VDR-watermarks
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/src/components/GlobalEvents/CreateRoomEvent.js
#	packages/client/src/components/GlobalEvents/EditRoomEvent.js
#	packages/shared/components/image-editor/Dropzone/index.tsx
#	packages/shared/components/language-combobox/LanguageCombobox.types.ts
#	packages/shared/components/tabs-container/TabsContainer.styled.tsx
#	packages/shared/components/tabs-container/TabsContainer.tsx
#	packages/shared/enums/index.ts
#	packages/shared/themes/base.ts
#	packages/shared/themes/dark.ts
#	packages/shared/utils/common.ts
2024-06-26 16:29:29 +03:00
72f7e3ea5d Deleted useless code. 2024-06-26 16:04:12 +03:00
ea56dece9e Refactoring. 2024-06-26 16:03:22 +03:00
3fb4684e51 Client: disabled the hotkeys in index editing mode 2024-06-26 15:35:49 +05:00
3ff194fc66 Refactoring. 2024-06-26 13:32:44 +03:00
ce8aebc6a4 Client: disabled the section context menu in index editing mode 2024-06-26 15:17:05 +05:00
523bf28fd9 Refactoring. 2024-06-26 13:06:47 +03:00
123665d3c9 Added icon preview for watermark. 2024-06-25 17:05:06 +03:00
bb7a9f3292 Client: Fix old config name 2024-06-25 12:35:49 +02:00
7e88a1567f Client: Add showing progress bar during index export 2024-06-25 12:17:06 +02:00
746094957b Client: Add showing error if trying export index before previous one has completed 2024-06-25 12:17:02 +02:00
796d90aa9f Client: Add new icon for export room index 2024-06-25 12:13:10 +02:00
b220f57954 Client: Add opening export file in same/new tab depending on setting. Move jsx out of mobx 2024-06-25 12:13:00 +02:00
e5dd58ac08 Client: Add export room index to list and info panel context menu 2024-06-25 12:10:28 +02:00
ee89a8a40b Client: Shared: ExportRoomIndex: Add error handling. Add typescript 2024-06-25 12:10:23 +02:00
3e3d15f715 Client: Shared: Connect room index export to api 2024-06-25 12:09:24 +02:00
16f7f3d3d4 Client: Shared: Add export index layout with fake api 2024-06-25 12:09:02 +02:00
643b92a49f Client: added Index translation 2024-06-24 19:13:28 +05:00
df2d67c1df Client: FilesStore: fixed sorting for VDR indexing 2024-06-24 18:06:13 +05:00
1b0425216c Shared: TableHeader: fix table after adding dynamic index column 2024-06-24 15:21:44 +05:00
ef264c7755 Shared: delete old code 2024-06-24 15:20:44 +05:00
efbeb95641 Shared: TableHeader: fixed the calculation of the old width for the dynamic column index 2024-06-24 14:14:13 +05:00
1788aacf93 Merge branch 'develop' into feature/VDR-indexing
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/src/components/FilesPanels/index.js
#	packages/client/src/pages/Home/Section/Body/TableView/TableContainer.js
#	packages/client/src/pages/Home/Section/Body/TableView/TableHeader.js
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/client/src/store/FilesActionsStore.js
#	packages/shared/api/files/index.ts
#	packages/shared/components/table/TableHeader.tsx
#	packages/shared/enums/index.ts
#	packages/shared/themes/dark.ts
#	public/locales/en/Common.json
2024-06-24 14:12:56 +05:00
c67f5ad7e3 Client: added a dynamic change in the width of the column index 2024-06-21 19:41:46 +05:00
4dfce3d3d6 Client: create indexColumnSize getter 2024-06-21 19:40:37 +05:00
e4dbd0df7a Client: removed indents for index cell 2024-06-21 19:39:36 +05:00
d2fdbecefe Web: VDR-lifetime: fixed dateOptions translation 2024-06-21 13:10:22 +03:00
b73a9526d2 Refactoring. 2024-06-21 12:22:21 +03:00
4e77bdf335 Merge branch 'feature/VDR-room' into feature/VDR-lifetime
# Conflicts:
#	packages/client/src/components/dialogs/ConflictResolveDialog/index.tsx
#	packages/client/src/store/FilesActionsStore.js
2024-06-20 11:45:20 +03:00
33145f2a3f Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/shared/enums/index.ts
#	public/locales/en/Common.json
2024-06-20 11:44:03 +03:00
92c4622217 Web: VDR-lifetime: fixed tooltip 2024-06-20 11:36:23 +03:00
03f23b0be3 Web: VDR-lifetime: added file lifetime block to the info-panel 2024-06-20 11:35:49 +03:00
d032fff28d Web: VDR-lifetime: added logic for displaying the file lifetime icon 2024-06-19 18:40:23 +03:00
f32561fa5f Web: Files: ConflictResolveDialog: fixed translations 2024-06-19 18:38:15 +03:00
eceba70e98 Web: VDR-lifetime: added changeRoomLifetime, fixed header tooltip 2024-06-18 17:54:37 +03:00
afcd61e64e Client: added toast if an error occurs when change index 2024-06-18 16:31:46 +05:00
bda0c950a1 Web: VDR-lifetime: added changeRoomLifetime 2024-06-18 14:20:27 +03:00
d90c1a1c74 Client: TableHeader: removed the ability to disable the index column 2024-06-18 16:19:38 +05:00
d9b50962cf Client: disabling the filter in index editing mode 2024-06-18 16:13:13 +05:00
178da67a3c Shared: fixed the colors of the index change buttons 2024-06-17 19:40:55 +05:00
4a8ee6e20c Client: corrected colors in index editing mode 2024-06-17 19:30:14 +05:00
b8f4ec5fbb Web: VDR-Lifetime: fixed room tooltip 2024-06-17 17:29:31 +03:00
365fcbbb9c Client: index column expanded 2024-06-17 18:53:44 +05:00
a7307c94da Client: rewrite indexing components to typescript 2024-06-17 18:52:47 +05:00
5b687d6f8c Merge branch 'feature/VDR-room' into feature/VDR-lifetime 2024-06-17 13:37:45 +03:00
4b96f5c99e Web: VDR: fixed merge 2024-06-17 13:37:06 +03:00
0bfe24d0cb Merge branch 'feature/VDR-room' into feature/VDR-lifetime
# Conflicts:
#	packages/client/src/components/FilesPanels/index.js
#	packages/shared/components/selector/Selector.tsx
#	packages/shared/components/selector/sub-components/Body.tsx
#	packages/shared/components/selector/sub-components/Item.tsx
#	packages/shared/selectors/Files/FilesSelector.types.ts
#	packages/shared/selectors/Files/index.tsx
2024-06-17 12:38:16 +03:00
47d70ea8f2 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
2024-06-17 12:34:23 +03:00
cc644180a5 Client: fixed table bug with indexing mode 2024-06-14 19:46:34 +05:00
179a6c42f2 Client: fix after merge 2024-06-13 16:05:31 +05:00
fc8e894362 Client: added changeIndex to row view 2024-06-13 16:03:42 +05:00
02772b660b Client: fixed update indexing selection 2024-06-13 15:57:53 +05:00
d083d108c8 Client: fixed onMouseUp indexing change logic 2024-06-10 16:35:12 +05:00
86f4555c1b Client: fixed VDR Room description after merge 2024-06-10 16:11:48 +05:00
d15445301c Merge branch 'develop' into feature/VDR-indexing
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/public/locales/en/Files.json
#	packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js
#	packages/client/src/helpers/filesUtils.js
#	packages/client/src/store/CreateEditRoomStore.js
#	public/locales/en/Common.json
2024-06-10 16:05:56 +05:00
0bcbd98a32 Shared: added MoveIndex to VDRIndexingAction 2024-06-10 15:38:53 +05:00
480f46a599 Client: corrected changeIndex logic for drag n drop 2024-06-10 15:38:24 +05:00
0eccb51598 Client: Section: Body: indexing separator refactoring 2024-06-10 15:36:00 +05:00
c1ea56d06b Shared: Table: delete extra code 2024-06-07 18:27:51 +05:00
2733e61aaa Client: Section: Body: added display of separator during drag and drop in index editing mode 2024-06-07 18:27:03 +05:00
3af4f321cb Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/public/locales/en/Files.json
#	packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js
#	packages/client/src/helpers/filesUtils.js
#	packages/client/src/store/CreateEditRoomStore.js
#	public/locales/en/Common.json
2024-06-07 16:25:58 +03:00
c8c724e63b Web: Client: fixed drag n drop style when editing index mode enabled 2024-06-06 20:02:17 +05:00
afd8251e27 Revert "Client: fixed row view for indexing"
This reverts commit b7517458df.

# Conflicts:
#	packages/client/src/pages/Home/Section/Header/index.js
2024-06-05 13:19:52 +05:00
ff514b79d3 Client: created ReorderIndexDialog 2024-05-31 15:50:28 +05:00
8a8ed799fb Client: added translation 2024-05-31 15:48:36 +05:00
410b2814c7 Client: Table: fixed index column size 2024-05-30 15:25:45 +05:00
b7517458df Client: fixed row view for indexing 2024-05-30 15:24:04 +05:00
49d9c7c3d9 Merge branch 'develop' into feature/VDR-indexing
# Conflicts:
#	packages/client/src/pages/Home/Section/Body/TableView/TableHeader.js
2024-05-29 13:32:12 +05:00
377779d9d3 Merge remote-tracking branch 'remotes/origin/feature/VDR-room' into feature/VDR-indexing
# Conflicts:
#	packages/client/src/HOCs/withQuickButtons.js
#	packages/client/src/components/QuickButtons.js
#	packages/client/src/pages/Home/Section/Filter/index.js
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/client/src/store/ContextOptionsStore.js
#	packages/client/src/store/FilesStore.js
#	packages/client/src/store/TableStore.js
#	packages/client/src/store/index.js
#	packages/shared/api/files/index.ts
#	packages/shared/enums/index.ts
#	public/locales/en/Common.json
2024-05-29 13:29:15 +05:00
1b2ff05cd8 Client: FilesStore: added sorting in get files list for vdr indexing 2024-05-28 17:33:44 +05:00
eeb29a7cee Client: TableView: fixed indexing style 2024-05-27 19:38:02 +05:00
8f69a3cf60 Client: create StyledIndexHeader, fix styles 2024-05-27 19:37:24 +05:00
a9e075ee51 Merge branch 'feature/VDR-room' into feature/VDR-lifetime 2024-05-24 17:04:30 +03:00
f441cd4546 Merge branch 'develop' into feature/VDR-room 2024-05-24 17:03:07 +03:00
4c349f5d14 Client: fixed column size in indexing room 2024-05-23 21:17:33 +05:00
131d99d84c Client: fixed the logic for determining the selected elements, refactoring 2024-05-23 21:13:03 +05:00
328df23d4a Merge branch 'feature/VDR-room' into feature/VDR-lifetime 2024-05-22 12:57:39 +03:00
d2b6d23d9a Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/src/pages/Home/InfoPanel/Body/sub-components/ItemTitle/Rooms/index.js
2024-05-22 12:57:07 +03:00
3d9ffaf309 Web: Files: VDR-lifetime: fixed merge 2024-05-22 12:55:27 +03:00
a0a4abadae Client: fixed styles for highlight changed indexes 2024-05-21 19:50:16 +05:00
e14319dce2 Client: added indexingStore to filesActionsStore 2024-05-21 19:49:45 +05:00
fb712b916a Client: added selection for to highlight changed indexes 2024-05-21 19:48:56 +05:00
68c06ac91a Merge branch 'feature/VDR-room' into feature/VDR-lifetime
# Conflicts:
#	packages/client/src/components/FilesPanels/index.js
#	packages/client/src/components/FilesSelector/index.tsx
#	packages/client/src/components/QuickButtons.js
#	packages/client/src/pages/Home/Section/Header/index.js
#	packages/components/selector/Selector.types.ts
#	packages/components/selector/index.tsx
#	packages/components/selector/sub-components/Body/Body.types.ts
#	packages/components/selector/sub-components/Item/Item.types.ts
#	packages/components/selector/sub-components/Item/StyledItem.ts
#	packages/components/selector/sub-components/Item/index.tsx
#	packages/components/themes/base.js
#	packages/shared/components/navigation/Navigation.tsx
#	packages/shared/components/selector/sub-components/Body.tsx
#	packages/shared/themes/dark.ts
2024-05-21 14:38:28 +03:00
a3c9c55ad4 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/src/pages/Home/InfoPanel/Body/sub-components/ItemTitle/Rooms/index.js
#	packages/client/src/pages/Home/InfoPanel/Body/views/History/index.js
2024-05-21 12:55:23 +03:00
6fb0edff91 Web: Client: Refactoring. 2024-05-20 12:42:53 +03:00
5feca2b857 Shared: Selectors: Added return new field. 2024-05-20 10:23:33 +03:00
2c50e66e94 Web: Client: IndexingStore: fixed exit from index editing mode 2024-05-16 19:37:10 +05:00
75abe1a629 Client: added indexing in nested structures 2024-05-16 18:39:46 +05:00
02fb8738ce Web: Refactoring. 2024-05-14 18:13:01 +03:00
ea78cff437 Web: Removed changing watermark data if it does not pass the conditions 2024-05-14 16:27:32 +03:00
5476c53716 Web: Added a warning dialog if the watermark information is not filled in. 2024-05-14 14:41:29 +03:00
dfaa8f9d4e Web: Fixed display of percentages and degrees. 2024-05-14 12:35:07 +03:00
df808d2e15 Web: Added a component with an image. 2024-05-13 20:42:56 +03:00
80b943c0e7 Web: Shared: Image processing is moved to a general function. 2024-05-13 20:41:51 +03:00
5be75c1745 Client: added reorder action, fixed change index 2024-05-13 20:04:30 +05:00
f312ffe5e5 Client: added change index action with quick button in table view 2024-05-13 19:31:50 +05:00
3e390d8e5c Client: fixed editing index mode: disable selection and file transition 2024-05-13 19:30:33 +05:00
aa17569192 Shared: Api: added change and reorder function 2024-05-13 19:28:32 +05:00
b26186c8cf Shared: added VDR Indexing enums 2024-05-13 19:27:43 +05:00
60bf65af7d Client: set a filter for indexing 2024-05-07 18:49:39 +05:00
4784395b02 Web: Client/Shared: Added watermark deletion. 2024-05-07 16:34:23 +03:00
c3334f0695 Web: Client: Refactoring. 2024-05-07 16:01:59 +03:00
029e6534af Web: Client: Added check for differences between selected options for the room editing dialog. 2024-05-07 13:07:22 +03:00
3ffb98a9df Web: Client/Shared: Added watermark editing, refactoring. 2024-05-06 18:19:23 +03:00
be153f7d2c Web: Client/Shared: Added getting information about watermarks. 2024-05-06 13:42:09 +03:00
059717eac7 Client: IndexHeader: fixed mobile padding 2024-05-03 19:39:24 +05:00
e4178cc974 Client: added index arrow to row view 2024-05-03 19:30:30 +05:00
97bca2e6ec Shared: infiniteLoader: change column size when edit index mode enabled 2024-05-03 19:29:30 +05:00
9ca89604ac Client: added index arrow to table view if edit index mode enabled 2024-05-03 19:25:35 +05:00
06586acd73 Shared: ColorTheme: create index icon button 2024-05-03 19:21:14 +05:00
17c7a4216c Client: disable quick buttons when edit index mode 2024-05-03 19:19:32 +05:00
8bac9a7e0b Client: Components: Refactoring. 2024-05-03 13:35:29 +03:00
c07d7218b3 Web: Added saving watermark information for text. 2024-05-02 16:22:22 +03:00
b36b3e9648 Shared: Components: TabsContainer: Fixed initial value. 2024-05-02 15:24:09 +03:00
b5debea181 Fixed after merge. 2024-05-02 11:59:31 +03:00
0dfab5563b Merge branch 'feature/VDR-room' into feature/VDR-watermarks
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/components/tabs-container/index.js
#	packages/components/tabs-container/styled-tabs-container.js
#	packages/components/themes/base.js
#	packages/components/utils/globalColors.js
#	public/locales/en/Common.json
2024-05-02 10:42:07 +03:00
000efcfac6 Client: TableHeader: changed the column name from idx to # 2024-04-27 18:25:50 +05:00
6936cf92df Client: added disabling the context menu, selecting a file/folder and info panel when edit index mode enable 2024-04-27 18:24:57 +05:00
f803ba930b Web: disabled the sorting and tile view combobox for vdr indexing 2024-04-27 18:22:07 +05:00
9ee552e0d8 Client: ContextOptionsStore: added edit index option 2024-04-26 16:04:22 +05:00
5cf2d32c24 Web: Client: created TableIndexHeader 2024-04-26 16:03:45 +05:00
06fee9c6b4 Public: Locales: added en translations for indexing 2024-04-26 16:02:27 +05:00
e917242293 Public: added svg for indexing 2024-04-26 15:55:17 +05:00
06e124a422 Client: added indexingStore 2024-04-26 15:54:07 +05:00
00e4b09c7b Client: created indexingStore 2024-04-26 15:39:03 +05:00
f5416db8e0 Client: InfoPanel: DetailsHelper: added index 2024-04-23 20:04:27 +05:00
e1f2b377d8 Client: TableView: removed the sorting option for VDR indexing 2024-04-22 19:53:12 +05:00
2472f28d78 Client: TableHeader: fixed column jump when changing width 2024-04-22 19:33:20 +05:00
758aa39d8f Client: TableView: fixed drag n drop styles for idx column 2024-04-22 19:32:24 +05:00
a0f290205d Client: RowsView: added idx for VDR indexing 2024-04-22 19:31:50 +05:00
e8eb0db251 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	public/locales/en/Common.json
2024-04-22 15:43:39 +04:00
33c09cbcd3 Merge branch 'develop' of github.com:ONLYOFFICE/DocSpace-client into feature/VDR-indexing
# Conflicts:
#	public/locales/en/Common.json
2024-04-22 16:41:54 +05:00
c1ab48720d Shared: TableHeader: reduced the width of the first column for vdr indexing 2024-04-19 19:52:04 +05:00
98de3ea7e7 Client: TableView: set the default width for the index column 2024-04-19 19:48:37 +05:00
aa83bae8c6 Client: TableView: fixed index cell indention 2024-04-19 19:47:56 +05:00
83325a89df Client: TableView: logic changes for vdr indexes 2024-04-18 21:11:09 +05:00
dc7f45f7f6 Client: added localStorage for VDR indexing 2024-04-18 21:09:41 +05:00
5eb6f951cb Client: TableView: added idx column 2024-04-17 20:22:58 +05:00
54dc154567 Client: FilesStore: started throwing order prop for indexing 2024-04-17 20:22:09 +05:00
8c3fe05203 Client: SelectedFolderStore: added indexing prop 2024-04-17 20:21:30 +05:00
bf1bd01793 Client: TableStore: added indexColumnIsEnabled prop 2024-04-17 20:20:59 +05:00
e27e7dbc7a Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/public/locales/en/Files.json
#	packages/client/src/components/GlobalEvents/EditRoomEvent.js
#	packages/client/src/components/dialogs/CreateEditRoomDialog/EditRoomDialog.js
#	packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/SetRoomParams.js
#	packages/client/src/pages/Home/InfoPanel/Body/sub-components/ItemTitle/Rooms/index.js
#	packages/client/src/pages/Home/InfoPanel/Body/sub-components/ItemTitle/index.js
#	packages/client/src/store/CreateEditRoomStore.js
#	packages/client/src/store/FilesStore.js
#	packages/client/src/store/InfoPanelStore.js
2024-04-15 18:31:54 +04:00
b6d67adc3a Merge branch 'feature/VDR-room' into feature/vdr-room-history-data-filter 2024-02-14 10:33:49 +03:00
6ab5d32801 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/public/locales/en/CreateEditRoomDialog.json
#	packages/client/src/components/dialogs/CreateEditRoomDialog/data/index.js
#	packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/SetRoomParams.js
#	packages/client/src/helpers/filesUtils.js
#	packages/client/src/store/FilesSettingsStore.js
#	packages/client/src/store/FilesStore.js
#	packages/common/constants/index.js
#	packages/components/room-logo/index.js
2024-02-12 13:28:53 +03:00
94ad3b879f Web:Client:Refactoring for Calendar. 2024-01-29 11:51:06 +03:00
bcba14cc8a Web:Add icon calendar. 2024-01-26 17:37:35 +03:00
5174a80282 Web:Client:Added calendar styles for mobile phones in vertical position. 2024-01-26 17:05:53 +03:00
0471b801ec Web:Client:Add close calendar when clicking outside the area. 2024-01-26 13:21:08 +03:00
8c4e888350 Web:Client:Added calendar date reset after scrolling to record in history. 2024-01-26 10:58:16 +03:00
a88958efbb Web:Client:Added transition to the nearest new date if there is no entry in the history for the selected date. 2024-01-25 18:10:19 +03:00
5a166b1c58 Web: Add setCalendarDay, open CalendarComponent. 2024-01-24 11:54:07 +03:00
de668ed15e Merge 2024-01-23 17:40:31 +03:00
04368e7953 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/SetRoomParams.js
2023-12-25 09:47:01 +01:00
8f58a7bc9f Deleted document preview. 2023-12-07 17:11:34 +03:00
f15252317e Added watermark to text component. 2023-12-07 17:10:52 +03:00
8c2a3f1216 Created watermark component. 2023-12-07 17:10:20 +03:00
83e1180c37 Web: TabsContainer: Added support for adding a border. 2023-12-07 17:09:44 +03:00
8cb87da1d5 PortalSettings: Watermark: Added viewer info component. 2023-12-07 17:05:18 +03:00
90fddc25ab Web: Components: TabsContainer: added support for scrolling and multiple mode. 2023-12-07 17:03:05 +03:00
1568b4954f Added Watermarks content. 2023-12-07 17:01:39 +03:00
d47c6beb31 Web: Watermarks: Added Checkbox. 2023-12-07 17:00:01 +03:00
4585f388e6 Watermarks: Added text component. 2023-12-07 16:59:21 +03:00
da02a34b89 Web: Files: VRD: added creation with auto indexing 2023-12-05 19:42:46 +03:00
df56be32e6 Web: Files: Fixed VDR creation, fixed translations 2023-12-05 18:41:59 +03:00
457d5993e1 Web: Components: Selector: fixed styles 2023-11-24 14:29:42 +03:00
119ba72c1a Web: Files: added lifetime dialog 2023-11-23 16:19:50 +03:00
d98a127963 Web: Files: removed duplicate code 2023-11-23 14:20:26 +03:00
e299804a58 Web: Files: VDR: Lifetime: fixed title-icon styles 2023-11-23 14:20:04 +03:00
99bd0055cc Web: Files: fixed selector text 2023-11-23 13:25:00 +03:00
0e61a9028a Web: Files: Fixed lifetime tooltip for FilesSelector 2023-11-23 13:16:03 +03:00
7ee7f5bf29 Web: Files: changed lifetime translation 2023-11-22 17:30:22 +03:00
02ff6d65f6 Web: Files: added VDR-lifetime 2023-11-22 15:55:19 +03:00
ec75beee04 Web: Files: CreateEditRoomDialog: removed hugeMobile 2023-11-22 12:36:47 +03:00
e125c70d65 Web: Files: fixed merge 2023-11-22 12:27:10 +03:00
365a756dfd Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/SetRoomParams.js
#	packages/common/utils/image-helpers.js
2023-11-22 11:53:41 +03:00
0eba7d8c22 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/SetRoomParams.js
2023-10-13 18:33:32 +03:00
88e39b44b6 Web:Client:Add Portal. 2023-10-10 13:52:43 +03:00
98d7a86a0a Web:Client:Fix minDate. 2023-10-05 09:12:32 +03:00
f720d08874 Web:Add minDate, maxDate in Calendar. 2023-10-04 17:46:55 +03:00
be63e1be5b Web:Added scrolling to element by date selected in the calendar. 2023-10-04 16:23:46 +03:00
VladaGaz
54df145654 Merge 2023-10-04 15:33:03 +03:00
f537c7baf6 Merge branch 'develop' into feature/VDR-room
# Conflicts:
#	packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/SetRoomParams.js
#	public/locales/en/Common.json
2023-10-04 11:57:27 +03:00
gazizova-vlada
6ed7eacf8c Web:Client:Add CalendarComponent. 2023-09-26 10:40:43 +03:00
be954b594c Web: Files: FileLifetime: fixed mobile view 2023-09-14 17:03:33 +03:00
7a3cb28683 Web: Files: added FileLifetime block 2023-09-14 15:44:06 +03:00
920e634322 Merge branch 'develop' into feature/VDR-room 2023-09-14 12:56:59 +03:00
3ab8e3972c Web: Files: fixed VirtualDataRoom icon 2023-09-13 15:00:07 +03:00
e92b3b1485 Web: Files: fixed display of fields for VDR-rooms 2023-09-13 12:32:24 +03:00
a508444e6e Web: Files: added VDR-room 2023-09-13 12:20:24 +03:00
133 changed files with 4739 additions and 508 deletions

View File

@ -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:"
}

View File

@ -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"
}

View File

@ -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",

View File

@ -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": "В данный момент эта комната пуста.",

View File

@ -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,
};

View File

@ -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,

View File

@ -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));

View File

@ -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)));

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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,63 +156,93 @@ 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">
{isAvailableLockFile && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={iconLock}
className="badge lock-file icons-group"
size={sizeQuickButton}
data-id={id}
data-locked={locked ? true : false}
onClick={onClickLock}
color={colorLock}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("UnblockVersion")}
/>
)}
{isAvailableDownloadFile && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={FileActionsDownloadReactSvgUrl}
className="badge download-file icons-group"
size={sizeQuickButton}
onClick={onClickDownload}
color={colorLock}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("Common:Download")}
/>
)}
{showCopyLinkIcon && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={LinkReactSvgUrl}
className="badge copy-link icons-group"
size={sizeQuickButton}
onClick={onCopyPrimaryLink}
color={colorLock}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("Files:CopySharedLink")}
/>
)}
{isAvailableShareFile && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={LinkReactSvgUrl}
className="badge copy-link icons-group"
size={sizeQuickButton}
onClick={onClickShare}
color={colorShare}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("Files:CopySharedLink")}
/>
)}
{/* {fileExst && !isTrashFolder && displayBadges && (
<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}
iconName={iconLock}
className="badge lock-file icons-group"
size={sizeQuickButton}
data-id={id}
data-locked={locked ? true : false}
onClick={onClickLock}
color={colorLock}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("UnblockVersion")}
/>
)}
{isAvailableDownloadFile && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={FileActionsDownloadReactSvgUrl}
className="badge download-file icons-group"
size={sizeQuickButton}
onClick={onClickDownload}
color={colorLock}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("Common:Download")}
/>
)}
{showCopyLinkIcon && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={LinkReactSvgUrl}
className="badge copy-link icons-group"
size={sizeQuickButton}
onClick={onCopyPrimaryLink}
color={colorLock}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("Files:CopySharedLink")}
/>
)}
{isAvailableShareFile && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={LinkReactSvgUrl}
className="badge copy-link icons-group"
size={sizeQuickButton}
onClick={onClickShare}
color={colorShare}
isDisabled={isDisabled}
hoverColor={theme.filesQuickButtons.sharedColor}
title={t("Files:CopySharedLink")}
/>
)}
{/* {fileExst && !isTrashFolder && displayBadges && (
<ColorTheme
themeId={ThemeId.IconButton}
iconName={iconFavorite}
@ -206,7 +256,9 @@ const QuickButtons = (props) => {
hoverColor={theme.filesQuickButtons.hoverColor}
/>
)} */}
</div>
</>
)}
</StyledQuickButtons>
);
};

View File

@ -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,

View File

@ -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")}

View File

@ -103,6 +103,7 @@ const CreateRoomDialog = ({
y: 0.5,
zoom: 1,
},
indexing: false, // VDR Automatic indexing
};
const [roomParams, setRoomParams] = useState({ ...startRoomParams });

View File

@ -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));

View File

@ -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");
}

View File

@ -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;

View File

@ -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}

View File

@ -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;

View File

@ -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}&deg;
</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}&#37;
</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}&#37;</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}&deg;</div>
</ComboBox>
</div>
</div>
</div>
)}
</StyledWatermark>
);
};
export default inject(({ createEditRoomStore }) => {
const { setWatermarks, initialWatermarksSettings, watermarksSettings } =
createEditRoomStore;
const { imageUrl } = watermarksSettings;
return {
setWatermarks,
initialWatermarksSettings,
imageUrl,
};
})(observer(ImageWatermark));

View File

@ -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 };

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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")}

View File

@ -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 };

View File

@ -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));

View File

@ -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));

View File

@ -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");
}

View 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>
&nbsp;
<Text as="span" fontSize="12px">
{t<string>("Files:FileExportedToMyDocuments")}
</Text>
</>
);
toastr.success(toastMessage);
};

View File

@ -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);
};

View File

@ -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,

View File

@ -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;

View File

@ -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,
};
},
)(

View File

@ -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));

View File

@ -44,6 +44,7 @@ const HistoryBlock = ({ t, feed, isLastEntity }) => {
return (
<StyledHistoryBlock
className={date}
withBottomDivider={!isLastEntity}
isUserAction={isUserAction}
>

View File

@ -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)));

View File

@ -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));

View File

@ -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,
};
},
)(

View File

@ -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}

View File

@ -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`

View File

@ -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));

View File

@ -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,
};
},
)(

View File

@ -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

View File

@ -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>
<Checkbox
className="table-container_row-checkbox"
onChange={onChange}
isChecked={checked}
title={t("Common:TitleSelectFile")}
/>
{!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}

View File

@ -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;

View File

@ -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));

View File

@ -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,
};
},
)(

View File

@ -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,

View File

@ -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,
};
},
)(

View File

@ -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 };

View 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;

View File

@ -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,28 +108,32 @@ const SelectionArea = (props) => {
);
};
export default inject(({ filesStore, treeFoldersStore, infoPanelStore }) => {
const {
dragging,
viewAs,
setSelections,
getCountTilesInRow,
folders,
files,
} = filesStore;
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
const { isVisible: isInfoPanelVisible } = infoPanelStore;
export default inject(
({ filesStore, treeFoldersStore, infoPanelStore, indexingStore }) => {
const {
dragging,
viewAs,
setSelections,
getCountTilesInRow,
folders,
files,
} = filesStore;
const { isRoomsFolder, isArchiveFolder } = treeFoldersStore;
const { isVisible: isInfoPanelVisible } = infoPanelStore;
const { isIndexEditingMode } = indexingStore;
const isRooms = isRoomsFolder || isArchiveFolder;
const isRooms = isRoomsFolder || isArchiveFolder;
return {
dragging,
viewAs,
setSelections,
getCountTilesInRow,
isRooms,
foldersLength: folders.length,
filesLength: files.length,
isInfoPanelVisible,
};
})(observer(SelectionArea));
return {
dragging,
viewAs,
setSelections,
getCountTilesInRow,
isRooms,
foldersLength: folders.length,
filesLength: files.length,
isInfoPanelVisible,
isIndexEditingMode,
};
},
)(observer(SelectionArea));

View File

@ -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,

View File

@ -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);

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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";
}

View File

@ -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;

View 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;

View File

@ -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;

View File

@ -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) => {

View File

@ -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,7 +441,9 @@ class TableStore {
? `${TABLE_TRASH_COLUMNS}=${userId}`
: isRecentTab
? `${TABLE_RECENT_COLUMNS}=${userId}`
: `${TABLE_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,7 +463,9 @@ class TableStore {
? `${COLUMNS_TRASH_SIZE}=${userId}`
: isRecentTab
? `${COLUMNS_RECENT_SIZE}=${userId}`
: `${COLUMNS_SIZE}=${userId}`;
: isIndexing
? `${COLUMNS_VDR_INDEXING_SIZE}=${userId}`
: `${COLUMNS_SIZE}=${userId}`;
return isFrame ? `SDK_${columnStorageName}` : columnStorageName;
}

View File

@ -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;

View 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;

View File

@ -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",

View File

@ -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`,
});
}

View File

@ -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;
};

View File

@ -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};

View File

@ -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,6 +107,49 @@ const Calendar = ({
setObservedDate(date);
}, [initialDate, maxDate, minDate]);
const CalendarBodyNode =
selectedScene === 0 ? (
<Days
observedDate={observedDate}
setObservedDate={setObservedDate}
setSelectedScene={setSelectedScene}
selectedDate={selectedDate}
handleDateChange={handleDateChange}
minDate={resultMinDate}
maxDate={resultMaxDate}
isMobile={isMobile || false}
isScroll={isScroll}
/>
) : selectedScene === 1 ? (
<Months
observedDate={observedDate}
setObservedDate={setObservedDate}
setSelectedScene={setSelectedScene}
selectedDate={selectedDate}
minDate={resultMinDate}
maxDate={resultMaxDate}
isMobile={isMobile || false}
isScroll={isScroll}
/>
) : (
<Years
observedDate={observedDate}
setObservedDate={setObservedDate}
setSelectedScene={setSelectedScene}
selectedDate={selectedDate}
minDate={resultMinDate}
maxDate={resultMaxDate}
isMobile={isMobile || false}
isScroll={isScroll}
/>
);
const CalendarNode = isScroll ? (
<Scrollbar>{CalendarBodyNode}</Scrollbar>
) : (
CalendarBodyNode
);
return (
<StyledContainerTheme
id={id}
@ -114,39 +159,9 @@ const Calendar = ({
ref={forwardedRef}
$currentColorScheme={theme?.currentColorScheme}
data-testid="calendar"
isScroll={isScroll}
>
{selectedScene === 0 ? (
<Days
observedDate={observedDate}
setObservedDate={setObservedDate}
setSelectedScene={setSelectedScene}
selectedDate={selectedDate}
handleDateChange={handleDateChange}
minDate={resultMinDate}
maxDate={resultMaxDate}
isMobile={isMobile || false}
/>
) : selectedScene === 1 ? (
<Months
observedDate={observedDate}
setObservedDate={setObservedDate}
setSelectedScene={setSelectedScene}
selectedDate={selectedDate}
minDate={resultMinDate}
maxDate={resultMaxDate}
isMobile={isMobile || false}
/>
) : (
<Years
observedDate={observedDate}
setObservedDate={setObservedDate}
setSelectedScene={setSelectedScene}
selectedDate={selectedDate}
minDate={resultMinDate}
maxDate={resultMaxDate}
isMobile={isMobile || false}
/>
)}
{CalendarNode}
</StyledContainerTheme>
);
};

View File

@ -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 {

View File

@ -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}
/>
</>
);

View File

@ -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>
);

View File

@ -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}
/>
</>
);

View File

@ -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>
);

View File

@ -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}
/>
</>
);

View File

@ -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>
);

View File

@ -39,4 +39,5 @@ export const enum ThemeId {
IndicatorLoader = "indicatorLoader",
Progress = "progress",
SubmenuText = "submenuText",
IndexIconButton = "indexIconButton",
}

View File

@ -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 (

View File

@ -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;

View File

@ -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;

View File

@ -233,6 +233,9 @@ export interface FilterProps {
isPeopleAccounts: boolean;
isGroupsAccounts: boolean;
isInsideGroup: boolean;
isIndexing: boolean;
isIndexEditingMode: boolean;
filterTitle: string;
sortByTitle: string;

View File

@ -71,6 +71,9 @@ const FilterInput = React.memo(
isPeopleAccounts,
isGroupsAccounts,
isInsideGroup,
isIndexing,
isIndexEditingMode,
filterTitle,
sortByTitle,
@ -208,43 +211,49 @@ const FilterInput = React.memo(
onClearSearch={onClearSearch}
id="filter_search-input"
size={InputSize.base}
isDisabled={isIndexEditingMode}
onFocus={onInputFocus}
/>
<FilterButton
id="filter-button"
onFilter={onFilter}
getFilterData={getFilterData}
selectedFilterValue={selectedFilterValue}
filterHeader={filterHeader}
selectorLabel={selectorLabel}
isRooms={isRooms}
isAccounts={isAccounts}
isPeopleAccounts={isPeopleAccounts}
isGroupsAccounts={isGroupsAccounts}
isInsideGroup={isInsideGroup}
title={filterTitle}
userId={userId}
disableThirdParty={disableThirdParty}
/>
<SortButton
id="sort-by-button"
onSort={onSort}
getSortData={getSortData}
getSelectedSortData={getSelectedSortData}
view={view}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
onChangeViewAs={onChangeViewAs}
onSortButtonClick={onSortButtonClick}
viewSelectorVisible={
viewSettings &&
viewSelectorVisible &&
currentDeviceType !== DeviceType.desktop
}
title={sortByTitle}
/>
{!isIndexEditingMode && (
<FilterButton
id="filter-button"
onFilter={onFilter}
getFilterData={getFilterData}
selectedFilterValue={selectedFilterValue}
filterHeader={filterHeader}
selectorLabel={selectorLabel}
isRooms={isRooms}
isAccounts={isAccounts}
isPeopleAccounts={isPeopleAccounts}
isGroupsAccounts={isGroupsAccounts}
isInsideGroup={isInsideGroup}
title={filterTitle}
userId={userId}
disableThirdParty={disableThirdParty}
/>
)}
{!isIndexing && (
<SortButton
id="sort-by-button"
onSort={onSort}
getSortData={getSortData}
getSelectedSortData={getSelectedSortData}
view={view}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
onChangeViewAs={onChangeViewAs}
onSortButtonClick={onSortButtonClick}
viewSelectorVisible={
viewSettings &&
viewSelectorVisible &&
currentDeviceType !== DeviceType.desktop
}
title={sortByTitle}
/>
)}
{viewSettings &&
!isIndexing &&
currentDeviceType === DeviceType.desktop &&
viewSelectorVisible && (
<ViewSelector

View File

@ -33,4 +33,5 @@ export const enum FloatingButtonIcons {
plus = "plus",
minus = "minus",
refresh = "refresh",
exportIndex = "exportIndex",
}

View File

@ -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) => {

View 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;

View File

@ -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);

View File

@ -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 &&

View File

@ -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;

View File

@ -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 };

View File

@ -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}
/>
</>
)}

View File

@ -203,5 +203,6 @@ export interface INavigationProps {
onNavigationButtonClick?: () => void;
tariffBar: React.ReactElement;
showNavigationButton: boolean;
titleIconTooltip?: string;
onContextOptionsClick?: () => void;
}

View File

@ -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:

View File

@ -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:

View File

@ -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,30 +222,51 @@ const Row = (props: RowProps) => {
{renderContentElement && (
<StyledContentElement>{contentElement}</StyledContentElement>
)}
{renderContext ? (
<ContextMenuButton
isFill
className="expandButton"
getData={getOptions}
directionX="right"
displayType={ContextMenuButtonDisplayType.toggle}
onClick={onContextMenu}
title={contextTitle}
/>
{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)}
/>
</>
) : (
<div className="expandButton"> </div>
<>
{renderContext ? (
<ContextMenuButton
isFill
className="expandButton"
getData={getOptions}
directionX="right"
displayType={ContextMenuButtonDisplayType.toggle}
onClick={onContextMenu}
title={contextTitle}
/>
) : (
<div className="expandButton"> </div>
)}
<ContextMenu
getContextModel={getContextModel}
model={contextData.contextOptions || []}
ref={cm}
header={contextMenuHeader}
withBackdrop={isMobileUtils()}
onHide={rowContextClose}
isRoom={isRoom}
isArchive={isArchive}
badgeUrl={badgeUrl}
/>
</>
)}
<ContextMenu
getContextModel={getContextModel}
model={contextData.contextOptions || []}
ref={cm}
header={contextMenuHeader}
withBackdrop={isMobileUtils()}
onHide={rowContextClose}
isRoom={isRoom}
isArchive={isArchive}
badgeUrl={badgeUrl}
/>
</StyledOptionButton>
</StyledRow>
);

View File

@ -69,6 +69,7 @@ export interface RowProps {
mode?: TMode;
/** Removes the borders */
withoutBorder?: boolean;
isIndexEditingMode: boolean;
isRoom?: boolean;
contextTitle?: string;
badgesComponent?: React.ReactNode;

View File

@ -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`

View File

@ -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