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: Add custom intentsApi prop to TriggerManager #1663

Merged
merged 6 commits into from
Jun 14, 2022
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
10 changes: 8 additions & 2 deletions packages/cozy-harvest-lib/src/components/AccountModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import KonnectorModalHeader from './KonnectorModalHeader'
import { withMountPointHistory } from './MountPointContext'
import withLocales from './hoc/withLocales'
import DialogContent from '@material-ui/core/DialogContent'
import { intentsApiProptype } from '../helpers/proptypes'

/**
* Takes an accountId and a list of accounts containing their respecting triggers
Expand Down Expand Up @@ -113,7 +114,8 @@ export class AccountModal extends Component {
initialActiveTab,
breakpoints: { isMobile },
showAccountSelection,
showNewAccountButton
showNewAccountButton,
intentsApi
} = this.props
const { trigger, account, fetching, error } = this.state
return (
Expand Down Expand Up @@ -167,6 +169,7 @@ export class AccountModal extends Component {
onAccountDeleted={onDismiss}
addAccount={() => replaceHistory('/new')}
showNewAccountButton={showNewAccountButton}
intentsApi={intentsApi}
/>
</DialogContent>
)}
Expand Down Expand Up @@ -208,7 +211,10 @@ AccountModal.propTypes = {
showAccountSelection: PropTypes.bool,

/** @type {Boolean} Whether to show the button to add a new account */
showNewAccountButton: PropTypes.bool
showNewAccountButton: PropTypes.bool,

// custom intents api. Can have fetchSessionCode, showInAppBrowser, closeInAppBrowser at the moment
intentsApi: intentsApiProptype
}

export default flow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
DialogCloseButton
} from 'cozy-ui/transpiled/react/CozyDialogs'
import DialogContent from '@material-ui/core/DialogContent'
import { intentsApiProptype } from '../helpers/proptypes'

