cozy-harvest-lib 8.2.1 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [8.3.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@8.2.1...cozy-harvest-lib@8.3.0) (2022-04-15)
7
+
8
+
9
+ ### Features
10
+
11
+ * Implement ViewerModal as Route ([bdb6e88](https://github.com/cozy/cozy-libs/commit/bdb6e88e4c70217654f3b72828dee964aa2a4d1b))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [8.2.1](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@8.2.0...cozy-harvest-lib@8.2.1) (2022-04-13)
7
18
 
8
19
 
@@ -4,6 +4,7 @@ import { Switch, Route, Redirect } from 'react-router';
4
4
  import { withStyles } from '@material-ui/core/styles';
5
5
  import Dialog from 'cozy-ui/transpiled/react/Dialog';
6
6
  import { DialogCloseButton, useCozyDialog } from 'cozy-ui/transpiled/react/CozyDialogs';
7
+ import { useVaultUnlockContext, VaultUnlockProvider, VaultUnlockPlaceholder } from 'cozy-keys-lib';
7
8
  import KonnectorAccounts from './KonnectorAccounts';
8
9
  import AccountModal from './AccountModal';
9
10
  import NewAccountModal from './NewAccountModal';
@@ -14,7 +15,7 @@ import HarvestVaultProvider from './HarvestVaultProvider';
14
15
  import { MountPointProvider } from './MountPointContext';
15
16
  import DialogContext from './DialogContext';
16
17
  import { DatacardOptions } from './Datacards/DatacardOptionsContext';
17
- import { useVaultUnlockContext, VaultUnlockProvider, VaultUnlockPlaceholder } from 'cozy-keys-lib';
18
+ import { ViewerModal } from '../datacards/ViewerModal';
18
19
  /**
19
20
  * Dialog will not be centered vertically since we need the modal to "stay in place"
20
21
  * when changing tabs. Since tabs content's height is not the same between the data
@@ -98,6 +99,12 @@ var Routes = function Routes(_ref) {
98
99
  accounts: accountsAndTriggers
99
100
  });
100
101
  }
102
+ }), /*#__PURE__*/React.createElement(Route, {
103
+ path: "".concat(konnectorRoot, "/viewer/:accountId/:folderToSaveId/:fileIndex"),
104
+ exact: true,
105
+ render: function render(routeComponentProps) {
106
+ return /*#__PURE__*/React.createElement(ViewerModal, routeComponentProps);
107
+ }
101
108
  }), /*#__PURE__*/React.createElement(Route, {
102
109
  path: "".concat(konnectorRoot, "/new"),
103
110
  exact: true,
@@ -1,10 +1,7 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
- import React, { useState, useMemo } from 'react';
2
+ import React, { useContext, useState } from 'react';
4
3
  import PropTypes from 'prop-types';
5
4
  import get from 'lodash/get';
6
- import sortBy from 'lodash/sortBy';
7
- import uniq from 'lodash/uniq';
8
5
  import keyBy from 'lodash/keyBy';
9
6
  import 'leaflet/dist/leaflet.css';
10
7
  import Skeleton from '@material-ui/lab/Skeleton';
@@ -12,13 +9,11 @@ import List from '@material-ui/core/List';
12
9
  import ListItem from '@material-ui/core/ListItem';
13
10
  import ListItemIcon from '@material-ui/core/ListItemIcon';
14
11
  import Slide from '@material-ui/core/Slide';
15
- import Modal from '@material-ui/core/Modal';
16
12
  import ListItemText from 'cozy-ui/transpiled/react/ListItemText';
13
+ import { RealTimeQueries } from 'cozy-client';
17
14
  import palette from 'cozy-ui/transpiled/react/palette';
18
15
  import { Media, Bd, Img } from 'cozy-ui/transpiled/react/Media';
19
16
  import Circle from 'cozy-ui/transpiled/react/Circle';
20
- import Portal from 'cozy-ui/transpiled/react/Portal';
21
- import Viewer from 'cozy-ui/transpiled/react/Viewer';
22
17
  import Card from 'cozy-ui/transpiled/react/Card';
23
18
  import Icon from 'cozy-ui/transpiled/react/Icon';
24
19
  import FileIcon from 'cozy-ui/transpiled/react/Icons/File';
@@ -26,8 +21,9 @@ import Typography from 'cozy-ui/transpiled/react/Typography';
26
21
  import { useI18n } from 'cozy-ui/transpiled/react/I18n';
27
22
  import AppLinkCard, { AppLinkButton } from '../components/cards/AppLinkCard';
28
23
  import appLinksProps from '../components/KonnectorConfiguration/DataTab/appLinksProps';
29
- import CozyClient, { Q, queryConnect, isQueryLoading, hasQueryBeenLoaded, RealTimeQueries } from 'cozy-client';
24
+ import { MountPointContext } from '../components/MountPointContext';
30
25
  import { getFileIcon } from './mime-utils';
26
+ import { useDataCardFiles } from './useDataCardFiles';
31
27
 
32
28
  var LoadingFileListItem = function LoadingFileListItem(_ref) {
33
29
  var divider = _ref.divider;
@@ -85,10 +81,14 @@ var FileCard = function FileCard(_ref4) {
85
81
  var files = _ref4.files,
86
82
  loading = _ref4.loading,
87
83
  konnector = _ref4.konnector,
88
- trigger = _ref4.trigger;
84
+ trigger = _ref4.trigger,
85
+ accountId = _ref4.accountId;
89
86
 
90
87
  var _useI18n2 = useI18n(),
91
- t = _useI18n2.t; // Remember files that were there initially so that we do not
88
+ t = _useI18n2.t;
89
+
90
+ var _useContext = useContext(MountPointContext),
91
+ pushHistory = _useContext.pushHistory; // Remember files that were there initially so that we do not
92
92
  // animate their ListItem.
93
93
  // Only files coming from realtime and that are added to files
94
94
  // while the component is mounted will be animated.
@@ -102,19 +102,6 @@ var FileCard = function FileCard(_ref4) {
102
102
  _useState2 = _slicedToArray(_useState, 1),
103
103
  initialFilesById = _useState2[0];
104
104
 
105
- var _useState3 = useState(null),
106
- _useState4 = _slicedToArray(_useState3, 2),
107
- viewerIndex = _useState4[0],
108
- setViewerIndex = _useState4[1];
109
-
110
- var handleCloseViewer = function handleCloseViewer() {
111
- return setViewerIndex(null);
112
- };
113
-
114
- var handleFileChange = function handleFileChange(file, newIndex) {
115
- return setViewerIndex(newIndex);
116
- };
117
-
118
105
  return /*#__PURE__*/React.createElement(Card, {
119
106
  className: "u-ph-0 u-pb-0 u-ov-hidden"
120
107
  }, /*#__PURE__*/React.createElement("div", {
@@ -149,21 +136,12 @@ var FileCard = function FileCard(_ref4) {
149
136
  key: file._id
150
137
  }, /*#__PURE__*/React.createElement(FileListItem, {
151
138
  onClick: function onClick() {
152
- return setViewerIndex(i);
139
+ pushHistory("/viewer/".concat(accountId, "/").concat(get(trigger, 'message.folder_to_save'), "/").concat(i));
153
140
  },
154
141
  file: file,
155
142
  divider: i !== files.length - 1
156
143
  }));
157
- })), viewerIndex !== null && /*#__PURE__*/React.createElement(Portal, {
158
- into: "body"
159
- }, /*#__PURE__*/React.createElement(Modal, {
160
- open: true
161
- }, /*#__PURE__*/React.createElement(Viewer, {
162
- files: files,
163
- currentIndex: viewerIndex,
164
- onCloseRequest: handleCloseViewer,
165
- onChangeRequest: handleFileChange
166
- }))), /*#__PURE__*/React.createElement("div", {
144
+ })), /*#__PURE__*/React.createElement("div", {
167
145
  className: "u-ta-right u-mv-half u-mh-1"
168
146
  }, /*#__PURE__*/React.createElement(AppLinkButton, {
169
147
  slug: "drive",
@@ -171,63 +149,27 @@ var FileCard = function FileCard(_ref4) {
171
149
  })));
172
150
  };
173
151
 
174
- var makeFolderToSaveQueryFromProps = function makeFolderToSaveQueryFromProps(_ref5) {
175
- var trigger = _ref5.trigger;
176
- return {
177
- query: Q('io.cozy.files').where({
178
- dir_id: trigger.message.folder_to_save,
179
- trashed: false
180
- }).indexFields(['dir_id', 'cozyMetadata.createdAt']).sortBy([{
181
- dir_id: 'desc'
182
- }, {
183
- 'cozyMetadata.createdAt': 'desc'
184
- }]).limitBy(5),
185
- as: "fileDataCard_io.cozy.files/".concat(trigger.message.folder_to_save, "/io.cozy.files"),
186
- fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
187
- };
188
- };
152
+ var FileDataCard = function FileDataCard(_ref5) {
153
+ var accountId = _ref5.accountId,
154
+ konnector = _ref5.konnector,
155
+ trigger = _ref5.trigger;
189
156
 
190
- var makeSourceAccountQueryFromProps = function makeSourceAccountQueryFromProps(_ref6) {
191
- var accountId = _ref6.accountId;
192
- return {
193
- query: Q('io.cozy.files').where({
194
- 'cozyMetadata.sourceAccount': accountId,
195
- trashed: false
196
- }).indexFields(['cozyMetadata.sourceAccount', 'cozyMetadata.createdAt']).sortBy([{
197
- 'cozyMetadata.sourceAccount': 'desc'
198
- }, {
199
- 'cozyMetadata.createdAt': 'desc'
200
- }]).limitBy(5),
201
- as: "fileDataCard_io.cozy.accounts/".concat(accountId, "/io.cozy.files"),
202
- fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
203
- };
204
- };
157
+ var _useDataCardFiles = useDataCardFiles(accountId, trigger.message.folder_to_save),
158
+ data = _useDataCardFiles.data,
159
+ fetchStatus = _useDataCardFiles.fetchStatus;
205
160
 
206
- var FileDataCard = function FileDataCard(_ref7) {
207
- var folderToSaveFiles = _ref7.folderToSaveFiles,
208
- sourceAccountFiles = _ref7.sourceAccountFiles,
209
- konnector = _ref7.konnector,
210
- trigger = _ref7.trigger;
211
- var files1 = folderToSaveFiles.data;
212
- var files2 = sourceAccountFiles.data;
213
- var noFiles = hasQueryBeenLoaded(folderToSaveFiles) && files1.length === 0 && hasQueryBeenLoaded(sourceAccountFiles) && files2.length === 0;
214
- var isLoading = isQueryLoading(folderToSaveFiles) || isQueryLoading(sourceAccountFiles);
215
- var files = useMemo(function () {
216
- return sortBy(uniq([].concat(_toConsumableArray(files1), _toConsumableArray(files2)), function (x) {
217
- return x._id;
218
- }), function (x) {
219
- return get(x, 'cozyMetadata.createdAt');
220
- }).reverse().slice(0, 5);
221
- }, [files1, files2]);
161
+ var isLoading = fetchStatus === 'loading';
162
+ var noFiles = fetchStatus === 'empty' || fetchStatus === 'failed';
222
163
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(RealTimeQueries, {
223
164
  doctype: "io.cozy.files"
224
165
  }), noFiles ? /*#__PURE__*/React.createElement(AppLinkCard, appLinksProps.drive({
225
166
  trigger: trigger
226
167
  })) : /*#__PURE__*/React.createElement(FileCard, {
227
- files: files,
168
+ files: data,
228
169
  loading: isLoading,
229
170
  konnector: konnector,
230
- trigger: trigger
171
+ trigger: trigger,
172
+ accountId: accountId
231
173
  }));
232
174
  };
233
175
 
@@ -236,11 +178,4 @@ FileDataCard.propTypes = {
236
178
  accountId: PropTypes.string.isRequired,
237
179
  trigger: PropTypes.object.isRequired
238
180
  };
239
- export default queryConnect({
240
- folderToSaveFiles: function folderToSaveFiles(props) {
241
- return makeFolderToSaveQueryFromProps(props);
242
- },
243
- sourceAccountFiles: function sourceAccountFiles(props) {
244
- return makeSourceAccountQueryFromProps(props);
245
- }
246
- })(FileDataCard);
181
+ export default FileDataCard;
@@ -0,0 +1,40 @@
1
+ import React, { useContext } from 'react';
2
+ import Overlay from 'cozy-ui/transpiled/react/Overlay';
3
+ import Viewer from 'cozy-ui/transpiled/react/Viewer';
4
+ import { MountPointContext } from '../components/MountPointContext';
5
+ import { useDataCardFiles } from './useDataCardFiles';
6
+ export var ViewerModal = function ViewerModal(_ref) {
7
+ var _ref$match$params = _ref.match.params,
8
+ accountId = _ref$match$params.accountId,
9
+ folderToSaveId = _ref$match$params.folderToSaveId,
10
+ fileIndex = _ref$match$params.fileIndex;
11
+
12
+ var _useContext = useContext(MountPointContext),
13
+ pushHistory = _useContext.pushHistory,
14
+ replaceHistory = _useContext.replaceHistory;
15
+
16
+ var _useDataCardFiles = useDataCardFiles(accountId, folderToSaveId),
17
+ data = _useDataCardFiles.data,
18
+ fetchStatus = _useDataCardFiles.fetchStatus;
19
+
20
+ var handleCloseViewer = function handleCloseViewer() {
21
+ return replaceHistory("/accounts");
22
+ };
23
+
24
+ var handleFileChange = function handleFileChange(_file, newIndex) {
25
+ return pushHistory("/viewer/".concat(accountId, "/").concat(folderToSaveId, "/").concat(newIndex));
26
+ };
27
+
28
+ if (fetchStatus === 'empty' || fetchStatus === 'failed' || fetchStatus === 'loaded' && fileIndex > data.length) {
29
+ handleCloseViewer();
30
+ return null;
31
+ }
32
+
33
+ if (fetchStatus === 'loading') return /*#__PURE__*/React.createElement(Overlay, null);
34
+ return /*#__PURE__*/React.createElement(Overlay, null, /*#__PURE__*/React.createElement(Viewer, {
35
+ files: data,
36
+ currentIndex: Number(fileIndex),
37
+ onCloseRequest: handleCloseViewer,
38
+ onChangeRequest: handleFileChange
39
+ }));
40
+ };
@@ -0,0 +1,63 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import { useMemo } from 'react';
3
+ import get from 'lodash/get';
4
+ import sortBy from 'lodash/sortBy';
5
+ import uniq from 'lodash/uniq';
6
+ import CozyClient, { Q, useQuery, hasQueryBeenLoaded } from 'cozy-client';
7
+
8
+ var useFolderToSaveFiles = function useFolderToSaveFiles(folderToSaveId) {
9
+ return useQuery(Q('io.cozy.files').where({
10
+ dir_id: folderToSaveId,
11
+ trashed: false
12
+ }).indexFields(['dir_id', 'cozyMetadata.createdAt']).sortBy([{
13
+ dir_id: 'desc'
14
+ }, {
15
+ 'cozyMetadata.createdAt': 'desc'
16
+ }]).limitBy(5), {
17
+ as: "fileDataCard_io.cozy.files/".concat(folderToSaveId, "/io.cozy.files"),
18
+ fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
19
+ });
20
+ };
21
+
22
+ var useSourceAccountFiles = function useSourceAccountFiles(accountId) {
23
+ return useQuery(Q('io.cozy.files').where({
24
+ 'cozyMetadata.sourceAccount': accountId,
25
+ trashed: false
26
+ }).indexFields(['cozyMetadata.sourceAccount', 'cozyMetadata.createdAt']).sortBy([{
27
+ 'cozyMetadata.sourceAccount': 'desc'
28
+ }, {
29
+ 'cozyMetadata.createdAt': 'desc'
30
+ }]).limitBy(5), {
31
+ as: "fileDataCard_io.cozy.accounts/".concat(accountId, "/io.cozy.files"),
32
+ fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
33
+ });
34
+ };
35
+
36
+ var getResponse = function getResponse(folderToSaveFiles, sourceAccountFiles) {
37
+ var loaded = Boolean(hasQueryBeenLoaded(folderToSaveFiles) && hasQueryBeenLoaded(sourceAccountFiles));
38
+ if (folderToSaveFiles.fetchStatus === 'failed' && sourceAccountFiles === 'failed') return {
39
+ fetchStatus: 'failed'
40
+ };
41
+ if (loaded && folderToSaveFiles.data.length === 0 && sourceAccountFiles.data.length === 0) return {
42
+ fetchStatus: 'empty'
43
+ };
44
+ if (loaded) return {
45
+ data: sortBy(uniq([].concat(_toConsumableArray(folderToSaveFiles.data), _toConsumableArray(sourceAccountFiles.data)), function (x) {
46
+ return x._id;
47
+ }), function (x) {
48
+ return get(x, 'cozyMetadata.createdAt');
49
+ }).reverse().slice(0, 5),
50
+ fetchStatus: 'loaded'
51
+ };
52
+ return {
53
+ fetchStatus: 'loading'
54
+ };
55
+ };
56
+
57
+ export var useDataCardFiles = function useDataCardFiles(accountId, folderToSaveId) {
58
+ var folderToSaveFiles = useFolderToSaveFiles(folderToSaveId);
59
+ var sourceAccountFiles = useSourceAccountFiles(accountId);
60
+ return useMemo(function () {
61
+ return getResponse(folderToSaveFiles, sourceAccountFiles);
62
+ }, [folderToSaveFiles, sourceAccountFiles]);
63
+ };
@@ -0,0 +1,242 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+
3
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
4
+
5
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
6
+
7
+ import { renderHook } from '@testing-library/react-hooks';
8
+ import { useDataCardFiles } from './useDataCardFiles';
9
+ var mockUseQuery = jest.fn();
10
+ var mockFile1 = {
11
+ cozyMetadata: {
12
+ createdAt: '1970-01-01T00:00:11Z'
13
+ }
14
+ };
15
+ var mockFile2 = {
16
+ cozyMetadata: {
17
+ createdAt: '1970-01-01T00:00:10Z'
18
+ }
19
+ };
20
+ var mockFile3 = {
21
+ cozyMetadata: {
22
+ createdAt: '1970-01-01T00:00:09Z'
23
+ }
24
+ };
25
+ var mockFile4 = {
26
+ cozyMetadata: {
27
+ createdAt: '1970-01-01T00:00:08Z'
28
+ }
29
+ };
30
+ var mockFile5 = {
31
+ cozyMetadata: {
32
+ createdAt: '1970-01-01T00:00:07Z'
33
+ }
34
+ };
35
+ var mockFile6 = {
36
+ cozyMetadata: {
37
+ createdAt: '1970-01-01T00:00:06Z'
38
+ }
39
+ };
40
+ var mockFile7 = {
41
+ cozyMetadata: {
42
+ createdAt: '1970-01-01T00:00:05Z'
43
+ }
44
+ };
45
+ var mockFile8 = {
46
+ cozyMetadata: {
47
+ createdAt: '1970-01-01T00:00:04Z'
48
+ }
49
+ };
50
+ var mockFile9 = {
51
+ cozyMetadata: {
52
+ createdAt: '1970-01-01T00:00:03Z'
53
+ }
54
+ };
55
+ var mockFile10 = {
56
+ cozyMetadata: {
57
+ createdAt: '1970-01-01T00:00:02Z'
58
+ }
59
+ };
60
+ var mockFile11 = {
61
+ cozyMetadata: {
62
+ createdAt: '1970-01-01T00:00:01Z'
63
+ }
64
+ };
65
+ var mockFile12 = {
66
+ cozyMetadata: {
67
+ createdAt: '1970-01-01T00:00:00Z'
68
+ }
69
+ };
70
+ jest.mock('cozy-client', function () {
71
+ return _objectSpread(_objectSpread({}, jest.requireActual('cozy-client')), {}, {
72
+ useQuery: function useQuery() {
73
+ return mockUseQuery();
74
+ }
75
+ });
76
+ });
77
+ afterEach(function () {
78
+ mockUseQuery.mockClear();
79
+ });
80
+ it('handles files pending', function () {
81
+ mockUseQuery.mockReturnValueOnce({
82
+ fetchStatus: 'pending',
83
+ data: null
84
+ });
85
+ mockUseQuery.mockReturnValueOnce({
86
+ fetchStatus: 'pending',
87
+ data: null
88
+ });
89
+
90
+ var _renderHook = renderHook(function () {
91
+ return useDataCardFiles('1', '2');
92
+ }),
93
+ result = _renderHook.result;
94
+
95
+ expect(result.current).toStrictEqual({
96
+ fetchStatus: 'loading'
97
+ });
98
+ });
99
+ it('handles files loading', function () {
100
+ mockUseQuery.mockReturnValueOnce({
101
+ fetchStatus: 'loading',
102
+ data: null
103
+ });
104
+ mockUseQuery.mockReturnValueOnce({
105
+ fetchStatus: 'loading',
106
+ data: null
107
+ });
108
+
109
+ var _renderHook2 = renderHook(function () {
110
+ return useDataCardFiles('1', '2');
111
+ }),
112
+ result = _renderHook2.result;
113
+
114
+ expect(result.current).toStrictEqual({
115
+ fetchStatus: 'loading'
116
+ });
117
+ });
118
+ it('handles files loading with partial data', function () {
119
+ mockUseQuery.mockReturnValueOnce({
120
+ fetchStatus: 'loading',
121
+ data: [mockFile1]
122
+ });
123
+ mockUseQuery.mockReturnValueOnce({
124
+ fetchStatus: 'loading',
125
+ data: [mockFile2]
126
+ });
127
+
128
+ var _renderHook3 = renderHook(function () {
129
+ return useDataCardFiles('1', '2');
130
+ }),
131
+ result = _renderHook3.result;
132
+
133
+ expect(result.current).toStrictEqual({
134
+ fetchStatus: 'loading'
135
+ });
136
+ });
137
+ it('handles files loading with more partial data', function () {
138
+ mockUseQuery.mockReturnValueOnce({
139
+ fetchStatus: 'loading',
140
+ data: [mockFile1, mockFile2]
141
+ });
142
+ mockUseQuery.mockReturnValueOnce({
143
+ fetchStatus: 'loading',
144
+ data: [mockFile3, mockFile4]
145
+ });
146
+
147
+ var _renderHook4 = renderHook(function () {
148
+ return useDataCardFiles('1', '2');
149
+ }),
150
+ result = _renderHook4.result;
151
+
152
+ expect(result.current).toStrictEqual({
153
+ fetchStatus: 'loading'
154
+ });
155
+ });
156
+ it('handles files loading with empty data', function () {
157
+ mockUseQuery.mockReturnValueOnce({
158
+ fetchStatus: 'loaded',
159
+ data: [],
160
+ lastFetch: 1
161
+ });
162
+ mockUseQuery.mockReturnValueOnce({
163
+ fetchStatus: 'loaded',
164
+ data: [],
165
+ lastFetch: 1
166
+ });
167
+
168
+ var _renderHook5 = renderHook(function () {
169
+ return useDataCardFiles('1', '2');
170
+ }),
171
+ result = _renderHook5.result;
172
+
173
+ expect(result.current).toStrictEqual({
174
+ fetchStatus: 'empty'
175
+ });
176
+ });
177
+ it('handles files loaded and return in correct order', function () {
178
+ mockUseQuery.mockReturnValueOnce({
179
+ fetchStatus: 'loaded',
180
+ data: [mockFile2, mockFile1],
181
+ lastFetch: 1
182
+ });
183
+ mockUseQuery.mockReturnValueOnce({
184
+ fetchStatus: 'loaded',
185
+ data: [mockFile3, mockFile4],
186
+ lastFetch: 1
187
+ });
188
+
189
+ var _renderHook6 = renderHook(function () {
190
+ return useDataCardFiles('1', '2');
191
+ }),
192
+ result = _renderHook6.result;
193
+
194
+ expect(result.current).toStrictEqual({
195
+ data: [mockFile1, mockFile2, mockFile3, mockFile4],
196
+ fetchStatus: 'loaded'
197
+ });
198
+ });
199
+ it('handles files loaded with identical double return', function () {
200
+ mockUseQuery.mockReturnValueOnce({
201
+ fetchStatus: 'loaded',
202
+ data: [mockFile1, mockFile2, mockFile3, mockFile4],
203
+ lastFetch: 1
204
+ });
205
+ mockUseQuery.mockReturnValueOnce({
206
+ fetchStatus: 'loaded',
207
+ data: [mockFile3, mockFile2, mockFile3, mockFile4],
208
+ lastFetch: 1
209
+ });
210
+
211
+ var _renderHook7 = renderHook(function () {
212
+ return useDataCardFiles('1', '2');
213
+ }),
214
+ result = _renderHook7.result;
215
+
216
+ expect(result.current).toStrictEqual({
217
+ data: [mockFile1, mockFile2, mockFile3, mockFile4],
218
+ fetchStatus: 'loaded'
219
+ });
220
+ });
221
+ it('handles files loaded with more than 5 result', function () {
222
+ mockUseQuery.mockReturnValueOnce({
223
+ fetchStatus: 'loaded',
224
+ data: [mockFile1, mockFile2, mockFile3, mockFile4, mockFile5, mockFile6],
225
+ lastFetch: 1
226
+ });
227
+ mockUseQuery.mockReturnValueOnce({
228
+ fetchStatus: 'loaded',
229
+ data: [mockFile7, mockFile8, mockFile9, mockFile10, mockFile11, mockFile12],
230
+ lastFetch: 1
231
+ });
232
+
233
+ var _renderHook8 = renderHook(function () {
234
+ return useDataCardFiles('1', '2');
235
+ }),
236
+ result = _renderHook8.result;
237
+
238
+ expect(result.current).toStrictEqual({
239
+ data: [mockFile1, mockFile2, mockFile3, mockFile4, mockFile5],
240
+ fetchStatus: 'loaded'
241
+ });
242
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-harvest-lib",
3
- "version": "8.2.1",
3
+ "version": "8.3.0",
4
4
  "description": "Provides logic, modules and components for Cozy's harvest applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -85,5 +85,5 @@
85
85
  "react-router-dom": "^5.0.1"
86
86
  },
87
87
  "sideEffects": false,
88
- "gitHead": "34e61fe01b642f5a6640a52d70383ead998fdb70"
88
+ "gitHead": "9032817fd4e41f61aef183ae8a9ef5abbad418e0"
89
89
  }
@@ -7,6 +7,11 @@ import {
7
7
  DialogCloseButton,
8
8
  useCozyDialog
9
9
  } from 'cozy-ui/transpiled/react/CozyDialogs'
10
+ import {
11
+ useVaultUnlockContext,
12
+ VaultUnlockProvider,
13
+ VaultUnlockPlaceholder
14
+ } from 'cozy-keys-lib'
10
15
 
11
16
  import KonnectorAccounts from './KonnectorAccounts'
12
17
  import AccountModal from './AccountModal'
@@ -18,11 +23,8 @@ import HarvestVaultProvider from './HarvestVaultProvider'
18
23
  import { MountPointProvider } from './MountPointContext'
19
24
  import DialogContext from './DialogContext'
20
25
  import { DatacardOptions } from './Datacards/DatacardOptionsContext'
21
- import {
22
- useVaultUnlockContext,
23
- VaultUnlockProvider,
24
- VaultUnlockPlaceholder
25
- } from 'cozy-keys-lib'
26
+
27
+ import { ViewerModal } from '../datacards/ViewerModal'
26
28
 
27
29
  /**
28
30
  * Dialog will not be centered vertically since we need the modal to "stay in place"
@@ -101,6 +103,13 @@ const Routes = ({ konnectorRoot, konnector, onDismiss, datacardOptions }) => {
101
103
  />
102
104
  )}
103
105
  />
106
+ <Route
107
+ path={`${konnectorRoot}/viewer/:accountId/:folderToSaveId/:fileIndex`}
108
+ exact
109
+ render={routeComponentProps => (
110
+ <ViewerModal {...routeComponentProps} />
111
+ )}
112
+ />
104
113
  <Route
105
114
  path={`${konnectorRoot}/new`}
106
115
  exact
@@ -1,8 +1,6 @@
1
- import React, { useState, useMemo } from 'react'
1
+ import React, { useContext, useState } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import get from 'lodash/get'
4
- import sortBy from 'lodash/sortBy'
5
- import uniq from 'lodash/uniq'
6
4
  import keyBy from 'lodash/keyBy'
7
5
 
8
6
  import 'leaflet/dist/leaflet.css'
@@ -12,15 +10,13 @@ import List from '@material-ui/core/List'
12
10
  import ListItem from '@material-ui/core/ListItem'
13
11
  import ListItemIcon from '@material-ui/core/ListItemIcon'
14
12
  import Slide from '@material-ui/core/Slide'
15
- import Modal from '@material-ui/core/Modal'
16
13
 
17
14
  import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
18
15
 
16
+ import { RealTimeQueries } from 'cozy-client'
19
17
  import palette from 'cozy-ui/transpiled/react/palette'
20
18
  import { Media, Bd, Img } from 'cozy-ui/transpiled/react/Media'
21
19
  import Circle from 'cozy-ui/transpiled/react/Circle'
22
- import Portal from 'cozy-ui/transpiled/react/Portal'
23
- import Viewer from 'cozy-ui/transpiled/react/Viewer'
24
20
  import Card from 'cozy-ui/transpiled/react/Card'
25
21
  import Icon from 'cozy-ui/transpiled/react/Icon'
26
22
  import FileIcon from 'cozy-ui/transpiled/react/Icons/File'
@@ -29,16 +25,9 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n'
29
25
 
30
26
  import AppLinkCard, { AppLinkButton } from '../components/cards/AppLinkCard'
31
27
  import appLinksProps from '../components/KonnectorConfiguration/DataTab/appLinksProps'
32
-
33
- import CozyClient, {
34
- Q,
35
- queryConnect,
36
- isQueryLoading,
37
- hasQueryBeenLoaded,
38
- RealTimeQueries
39
- } from 'cozy-client'
40
-
28
+ import { MountPointContext } from '../components/MountPointContext'
41
29
  import { getFileIcon } from './mime-utils'
30
+ import { useDataCardFiles } from './useDataCardFiles'
42
31
 
43
32
  const LoadingFileListItem = ({ divider }) => {
44
33
  return (
@@ -87,17 +76,15 @@ const TransitionWrapper = ({ children }) => {
87
76
  )
88
77
  }
89
78
 
90
- const FileCard = ({ files, loading, konnector, trigger }) => {
79
+ const FileCard = ({ files, loading, konnector, trigger, accountId }) => {
91
80
  const { t } = useI18n()
81
+ const { pushHistory } = useContext(MountPointContext)
92
82
 
93
83
  // Remember files that were there initially so that we do not
94
84
  // animate their ListItem.
95
85
  // Only files coming from realtime and that are added to files
96
86
  // while the component is mounted will be animated.
97
87
  const [initialFilesById] = useState(() => keyBy(files, x => x._id))
98
- const [viewerIndex, setViewerIndex] = useState(null)
99
- const handleCloseViewer = () => setViewerIndex(null)
100
- const handleFileChange = (file, newIndex) => setViewerIndex(newIndex)
101
88
 
102
89
  return (
103
90
  <Card className="u-ph-0 u-pb-0 u-ov-hidden">
@@ -138,7 +125,14 @@ const FileCard = ({ files, loading, konnector, trigger }) => {
138
125
  return (
139
126
  <ItemWrapper key={file._id}>
140
127
  <FileListItem
141
- onClick={() => setViewerIndex(i)}
128
+ onClick={() => {
129
+ pushHistory(
130
+ `/viewer/${accountId}/${get(
131
+ trigger,
132
+ 'message.folder_to_save'
133
+ )}/${i}`
134
+ )
135
+ }}
142
136
  file={file}
143
137
  divider={i !== files.length - 1}
144
138
  />
@@ -147,18 +141,6 @@ const FileCard = ({ files, loading, konnector, trigger }) => {
147
141
  })
148
142
  )}
149
143
  </List>
150
- {viewerIndex !== null && (
151
- <Portal into="body">
152
- <Modal open={true}>
153
- <Viewer
154
- files={files}
155
- currentIndex={viewerIndex}
156
- onCloseRequest={handleCloseViewer}
157
- onChangeRequest={handleFileChange}
158
- />
159
- </Modal>
160
- </Portal>
161
- )}
162
144
  <div className="u-ta-right u-mv-half u-mh-1">
163
145
  <AppLinkButton
164
146
  slug="drive"
@@ -169,60 +151,14 @@ const FileCard = ({ files, loading, konnector, trigger }) => {
169
151
  )
170
152
  }
171
153
 
172
- const makeFolderToSaveQueryFromProps = ({ trigger }) => ({
173
- query: Q('io.cozy.files')
174
- .where({
175
- dir_id: trigger.message.folder_to_save,
176
- trashed: false
177
- })
178
- .indexFields(['dir_id', 'cozyMetadata.createdAt'])
179
- .sortBy([{ dir_id: 'desc' }, { 'cozyMetadata.createdAt': 'desc' }])
180
- .limitBy(5),
181
- as: `fileDataCard_io.cozy.files/${trigger.message.folder_to_save}/io.cozy.files`,
182
- fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
183
- })
184
-
185
- const makeSourceAccountQueryFromProps = ({ accountId }) => ({
186
- query: Q('io.cozy.files')
187
- .where({
188
- 'cozyMetadata.sourceAccount': accountId,
189
- trashed: false
190
- })
191
- .indexFields(['cozyMetadata.sourceAccount', 'cozyMetadata.createdAt'])
192
- .sortBy([
193
- { 'cozyMetadata.sourceAccount': 'desc' },
194
- { 'cozyMetadata.createdAt': 'desc' }
195
- ])
196
- .limitBy(5),
197
- as: `fileDataCard_io.cozy.accounts/${accountId}/io.cozy.files`,
198
- fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
199
- })
200
-
201
- const FileDataCard = ({
202
- folderToSaveFiles,
203
- sourceAccountFiles,
204
- konnector,
205
- trigger
206
- }) => {
207
- const { data: files1 } = folderToSaveFiles
208
- const { data: files2 } = sourceAccountFiles
209
-
210
- const noFiles =
211
- hasQueryBeenLoaded(folderToSaveFiles) &&
212
- files1.length === 0 &&
213
- hasQueryBeenLoaded(sourceAccountFiles) &&
214
- files2.length === 0
215
- const isLoading =
216
- isQueryLoading(folderToSaveFiles) || isQueryLoading(sourceAccountFiles)
154
+ const FileDataCard = ({ accountId, konnector, trigger }) => {
155
+ const { data, fetchStatus } = useDataCardFiles(
156
+ accountId,
157
+ trigger.message.folder_to_save
158
+ )
159
+ const isLoading = fetchStatus === 'loading'
160
+ const noFiles = fetchStatus === 'empty' || fetchStatus === 'failed'
217
161
 
218
- const files = useMemo(() => {
219
- return sortBy(
220
- uniq([...files1, ...files2], x => x._id),
221
- x => get(x, 'cozyMetadata.createdAt')
222
- )
223
- .reverse()
224
- .slice(0, 5)
225
- }, [files1, files2])
226
162
  return (
227
163
  <>
228
164
  <RealTimeQueries doctype="io.cozy.files" />
@@ -230,10 +166,11 @@ const FileDataCard = ({
230
166
  <AppLinkCard {...appLinksProps.drive({ trigger })} />
231
167
  ) : (
232
168
  <FileCard
233
- files={files}
169
+ files={data}
234
170
  loading={isLoading}
235
171
  konnector={konnector}
236
172
  trigger={trigger}
173
+ accountId={accountId}
237
174
  />
238
175
  )}
239
176
  </>
@@ -246,7 +183,4 @@ FileDataCard.propTypes = {
246
183
  trigger: PropTypes.object.isRequired
247
184
  }
248
185
 
249
- export default queryConnect({
250
- folderToSaveFiles: props => makeFolderToSaveQueryFromProps(props),
251
- sourceAccountFiles: props => makeSourceAccountQueryFromProps(props)
252
- })(FileDataCard)
186
+ export default FileDataCard
@@ -0,0 +1,43 @@
1
+ import React, { useContext } from 'react'
2
+
3
+ import Overlay from 'cozy-ui/transpiled/react/Overlay'
4
+ import Viewer from 'cozy-ui/transpiled/react/Viewer'
5
+
6
+ import { MountPointContext } from '../components/MountPointContext'
7
+ import { useDataCardFiles } from './useDataCardFiles'
8
+
9
+ export const ViewerModal = ({
10
+ match: {
11
+ params: { accountId, folderToSaveId, fileIndex }
12
+ }
13
+ }) => {
14
+ const { pushHistory, replaceHistory } = useContext(MountPointContext)
15
+ const { data, fetchStatus } = useDataCardFiles(accountId, folderToSaveId)
16
+
17
+ const handleCloseViewer = () => replaceHistory(`/accounts`)
18
+ const handleFileChange = (_file, newIndex) =>
19
+ pushHistory(`/viewer/${accountId}/${folderToSaveId}/${newIndex}`)
20
+
21
+ if (
22
+ fetchStatus === 'empty' ||
23
+ fetchStatus === 'failed' ||
24
+ (fetchStatus === 'loaded' && fileIndex > data.length)
25
+ ) {
26
+ handleCloseViewer()
27
+
28
+ return null
29
+ }
30
+
31
+ if (fetchStatus === 'loading') return <Overlay />
32
+
33
+ return (
34
+ <Overlay>
35
+ <Viewer
36
+ files={data}
37
+ currentIndex={Number(fileIndex)}
38
+ onCloseRequest={handleCloseViewer}
39
+ onChangeRequest={handleFileChange}
40
+ />
41
+ </Overlay>
42
+ )
43
+ }
@@ -0,0 +1,84 @@
1
+ import { useMemo } from 'react'
2
+ import get from 'lodash/get'
3
+ import sortBy from 'lodash/sortBy'
4
+ import uniq from 'lodash/uniq'
5
+
6
+ import CozyClient, { Q, useQuery, hasQueryBeenLoaded } from 'cozy-client'
7
+
8
+ const useFolderToSaveFiles = folderToSaveId =>
9
+ useQuery(
10
+ Q('io.cozy.files')
11
+ .where({
12
+ dir_id: folderToSaveId,
13
+ trashed: false
14
+ })
15
+ .indexFields(['dir_id', 'cozyMetadata.createdAt'])
16
+ .sortBy([{ dir_id: 'desc' }, { 'cozyMetadata.createdAt': 'desc' }])
17
+ .limitBy(5),
18
+ {
19
+ as: `fileDataCard_io.cozy.files/${folderToSaveId}/io.cozy.files`,
20
+ fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
21
+ }
22
+ )
23
+
24
+ const useSourceAccountFiles = accountId =>
25
+ useQuery(
26
+ Q('io.cozy.files')
27
+ .where({ 'cozyMetadata.sourceAccount': accountId, trashed: false })
28
+ .indexFields(['cozyMetadata.sourceAccount', 'cozyMetadata.createdAt'])
29
+ .sortBy([
30
+ { 'cozyMetadata.sourceAccount': 'desc' },
31
+ { 'cozyMetadata.createdAt': 'desc' }
32
+ ])
33
+ .limitBy(5),
34
+ {
35
+ as: `fileDataCard_io.cozy.accounts/${accountId}/io.cozy.files`,
36
+ fetchPolicy: CozyClient.fetchPolicies.olderThan(30 * 1000)
37
+ }
38
+ )
39
+
40
+ const getResponse = (folderToSaveFiles, sourceAccountFiles) => {
41
+ const loaded = Boolean(
42
+ hasQueryBeenLoaded(folderToSaveFiles) &&
43
+ hasQueryBeenLoaded(sourceAccountFiles)
44
+ )
45
+
46
+ if (
47
+ folderToSaveFiles.fetchStatus === 'failed' &&
48
+ sourceAccountFiles === 'failed'
49
+ )
50
+ return { fetchStatus: 'failed' }
51
+
52
+ if (
53
+ loaded &&
54
+ folderToSaveFiles.data.length === 0 &&
55
+ sourceAccountFiles.data.length === 0
56
+ )
57
+ return { fetchStatus: 'empty' }
58
+
59
+ if (loaded)
60
+ return {
61
+ data: sortBy(
62
+ uniq(
63
+ [...folderToSaveFiles.data, ...sourceAccountFiles.data],
64
+ x => x._id
65
+ ),
66
+ x => get(x, 'cozyMetadata.createdAt')
67
+ )
68
+ .reverse()
69
+ .slice(0, 5),
70
+ fetchStatus: 'loaded'
71
+ }
72
+
73
+ return { fetchStatus: 'loading' }
74
+ }
75
+
76
+ export const useDataCardFiles = (accountId, folderToSaveId) => {
77
+ const folderToSaveFiles = useFolderToSaveFiles(folderToSaveId)
78
+ const sourceAccountFiles = useSourceAccountFiles(accountId)
79
+
80
+ return useMemo(
81
+ () => getResponse(folderToSaveFiles, sourceAccountFiles),
82
+ [folderToSaveFiles, sourceAccountFiles]
83
+ )
84
+ }
@@ -0,0 +1,135 @@
1
+ import { renderHook } from '@testing-library/react-hooks'
2
+
3
+ import { useDataCardFiles } from './useDataCardFiles'
4
+
5
+ const mockUseQuery = jest.fn()
6
+ const mockFile1 = { cozyMetadata: { createdAt: '1970-01-01T00:00:11Z' } }
7
+ const mockFile2 = { cozyMetadata: { createdAt: '1970-01-01T00:00:10Z' } }
8
+ const mockFile3 = { cozyMetadata: { createdAt: '1970-01-01T00:00:09Z' } }
9
+ const mockFile4 = { cozyMetadata: { createdAt: '1970-01-01T00:00:08Z' } }
10
+ const mockFile5 = { cozyMetadata: { createdAt: '1970-01-01T00:00:07Z' } }
11
+ const mockFile6 = { cozyMetadata: { createdAt: '1970-01-01T00:00:06Z' } }
12
+ const mockFile7 = { cozyMetadata: { createdAt: '1970-01-01T00:00:05Z' } }
13
+ const mockFile8 = { cozyMetadata: { createdAt: '1970-01-01T00:00:04Z' } }
14
+ const mockFile9 = { cozyMetadata: { createdAt: '1970-01-01T00:00:03Z' } }
15
+ const mockFile10 = { cozyMetadata: { createdAt: '1970-01-01T00:00:02Z' } }
16
+ const mockFile11 = { cozyMetadata: { createdAt: '1970-01-01T00:00:01Z' } }
17
+ const mockFile12 = { cozyMetadata: { createdAt: '1970-01-01T00:00:00Z' } }
18
+
19
+ jest.mock('cozy-client', () => ({
20
+ ...jest.requireActual('cozy-client'),
21
+ useQuery: () => mockUseQuery()
22
+ }))
23
+
24
+ afterEach(() => {
25
+ mockUseQuery.mockClear()
26
+ })
27
+
28
+ it('handles files pending', () => {
29
+ mockUseQuery.mockReturnValueOnce({ fetchStatus: 'pending', data: null })
30
+ mockUseQuery.mockReturnValueOnce({ fetchStatus: 'pending', data: null })
31
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
32
+ expect(result.current).toStrictEqual({ fetchStatus: 'loading' })
33
+ })
34
+
35
+ it('handles files loading', () => {
36
+ mockUseQuery.mockReturnValueOnce({ fetchStatus: 'loading', data: null })
37
+ mockUseQuery.mockReturnValueOnce({ fetchStatus: 'loading', data: null })
38
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
39
+ expect(result.current).toStrictEqual({ fetchStatus: 'loading' })
40
+ })
41
+
42
+ it('handles files loading with partial data', () => {
43
+ mockUseQuery.mockReturnValueOnce({
44
+ fetchStatus: 'loading',
45
+ data: [mockFile1]
46
+ })
47
+ mockUseQuery.mockReturnValueOnce({
48
+ fetchStatus: 'loading',
49
+ data: [mockFile2]
50
+ })
51
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
52
+ expect(result.current).toStrictEqual({ fetchStatus: 'loading' })
53
+ })
54
+
55
+ it('handles files loading with more partial data', () => {
56
+ mockUseQuery.mockReturnValueOnce({
57
+ fetchStatus: 'loading',
58
+ data: [mockFile1, mockFile2]
59
+ })
60
+ mockUseQuery.mockReturnValueOnce({
61
+ fetchStatus: 'loading',
62
+ data: [mockFile3, mockFile4]
63
+ })
64
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
65
+ expect(result.current).toStrictEqual({ fetchStatus: 'loading' })
66
+ })
67
+
68
+ it('handles files loading with empty data', () => {
69
+ mockUseQuery.mockReturnValueOnce({
70
+ fetchStatus: 'loaded',
71
+ data: [],
72
+ lastFetch: 1
73
+ })
74
+ mockUseQuery.mockReturnValueOnce({
75
+ fetchStatus: 'loaded',
76
+ data: [],
77
+ lastFetch: 1
78
+ })
79
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
80
+ expect(result.current).toStrictEqual({ fetchStatus: 'empty' })
81
+ })
82
+
83
+ it('handles files loaded and return in correct order', () => {
84
+ mockUseQuery.mockReturnValueOnce({
85
+ fetchStatus: 'loaded',
86
+ data: [mockFile2, mockFile1],
87
+ lastFetch: 1
88
+ })
89
+ mockUseQuery.mockReturnValueOnce({
90
+ fetchStatus: 'loaded',
91
+ data: [mockFile3, mockFile4],
92
+ lastFetch: 1
93
+ })
94
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
95
+ expect(result.current).toStrictEqual({
96
+ data: [mockFile1, mockFile2, mockFile3, mockFile4],
97
+ fetchStatus: 'loaded'
98
+ })
99
+ })
100
+
101
+ it('handles files loaded with identical double return', () => {
102
+ mockUseQuery.mockReturnValueOnce({
103
+ fetchStatus: 'loaded',
104
+ data: [mockFile1, mockFile2, mockFile3, mockFile4],
105
+ lastFetch: 1
106
+ })
107
+ mockUseQuery.mockReturnValueOnce({
108
+ fetchStatus: 'loaded',
109
+ data: [mockFile3, mockFile2, mockFile3, mockFile4],
110
+ lastFetch: 1
111
+ })
112
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
113
+ expect(result.current).toStrictEqual({
114
+ data: [mockFile1, mockFile2, mockFile3, mockFile4],
115
+ fetchStatus: 'loaded'
116
+ })
117
+ })
118
+
119
+ it('handles files loaded with more than 5 result', () => {
120
+ mockUseQuery.mockReturnValueOnce({
121
+ fetchStatus: 'loaded',
122
+ data: [mockFile1, mockFile2, mockFile3, mockFile4, mockFile5, mockFile6],
123
+ lastFetch: 1
124
+ })
125
+ mockUseQuery.mockReturnValueOnce({
126
+ fetchStatus: 'loaded',
127
+ data: [mockFile7, mockFile8, mockFile9, mockFile10, mockFile11, mockFile12],
128
+ lastFetch: 1
129
+ })
130
+ const { result } = renderHook(() => useDataCardFiles('1', '2'))
131
+ expect(result.current).toStrictEqual({
132
+ data: [mockFile1, mockFile2, mockFile3, mockFile4, mockFile5],
133
+ fetchStatus: 'loaded'
134
+ })
135
+ })