diff --git a/src/components/notes/editor-view.jsx b/src/components/notes/editor-view.jsx index 91c5750d..b5cbfd72 100644 --- a/src/components/notes/editor-view.jsx +++ b/src/components/notes/editor-view.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useEffect } from 'react' +import React, { useCallback, useRef, useEffect, useMemo } from 'react' import { Editor, WithEditorActions } from '@atlaskit/editor-core' @@ -17,7 +17,6 @@ function EditorView(props) { const { defaultValue, onTitleChange, - onContentChange, defaultTitle, title, collabProvider, @@ -37,6 +36,14 @@ function EditorView(props) { [onTitleChange] ) + const onContentChange = useMemo( + () => { + const nullCallback = () => {} + return props.onContentChange || nullCallback + }, + [props.onContentChange] + ) + useEffect(() => updateTextareaHeight(titleEl.current), []) return ( diff --git a/src/components/notes/editor.jsx b/src/components/notes/editor.jsx index 559acc47..8043e11d 100644 --- a/src/components/notes/editor.jsx +++ b/src/components/notes/editor.jsx @@ -1,10 +1,4 @@ -import React, { - useCallback, - useState, - useEffect, - useMemo, - useContext -} from 'react' +import React, { useEffect, useContext } from 'react' import { withClient } from 'cozy-client' @@ -13,196 +7,58 @@ import EditorLoading from 'components/notes/editor-loading' import EditorLoadingError from 'components/notes/editor-loading-error' import SharingWidget from 'components/notes/sharing' import BackFromEditing from 'components/notes/back_from_editing' - import IsPublicContext from 'components/IsPublicContext' -import CollabProvider from 'lib/collab/provider' -import ServiceClient from 'lib/collab/stack-client' - -import { - getShortNameFromClient, - getParentFolderLink, - getAppFullName -} from 'lib/utils.js' +import useNote from 'hooks/useNote' +import useServiceClient from 'hooks/useServiceClient' +import useCollabProvider from 'hooks/useCollabProvider' +import useTitleChanges from 'hooks/useTitleChanges' +import useForceSync from 'hooks/useForceSync' +import useReturnUrl from 'hooks/useReturnUrl' +import useUser from 'hooks/useUser' import { translate } from 'cozy-ui/react/I18n' import useEventListener from 'cozy-ui/react/hooks/useEventListener' -function setPageTitle(appFullName, title) { - document.title = - title && title != '' ? `${appFullName} - ${title}` : appFullName -} - -async function loadNote( - serviceClient, - noteId, - loading, - setLoading, - setDoc, - setTitle -) { - try { - if (!loading) { - setLoading(true) - } - const doc = await serviceClient.getDoc(noteId) - setTitle(doc.title || '') - setDoc(doc) - } catch (e) { - setTitle(false) - setDoc(false) - } - setLoading(false) -} - -function getLocalTitleChangeCallback(serviceClient, noteId, title, setTitle) { - return e => { - const newTitle = e.target.value - const modifiedTitle = newTitle - if (title != modifiedTitle) { - setTitle(modifiedTitle) - serviceClient.setTitle(noteId, modifiedTitle) - } - } -} - const Editor = translate()( withClient(function(props) { - const { client, noteId, t } = props - const userName = useMemo( - () => props.userName || getShortNameFromClient(client), - [props.userName] - ) - const appFullName = useMemo(getAppFullName) - const isPublic = useContext(IsPublicContext) - - // alias for later shortcuts - const docId = noteId - const userId = userName - const cozyClient = client - - // state - const [loading, setLoading] = useState(true) - const [doc, setDoc] = useState(undefined) - const [title, setTitle] = useState(undefined) + // base parameters + const { client: cozyClient, noteId, t } = props // plugins and config - const serviceClient = useMemo( - () => { - return new ServiceClient({ userId, userName, cozyClient }) - }, - [noteId] - ) - const docVersion = doc && doc.version - //console.log("docVersion", doc, doc && doc.version, docVersion) - const collabProvider = useMemo( - () => { - //console.log("collab provider memo", docVersion) - if (docVersion !== undefined) { - //console.log('new collabProvider') - const provider = new CollabProvider( - { version: doc.version, docId }, - serviceClient - ) - // The following object is defined in an Atlassian API. - // `provider` expects a Promise, even if we wouldn't - // need it ourselves. - return { - useNativePlugin: true, - provider: Promise.resolve(provider), - inviteToEditHandler: () => undefined, - isInviteToEditButtonSelected: false, - userId: serviceClient.getSessionId() - } - } else { - return null - } - }, - [noteId, docVersion, userName, serviceClient] - ) - //console.log("end collabProviderMemo", collabProvider) - - // fetch the actual note on load - useEffect( - () => { - loadNote(serviceClient, noteId, loading, setLoading, setDoc, setTitle) - }, - [noteId] - ) + const isPublic = useContext(IsPublicContext) + const { userName, userId } = useUser({ + userName: props.userName, + cozyClient + }) + const serviceClient = useServiceClient({ userId, userName, cozyClient }) + const { loading, title, doc, setTitle } = useNote({ serviceClient, noteId }) + const returnUrl = useReturnUrl({ + returnUrl: props.returnUrl, + cozyClient, + doc + }) + const collabProvider = useCollabProvider({ + noteId, + serviceClient, + docVersion: doc && doc.version + }) // callbacks - const onContentChange = useCallback(() => null, [noteId]) - const onLocalTitleChange = useCallback( - getLocalTitleChangeCallback(serviceClient, noteId, title, setTitle), - [noteId, setTitle, serviceClient] - ) - const onRemoteTitleChange = useCallback( - modifiedTitle => { - if (title != modifiedTitle) { - setTitle(modifiedTitle) - } - }, - [noteId, setTitle] - ) - useMemo( - () => { - serviceClient.onTitleUpdated(noteId, onRemoteTitleChange) - }, - [onRemoteTitleChange, serviceClient] - ) - - useEffect( - () => { - setPageTitle(appFullName, title) - }, - [title] - ) - - const returnUrl = useMemo( - () => { - if (props.returnUrl !== undefined) { - return props.returnUrl - } else if (doc) { - return getParentFolderLink(client, doc.file) - } else if (!isPublic) { - return '/' - } else { - return undefined - } - }, - [props.returnUrl, doc] - ) - - // Failure in loading the note ? - useEffect( - () => { - if (!loading && !doc) { - // eslint-disable-next-line no-console - console.warn(`Could not load note ${noteId}`) - } - }, - [loading, doc] - ) - - // Be sure to save everything before leaving - // and force sync with the io.cozy.file - async function forceSync() { - if (doc) { - // wait for every event to finish - const provider = await collabProvider.provider - await provider.channel.ensureEmptyQueue() - // then force a server sync - await serviceClient.sync(docId) - } - } - useEffect(() => forceSync, [docId, doc]) - // Sync on unload will probably be stopped by the browser, - // as most async code on unload, but let's try anyway - const emergencySync = useCallback(function() { - if (doc && docId) { - serviceClient.sync(docId) // force a server sync - } - }, []) + const { onLocalTitleChange } = useTitleChanges({ + noteId, + title, + setTitle, + serviceClient + }) + const { forceSync, emergencySync } = useForceSync({ + doc, + serviceClient, + collabProvider + }) + // when leaving the component or changing doc + useEffect(() => forceSync, [noteId, doc, forceSync]) + // when quitting the webpage useEventListener(window, 'unload', emergencySync) // rendering @@ -212,7 +68,6 @@ const Editor = translate()( return ( setShowModal(!showModal), []) const onClose = useCallback(() => setShowModal(false), []) - const docId = file.id + const noteId = file.id return ( (parent && ( { + if (serviceClient && docVersion !== undefined) { + const provider = new CollabProvider( + { version: docVersion, noteId }, + serviceClient + ) + // The following object is defined in an Atlassian API. + // `provider` expects a Promise, even if we wouldn't + // need it ourselves. + return { + useNativePlugin: true, + provider: Promise.resolve(provider), + inviteToEditHandler: () => undefined, + isInviteToEditButtonSelected: false, + userId: serviceClient.getSessionId() + } + } else { + return null + } + }, + [noteId, docVersion, serviceClient] + ) +} + +export default useCollabProvider diff --git a/src/hooks/useFetchNotesByIds.jsx b/src/hooks/useFetchNotesByIds.jsx index 008ba3aa..3a5c775b 100644 --- a/src/hooks/useFetchNotesByIds.jsx +++ b/src/hooks/useFetchNotesByIds.jsx @@ -1,9 +1,11 @@ import { useState, useEffect } from 'react' import cloneDeep from 'lodash/cloneDeep' import get from 'lodash/get' + const useFetchNotesByIds = client => { const [notes, setNotes] = useState([]) const [fetchStatus, setFetchStatus] = useState('loading') + useEffect(() => { const fetchData = async () => { try { @@ -33,6 +35,7 @@ const useFetchNotesByIds = client => { setFetchStatus('loaded') } catch (error) { setFetchStatus('errored') + // eslint-disable-next-line no-console console.log({ error }) } } diff --git a/src/hooks/useForceSync.js b/src/hooks/useForceSync.js new file mode 100644 index 00000000..dcd6bb9d --- /dev/null +++ b/src/hooks/useForceSync.js @@ -0,0 +1,35 @@ +import { useCallback } from 'react' + +function useForceSync({ doc, serviceClient, collabProvider }) { + const noteId = doc && doc.file.id + + // Be sure to save everything before leaving + // and force sync with the io.cozy.file + const forceSync = useCallback( + async function forceSync() { + if (doc && collabProvider && serviceClient) { + // wait for every event to finish + const provider = await collabProvider.provider + await provider.channel.ensureEmptyQueue() + // then force a server sync + await serviceClient.sync(noteId) + } + }, + [doc, collabProvider] + ) + + // Sync on unload will probably be stopped by the browser, + // as most async code on unload, but let's try anyway + const emergencySync = useCallback( + function() { + if (doc && serviceClient) { + serviceClient.sync(noteId) // force a server sync + } + }, + [doc, serviceClient] + ) + + return { forceSync, emergencySync } +} + +export default useForceSync diff --git a/src/hooks/useNote.js b/src/hooks/useNote.js new file mode 100644 index 00000000..a8f2d491 --- /dev/null +++ b/src/hooks/useNote.js @@ -0,0 +1,52 @@ +import { useState, useEffect } from 'react' + +function useNote({ serviceClient, noteId }) { + // `docId` is the id of the note for which we have a state. + // `noteId` is the id of the requested note. + const [docId, setDocId] = useState(noteId) + const [loading, setLoading] = useState(true) + const [doc, setDoc] = useState(undefined) + const [title, setTitle] = useState(undefined) + + // reload if ever noteId changes + useEffect( + () => { + if (docId !== noteId) { + setLoading(true) + setTitle(undefined) + setDoc(undefined) + setDocId(noteId) + } + }, + [noteId, docId, setLoading, setTitle, setDoc, setDocId] + ) + + // load the note + useEffect( + () => { + const loadNote = async function() { + try { + if (!loading) setLoading(true) + const doc = await serviceClient.getDoc(docId) + setTitle(doc.title || '') + setDoc(doc) + } catch (e) { + setTitle(false) + setDoc(false) + // eslint-disable-next-line no-console + console.warn(`Could not load note ${docId}`) + } + setLoading(false) + } + if (serviceClient) { + if (!doc || doc.file.id != docId) loadNote() + } + }, + [docId, setLoading, serviceClient, setDoc, setTitle] + ) + + if (docId == noteId) return { loading, title, doc, setTitle } + else return { loading: true, title: undefined, doc: undefined, setTitle } +} + +export default useNote diff --git a/src/hooks/useReturnUrl.js b/src/hooks/useReturnUrl.js new file mode 100644 index 00000000..3f3c4d4a --- /dev/null +++ b/src/hooks/useReturnUrl.js @@ -0,0 +1,24 @@ +import { useMemo, useContext } from 'react' +import IsPublicContext from 'components/IsPublicContext' +import { getParentFolderLink } from 'lib/utils.js' + +function useReturnUrl({ returnUrl, cozyClient, doc }) { + const isPublic = useContext(IsPublicContext) + + return useMemo( + () => { + if (returnUrl !== undefined) { + return returnUrl + } else if (doc) { + return getParentFolderLink(cozyClient, doc.file) + } else if (!isPublic) { + return '/' + } else { + return undefined + } + }, + [returnUrl, doc, isPublic] + ) +} + +export default useReturnUrl diff --git a/src/hooks/useServiceClient.js b/src/hooks/useServiceClient.js new file mode 100644 index 00000000..5c289289 --- /dev/null +++ b/src/hooks/useServiceClient.js @@ -0,0 +1,13 @@ +import { useMemo } from 'react' +import ServiceClient from 'lib/collab/stack-client' + +function useServiceClient({ userId, userName, cozyClient }) { + return useMemo( + () => { + return new ServiceClient({ userId, userName, cozyClient }) + }, + [userId, userName] + ) +} + +export default useServiceClient diff --git a/src/hooks/useTitleChanges.js b/src/hooks/useTitleChanges.js new file mode 100644 index 00000000..9cc70730 --- /dev/null +++ b/src/hooks/useTitleChanges.js @@ -0,0 +1,43 @@ +import { useMemo, useEffect, useCallback } from 'react' +import { getAppFullName } from 'lib/utils.js' + +function useTitleChanges({ noteId, title, setTitle, serviceClient }) { + // Title change because of a local change from the editor + const onLocalTitleChange = useCallback( + serviceClient + ? e => { + const newTitle = e.target.value + const modifiedTitle = newTitle + if (title != modifiedTitle) { + setTitle(modifiedTitle) + serviceClient.setTitle(noteId, modifiedTitle) + } + } + : () => {}, + [noteId, title, setTitle, serviceClient] + ) + // Title change because of a remote change from the stack + useMemo( + () => { + serviceClient && + serviceClient.onTitleUpdated(noteId, modifiedTitle => { + if (title != modifiedTitle) { + setTitle(modifiedTitle) + } + }) + }, + [title, setTitle, serviceClient] + ) + // whatever the change is, keep the tab title updated + const appFullName = useMemo(getAppFullName, []) + useEffect( + () => { + document.title = + title && title != '' ? `${appFullName} - ${title}` : appFullName + }, + [title] + ) + return { onLocalTitleChange } +} + +export default useTitleChanges diff --git a/src/hooks/useUser.js b/src/hooks/useUser.js new file mode 100644 index 00000000..f7dff5bc --- /dev/null +++ b/src/hooks/useUser.js @@ -0,0 +1,14 @@ +import { useMemo } from 'react' + +import { getShortNameFromClient } from 'lib/utils.js' + +function useUser({ userName: providedUserName, cozyClient }) { + const userName = useMemo( + () => providedUserName || getShortNameFromClient(cozyClient), + [providedUserName] + ) + const userId = userName + return { userId, userName } +} + +export default useUser diff --git a/src/lib/collab/channel.js b/src/lib/collab/channel.js index 53ee5747..48a188a2 100644 --- a/src/lib/collab/channel.js +++ b/src/lib/collab/channel.js @@ -169,8 +169,8 @@ export class Channel { if (steps.length === 0) return try { - const { docId } = this.config - const response = await this.service.pushSteps(docId, version, steps) + const { noteId } = this.config + const response = await this.service.pushSteps(noteId, version, steps) this.rebaseStepsInQueue() this.resetBackoff() this.isSending = false @@ -196,12 +196,12 @@ export class Channel { * Connect to pubsub to start receiving events */ async connect(version, doc) { - const { docId } = this.config - this.service.join(docId) - this.service.onStepsCreated(docId, data => { + const { noteId } = this.config + this.service.join(noteId) + this.service.onStepsCreated(noteId, data => { this.emit('data', { version: data.version, steps: [data] }) }) - this.service.onTelepointerUpdated(docId, payload => { + this.service.onTelepointerUpdated(noteId, payload => { this.emit('telepointer', payload) }) this.emit('connected', { @@ -214,16 +214,16 @@ export class Channel { * Get steps from version x to latest */ async getSteps(version) { - const { docId } = this.config - return await this.service.getSteps(docId, version) + const { noteId } = this.config + return await this.service.getSteps(noteId, version) } /** * Send telepointer */ async sendTelepointer(data) { - const { docId } = this.config - return await this.service.pushTelepointer(docId, data) + const { noteId } = this.config + return await this.service.pushTelepointer(noteId, data) } /** diff --git a/src/lib/collab/channel.spec.js b/src/lib/collab/channel.spec.js index adbfb81f..c5f06c2d 100644 --- a/src/lib/collab/channel.spec.js +++ b/src/lib/collab/channel.spec.js @@ -5,8 +5,8 @@ jest.mock('prosemirror-collab', () => { return { getVersion: jest.fn(), sendableSteps: jest.fn() } }) -const docId = 'myDocId' -const config = { docId } +const noteId = 'myDocId' +const config = { noteId } const service = { pushSteps: jest.fn(), onStepsCreated: jest.fn(), @@ -370,13 +370,13 @@ describe('Channel', () => { await new Promise(resolve => window.setTimeout(resolve, 500)) expect(service.pushSteps).toHaveBeenNthCalledWith( 1, - docId, + noteId, firstVersion, steps.localSteps.steps ) expect(service.pushSteps).toHaveBeenNthCalledWith( 2, - docId, + noteId, lastVersion, lastSteps.steps ) @@ -400,7 +400,7 @@ describe('Channel', () => { version, doc ) - expect(service.join).toHaveBeenCalledWith(docId) + expect(service.join).toHaveBeenCalledWith(noteId) }) it('emits for new steps from the server', async done => { diff --git a/src/lib/collab/provider.spec.js b/src/lib/collab/provider.spec.js index aaf9b975..005a4fa2 100644 --- a/src/lib/collab/provider.spec.js +++ b/src/lib/collab/provider.spec.js @@ -6,7 +6,7 @@ jest.mock('prosemirror-collab', () => { return { getVersion: jest.fn(), sendableSteps: jest.fn() } }) -const docId = 'myDocId' +const noteId = 'myDocId' const version = 96 const userId = 'myuser' const sessionId = `${userId}:1425` @@ -22,7 +22,7 @@ const service = { getSessionId: jest.fn(), getUserId: jest.fn() } -const config = { docId, version, channel } +const config = { noteId, version, channel } const getState = jest.fn() const steps = [{ example: version + 1 }, { example: version + 2 }] diff --git a/src/lib/collab/stack-client.js b/src/lib/collab/stack-client.js index 3dba9993..2447f829 100644 --- a/src/lib/collab/stack-client.js +++ b/src/lib/collab/stack-client.js @@ -116,26 +116,30 @@ export class ServiceClient { /** * Force the note to be written to the io.cozy.files now - * @param {uuid} docId - note id + * @param {uuid} noteId - note id */ - async sync(docId) { - await this.stackClient.fetchJSON('POST', this.path(docId, 'sync')) + async sync(noteId) { + await this.stackClient.fetchJSON('POST', this.path(noteId, 'sync')) } /** * Change the title of a note - * @param {uuid} docId + * @param {uuid} noteId * @param {string} title */ - async setTitle(docId, title) { + async setTitle(noteId, title) { const titleDoc = { data: { type: 'io.cozy.notes.documents', - id: docId, + id: noteId, attributes: this.client2server({ title: title }) } } - await this.stackClient.fetchJSON('PUT', this.path(docId, 'title'), titleDoc) + await this.stackClient.fetchJSON( + 'PUT', + this.path(noteId, 'title'), + titleDoc + ) } /** @@ -155,11 +159,11 @@ export class ServiceClient { /** * Join a doc - listen to realtime events for this doc - * @param {uui} docId + * @param {uui} noteId */ - async join(docId) { + async join(noteId) { const onRealtimeCreated = function(doc) { - if (doc.id == docId) { + if (doc.id == noteId) { return this.onRealtimeEvent(doc) } else { return undefined @@ -174,13 +178,13 @@ export class ServiceClient { this.realtime.subscribe( 'updated', 'io.cozy.notes.events', - docId, + noteId, this.onRealtimeEvent ), this.realtime.subscribe( 'deleted', 'io.cozy.notes.events', - docId, + noteId, this.onRealtimeEvent ) ]) @@ -188,33 +192,33 @@ export class ServiceClient { /** * Listen for new steps from the server - * @param {uuid} docId + * @param {uuid} noteId * @param {Function} callback */ - async onStepsCreated(docId, callback) { - this.setCallback('io.cozy.notes.steps', docId, data => + async onStepsCreated(noteId, callback) { + this.setCallback('io.cozy.notes.steps', noteId, data => callback(this.server2client(data)) ) } /** * Listen for new cursors positions from the server - * @param {uuid} docId + * @param {uuid} noteId * @param {Function} callback */ - async onTelepointerUpdated(docId, callback) { - this.setCallback('io.cozy.notes.telepointers', docId, data => + async onTelepointerUpdated(noteId, callback) { + this.setCallback('io.cozy.notes.telepointers', noteId, data => callback(this.server2client(data)) ) } /** * Listen for title changes in the document - * @param {uuid} docId + * @param {uuid} noteId * @param {Function} callback - function that get a title as paramerer */ - async onTitleUpdated(docId, callback) { - this.setCallback('io.cozy.notes.documents', docId, doc => { + async onTitleUpdated(noteId, callback) { + this.setCallback('io.cozy.notes.documents', noteId, doc => { return !doc.sessionID || doc.sessionID != this.sessionId ? callback(doc.title) : null @@ -223,10 +227,10 @@ export class ServiceClient { /** * Get the full document for a note - * @param {uuid} docId + * @param {uuid} noteId */ - async getDoc(docId) { - const res = await this.stackClient.fetchJSON('GET', this.path(docId)) + async getDoc(noteId) { + const res = await this.stackClient.fetchJSON('GET', this.path(noteId)) return { doc: res.data.attributes.metadata.content, version: res.data.attributes.metadata.version, @@ -237,11 +241,11 @@ export class ServiceClient { /** * Push new local steps to the server - * @param {uuid} docId + * @param {uuid} noteId * @param {integer} version * @param {Object[]} steps */ - async pushSteps(docId, version, steps) { + async pushSteps(noteId, version, steps) { const options = { headers: { 'if-match': version } } const stepsDoc = { data: steps.map(step => ({ @@ -253,7 +257,7 @@ export class ServiceClient { // which will occurs if the server has more versions than us await this.stackClient.fetchJSON( 'PATCH', - this.path(docId), + this.path(noteId), stepsDoc, options ) @@ -262,17 +266,17 @@ export class ServiceClient { /** * Fetch steps from the server since the provided versions - * @param {uuid} docId + * @param {uuid} noteId * @param {integer} version * @returns {{version, steps}|{doc, version}} * If the server doesn't have all requested steps in memory, * it could returns the whole document in its current version */ - async getSteps(docId, version) { + async getSteps(noteId, version) { try { const res = await this.stackClient.fetchJSON( 'GET', - `${this.path(docId, 'steps')}?Version=${version}` + `${this.path(noteId, 'steps')}?Version=${version}` ) if (res.data.length == 0) { return { steps: [], version } @@ -292,13 +296,13 @@ export class ServiceClient { // it responds with a full document and a title const ev = 'io.cozy.notes.documents' const realtimeDoc = { - _id: docId, + _id: noteId, _type: 'io.cozy.notes.events', doctype: ev, title: meta.title } - if (this.callbacks[ev] && this.callbacks[ev][docId]) { - this.callbacks[ev][docId](realtimeDoc) + if (this.callbacks[ev] && this.callbacks[ev][noteId]) { + this.callbacks[ev][noteId](realtimeDoc) } return { doc: doc, @@ -312,20 +316,20 @@ export class ServiceClient { /** * Push new local cursor position to the server - * @param {uuid} docId + * @param {uuid} noteId * @param {Object} data */ - async pushTelepointer(docId, data) { + async pushTelepointer(noteId, data) { const telepointerDoc = { data: { type: 'io.cozy.notes.telepointers', - id: docId, + id: noteId, attributes: this.client2server(data) } } return this.stackClient.fetchJSON( 'PUT', - this.path(docId, 'telepointer'), + this.path(noteId, 'telepointer'), telepointerDoc ) } diff --git a/src/lib/collab/stack-client.spec.js b/src/lib/collab/stack-client.spec.js index 77e3ec51..288551cc 100644 --- a/src/lib/collab/stack-client.spec.js +++ b/src/lib/collab/stack-client.spec.js @@ -6,7 +6,7 @@ import CozyRealtime from 'cozy-realtime' import { createMockClient } from 'cozy-client/dist/mock' const userId = 'myuser' -const docId = 'e3a0921e38f9687cd9e8c60a3c04f9d0' +const noteId = 'e3a0921e38f9687cd9e8c60a3c04f9d0' const sessionId = userId + ':1577115994661.661.0.16413785152829485' const cozyClient = createMockClient({}) const realtime = { subscribe: jest.fn() } @@ -326,7 +326,7 @@ describe('ServiceClient', () => { const eventType = 'io.cozy.notes.events' const telepointerEvent = 'io.cozy.notes.telepointers' const telepointerUpdateDoc = { - _id: docId, + _id: noteId, doctype: telepointerEvent, selection: { anchor: 6, head: 6, type: 'textSelection' }, sessionID: sessionId, @@ -335,7 +335,7 @@ describe('ServiceClient', () => { } const stepsEvent = 'io.cozy.notes.steps' const stepsUpdateDoc = { - _id: docId, + _id: noteId, _rev: '1-03dc114ca621366c35f9bef06205da53', doctype: stepsEvent, from: 6, @@ -349,7 +349,7 @@ describe('ServiceClient', () => { const titleEvent = 'io.cozy.notes.documents' const newTitle = 'hello my new title' const titleUpdateDoc = { - _id: docId, + _id: noteId, doctype: titleEvent, sessionID: sessionId, title: newTitle @@ -361,7 +361,7 @@ describe('ServiceClient', () => { describe('join', () => { it('should subscribe to realtime', async () => { const service = new ServiceClient({ userId, cozyClient, realtime }) - await service.join(docId) + await service.join(noteId) expect(realtime.subscribe).toHaveBeenCalled() expect(realtime.subscribe.mock.calls[0][1]).toBe(eventType) }) @@ -377,13 +377,13 @@ describe('ServiceClient', () => { }) const onSteps = jest.fn() const service = new ServiceClient({ userId, cozyClient, realtime }) - await service.join(docId) - await service.onStepsCreated(docId, onSteps) + await service.join(noteId) + await service.onStepsCreated(noteId, onSteps) expect(callback).toBeDefined() callback(stepsUpdateDoc) expect(onSteps).toHaveBeenCalled() const data = onSteps.mock.calls[0][0] - expect(data._id).toBe(docId) + expect(data._id).toBe(noteId) expect(data.doctype).toBe(stepsEvent) }) }) @@ -398,13 +398,13 @@ describe('ServiceClient', () => { }) const onTelepointer = jest.fn() const service = new ServiceClient({ userId, cozyClient, realtime }) - await service.join(docId) - await service.onTelepointerUpdated(docId, onTelepointer) + await service.join(noteId) + await service.onTelepointerUpdated(noteId, onTelepointer) expect(callback).toBeDefined() callback(telepointerUpdateDoc) expect(onTelepointer).toHaveBeenCalled() const data = onTelepointer.mock.calls[0][0] - expect(data._id).toBe(docId) + expect(data._id).toBe(noteId) expect(data.doctype).toBe(telepointerEvent) }) }) @@ -419,8 +419,8 @@ describe('ServiceClient', () => { }) const onTitle = jest.fn() const service = new ServiceClient({ userId, cozyClient, realtime }) - await service.join(docId) - await service.onTitleUpdated(docId, onTitle) + await service.join(noteId) + await service.onTitleUpdated(noteId, onTitle) expect(callback).toBeDefined() callback(titleUpdateDoc) expect(onTitle).toHaveBeenCalled() @@ -438,8 +438,8 @@ describe('ServiceClient', () => { }) const onSteps = jest.fn() const service = new ServiceClient({ userId, cozyClient, realtime }) - await service.join(docId) - await service.onStepsCreated(docId, onSteps) + await service.join(noteId) + await service.onStepsCreated(noteId, onSteps) expect(callback).toBeDefined() callback(stepsUpdateDoc) expect(onSteps).toHaveBeenCalled() @@ -468,13 +468,13 @@ describe('ServiceClient', () => { it('should call the stack with title', async () => { const title = 'hello my new title' const service = new ServiceClient({ userId, cozyClient }) - await service.setTitle(docId, title) + await service.setTitle(noteId, title) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalled() const fetchJSON = cozyClient.stackClient.fetchJSON const called = fetchJSON.mock.calls.length const lastCall = fetchJSON.mock.calls[called - 1] expect(lastCall[0]).toBe('PUT') - expect(lastCall[1]).toBe(`/notes/${docId}/title`) + expect(lastCall[1]).toBe(`/notes/${noteId}/title`) expect(lastCall[2].data.type).toBe('io.cozy.notes.documents') expect(lastCall[2].data.attributes.title).toBe(title) }) @@ -486,7 +486,7 @@ describe('ServiceClient', () => { const serverResponse = { data: { type: 'io.cozy.files', - id: docId, + id: noteId, attributes: { type: 'file', name: 'zdzadda.cozy-note', @@ -520,39 +520,39 @@ describe('ServiceClient', () => { it('should call the stack', async () => { const service = new ServiceClient({ userId, cozyClient }) - await service.getDoc(docId) + await service.getDoc(noteId) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalled() const fetchJSON = cozyClient.stackClient.fetchJSON const called = fetchJSON.mock.calls.length const lastCall = fetchJSON.mock.calls[called - 1] expect(lastCall[0]).toBe('GET') - expect(lastCall[1]).toBe(`/notes/${docId}`) + expect(lastCall[1]).toBe(`/notes/${noteId}`) }) it('should return the doc content', async () => { const service = new ServiceClient({ userId, cozyClient }) - const { doc } = await service.getDoc(docId) + const { doc } = await service.getDoc(noteId) expect(doc).toHaveProperty('type', 'doc') expect(doc).toHaveProperty('content') }) it('should return the version', async () => { const service = new ServiceClient({ userId, cozyClient }) - const { version } = await service.getDoc(docId) + const { version } = await service.getDoc(noteId) expect(version).toBe(serverVersion) }) it('should return the title', async () => { const service = new ServiceClient({ userId, cozyClient }) - const { title } = await service.getDoc(docId) + const { title } = await service.getDoc(noteId) expect(title).toBe(serverTitle) }) it('should return the file document', async () => { const service = new ServiceClient({ userId, cozyClient }) - const { file } = await service.getDoc(docId) + const { file } = await service.getDoc(noteId) expect(file.type).toBe('io.cozy.files') - expect(file.id).toBe(docId) + expect(file.id).toBe(noteId) }) }) @@ -584,13 +584,13 @@ describe('ServiceClient', () => { it('should send to the server', async () => { const service = new ServiceClient({ userId, cozyClient }) - await service.pushSteps(docId, localVersion, localSteps) + await service.pushSteps(noteId, localVersion, localSteps) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalled() const fetchJSON = cozyClient.stackClient.fetchJSON const called = fetchJSON.mock.calls.length const lastCall = fetchJSON.mock.calls[called - 1] expect(lastCall[0]).toBe('PATCH') - expect(lastCall[1]).toBe(`/notes/${docId}`) + expect(lastCall[1]).toBe(`/notes/${noteId}`) expect(lastCall[2].data).toHaveLength(localSteps.length) // for conflict resolution expect(lastCall[3]).toHaveProperty('headers.if-match', localVersion) @@ -598,7 +598,7 @@ describe('ServiceClient', () => { it('should send io.cozy.notes.steps documents', async () => { const service = new ServiceClient({ userId, cozyClient }) - await service.pushSteps(docId, localVersion, localSteps) + await service.pushSteps(noteId, localVersion, localSteps) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalled() const fetchJSON = cozyClient.stackClient.fetchJSON const called = fetchJSON.mock.calls.length @@ -612,7 +612,7 @@ describe('ServiceClient', () => { throw new Error() }) const service = new ServiceClient({ userId, cozyClient }) - const push = service.pushSteps(docId, localVersion, localSteps) + const push = service.pushSteps(noteId, localVersion, localSteps) await expect(push).rejects.toBeInstanceOf(Error) }) }) @@ -654,7 +654,7 @@ describe('ServiceClient', () => { const remoteDocument = { data: { type: 'io.cozy.files', - id: docId, + id: noteId, attributes: { type: 'file', name: 'zdzadda.cozy-note', @@ -685,19 +685,19 @@ describe('ServiceClient', () => { it('should ask the server', async () => { cozyClient.stackClient.fetchJSON.mockImplementation(() => ({ data: [] })) const service = new ServiceClient({ userId, cozyClient }) - await service.getSteps(docId, localVersion) + await service.getSteps(noteId, localVersion) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalled() const fetchJSON = cozyClient.stackClient.fetchJSON const called = fetchJSON.mock.calls.length const lastCall = fetchJSON.mock.calls[called - 1] expect(lastCall[0]).toBe('GET') - expect(lastCall[1]).toBe(`/notes/${docId}/steps?Version=${localVersion}`) + expect(lastCall[1]).toBe(`/notes/${noteId}/steps?Version=${localVersion}`) }) it('should return steps when the server has some', async () => { cozyClient.stackClient.fetchJSON.mockImplementation(() => remoteSteps) const service = new ServiceClient({ userId, cozyClient }) - const res = await service.getSteps(docId, localVersion) + const res = await service.getSteps(noteId, localVersion) expect(res).toHaveProperty('version', remoteVersion) expect(res).toHaveProperty('steps') expect(res.steps).toHaveLength(2) @@ -706,7 +706,7 @@ describe('ServiceClient', () => { it('should return steps with a `sessionId`', async () => { cozyClient.stackClient.fetchJSON.mockImplementation(() => remoteSteps) const service = new ServiceClient({ userId, cozyClient }) - const res = await service.getSteps(docId, localVersion) + const res = await service.getSteps(noteId, localVersion) expect(res.steps[0]).toHaveProperty( 'sessionId', remoteSteps.data[0].attributes.sessionID @@ -716,7 +716,7 @@ describe('ServiceClient', () => { it('should return current version when there is 0 steps', async () => { cozyClient.stackClient.fetchJSON.mockImplementation(() => ({ data: [] })) const service = new ServiceClient({ userId, cozyClient }) - const res = await service.getSteps(docId, localVersion) + const res = await service.getSteps(noteId, localVersion) expect(res).toHaveProperty('version', localVersion) expect(res).toHaveProperty('steps', []) expect(res.steps).toHaveLength(0) @@ -730,7 +730,7 @@ describe('ServiceClient', () => { } }) const service = new ServiceClient({ userId, cozyClient }) - const res = await service.getSteps(docId, localVersion) + const res = await service.getSteps(noteId, localVersion) expect(res).toHaveProperty('version', remoteVersion) expect(res).not.toHaveProperty('steps') expect(res).toHaveProperty('doc', serverDoc) @@ -745,8 +745,8 @@ describe('ServiceClient', () => { }) const callback = jest.fn() const service = new ServiceClient({ userId, cozyClient }) - service.onTitleUpdated(docId, callback) - await service.getSteps(docId, localVersion) + service.onTitleUpdated(noteId, callback) + await service.getSteps(noteId, localVersion) expect(callback).toHaveBeenLastCalledWith(remoteTitle) }) }) @@ -765,13 +765,13 @@ describe('ServiceClient', () => { it('should send data to the server', async () => { const service = new ServiceClient({ userId, cozyClient }) - await service.pushTelepointer(docId, telepointerData) + await service.pushTelepointer(noteId, telepointerData) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalled() const fetchJSON = cozyClient.stackClient.fetchJSON const called = fetchJSON.mock.calls.length const lastCall = fetchJSON.mock.calls[called - 1] expect(lastCall[0]).toBe('PUT') - expect(lastCall[1]).toBe(`/notes/${docId}/telepointer`) + expect(lastCall[1]).toBe(`/notes/${noteId}/telepointer`) expect(lastCall[2]).toHaveProperty('data.type', telepointerEvent) expect(lastCall[2]).toHaveProperty('data.attributes.type', 'telepointer') }) @@ -784,10 +784,10 @@ describe('ServiceClient', () => { it('should call the server', async () => { const service = new ServiceClient({ userId, cozyClient }) - await service.sync(docId) + await service.sync(noteId) expect(cozyClient.stackClient.fetchJSON).toHaveBeenCalledWith( 'POST', - `/notes/${docId}/sync` + `/notes/${noteId}/sync` ) }) })