diff --git a/.eslintrc.json b/.eslintrc.json
index 938f4e68e3..9a1264d8b8 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -4,7 +4,6 @@
"no-console": 1,
"no-param-reassign": "error",
"react-hooks/exhaustive-deps": "error"
-
},
"globals": {
"fixture": false
@@ -13,5 +12,13 @@
"react": {
"version": "detect"
}
- }
+ },
+ "overrides": [
+ {
+ "files": ["*.spec.js[x]"],
+ "rules": {
+ "react/display-name": "off"
+ }
+ }
+ ]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0f3ac2370..b857725bbb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## ✨ Features
* Upgrade cozy-bar@7.20.1 to be able to call onSelect function
+* Improve speed of search suggestion by preventing fetch notes url until click
* Update cozy-stack-client and cozy-pouch-link to sync with cozy-client version
* Update cozy-ui
- Modify Viewers to handle [68.0.0 BC](https://github.com/cozy/cozy-ui/releases/tag/v68.0.0)
diff --git a/src/drive/web/modules/services/components/SuggestionProvider.jsx b/src/drive/web/modules/services/components/SuggestionProvider.jsx
index abfccb3873..b96da21af9 100644
--- a/src/drive/web/modules/services/components/SuggestionProvider.jsx
+++ b/src/drive/web/modules/services/components/SuggestionProvider.jsx
@@ -4,7 +4,6 @@ import FuzzyPathSearch from '../FuzzyPathSearch'
import { withClient } from 'cozy-client'
import { TYPE_DIRECTORY, makeNormalizedFile } from './helpers'
-import { getIconUrl } from './iconContext'
class SuggestionProvider extends React.Component {
componentDidMount() {
@@ -38,7 +37,7 @@ class SuggestionProvider extends React.Component {
title: result.name,
subtitle: result.path,
term: result.name,
- onSelect: 'open:' + result.url,
+ onSelect: result.onSelect || 'open:' + result.url,
icon: result.icon
}))
},
@@ -54,7 +53,7 @@ class SuggestionProvider extends React.Component {
return new Promise(async resolve => {
const resp = await cozy.client.fetchJSON(
'GET',
- `/data/io.cozy.files/_all_docs?include_docs=true`
+ '/data/io.cozy.files/_all_docs?include_docs=true'
)
const files = resp.rows
// TODO: fix me
@@ -73,11 +72,8 @@ class SuggestionProvider extends React.Component {
.filter(notInTrash)
.filter(notOrphans)
- const normalizedFiles = await Promise.all(
- normalizedFilesPrevious.map(
- async file =>
- await makeNormalizedFile(client, folders, file, getIconUrl)
- )
+ const normalizedFiles = normalizedFilesPrevious.map(file =>
+ makeNormalizedFile(client, folders, file)
)
this.fuzzyPathSearch = new FuzzyPathSearch(normalizedFiles)
diff --git a/src/drive/web/modules/services/components/SuggestionProvider.spec.jsx b/src/drive/web/modules/services/components/SuggestionProvider.spec.jsx
new file mode 100644
index 0000000000..1392f94244
--- /dev/null
+++ b/src/drive/web/modules/services/components/SuggestionProvider.spec.jsx
@@ -0,0 +1,98 @@
+import React from 'react'
+import { render, waitFor } from '@testing-library/react'
+import SuggestionProvider from './SuggestionProvider'
+import { dummyFile, dummyNote } from 'test/dummies/dummyFile'
+
+const makeFileWithDoc = file => ({ ...file, doc: file })
+const parentFolder = makeFileWithDoc(dummyFile({ _id: 'id-file' }))
+const folder = makeFileWithDoc(dummyFile({ dir_id: 'id-file' }))
+const note = makeFileWithDoc(
+ dummyNote({
+ dir_id: 'id-file',
+ name: 'name.cozy-note'
+ })
+)
+const mockClient = {
+ fetchJSON: jest.fn().mockReturnValue({ rows: [parentFolder, folder, note] })
+}
+const mockIntentAttributesClient = 'intent-attributes-client'
+
+jest.mock('cozy-client', () => ({
+ ...jest.requireActual('cozy-client'),
+ withClient: Component => () => {
+ const intent = {
+ _id: 'id_intent',
+ attributes: { client: mockIntentAttributesClient }
+ }
+ return
+ }
+}))
+jest.mock('./iconContext', () => ({ getIconUrl: () => 'iconUrl' }))
+
+describe('SuggestionProvider', () => {
+ let events = {}
+ let event
+
+ beforeEach(() => {
+ window.cozy.client = mockClient
+ window.addEventListener = jest.fn((event, callback) => {
+ events[event] = callback
+ })
+ window.parent.postMessage = jest.fn()
+ event = {
+ origin: mockIntentAttributesClient,
+ data: { query: 'name', id: 'id' }
+ }
+ })
+
+ it('should query all files to display fuzzy suggestion', () => {
+ // Given
+ render()
+
+ // When
+ events.message(event)
+
+ // Then
+ expect(mockClient.fetchJSON).toHaveBeenCalledWith(
+ 'GET',
+ '/data/io.cozy.files/_all_docs?include_docs=true'
+ )
+ })
+
+ it('should provide onSelect with open url when file is not a note + and function when it is a note', async () => {
+ // Given
+ render()
+
+ // When
+ events.message(event)
+
+ // Then
+ await waitFor(() => {
+ expect(window.parent.postMessage).toHaveBeenCalledWith(
+ {
+ id: 'id',
+ suggestions: [
+ {
+ icon: 'iconUrl',
+ id: 'id-file',
+ onSelect: 'open:http://localhost/#/folder/id-file',
+ subtitle: '/path',
+ term: 'name',
+ title: 'name'
+ },
+ {
+ icon: 'iconUrl',
+ id: 'id-file',
+ onSelect: 'id_note:id-file',
+ subtitle: '/path',
+ term: 'name.cozy-note',
+ title: 'name.cozy-note'
+ }
+ ],
+ type: 'intent-id_intent:data'
+ },
+ 'intent-attributes-client'
+ )
+ })
+ })
+})
diff --git a/src/drive/web/modules/services/components/helpers.js b/src/drive/web/modules/services/components/helpers.js
index a9e35a45a8..3247f0afc7 100644
--- a/src/drive/web/modules/services/components/helpers.js
+++ b/src/drive/web/modules/services/components/helpers.js
@@ -1,13 +1,26 @@
import { models } from 'cozy-client'
+import { getIconUrl } from './iconContext'
export const TYPE_DIRECTORY = 'directory'
-export const makeNormalizedFile = async (client, folders, file, getIconUrl) => {
+/**
+ * Normalize file for Front usage in component inside
+ *
+ * To reduce API call, the fetching of Note URL has been delayed
+ * inside an onSelect function called only if provided to
+ * see https://github.com/cozy/cozy-drive/pull/2663#discussion_r938671963
+ *
+ * @param {CozyClient} client - cozy client instance
+ * @param {[IOCozyFile]} folders - all the folders returned by API
+ * @param {IOCozyFile} file - file to normalize
+ * @returns file with normalized field to be used in AutoSuggestion
+ */
+export const makeNormalizedFile = (client, folders, file) => {
const isDir = file.type === TYPE_DIRECTORY
const dirId = isDir ? file._id : file.dir_id
const urlToFolder = `${window.location.origin}/#/folder/${dirId}`
- let path, url
+ let path, url, onSelect
if (isDir) {
path = file.path
url = urlToFolder
@@ -15,7 +28,7 @@ export const makeNormalizedFile = async (client, folders, file, getIconUrl) => {
const parentDir = folders.find(folder => folder._id === file.dir_id)
path = parentDir && parentDir.path ? parentDir.path : ''
if (models.file.isNote(file)) {
- url = await models.note.fetchURL(client, file)
+ onSelect = `id_note:${file.id}`
} else {
url = `${urlToFolder}/file/${file._id}`
}
@@ -26,6 +39,7 @@ export const makeNormalizedFile = async (client, folders, file, getIconUrl) => {
name: file.name,
path,
url,
+ onSelect,
icon: getIconUrl(file)
}
}
diff --git a/src/drive/web/modules/services/components/helpers.spec.js b/src/drive/web/modules/services/components/helpers.spec.js
index 57244d2ee2..73e615a247 100644
--- a/src/drive/web/modules/services/components/helpers.spec.js
+++ b/src/drive/web/modules/services/components/helpers.spec.js
@@ -1,7 +1,6 @@
import { createMockClient, models } from 'cozy-client'
import { makeNormalizedFile, TYPE_DIRECTORY } from './helpers'
-import { getIconUrl } from './iconContext'
jest.mock('./iconContext', () => ({ getIconUrl: () => 'iconUrl' }))
models.note.fetchURL = jest.fn(() => 'noteUrl')
@@ -19,7 +18,7 @@ const noteFileProps = {
}
describe('makeNormalizedFile', () => {
- it('should return correct values for a directory', async () => {
+ it('should return correct values for a directory', () => {
const folders = []
const file = {
_id: 'fileId',
@@ -28,14 +27,10 @@ describe('makeNormalizedFile', () => {
name: 'fileName'
}
- const normalizedFile = await makeNormalizedFile(
- client,
- folders,
- file,
- getIconUrl
- )
+ const normalizedFile = makeNormalizedFile(client, folders, file)
- expect(normalizedFile).toMatchObject({
+ expect(normalizedFile).toEqual({
+ icon: 'iconUrl',
id: 'fileId',
name: 'fileName',
path: 'filePath',
@@ -43,7 +38,7 @@ describe('makeNormalizedFile', () => {
})
})
- it('should return correct values for a file', async () => {
+ it('should return correct values for a file', () => {
const folders = [{ _id: 'folderId', path: 'folderPath' }]
const file = {
_id: 'fileId',
@@ -52,14 +47,10 @@ describe('makeNormalizedFile', () => {
name: 'fileName'
}
- const normalizedFile = await makeNormalizedFile(
- client,
- folders,
- file,
- getIconUrl
- )
+ const normalizedFile = makeNormalizedFile(client, folders, file)
- expect(normalizedFile).toMatchObject({
+ expect(normalizedFile).toEqual({
+ icon: 'iconUrl',
id: 'fileId',
name: 'fileName',
path: 'folderPath',
@@ -67,28 +58,47 @@ describe('makeNormalizedFile', () => {
})
})
- it('should return correct values for a note', async () => {
+ it('should return correct values for a note with on Select function - better for performance', () => {
const folders = [{ _id: 'folderId', path: 'folderPath' }]
const file = {
_id: 'fileId',
+ id: 'noteId',
dir_id: 'folderId',
type: 'file',
name: 'fileName',
...noteFileProps
}
- const normalizedFile = await makeNormalizedFile(
- client,
- folders,
- file,
- getIconUrl
- )
+ const normalizedFile = makeNormalizedFile(client, folders, file)
- expect(normalizedFile).toMatchObject({
+ expect(normalizedFile).toEqual({
+ icon: 'iconUrl',
id: 'fileId',
name: 'note.cozy-note',
path: 'folderPath',
- url: 'noteUrl'
+ onSelect: 'id_note:noteId'
+ })
+ })
+
+ it('should not return filled onSelect for a note without metadata', () => {
+ const folders = [{ _id: 'folderId', path: 'folderPath' }]
+ const file = {
+ _id: 'fileId',
+ id: 'noteId',
+ dir_id: 'folderId',
+ type: 'file',
+ name: 'note.cozy-note'
+ }
+
+ const normalizedFile = makeNormalizedFile(client, folders, file)
+
+ expect(normalizedFile).toEqual({
+ icon: 'iconUrl',
+ id: 'fileId',
+ name: 'note.cozy-note',
+ path: 'folderPath',
+ onSelect: undefined,
+ url: 'http://localhost/#/folder/folderId/file/fileId'
})
})
})
diff --git a/test/dummies/dummyFile.js b/test/dummies/dummyFile.js
new file mode 100644
index 0000000000..95abe3927f
--- /dev/null
+++ b/test/dummies/dummyFile.js
@@ -0,0 +1,37 @@
+// eslint-disable-next-line no-unused-vars
+const { IOCozyFile } = require('cozy-client/dist/types')
+
+/**
+ * Create a dummy file, with overridden value of given param
+ *
+ * @param {Partial} [file={}] - optional file with value to keep
+ * @returns {IOCozyFile} a dummy file
+ */
+export const dummyFile = file => ({
+ _id: 'id-file',
+ _type: 'doctype-file',
+ name: 'name',
+ id: 'id-file',
+ icon: 'icon',
+ path: '/path',
+ type: 'directory',
+ ...file
+})
+
+/**
+ * Create a dummy note, with overridden value of given param
+ *
+ * @param {Partial} [note={}]
+ * @returns {IOCozyFile} a dummy note
+ */
+export const dummyNote = note => ({
+ ...dummyFile(),
+ type: 'file',
+ metadata: {
+ content: '',
+ schema: '',
+ title: '',
+ version: ''
+ },
+ ...note
+})