Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Trigger replication on appstate changes and realtime events #1278

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"cozy-logger": "^1.10.0",
"cozy-minilog": "3.3.1",
"cozy-pouch-link": "^52.0.0",
"cozy-realtime": "^5.6.2",
"date-fns": "2.29.3",
"events": "^3.3.0",
"html-entities": "^2.3.3",
Expand Down
2 changes: 2 additions & 0 deletions src/@types/cozy-client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ declare module 'cozy-client' {
options?: QueryOptions
) => Promise<QueryResult>
links: CozyLink[]
plugins: Record<string, unknown>
registerPlugin: (plugin: unknown, options?: unknown) => void
}

export const createMockClient = (options?: ClientOptions): CozyClient =>
Expand Down
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
} from '/screens/home/hooks/useLauncherContext'
import LauncherView from '/screens/konnectors/LauncherView'
import { makeImportantFilesAvailableOfflineInBackground } from '/app/domain/io.cozy.files/importantFiles'
import { useOfflineReplicationOnRealtime } from '/app/domain/offline/hooks/useOfflineReplicationOnRealtime'
import { useShareFiles } from '/app/domain/osReceive/services/shareFilesService'
import { ClouderyOffer } from '/app/view/IAP/ClouderyOffer'
import { useDimensions } from '/libs/dimensions'
Expand Down Expand Up @@ -130,6 +131,7 @@ const App = ({ setClient }) => {
useNotifications()
useGeolocationTracking()
useCozyEnvironmentOverride()
useOfflineReplicationOnRealtime()
useOfflineDebugUniversalLinks(client)
usePerformancesUniversalLinks(client)

Expand Down
13 changes: 2 additions & 11 deletions src/app/domain/authentication/services/AuthService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Linking } from 'react-native'

import type CozyClient from 'cozy-client'
import Minilog from 'cozy-minilog'
import PouchLink from 'cozy-pouch-link'

import { asyncLogoutNoClient } from '/app/domain/authentication/utils/asyncLogoutNoClient'
import { triggerPouchReplication } from '/app/domain/offline/utils'

export const authLogger = Minilog('AuthService')
let clientInstance: CozyClient | null = null
Expand Down Expand Up @@ -34,8 +34,7 @@ const handleLogin = (): void => {
authLogger.info('Debounce replication')
if (clientInstance === null) throw new Error('No client instance set')

const pouchLink = getPouchLink(clientInstance)
pouchLink?.startReplicationWithDebounce()
triggerPouchReplication(clientInstance)
} catch (error) {
authLogger.error('Error while handling login', error)
}
Expand All @@ -47,11 +46,3 @@ export const startListening = (client: CozyClient): void => {
clientInstance.on('revoked', handleTokenError)
clientInstance.on('login', handleLogin)
}

const getPouchLink = (client?: CozyClient): PouchLink | null => {
if (!client) {
return null
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return client.links.find(link => link instanceof PouchLink) || null
}
54 changes: 54 additions & 0 deletions src/app/domain/offline/hooks/useOfflineReplicationOnRealtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useEffect } from 'react'

import { useClient } from 'cozy-client'
import type { CozyClientDocument } from 'cozy-client/types/types'
import Minilog from 'cozy-minilog'

import { triggerPouchReplication } from '/app/domain/offline/utils'
import { offlineDoctypes } from '/pouchdb/getLinks'

const log = Minilog('📶 useOfflineReplicationOnRealtime')

export const useOfflineReplicationOnRealtime = (): void => {
const client = useClient()

useEffect(() => {
if (!client) return

// @ts-expect-error client.plugins is not typed
const realtime = client.plugins.realtime as CozyRealtime

const triggerReplication =
(verb: string) =>
(doc: CozyClientDocument): void => {
const docInfo = `${verb} ${doc._type ?? ''} ${doc._id ?? ''}`
log.debug(`Trigger replication from realtime event (${docInfo})`)
triggerPouchReplication(client)
}

const triggerReplicationCreated = triggerReplication('created')
const triggerReplicationUpdated = triggerReplication('updated')
const triggerReplicationDeleted = triggerReplication('deleted')

offlineDoctypes.forEach(doctype => {
realtime.subscribe('created', doctype, triggerReplicationCreated)
realtime.subscribe('updated', doctype, triggerReplicationUpdated)
realtime.subscribe('deleted', doctype, triggerReplicationDeleted)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize we trigger a full replication on all doctypes for any realtime change, while we probably should only replicate for the concerned doctype, to save resources. But this is an improvement that can be made for later

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we will have to implement something to handle that scenario. For now the replication method always replicate all pouches and no mechanism has been implemented to debounce replications separately.

})

return () => {
offlineDoctypes.forEach(doctype => {
realtime.unsubscribe('created', doctype, triggerReplicationCreated)
realtime.unsubscribe('updated', doctype, triggerReplicationUpdated)
realtime.unsubscribe('deleted', doctype, triggerReplicationDeleted)
})
}
})
}

