Skip to content

Commit

Permalink
fix: Make sure that the file has a path after the real-time update
Browse files Browse the repository at this point in the history
Updating files in real time only returns the couchDB document, which has no path for files, only for folders. This commit adds a mechanism to make sure you have one before adding it to the cozy-client store. A file without a path creates problems during sharing checks, for example when moving a file.
  • Loading branch information
cballevre committed Feb 6, 2024
1 parent 4a3e19d commit 2ec1044
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 6 deletions.
130 changes: 130 additions & 0 deletions src/components/FilesRealTimeQueries.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { memo, useEffect } from 'react'

import { useClient, Mutations } from 'cozy-client'
import { ensureFilePath } from 'cozy-client/dist/models/file'
import { receiveMutationResult } from 'cozy-client/dist/store'

import { buildFileByIdQuery } from 'modules/queries'

/**
* Normalizes an object representing a CouchDB document
*
* Ensures existence of `_type`
*
* @public
* @param {CouchDBDocument} couchDBDoc - object representing the document
* @returns {CozyClientDocument} full normalized document
*/
const normalizeDoc = (couchDBDoc, doctype) => {
return {
id: couchDBDoc._id,
_type: doctype,
...couchDBDoc
}
}

/**
* DispatchChange
*
* @param {CozyClient} client CozyClient instane
* @param {Doctype} doctype Doctype of the document to update
* @param {CouchDBDocument} couchDBDoc Document to update
* @param {Mutation} mutationDefinitionCreator Mutation to apply
*/
const dispatchChange = (
client,
doctype,
couchDBDoc,
mutationDefinitionCreator
) => {
const data = normalizeDoc(couchDBDoc, doctype)
const response = {
data
}

const options = {}
client.dispatch(
receiveMutationResult(
client.generateRandomId(),
response,
options,
mutationDefinitionCreator(data)
)
)
}

const ensureFileHasPath = async (doc, client) => {
if (doc.path) return doc

const parentQuery = buildFileByIdQuery(doc.dir_id)
const parentResult = await client.fetchQueryAndGetFromState(parentQuery)

return ensureFilePath(doc, parentResult.data)
}

/**
* Component that subscribes to io.cozy.files document changes and keep the
* internal store updated. This is a copy of RealTimeQueries from cozy-client
* with a tweak to merge the changes with the existing document from the store.
* You can have more detail on the problematic we are solving here:
* https://github.com/cozy/cozy-client/issues/1412
*
* @param {object} options - Options
* @param {Doctype} options.doctype - The doctype to watch
* @returns {null} The component does not display anything.
*/
const FilesRealTimeQueries = ({
doctype = 'io.cozy.files',
computeDocBeforeDispatchCreate = ensureFileHasPath,
computeDocBeforeDispatchUpdate = ensureFileHasPath,
computeDocBeforeDispatchDelete = (doc, client) =>
ensureFileHasPath({ ...doc, _deleted: true }, client)
}) => {
const client = useClient()

useEffect(() => {
const realtime = client.plugins.realtime

if (!realtime) {
throw new Error(
'You must include the realtime plugin to use RealTimeQueries'
)
}

const dispatchCreate = async couchDBDoc => {
const doc = await computeDocBeforeDispatchCreate(couchDBDoc, client)
dispatchChange(client, doctype, doc, Mutations.createDocument)
}
const dispatchUpdate = async couchDBDoc => {
const doc = await computeDocBeforeDispatchUpdate(couchDBDoc, client)
dispatchChange(client, doctype, doc, Mutations.updateDocument)
}
const dispatchDelete = async couchDBDoc => {
const doc = await computeDocBeforeDispatchDelete(couchDBDoc, client)
dispatchChange(client, doctype, doc, Mutations.deleteDocument)
}

const subscribe = async () => {
await realtime.subscribe('created', doctype, dispatchCreate)
await realtime.subscribe('updated', doctype, dispatchUpdate)
await realtime.subscribe('deleted', doctype, dispatchDelete)
}
subscribe()

return () => {
realtime.unsubscribe('created', doctype, dispatchCreate)
realtime.unsubscribe('updated', doctype, dispatchUpdate)
realtime.unsubscribe('deleted', doctype, dispatchDelete)
}
}, [
client,
computeDocBeforeDispatchCreate,
computeDocBeforeDispatchDelete,
computeDocBeforeDispatchUpdate,
doctype
])

return null
}

export default memo(FilesRealTimeQueries)
5 changes: 2 additions & 3 deletions src/modules/views/Folder/FolderView.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React from 'react'
import { ModalManager } from 'react-cozy-helpers'

import { RealTimeQueries } from 'cozy-client'

import { NotFound } from 'components/Error/NotFound'
import FilesRealTimeQueries from 'components/FilesRealTimeQueries'
import { ModalStack } from 'lib/ModalContext'
import Main from 'modules/layout/Main'

const FolderView = ({ children, isNotFound }) => (
<Main>
<RealTimeQueries doctype="io.cozy.files" />
<FilesRealTimeQueries />
<ModalStack />
<ModalManager />
{isNotFound ? <NotFound /> : children}
Expand Down
5 changes: 2 additions & 3 deletions src/modules/views/OnlyOffice/Toolbar/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from 'react'

import { RealTimeQueries } from 'cozy-client'
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'

import FilesRealTimeQueries from 'components/FilesRealTimeQueries'
import { useRedirectLink } from 'hooks/useRedirectLink'
import { DOCTYPE_FILES } from 'lib/doctypes'
import { useOnlyOfficeContext } from 'modules/views/OnlyOffice/OnlyOfficeProvider'
import BackButton from 'modules/views/OnlyOffice/Toolbar/BackButton'
import FileIcon from 'modules/views/OnlyOffice/Toolbar/FileIcon'
Expand All @@ -31,7 +30,7 @@ const Toolbar = () => {

return (
<>
<RealTimeQueries doctype={DOCTYPE_FILES} />
<FilesRealTimeQueries />
<div className="u-flex u-flex-items-center u-flex-grow-1 u-ellipsis">
{!isMobile && (
<>
Expand Down

0 comments on commit 2ec1044

Please sign in to comment.