const createDummyKonnectorFromAccount = account => {
return {
Expand All @@ -36,7 +37,12 @@ const createDummyKonnectorFromAccount = account => {
* we lack a lot of data to show an AccountModal (no trigger, no konnector,
* no account), it was implemented as a separate component.
*/
const DisconnectedModal = ({ accounts, onClose, initialActiveTab }) => {
const DisconnectedModal = ({
accounts,
onClose,
initialActiveTab,
intentsApi
}) => {
const { t } = useI18n()
// We keep the konnector in a ref so that when we remove all accounts,
// we still have a konnector to show the icon
Expand Down Expand Up @@ -79,6 +85,7 @@ const DisconnectedModal = ({ accounts, onClose, initialActiveTab }) => {
<Contracts
contracts={accounts}
konnector={konnectorRef.current}
intentsApi={intentsApi}
/>
</DialogContent>
) : (
Expand All @@ -97,7 +104,9 @@ DisconnectedModal.defaultProps = {
}

DisconnectedModal.propTypes = {
accounts: PropTypes.array.isRequired
accounts: PropTypes.array.isRequired,
/** custom intents api. Can have fetchSessionCode, showInAppBrowser, closeInAppBrowser at the moment */
intentsApi: intentsApiProptype
}

export default withLocales(DisconnectedModal)
40 changes: 30 additions & 10 deletions packages/cozy-harvest-lib/src/components/InAppBrowser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,40 @@ import { useEffect } from 'react'
import PropTypes from 'prop-types'
import { useWebviewIntent } from 'cozy-intent'
import logger from '../logger'
import { intentsApiProptype } from '../helpers/proptypes'

const InAppBrowser = ({ url, onClose }) => {
const InAppBrowser = ({ url, onClose, intentsApi = {} }) => {
const webviewIntent = useWebviewIntent()
const fetchSessionCode = intentsApi?.fetchSessionCode
? intentsApi?.fetchSessionCode
: () => webviewIntent.call('fetchSessionCode')
const showInAppBrowser = intentsApi?.showInAppBrowser
? intentsApi?.showInAppBrowser
: url => webviewIntent.call('showInAppBrowser', { url })
const closeInAppBrowser = intentsApi?.closeInAppBrowser
? intentsApi?.closeInAppBrowser
: () => webviewIntent.call('closeInAppBrowser')

const tokenParamName = intentsApi?.tokenParamName
? intentsApi?.tokenParamName
: 'session_code'

const isReady = Boolean(
webviewIntent ||
(intentsApi?.fetchSessionCode &&
intentsApi?.showInAppBrowser &&
intentsApi?.closeInAppBrowser)
)

useEffect(() => {
async function insideEffect() {
if (webviewIntent) {
if (isReady) {
try {
const sessionCode = await webviewIntent.call('fetchSessionCode')
const sessionCode = await fetchSessionCode()
logger.debug('got session code', sessionCode)
const iabUrl = new URL(url)
iabUrl.searchParams.append('session_code', sessionCode)
const result = await webviewIntent.call('showInAppBrowser', {
url: iabUrl.toString()
})
iabUrl.searchParams.append(tokenParamName, sessionCode)
const result = await showInAppBrowser(iabUrl.toString())
if (result?.type !== 'dismiss' && result?.type !== 'cancel') {
logger.error('Unexpected InAppBrowser result', result)
}
Expand All @@ -30,15 +49,16 @@ const InAppBrowser = ({ url, onClose }) => {
}
insideEffect()
return function cleanup() {
webviewIntent.call('closeInAppBrowser')
closeInAppBrowser()
}
}, [webviewIntent, url, onClose])
}, [isReady, url, onClose])
return null
}

InAppBrowser.propTypes = {
url: PropTypes.string.isRequired,
onClose: PropTypes.func
onClose: PropTypes.func,
intentsApi: intentsApiProptype
}

export default InAppBrowser
24 changes: 24 additions & 0 deletions packages/cozy-harvest-lib/src/components/InAppBrowser.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,28 @@ describe('InAppBrowser', () => {
unmount()
expect(webviewService.call).toHaveBeenNthCalledWith(2, 'closeInAppBrowser')
})

it('should work with custom intents api', async () => {
const url = 'https://test.url'
const intentsApi = {
fetchSessionCode: jest.fn().mockResolvedValue('custom_api_session_code'),
showInAppBrowser: jest.fn(),
closeInAppBrowser: jest.fn(),
tokenParamName: 'custom_token_name'
}
const { unmount } = render(
<InAppBrowser url={url} intentsApi={intentsApi} />
)

await waitFor(() => expect(intentsApi.showInAppBrowser).toHaveBeenCalled())

unmount()

await waitFor(() => expect(intentsApi.closeInAppBrowser).toHaveBeenCalled())
expect(intentsApi.fetchSessionCode).toHaveBeenCalledTimes(1)
expect(intentsApi.showInAppBrowser).toHaveBeenNthCalledWith(
1,
'https://test.url/?custom_token_name=custom_api_session_code'
)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { findKonnectorPolicy } from '../../../konnector-policies'
import { isFlagshipApp } from 'cozy-device-helper'
import InAppBrowser from '../../InAppBrowser'
import withLocales from '../../hoc/withLocales'
import { intentsApiProptype } from '../../../helpers/proptypes'

const BIContractActivationWindow = ({ konnector, account, t }) => {
const BIContractActivationWindow = ({ konnector, account, t, intentsApi }) => {
const [initialUrl, setInitialUrl] = useState(null)
const [isWindowVisible, setWindowVisible] = useState(false)
const [shouldRefreshContracts, setShouldRefreshContracts] = useState(false)
Expand Down Expand Up @@ -46,32 +47,36 @@ const BIContractActivationWindow = ({ konnector, account, t }) => {
}
}, [konnector, account, client, konnectorPolicy])

return (
konnectorPolicy.fetchContractSynchronizationUrl ? (
<ListItem>
<Button disabled={!initialUrl} onClick={() => setWindowVisible(true)}>
{t('contracts.handle-synchronization')}
</Button>
{isWindowVisible &&
(isFlagshipApp() ? (
<InAppBrowser url={initialUrl} onClose={onPopupClosed} />
) : (
<Popup
initialUrl={initialUrl}
width="800"
height="800"
onClose={onPopupClosed}
/>
))}
</ListItem>
) : null
)
return konnectorPolicy.fetchContractSynchronizationUrl ? (
<ListItem>
<Button disabled={!initialUrl} onClick={() => setWindowVisible(true)}>
{t('contracts.handle-synchronization')}
</Button>
{isWindowVisible &&
(isFlagshipApp() ? (
<InAppBrowser
url={initialUrl}
onClose={onPopupClosed}
intentsApi={intentsApi}
/>
) : (
<Popup
initialUrl={initialUrl}
width="800"
height="800"
onClose={onPopupClosed}
/>
))}
</ListItem>
) : null
}

BIContractActivationWindow.propTypes = {
t: PropTypes.func,
account: PropTypes.object,
konnector: PropTypes.object
konnector: PropTypes.object,
/** custom intents api. Can have fetchSessionCode, showInAppBrowser, closeInAppBrowser at the moment */
intentsApi: intentsApiProptype
}

export default withLocales(BIContractActivationWindow)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import withLocales from '../../hoc/withLocales'
import { getAccountLabel } from './bankAccountHelpers'
import EditContract from './EditContract'
import BIContractActivationWindow from './BiContractActivationWindow'
import { intentsApiProptype } from '../../../helpers/proptypes'

const makeContractsConn = ({ account }) => {
const doctype = 'io.cozy.bank.accounts'
Expand Down Expand Up @@ -94,7 +95,7 @@ const customHeaderPerDoctype = {
'io.cozy.bank.accounts': 'bankAccounts'
}

const DumbContracts = ({ contracts, account, konnector }) => {
const DumbContracts = ({ contracts, account, konnector, intentsApi }) => {
const { t } = useI18n()
const contractData = contracts.data ? contracts.data : contracts

Expand Down Expand Up @@ -122,7 +123,11 @@ const DumbContracts = ({ contracts, account, konnector }) => {
/>
)
})}
<BIContractActivationWindow konnector={konnector} account={account} />
<BIContractActivationWindow
konnector={konnector}
account={account}
intentsApi={intentsApi}
/>
</NavigationListSection>
</NavigationList>
</MuiCozyTheme>
Expand All @@ -139,7 +144,9 @@ DumbContracts.propTypes = {
/** Can be present if showing contracts still linked to an account/konnector/trigger */
account: PropTypes.object,
/** Can be present if showing contracts still linked to an account/konnector/trigger */
konnector: PropTypes.object
konnector: PropTypes.object,
/** custom intents api. Can have fetchSessionCode, showInAppBrowser, closeInAppBrowser at the moment */
intentsApi: intentsApiProptype
}

export const ContractsForAccount = compose(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { useTrackPage, useTracker } from '../../hoc/tracking'
import RedirectToAccountFormButton from '../../RedirectToAccountFormButton'

import { ContractsForAccount } from './Contracts'
import { intentsApiProptype } from '../../helpers/proptypes'

const tabMobileNavListStyle = { borderTop: 'none' }

Expand Down Expand Up @@ -74,7 +75,8 @@ const ConfigurationTab = ({
addAccount,
onAccountDeleted,
showNewAccountButton,
flow
flow,
intentsApi
}) => {
const { t } = useI18n()
const { isMobile } = useBreakpoints()
Expand Down Expand Up @@ -214,7 +216,11 @@ const ConfigurationTab = ({
) : null}
</NavigationListSection>

<ContractsForAccount konnector={konnector} account={account} />
<ContractsForAccount
konnector={konnector}
account={account}
intentsApi={intentsApi}
/>
</NavigationList>
{showNewAccountButton ? (
<div
Expand All @@ -238,7 +244,9 @@ ConfigurationTab.propTypes = {
account: PropTypes.object.isRequired,
error: PropTypes.object,
addAccount: PropTypes.func.isRequired,
onAccountDeleted: PropTypes.func.isRequired
onAccountDeleted: PropTypes.func.isRequired,
/** custom intents api. Can have fetchSessionCode, showInAppBrowser, closeInAppBrowser at the moment */
intentsApi: intentsApiProptype
}

export default ConfigurationTab
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import useDOMMutations from '../hooks/useDOMMutations'
import FlowProvider from '../FlowProvider'
import DataTab from './DataTab'
import ConfigurationTab from './ConfigurationTab'
import { intentsApiProptype } from '../helpers/proptypes'

const tabIndexes = {
data: 0,
Expand Down Expand Up @@ -51,15 +52,16 @@ const DumbKonnectorAccountTabs = props => {
// TODO rename to onAddAccount
addAccount,
showNewAccountButton,
flow
flow,
intentsApi
} = props
const { isMobile } = useBreakpoints()
const [tab, setTab] = useState(
initialActiveTab
? tabIndexes[initialActiveTab]
: hasLoginError
? tabIndexes.configuration
: tabIndexes.data
? tabIndexes.configuration
: tabIndexes.data
)
const handleTabChange = (ev, newTab) => setTab(newTab)

Expand Down Expand Up @@ -107,6 +109,7 @@ const DumbKonnectorAccountTabs = props => {
addAccount={addAccount}
onAccountDeleted={onAccountDeleted}
showNewAccountButton={showNewAccountButton}
intentsApi={intentsApi}
/>
</SwipeableViews>
</div>
Expand All @@ -132,7 +135,9 @@ KonnectorAccountTabs.propTypes = {
addAccount: PropTypes.func.isRequired,

/** @type {string} Can be used to force the initial tab */
initialActiveTab: PropTypes.oneOf(['configuration', 'data'])
initialActiveTab: PropTypes.oneOf(['configuration', 'data']),
/** custom intents api. Can have fetchSessionCode, showInAppBrowser, closeInAppBrowser at the moment */
intentsApi: intentsApiProptype
}

export default KonnectorAccountTabs
Loading