diff --git a/src/hooks/useAppBootstrap.js b/src/hooks/useAppBootstrap.js index c00d813a9..f2921b993 100644 --- a/src/hooks/useAppBootstrap.js +++ b/src/hooks/useAppBootstrap.js @@ -5,6 +5,7 @@ import { useEffect, useState } from 'react' import { deconstructCozyWebLinkWithSlug } from 'cozy-client' import { handleLogsDeepLink } from '/app/domain/logger/deeplinkHandler' +import { handleDbDeepLink } from '/pouchdb/deeplinkHandler' import { SentryCustomTags, setSentryTag } from '/libs/monitoring/Sentry' import { manageIconCache } from '/libs/functions/iconTable' import { getDefaultIconParams } from '/libs/functions/openApp' @@ -160,6 +161,10 @@ export const useAppBootstrap = client => { return } + if (handleDbDeepLink(url, client)) { + return + } + if (!client) { const action = parseOnboardLink(url) diff --git a/src/hooks/useAppBootstrap.spec.js b/src/hooks/useAppBootstrap.spec.js index 799fd4c5f..64935073d 100644 --- a/src/hooks/useAppBootstrap.spec.js +++ b/src/hooks/useAppBootstrap.spec.js @@ -50,6 +50,7 @@ jest.mock('/libs/RootNavigation', () => ({ jest.mock('./useSplashScreen', () => ({ useSplashScreen: () => ({ hideSplashScreen: mockHideSplashScreen }) })) +jest.mock('/app/theme/SplashScreenService', () => ({})) jest.mock('/libs/functions/openApp', () => ({ getDefaultIconParams: jest.fn().mockReturnValue({}) diff --git a/src/pouchdb/deeplinkHandler.ts b/src/pouchdb/deeplinkHandler.ts new file mode 100644 index 000000000..0867436a6 --- /dev/null +++ b/src/pouchdb/deeplinkHandler.ts @@ -0,0 +1,23 @@ +import CozyClient from 'cozy-client' + +import strings from '/constants/strings.json' +import { sendDbByEmail } from '/pouchdb/sendDbByEmail' + +export const handleDbDeepLink = (url: string, client?: CozyClient): boolean => { + if (isSendDbDeepLink(url)) { + void sendDbByEmail(client) + + return true + } + + return false +} + +const isSendDbDeepLink = (url: string): boolean => { + const deepLinks = [ + `${strings.COZY_SCHEME}senddb`, + `${strings.UNIVERSAL_LINK_BASE}/senddb` + ] + + return deepLinks.includes(url.toLowerCase()) +} diff --git a/src/pouchdb/sendDbByEmail.ts b/src/pouchdb/sendDbByEmail.ts new file mode 100644 index 000000000..60a7659bb --- /dev/null +++ b/src/pouchdb/sendDbByEmail.ts @@ -0,0 +1,112 @@ +import { Alert, PermissionsAndroid } from 'react-native' +import Mailer from 'react-native-mail' +import RNFS from 'react-native-fs' +import RNFetchBlob from 'rn-fetch-blob' +import DeviceInfo from 'react-native-device-info' + +import type CozyClient from 'cozy-client' +import Minilog from 'cozy-minilog' + +import { fetchSupportMail } from '/app/domain/logger/supportEmail' +import { + hideSplashScreen, + showSplashScreen, + splashScreens +} from '/app/theme/SplashScreenService' +import { getInstanceAndFqdnFromClient } from '/libs/client' + +const log = Minilog('🗒️ DB Mailer') + +export const sendDbByEmail = async (client?: CozyClient): Promise => { + log.info('Send DB by email') + + if (!client) { + log.info('SendDbByEmail called with no client, return') + return + } + try { + + const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE + await PermissionsAndroid.request(permission) + + const supportEmail = await fetchSupportMail(client) + + const { fqdn } = getInstanceAndFqdnFromClient(client) + + const instance = client.getStackClient().uri ?? 'not logged app' + + const subject = `DB file for ${instance}` + + const files = await RNFS.readDir(RNFS.DocumentDirectoryPath) + + const dbFiles = files.filter(f => f.name.startsWith(`${fqdn}_`)) + + const externalFiles = [] + for (const dbFile of dbFiles) { + const dirs = RNFetchBlob.fs.dirs + + const internalPath = dbFile.path + + const date = Number(new Date()) + const externalPath = `${dirs.DCIMDir}/DbFile_${dbFile.name}${date}.sqlite` + + await RNFS.copyFile(internalPath, externalPath) + + externalFiles.push({ + path: externalPath + }) + } + + await showSplashScreen(splashScreens.SEND_LOG_EMAIL) + log.info('Start email intent') + Mailer.mail( + { + subject: subject, + recipients: [supportEmail], + body: buildMessageBody(), + customChooserTitle: 'This is my new title', // Android only (defaults to "Send Mail") + isHTML: true, + attachments: externalFiles + }, + (error, event) => { + Alert.alert( + error, + event, + [ + { + text: 'Ok', + onPress: (): void => log.debug('OK: Email Error Response') + }, + { + text: 'Cancel', + onPress: (): void => log.debug('CANCEL: Email Error Response') + } + ], + { cancelable: true } + ) + } + ) + log.info('Did finish email intent') + await hideSplashScreen(splashScreens.SEND_LOG_EMAIL) + +} +catch (err) { + console.log('🍎 ERORR WHILE EMAIL', err.message) +} +} + +const buildMessageBody = (): string => { + const appVersion = DeviceInfo.getVersion() + const appBuild = DeviceInfo.getBuildNumber() + const bundle = DeviceInfo.getBundleId() + const deviceBrand = DeviceInfo.getBrand() + const deviceModel = DeviceInfo.getModel() + const os = DeviceInfo.getSystemName() + const version = DeviceInfo.getSystemVersion() + + const appInfo = `App info: ${appVersion} (${appBuild})` + const bundleInfo = `App bundle: ${bundle}` + const deviceInfo = `Device info: ${deviceBrand} ${deviceModel} ${os} ${version}` + + return `${appInfo}\n${bundleInfo}\n${deviceInfo}` +}