Skip to content

Commit

Permalink
feat(mespapiers): Add ScanFlagshipActions component
Browse files Browse the repository at this point in the history
Addition of actions on the Scan step,
if the application is used with the flagship app
  • Loading branch information
Merkur39 committed May 19, 2023
1 parent a2391aa commit c83b72b
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 14 deletions.
11 changes: 9 additions & 2 deletions packages/cozy-mespapiers-lib/src/components/ModelSteps/Scan.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import IlluGenericNewPage from '../../assets/icons/IlluGenericNewPage.svg'
import { PaperDefinitionsStepPropTypes } from '../../constants/PaperDefinitionsPropTypes'
import CompositeHeader from '../CompositeHeader/CompositeHeader'

const Scan = ({ currentStep, onChangeFile, onChangeFilePicker }) => {
const Scan = ({
currentStep,
onChangeFile,
onChangeFilePicker,
onOpenFlagshipScan
}) => {
const { t } = useI18n()
const { illustration, text } = currentStep

Expand All @@ -31,6 +36,7 @@ const Scan = ({ currentStep, onChangeFile, onChangeFilePicker }) => {
<ScanActionsWrapper
onChangeFile={onChangeFile}
openFilePickerModal={() => setIsFilePickerModalOpen(true)}
onOpenFlagshipScan={onOpenFlagshipScan}
/>
</DialogActions>

Expand All @@ -47,7 +53,8 @@ const Scan = ({ currentStep, onChangeFile, onChangeFilePicker }) => {
Scan.propTypes = {
currentStep: PaperDefinitionsStepPropTypes,
onChangeFile: PropTypes.func,
onChangeFilePicker: PropTypes.func
onChangeFilePicker: PropTypes.func,
onOpenFlagshipScan: PropTypes.func
}

export default Scan
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import PropTypes from 'prop-types'
import React from 'react'
import React, { useState, useEffect } from 'react'

import { isMobile } from 'cozy-device-helper'
import { isFlagshipApp, isMobile } from 'cozy-device-helper'
import flag from 'cozy-flags'
import { useWebviewIntent } from 'cozy-intent'

import ScanDesktopActions from './ScanDesktopActions'
import ScanFlagshipActions from './ScanFlagshipActions'
import ScanMobileActions from './ScanMobileActions'

const ScanActionsWrapper = props => {
const [isFlagshipScanAvailable, setIsFlagshipScanAvailable] = useState(false)
const webviewIntent = useWebviewIntent()

useEffect(() => {
const checkScanDocument = async () => {
const isAvailable = await webviewIntent.call('isScannerAvailable')
setIsFlagshipScanAvailable(isAvailable)
}
webviewIntent && checkScanDocument()
}, [webviewIntent])

if ((isFlagshipApp() || flag('flagship.debug')) && isFlagshipScanAvailable) {
return <ScanFlagshipActions {...props} />
}

if (isMobile()) {
return <ScanMobileActions {...props} />
}
Expand All @@ -17,6 +35,7 @@ const ScanActionsWrapper = props => {
ScanActionsWrapper.propTypes = {
onChangeFile: PropTypes.func,
openFilePickerModal: PropTypes.func,
onOpenFlagshipScan: PropTypes.func
}

export default ScanActionsWrapper
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { render } from '@testing-library/react'
/* eslint-disable jest/no-focused-tests */
import { render, waitFor } from '@testing-library/react'
import React from 'react'

import { isMobile } from 'cozy-device-helper'
import { isMobile, isFlagshipApp } from 'cozy-device-helper'
import { useWebviewIntent } from 'cozy-intent'

import ScanWrapper from './ScanWrapper'
import AppLike from '../../../test/components/AppLike'
Expand All @@ -19,9 +21,14 @@ const mockFormData = ({ metadata = {}, data = [], contacts = [] } = {}) => ({
data,
contacts
})
jest.mock('cozy-intent', () => ({
...jest.requireActual('cozy-intent'),
useWebviewIntent: jest.fn()
}))
jest.mock('cozy-device-helper', () => ({
...jest.requireActual('cozy-device-helper'),
isMobile: jest.fn()
isMobile: jest.fn(),
isFlagshipApp: jest.fn()
}))
jest.mock('../Hooks/useFormData')
/* eslint-disable react/display-name */
Expand All @@ -31,19 +38,30 @@ jest.mock('../CompositeHeader/CompositeHeader', () => () => (
jest.mock('./AcquisitionResult', () => () => (
<div data-testid="AcquisitionResult" />
))
jest.mock('../ModelSteps/ScanMobileActions', () => () => (
jest.mock('./ScanMobileActions', () => () => (
<div data-testid="ScanMobileActions" />
))
jest.mock('../ModelSteps/ScanDesktopActions', () => () => (
jest.mock('./ScanFlagshipActions', () => () => (
<div data-testid="ScanFlagshipActions" />
))
jest.mock('./ScanDesktopActions', () => () => (
<div data-testid="ScanDesktopActions" />
))
/* eslint-enable react/display-name */

const setup = ({
setFormData = jest.fn(),
formData = mockFormData(),
currentStep = mockCurrentStep()
currentStep = mockCurrentStep(),
isMobileMock = false,
isFlagshipAppMock = false,
isScannerAvailable = false
} = {}) => {
isMobile.mockReturnValue(isMobileMock || isFlagshipAppMock)
isFlagshipApp.mockReturnValue(isFlagshipAppMock)
useWebviewIntent.mockReturnValue({
call: jest.fn().mockResolvedValue(isScannerAvailable)
})
useFormData.mockReturnValue({
setFormData,
formData
Expand All @@ -66,14 +84,33 @@ describe('Scan component:', () => {
})

it('ScanMobileActions component must be displayed if is on Mobile', () => {
isMobile.mockReturnValue(true)
const { queryByTestId } = setup()
const { queryByTestId } = setup({ isMobileMock: true })

expect(queryByTestId('ScanMobileActions')).toBeTruthy()
})

it('ScanMobileActions component must be displayed if is on flagship app but Scanner is unavailable', async () => {
const { queryByTestId } = setup({
isFlagshipAppMock: true,
isScannerAvailable: false
})

await waitFor(() => {
expect(queryByTestId('ScanMobileActions')).toBeTruthy()
})
})
it('ScanFlagshipActions component must be displayed if is on flagship app & Scanner is available', async () => {
const { queryByTestId } = setup({
isFlagshipAppMock: true,
isScannerAvailable: true
})

await waitFor(() => {
expect(queryByTestId('ScanFlagshipActions')).toBeTruthy()
})
})

it('ScanDesktopActions component must be displayed if is on Desktop', () => {
isMobile.mockReturnValue(false)
const { queryByTestId } = setup()

expect(queryByTestId('ScanDesktopActions')).toBeTruthy()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import PropTypes from 'prop-types'
import React from 'react'

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'

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

const ScanFlagshipActions = ({
openFilePickerModal,
onChangeFile,
onOpenFlagshipScan
}) => {
const { t } = useI18n()

return (
<>
<div>
<Divider textAlign="center" className="u-mv-1">
{t('Scan.divider')}
</Divider>
<Button
variant="secondary"
style={styleBtn}
onClick={openFilePickerModal}
startIcon={<Icon icon="folder-moveto" />}
label={t('Scan.selectPicFromCozy')}
data-testid="selectPicFromCozy-btn"
/>
<FileInput
onChange={onChangeFile}
className="u-w-100 u-ml-0"
onClick={e => e.stopPropagation()}
accept={'image/*,.pdf'}
data-testid="importPicFromMobile-btn"
>
<Button
variant="secondary"
component="a"
style={styleBtn}
startIcon={<Icon icon="phone-upload" />}
fullWidth
className="u-m-0"
label={t('Scan.importPicFromMobile')}
/>
</FileInput>
</div>
<Button
onClick={onOpenFlagshipScan}
startIcon={<Icon icon="camera" />}
fullWidth
className="u-m-0"
label={t('Scan.takePic')}
data-testid="importPicFromFlagshipScan-btn"
/>
</>
)
}

ScanFlagshipActions.propTypes = {
onChangeFile: PropTypes.func,
openFilePickerModal: PropTypes.func,
onOpenFlagshipScan: PropTypes.func
}

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

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

const setup = ({
onOpenFilePickerModal,
onChangeFile,
onOpenFlagshipScan
} = {}) => {
return render(
<AppLike>
<ScanFlagshipActions
onOpenFilePickerModal={
onOpenFilePickerModal ? onOpenFilePickerModal : undefined
}
onChangeFile={onChangeFile ? onChangeFile : undefined}
onOpenFlagshipScan={onOpenFlagshipScan ? onOpenFlagshipScan : undefined}
/>
</AppLike>
)
}

describe('ScanFlagshipActions', () => {
it('should called onOpenFilePickerModal function', () => {
const onOpenFilePickerModal = jest.fn()
const { getByTestId } = setup({
onOpenFilePickerModal
})

const submitButton = getByTestId('selectPicFromCozy-btn')

fireEvent.click(submitButton)

expect(onOpenFilePickerModal).toBeCalledTimes(1)
})

it('should have one input with type file attribute', () => {
const { container } = setup()
const inputFileButtons = container.querySelectorAll('input[type="file"]')

expect(inputFileButtons).toHaveLength(1)
})

it('should called onOpenFlagshipScan function', () => {
const onOpenFlagshipScan = jest.fn()
const { getByTestId } = setup({
onOpenFlagshipScan
})

const importPicFromFlagshipScanButton = getByTestId(
'importPicFromFlagshipScan-btn'
)

fireEvent.click(importPicFromFlagshipScanButton)

expect(onOpenFlagshipScan).toBeCalledTimes(1)
})
})
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React, { useState, useEffect } from 'react'

import { useClient, models } from 'cozy-client'
import { useWebviewIntent } from 'cozy-intent'
import log from 'cozy-logger'
import Alerter from 'cozy-ui/transpiled/react/Alerter'
import FilePicker from 'cozy-ui/transpiled/react/FilePicker'

import AcquisitionResult from './AcquisitionResult'
import Scan from './Scan'
import { isFileAlreadySelected } from './helpers'
import { isFileAlreadySelected, makeFileFromImageSource } from './helpers'
import { PaperDefinitionsStepPropTypes } from '../../constants/PaperDefinitionsPropTypes'
import { makeBlobWithCustomAttrs } from '../../helpers/makeBlobWithCustomAttrs'
import { useFormData } from '../Hooks/useFormData'
Expand All @@ -24,6 +27,7 @@ const ScanWrapper = ({ currentStep }) => {
const { formData, setFormData } = useFormData()
const { stepIndex, multipage, page } = currentStep
const [currentFile, setCurrentFile] = useState(null)
const webviewIntent = useWebviewIntent()

const onChangeFile = file => {
if (file) {
Expand Down Expand Up @@ -61,6 +65,20 @@ const ScanWrapper = ({ currentStep }) => {
}
}

const onOpenFlagshipScan = async () => {
try {
const base64 = await webviewIntent.call('scanDocument')
const file = await makeFileFromImageSource({
imageSrc: `data:image/png;base64,${base64}`,
imageName: 'flagshipScan',
imageType: 'image/png'
})
onChangeFile(file)
} catch (error) {
log('error', `Flagship scan error: ${error}`)
}
}

useEffect(() => {
const data = formData.data.filter(data => data.stepIndex === stepIndex)
const { file } = data[data.length - 1] || {}
Expand All @@ -85,6 +103,7 @@ const ScanWrapper = ({ currentStep }) => {
currentStep={currentStep}
onChangeFile={onChangeFile}
onChangeFilePicker={onChangeFilePicker}
onOpenFlagshipScan={onOpenFlagshipScan}
/>
)
}
Expand Down
32 changes: 32 additions & 0 deletions packages/cozy-mespapiers-lib/src/components/ModelSteps/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,35 @@ export const isSameFile = (currentFile, file) => {
}
return false
}

/**
* Make a File object from a base64 string or an url
*
* @param {Object} options
* @param {string} options.imageSrc - Image url or base64 string
* @param {string} options.imageName - Image name
* @param {string} options.imageType - Image type
* @returns {Promise<File>}
* @example
* const file = await makeFileFromString({ imageSrc: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAA', imageName: 'logo.png', imageType: 'image/png' })
*/
export const makeFileFromImageSource = async ({
imageSrc,
imageName,
imageType
} = {}) => {
try {
if (!imageSrc || !imageName || !imageType) {
return null
}
const resp = await fetch(imageSrc)
const blob = await resp.blob()
const newFile = new File([blob], imageName, {
type: imageType
})

return newFile
} catch (error) {
return null
}
}

0 comments on commit c83b72b

Please sign in to comment.