interface CozyRealtime {
subscribe: (event: string, type: string, handler: Subscription) => void
unsubscribe: (event: string, type: string, handler: Subscription) => void
}

type Subscription = (doc: CozyClientDocument) => void
19 changes: 19 additions & 0 deletions src/app/domain/offline/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import CozyClient from 'cozy-client'
import Minilog from 'cozy-minilog'
import PouchLink from 'cozy-pouch-link'

const log = Minilog('📶 Offline utils')

export const triggerPouchReplication = (client?: CozyClient): void => {
log.debug('Trigger PouchReplication (debounce)')
const pouchLink = getPouchLink(client)
pouchLink?.startReplicationWithDebounce()
}

export const getPouchLink = (client?: CozyClient): PouchLink | null => {
if (!client) {
return null
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return client.links.find(link => link instanceof PouchLink) || null
}
9 changes: 6 additions & 3 deletions src/hooks/useGlobalAppState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { showSplashScreen, splashScreens } from '/app/theme/SplashScreenService'
import { handleSecurityFlowWakeUp } from '/app/domain/authorization/services/SecurityService'
import { devlog } from '/core/tools/env'
import { synchronizeDevice } from '/app/domain/authentication/services/SynchronizeService'
import { triggerPouchReplication } from '/app/domain/offline/utils'

const log = Minilog('useGlobalAppState')

Expand Down Expand Up @@ -54,9 +55,11 @@ const onStateChange = (
if (isGoingToSleep(nextAppState)) handleSleep()

if (isGoingToWakeUp(nextAppState)) {
Promise.all([handleWakeUp(client), synchronizeDevice(client)]).catch(
reason => log.error('Failed when waking up', reason)
)
Promise.all([
handleWakeUp(client),
synchronizeDevice(client),
triggerPouchReplication(client)
]).catch(reason => log.error('Failed when waking up', reason))
}

appState = nextAppState
Expand Down
2 changes: 2 additions & 0 deletions src/libs/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CozyClient from 'cozy-client'
// @ts-ignore
import flag from 'cozy-flags'
import Minilog from 'cozy-minilog'
import { RealtimePlugin } from 'cozy-realtime'

import { normalizeFqdn } from './functions/stringHelpers'

Expand Down Expand Up @@ -77,6 +78,7 @@ export const getClient = async () => {
token
})

await client.registerPlugin(RealtimePlugin)
await client.registerPlugin(flag.plugin)
await client.plugins.flags.initializing

Expand Down
3 changes: 3 additions & 0 deletions src/libs/clientHelpers/createClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import CozyClient from 'cozy-client'
import flag from 'cozy-flags'
// @ts-expect-error cozy-realtime is not typed yet
import { RealtimePlugin } from 'cozy-realtime'

import { CozyClientPerformanceApi } from '/app/domain/performances/measure'
import strings from '/constants/strings.json'
Expand Down Expand Up @@ -69,6 +71,7 @@ export const finalizeClientCreation = async (

const registerPlugins = async (client: CozyClient): Promise<void> => {
await client.registerPlugin(flag.plugin, null)
await client.registerPlugin(RealtimePlugin, null)
}

const initializePlugins = async (client: CozyClient): Promise<void> => {
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8768,6 +8768,13 @@ cozy-pouch-link@^52.0.0:
pouchdb-browser "^7.2.2"
pouchdb-find "^7.2.2"

cozy-realtime@^5.6.2:
version "5.6.2"
resolved "https://registry.yarnpkg.com/cozy-realtime/-/cozy-realtime-5.6.2.tgz#08f82f6d19a7e7a8f288c46d3cb0e8ddddb9f17f"
integrity sha512-ev5a7PthtswJAuxQeJnrwl1jTQ+vLnBhtwkMLIP+7tGd0m3G4nEPgXy0iTuNt4VPBtItrPrNpHdEbMKR9XGgFQ==
dependencies:
"@cozy/minilog" "^1.0.0"

cozy-stack-client@^52.0.0:
version "52.0.0"
resolved "https://registry.yarnpkg.com/cozy-stack-client/-/cozy-stack-client-52.0.0.tgz#b146997637facc8de55033a84fd7e4b30ea4533d"
Expand Down
Loading