Skip to content

Commit

Permalink
feat: Add ScanDesktopActionsAlert component
Browse files Browse the repository at this point in the history
This component add an alert in Desktop mode
to warn of the existence of the Cozy Cloud app.
Allows you to install the app, via a QR Code or to hide this alert
  • Loading branch information
Merkur39 committed May 30, 2023
1 parent 9c3bff3 commit bda780c
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 4 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import PropTypes from 'prop-types'
import React from 'react'

import { IllustrationDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import Typography from 'cozy-ui/transpiled/react/Typography'

import QRCode from '../../../../assets/images/QRCode.png'
import appStoreIcon from '../../../../assets/images/appstore.png'
import playStoreIcon from '../../../../assets/images/playstore.png'
import { ANDROID_APP_URL, IOS_APP_URL } from '../../../../constants/const'

const QRCodeModal = ({ onClose }) => {
const { t } = useI18n()

return (
<IllustrationDialog
open
size="small"
transitionDuration={0}
disableGutters
onClose={onClose}
content={
<div
className="u-flex u-flex-column u-flex-items-center u-flex-justify-center u-pt-3 u-pb-2 u-ph-1-half"
data-testid="QRCodeModal"
>
<img src={QRCode} width="100%" alt="" aria-hidden />
<Typography gutterBottom variant="h3" color="textPrimary">
{t('QRCodeModal.title')}
</Typography>
<Typography
color="textSecondary"
className="u-ta-center"
dangerouslySetInnerHTML={{
__html: t('QRCodeModal.text', {
androidUrl: ANDROID_APP_URL,
androidIconSrc: playStoreIcon,
iosUrl: IOS_APP_URL,
iosIconSrc: appStoreIcon
})
}}
/>
</div>
}
/>
)
}

QRCodeModal.propTypes = {
onClose: PropTypes.func
}

export default QRCodeModal
Original file line number Diff line number Diff line change
@@ -1,28 +1,62 @@
import PropTypes from 'prop-types'
import React, { createRef } from 'react'

import { useClient, useQuery, hasQueryBeenLoaded } from 'cozy-client'
import log from 'cozy-logger'
import Button from 'cozy-ui/transpiled/react/Buttons'
import FileInput from 'cozy-ui/transpiled/react/FileInput'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import Icon from 'cozy-ui/transpiled/react/Icon'
import Divider from 'cozy-ui/transpiled/react/MuiCozyTheme/Divider'
import useEventListener from 'cozy-ui/transpiled/react/hooks/useEventListener'

import ScanDesktopActionsAlert from './ScanDesktopActionsAlert'
import { KEYS } from '../../../../constants/const'
import { SETTINGS_DOCTYPE } from '../../../../doctypes'
import { getAppSettings } from '../../../../helpers/queries'

const styleBtn = { color: 'var(--primaryTextColor)' }

const ScanDesktopActions = ({ onOpenFilePickerModal, onChangeFile }) => {
const { t } = useI18n()
const buttonRef = createRef()
const client = useClient()

const { data: settingsData, ...settingsQueryResult } = useQuery(
getAppSettings.definition,
getAppSettings.options
)
const isLoadedSettings = hasQueryBeenLoaded(settingsQueryResult)
const showAlert = isLoadedSettings
? settingsData[0].showScanDesktopActionsAlert ?? true
: true

const handleKeyDown = ({ key }) => {
if (key === KEYS.ENTER && buttonRef.current) {
buttonRef.current.click()
}
}

useEventListener(window, 'keydown', handleKeyDown)

const handleHideAlert = async () => {
if (isLoadedSettings) {
try {
await client.save({
...settingsData[0],
showScanDesktopActionsAlert: false,
_type: SETTINGS_DOCTYPE
})
} catch (error) {
log('error', 'Error when saving settings in ScanDesktopActions', error)
}
} else {
log(
'warn',
'Settings are not loaded when clicking to hide ScanDesktopActionsAlert'
)
}
}

return (
<>
<Button
Expand All @@ -48,6 +82,12 @@ const ScanDesktopActions = ({ onOpenFilePickerModal, onChangeFile }) => {
label={t('Scan.importPicFromDesktop')}
/>
</FileInput>
{showAlert && (
<div className="u-w-100 u-mv-1">
<Divider textAlign="center">{t('Scan.divider')}</Divider>
</div>
)}
{showAlert && <ScanDesktopActionsAlert onClose={handleHideAlert} />}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import { fireEvent, render } from '@testing-library/react'
import '@testing-library/jest-dom'
import { fireEvent, render, waitFor } from '@testing-library/react'
import React from 'react'

import { useQuery, hasQueryBeenLoaded } from 'cozy-client'

import ScanDesktopActions from './ScanDesktopActions'
import AppLike from '../../../../../test/components/AppLike'

const setup = ({ onOpenFilePickerModal, onChangeFile } = {}) => {
jest.mock('cozy-client/dist/hooks/useQuery', () => jest.fn())
jest.mock('cozy-client/dist/utils', () => ({
...jest.requireActual('cozy-client/dist/utils'),
hasQueryBeenLoaded: jest.fn()
}))

const setup = ({
onOpenFilePickerModal,
onChangeFile,
showScanDesktopActionsAlert,
clientSaveFn = jest.fn(),
isLoaded = true
} = {}) => {
const client = { save: clientSaveFn }
hasQueryBeenLoaded.mockReturnValue(isLoaded)
useQuery.mockReturnValue({
data: [
{
...(showScanDesktopActionsAlert != null
? { showScanDesktopActionsAlert }
: {})
}
]
})
return render(
<AppLike>
<AppLike client={client}>
<ScanDesktopActions
onOpenFilePickerModal={
onOpenFilePickerModal ? onOpenFilePickerModal : undefined
Expand Down Expand Up @@ -49,4 +75,49 @@ describe('ScanDesktopActions', () => {
fireEvent.change(inputFileButton)
expect(onChangeFileAction).toBeCalledTimes(1)
})

it('should display alert information if "showScanDesktopActionsAlert" is undefined', () => {
const { getByTestId } = setup()
const ScanDesktopActionsAlert = getByTestId('ScanDesktopActionsAlert')

expect(ScanDesktopActionsAlert).toBeInTheDocument()
})

it('should display alert information if "showScanDesktopActionsAlert" is true', () => {
const { getByTestId } = setup({ showScanDesktopActionsAlert: true })
const ScanDesktopActionsAlert = getByTestId('ScanDesktopActionsAlert')

expect(ScanDesktopActionsAlert).toBeInTheDocument()
})

it('should hide alert information if "showScanDesktopActionsAlert" is false', () => {
const { queryByTestId } = setup({ showScanDesktopActionsAlert: false })
const ScanDesktopActionsAlert = queryByTestId('ScanDesktopActionsAlert')

expect(ScanDesktopActionsAlert).toBeNull()
})

it('should call client.save when click on "No, thanks" button', async () => {
const clientSaveFn = jest.fn()
const { getByText } = setup({ clientSaveFn, isLoaded: true })
const hideButton = getByText('No, thanks')

fireEvent.click(hideButton)

await waitFor(() => {
expect(clientSaveFn).toBeCalledTimes(1)
})
})

it('should not call client.save when click on "No, thanks" button when settings is not loaded', async () => {
const clientSaveFn = jest.fn()
const { getByText } = setup({ clientSaveFn, isLoaded: false })
const hideButton = getByText('No, thanks')

fireEvent.click(hideButton)

await waitFor(() => {
expect(clientSaveFn).toBeCalledTimes(0)
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import PropTypes from 'prop-types'
import React, { useState } from 'react'

import Alert from 'cozy-ui/transpiled/react/Alert'
import AlertTitle from 'cozy-ui/transpiled/react/AlertTitle'
import Button from 'cozy-ui/transpiled/react/Buttons'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import Icon from 'cozy-ui/transpiled/react/Icon'

import QRCodeModal from './QRCodeModal'
import IlluScanner from '../../../../assets/icons/IlluScanner.svg'

const ScanDesktopActionsAlert = ({ onClose }) => {
const [showQRCodeModal, setShowQRCodeModal] = useState(false)

const { t } = useI18n()

const handleInstallApp = () => {
setShowQRCodeModal(true)
}

return (
<>
<Alert
block
icon={<Icon icon={IlluScanner} size={96} />}
action={
<>
<Button
component="a"
onClick={onClose}
variant="text"
label={t('ScanDesktopActionsAlert.actions.hide')}
/>
<Button
component="a"
onClick={handleInstallApp}
variant="text"
label={t('ScanDesktopActionsAlert.actions.install')}
/>
</>
}
data-testid="ScanDesktopActionsAlert"
>
<AlertTitle>{t('ScanDesktopActionsAlert.title')}</AlertTitle>
{t('ScanDesktopActionsAlert.text')}
</Alert>

{showQRCodeModal && (
<QRCodeModal onClose={() => setShowQRCodeModal(false)} />
)}
</>
)
}

ScanDesktopActionsAlert.propTypes = {
onClose: PropTypes.func
}

export default ScanDesktopActionsAlert
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import '@testing-library/jest-dom'
import { fireEvent, render } from '@testing-library/react'
import React from 'react'

import ScanDesktopActionsAlert from './ScanDesktopActionsAlert'
import AppLike from '../../../../../test/components/AppLike'

jest.mock('cozy-client/dist/utils', () => ({
...jest.requireActual('cozy-client/dist/utils'),
hasQueryBeenLoaded: jest.fn()
}))

const setup = ({ onCloseFn = jest.fn() } = {}) => {
return render(
<AppLike>
<ScanDesktopActionsAlert onClose={onCloseFn} />
</AppLike>
)
}

describe('ScanDesktopActionsAlert', () => {
it('should display QRCodeModal modal when click on "Install the app" button', () => {
const { getByText, getByTestId } = setup()
const installAppButton = getByText('Install the app')

fireEvent.click(installAppButton)

const QRCodeModal = getByTestId('QRCodeModal')

expect(QRCodeModal).toBeInTheDocument()
})

it('should call client.save when click on "No, thanks" button', () => {
const onCloseFn = jest.fn()
const { getByText } = setup({ onCloseFn })
const hideButton = getByText('No, thanks')

fireEvent.click(hideButton)

expect(onCloseFn).toBeCalledTimes(1)
})
})
12 changes: 12 additions & 0 deletions packages/cozy-mespapiers-lib/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,18 @@
"button": "Add manually"
}
},
"ScanDesktopActionsAlert": {
"title": "Scan with the Cozy Cloud app",
"text": "Install the Cozy Cloud application on your mobile to take a photo of your papers directly and extract the information automatically.",
"actions": {
"hide": "No, thanks",
"install": "Install the app"
}
},
"QRCodeModal": {
"title": "Scan the QR Code",
"text": "Or go directly to the <img src=%{iosIconSrc} /> <a href='%{iosUrl}' class='u-link' target='_blank' rel='noopener'>l’app-store</a><br>or <img src=%{androidIconSrc} /> <a href='%{androidUrl}' class='u-link' target='_blank' rel='noopener'>Google Play</a> to install the Cozy Cloud app"
},
"InstallAppModal": {
"title": "Install the app",
"text": "Install the Cozy Cloud app on your mobile to take photos of your papers directly.",
Expand Down
12 changes: 12 additions & 0 deletions packages/cozy-mespapiers-lib/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,18 @@
"button": "Ajouter manuellement"
}
},
"ScanDesktopActionsAlert": {
"title": "Scanner avec l’app Cozy Cloud",
"text": "Installer l’application Cozy Cloud sur votre mobile pour prendre directement vos papiers en photo et en extraire les informations automatiquement.",
"actions": {
"hide": "Non, merci",
"install": "Installer l'app"
}
},
"QRCodeModal": {
"title": "Scanner le QR Code",
"text": "Ou rendez vous directement sur <img src=%{iosIconSrc} /> <a href='%{iosUrl}' class='u-link' target='_blank' rel='noopener'>l’app-store</a><br>ou sur <img src=%{androidIconSrc} /> <a href='%{androidUrl}' class='u-link' target='_blank' rel='noopener'>Google Play</a> pour installer l’app Cozy Cloud"
},
"InstallAppModal": {
"title": "Installer l’application",
"text": "Installer l'application Cozy Cloud sur votre mobile pour prendre directement vos papiers en photo.",
Expand Down

0 comments on commit bda780c

Please sign in to comment.