Skip to content

Commit

Permalink
feat: Configure CozyClient to use CozyPouchLink
Browse files Browse the repository at this point in the history
We want the Flagship app to work when offline

To make this possible we configure cozy-client with CozyPouchLink which
role will be to synchronize necessary doctypes into a local PouchDB and
serve them from it instead of from the cozy-stack when the device is
offline

For now the list of synchronized doctypes is hardcoded but in the
future we expect to implement a dynamic list based on cozy-apps'
manifests

Related PR: cozy/cozy-client#1507
  • Loading branch information
Ldoppea committed Aug 26, 2024
1 parent 8c64abb commit 31f4803
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 3 deletions.
5 changes: 4 additions & 1 deletion src/libs/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export {
} from '/libs/clientHelpers/initClient'
export { call2FAInitClient } from '/libs/clientHelpers/twoFactorAuthentication'
import { CozyPersistedStorageKeys, getData } from '/libs/localStore/storage'
import { getLinks } from '/pouchdb/getLinks'

const log = Minilog('LoginScreen')

Expand All @@ -44,14 +45,16 @@ export const getClient = async () => {
return false
}
const { uri, oauthOptions, token } = oauthData
const links = getLinks()
const client = new CozyClient({
uri,
oauth: { token },
oauthOptions,
appMetadata: {
slug: 'flagship',
version: packageJSON.version
}
},
links
})
listenTokenRefresh(client)
client.getStackClient().setOAuthOptions(oauthOptions)
Expand Down
3 changes: 2 additions & 1 deletion src/libs/client.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ describe('client', () => {
appMetadata: {
slug: 'flagship',
version: packageJSON.version
}
},
links: expect.anything()
})
})

Expand Down
6 changes: 5 additions & 1 deletion src/libs/clientHelpers/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import googleServicesJson from '/../android/app/src/prod/google-services.json'
import packageJSON from '../../../package.json'

import { startListening } from '/app/domain/authentication/services/AuthService'
import { getLinks } from '/pouchdb/getLinks'

/**
* Create a CozyClient for the given Cozy instance and register it
Expand All @@ -21,6 +22,8 @@ import { startListening } from '/app/domain/authentication/services/AuthService'
* @returns {CozyClient} - The created and registered CozyClient
*/
export const createClient = async (instance: string): Promise<CozyClient> => {
const links = getLinks()

const options = {
scope: ['*'],
oauth: {
Expand All @@ -37,7 +40,8 @@ export const createClient = async (instance: string): Promise<CozyClient> => {
appMetadata: {
slug: 'flagship',
version: packageJSON.version
}
},
links
}

const client = new CozyClient(options)
Expand Down
39 changes: 39 additions & 0 deletions src/pouchdb/getLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { platformReactNative } from '/pouchdb/platformReactNative'

import { default as PouchLink } from 'cozy-pouch-link'

export const offlineDoctypes = [
// cozy-home
'io.cozy.accounts',
'io.cozy.apps',
'io.cozy.contacts',
'io.cozy.files',
'io.cozy.files.shortcuts',
'io.cozy.home.settings',
'io.cozy.jobs',
'io.cozy.konnectors',
'io.cozy.settings',
'io.cozy.apps.suggestions',
'io.cozy.triggers',
'io.cozy.apps_registry',

// mespapiers
'io.cozy.bills',
'io.cozy.sharings',
'io.cozy.mespapiers.settings',
'io.cozy.permissions'
]

export const getLinks = () => {
const pouchLinkOptions = {
doctypes: offlineDoctypes,
initialSync: true,
platform: platformReactNative
}

const pouchLink = new PouchLink({
...pouchLinkOptions
})

return [pouchLink]
}
34 changes: 34 additions & 0 deletions src/pouchdb/platformReactNative.appState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import EventEmitter from 'events'

import { AppState, AppStateStatus, NativeEventSubscription } from 'react-native'

import Minilog from 'cozy-minilog'

const log = Minilog('🛋️ PlatormReactNative.appState')

let appState = AppState.currentState
let appStateHandler: NativeEventSubscription | undefined = undefined

export const listenAppState = (eventEmitter: EventEmitter): void => {
appStateHandler = AppState.addEventListener('change', nextAppState => {
log.debug('🛋️ AppState event', nextAppState)
if (isGoingToSleep(nextAppState)) {
eventEmitter.emit('resume')
}
if (isGoingToWakeUp(nextAppState)) {
eventEmitter.emit('pause')
}

appState = nextAppState
})
}

export const stopListeningAppState = (): void => {
appStateHandler?.remove()
}

const isGoingToSleep = (nextAppState: AppStateStatus): boolean =>
Boolean(appState.match(/active/) && nextAppState === 'background')

const isGoingToWakeUp = (nextAppState: AppStateStatus): boolean =>
Boolean(appState.match(/background/) && nextAppState === 'active')
28 changes: 28 additions & 0 deletions src/pouchdb/platformReactNative.events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { EventEmitter } from 'events'

import { listenAppState } from '/pouchdb/platformReactNative.appState'
import { listenNetInfo } from '/pouchdb/platformReactNative.netInfo'

export const pouchDbEmitter = new EventEmitter()

const listenPouchEvents = (): void => {
listenAppState(pouchDbEmitter)
listenNetInfo(pouchDbEmitter)
}

listenPouchEvents()

export const events = {
addEventListener: (
eventName: string,
handler: (...args: unknown[]) => void
): void => {
pouchDbEmitter.addListener(eventName, handler)
},
removeEventListener: (
eventName: string,
handler: (...args: unknown[]) => void
): void => {
pouchDbEmitter.removeListener(eventName, handler)
}
}
5 changes: 5 additions & 0 deletions src/pouchdb/platformReactNative.isOnline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NetService } from '/libs/services/NetService'

export const isOnline = (): Promise<boolean> => {
return NetService.isConnected()
}
24 changes: 24 additions & 0 deletions src/pouchdb/platformReactNative.netInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import EventEmitter from 'events'

import NetInfo, { NetInfoSubscription } from '@react-native-community/netinfo'

import Minilog from 'cozy-minilog'

const log = Minilog('🛋️ PlatormReactNative.netInfo')

let netInfoHandler: NetInfoSubscription | undefined = undefined

export const listenNetInfo = (eventEmitter: EventEmitter): void => {
netInfoHandler = NetInfo.addEventListener(state => {
log.debug('🛋️ NetInfo event', state.isConnected)
if (state.isConnected) {
eventEmitter.emit('online')
} else {
eventEmitter.emit('offline')
}
})
}

export const stopListeningNetInfo = (): void => {
netInfoHandler?.()
}
14 changes: 14 additions & 0 deletions src/pouchdb/platformReactNative.storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import AsyncStorage from '@react-native-async-storage/async-storage'

export const storage = {
getItem: async (key: string): Promise<string | null> => {
return AsyncStorage.getItem(key)
},
setItem: async (key: string, value: string | undefined): Promise<void> => {
if (value === undefined) return
return AsyncStorage.setItem(key, value)
},
removeItem: async (key: string): Promise<void> => {
return AsyncStorage.removeItem(key)
}
}
12 changes: 12 additions & 0 deletions src/pouchdb/platformReactNative.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { events } from '/pouchdb/platformReactNative.events'
import { isOnline } from '/pouchdb/platformReactNative.isOnline'
import { storage } from '/pouchdb/platformReactNative.storage'
import PouchDB from '/pouchdb/pouchdb'

export const platformReactNative = {
storage,
events,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
pouchAdapter: PouchDB,
isOnline
}

0 comments on commit 31f4803

Please sign in to comment.