mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 11:42:54 -05:00
Compare commits
8 Commits
react-quer
...
mobile/lin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19285ed594 | ||
|
|
62fa79effc | ||
|
|
89cea7ecc9 | ||
|
|
465a3a3fa5 | ||
|
|
d1d3e360f5 | ||
|
|
bfa8115452 | ||
|
|
7ebf687a96 | ||
|
|
fb2ec46981 |
@@ -2,6 +2,8 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { useTranslation, Trans } from 'react-i18next';
|
import { useTranslation, Trans } from 'react-i18next';
|
||||||
|
|
||||||
import { Button } from '@actual-app/components/button';
|
import { Button } from '@actual-app/components/button';
|
||||||
|
import { useResponsive } from '@actual-app/components/hooks/useResponsive';
|
||||||
|
import { Stack } from '@actual-app/components/stack';
|
||||||
import { Text } from '@actual-app/components/text';
|
import { Text } from '@actual-app/components/text';
|
||||||
import { theme } from '@actual-app/components/theme';
|
import { theme } from '@actual-app/components/theme';
|
||||||
import { Tooltip } from '@actual-app/components/tooltip';
|
import { Tooltip } from '@actual-app/components/tooltip';
|
||||||
@@ -38,6 +40,7 @@ import {
|
|||||||
Cell,
|
Cell,
|
||||||
} from '@desktop-client/components/table';
|
} from '@desktop-client/components/table';
|
||||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||||
|
import { useFormat } from '@desktop-client/hooks/useFormat';
|
||||||
import { closeModal } from '@desktop-client/modals/modalsSlice';
|
import { closeModal } from '@desktop-client/modals/modalsSlice';
|
||||||
import { useDispatch } from '@desktop-client/redux';
|
import { useDispatch } from '@desktop-client/redux';
|
||||||
|
|
||||||
@@ -107,8 +110,12 @@ export function SelectLinkedAccountsModal({
|
|||||||
}, [externalAccounts, syncSource, requisitionId]);
|
}, [externalAccounts, syncSource, requisitionId]);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { isNarrowWidth } = useResponsive();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const localAccounts = useAccounts().filter(a => a.closed === 0);
|
const localAccounts = useAccounts().filter(a => a.closed === 0);
|
||||||
|
const [draftLinkAccounts] = useState<Map<string, 'linking' | 'unlinking'>>(
|
||||||
|
new Map(),
|
||||||
|
);
|
||||||
const [chosenAccounts, setChosenAccounts] = useState<Record<string, string>>(
|
const [chosenAccounts, setChosenAccounts] = useState<Record<string, string>>(
|
||||||
() => {
|
() => {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
@@ -216,133 +223,216 @@ export function SelectLinkedAccountsModal({
|
|||||||
|
|
||||||
if (localAccountId) {
|
if (localAccountId) {
|
||||||
updatedAccounts[externalAccount.account_id] = localAccountId;
|
updatedAccounts[externalAccount.account_id] = localAccountId;
|
||||||
|
draftLinkAccounts.set(externalAccount.account_id, 'linking');
|
||||||
} else {
|
} else {
|
||||||
delete updatedAccounts[externalAccount.account_id];
|
delete updatedAccounts[externalAccount.account_id];
|
||||||
|
draftLinkAccounts.set(externalAccount.account_id, 'unlinking');
|
||||||
}
|
}
|
||||||
|
|
||||||
return updatedAccounts;
|
return updatedAccounts;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getChosenAccount = (accountId: string) => {
|
||||||
|
const chosenId = chosenAccounts[accountId];
|
||||||
|
if (!chosenId) return undefined;
|
||||||
|
|
||||||
|
if (chosenId === addOnBudgetAccountOption.id) {
|
||||||
|
return addOnBudgetAccountOption;
|
||||||
|
}
|
||||||
|
if (chosenId === addOffBudgetAccountOption.id) {
|
||||||
|
return addOffBudgetAccountOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
return localAccounts.find(acc => acc.id === chosenId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = useMemo(() => {
|
||||||
|
const s = new Set(draftLinkAccounts.values());
|
||||||
|
if (s.has('linking') && s.has('unlinking')) {
|
||||||
|
return t('Link and unlink accounts');
|
||||||
|
} else if (s.has('linking')) {
|
||||||
|
return t('Link accounts');
|
||||||
|
} else if (s.has('unlinking')) {
|
||||||
|
return t('Unlink accounts');
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('Link or unlink accounts');
|
||||||
|
}, [draftLinkAccounts, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
name="select-linked-accounts"
|
name="select-linked-accounts"
|
||||||
containerProps={{ style: { width: 1000 } }}
|
containerProps={{
|
||||||
|
style: isNarrowWidth
|
||||||
|
? {
|
||||||
|
width: '100vw',
|
||||||
|
maxWidth: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
margin: 0,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}
|
||||||
|
: { width: 1000 },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{({ state: { close } }) => (
|
{({ state: { close } }) => (
|
||||||
<>
|
<View
|
||||||
|
style={{ display: 'flex', flexDirection: 'column', height: '100%' }}
|
||||||
|
>
|
||||||
<ModalHeader
|
<ModalHeader
|
||||||
title={t('Link Accounts')}
|
title={t('Link Accounts')}
|
||||||
rightContent={<ModalCloseButton onPress={close} />}
|
rightContent={<ModalCloseButton onPress={close} />}
|
||||||
/>
|
/>
|
||||||
<Text style={{ marginBottom: 10 }}>
|
|
||||||
<Trans>
|
|
||||||
We found the following accounts. Select which ones you want to
|
|
||||||
add:
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flex: 'unset',
|
padding: isNarrowWidth ? '0 16px' : '0 20px',
|
||||||
height: 300,
|
flexShrink: 0,
|
||||||
border: '1px solid ' + theme.tableBorder,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TableHeader>
|
<Text style={{ marginBottom: 20 }}>
|
||||||
<Cell name={t('Institution to Sync')} width={175} />
|
<Trans>
|
||||||
<Cell name={t('Bank Account To Sync')} width={175} />
|
We found the following accounts. Select which ones you want to
|
||||||
<Cell name={t('Balance')} width={80} />
|
add:
|
||||||
<Cell name={t('Account in Actual')} width="flex" />
|
</Trans>
|
||||||
<Cell name={t('Actions')} width={150} />
|
</Text>
|
||||||
</TableHeader>
|
|
||||||
|
|
||||||
<Table<
|
|
||||||
SelectLinkedAccountsModalProps['externalAccounts'][number] & {
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
>
|
|
||||||
items={propsWithSortedExternalAccounts.externalAccounts.map(
|
|
||||||
account => ({
|
|
||||||
...account,
|
|
||||||
id: account.account_id,
|
|
||||||
}),
|
|
||||||
)}
|
|
||||||
style={{ backgroundColor: theme.tableHeaderBackground }}
|
|
||||||
getItemKey={String}
|
|
||||||
renderItem={({ item }) => (
|
|
||||||
<View key={item.id}>
|
|
||||||
<TableRow
|
|
||||||
externalAccount={item}
|
|
||||||
chosenAccount={
|
|
||||||
chosenAccounts[item.account_id] ===
|
|
||||||
addOnBudgetAccountOption.id
|
|
||||||
? addOnBudgetAccountOption
|
|
||||||
: chosenAccounts[item.account_id] ===
|
|
||||||
addOffBudgetAccountOption.id
|
|
||||||
? addOffBudgetAccountOption
|
|
||||||
: localAccounts.find(
|
|
||||||
acc => chosenAccounts[item.account_id] === acc.id,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
unlinkedAccounts={unlinkedAccounts}
|
|
||||||
onSetLinkedAccount={onSetLinkedAccount}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{isNarrowWidth ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
overflowY: 'auto',
|
||||||
|
padding: '0 16px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{propsWithSortedExternalAccounts.externalAccounts.map(account => (
|
||||||
|
<AccountCard
|
||||||
|
key={account.account_id}
|
||||||
|
externalAccount={account}
|
||||||
|
chosenAccount={getChosenAccount(account.account_id)}
|
||||||
|
unlinkedAccounts={unlinkedAccounts}
|
||||||
|
onSetLinkedAccount={onSetLinkedAccount}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 'unset',
|
||||||
|
height: 300,
|
||||||
|
border: '1px solid ' + theme.tableBorder,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TableHeader>
|
||||||
|
<Cell value={t('Institution to Sync')} width={175} />
|
||||||
|
<Cell value={t('Bank Account To Sync')} width={175} />
|
||||||
|
<Cell value={t('Balance')} width={80} />
|
||||||
|
<Cell value={t('Account in Actual')} width="flex" />
|
||||||
|
<Cell value={t('Actions')} width={150} />
|
||||||
|
</TableHeader>
|
||||||
|
|
||||||
|
<Table<
|
||||||
|
SelectLinkedAccountsModalProps['externalAccounts'][number] & {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
items={propsWithSortedExternalAccounts.externalAccounts.map(
|
||||||
|
account => ({
|
||||||
|
...account,
|
||||||
|
id: account.account_id,
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
style={{ backgroundColor: theme.tableHeaderBackground }}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<View key={item.id}>
|
||||||
|
<TableRow
|
||||||
|
externalAccount={item}
|
||||||
|
chosenAccount={getChosenAccount(item.account_id)}
|
||||||
|
unlinkedAccounts={unlinkedAccounts}
|
||||||
|
onSetLinkedAccount={onSetLinkedAccount}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: isNarrowWidth ? 'center' : 'flex-end',
|
||||||
marginTop: 10,
|
...(isNarrowWidth
|
||||||
|
? {
|
||||||
|
padding: '16px',
|
||||||
|
flexShrink: 0,
|
||||||
|
borderTop: `1px solid ${theme.tableBorder}`,
|
||||||
|
}
|
||||||
|
: { marginTop: 10 }),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onPress={onNext}
|
onPress={onNext}
|
||||||
isDisabled={!Object.keys(chosenAccounts).length}
|
isDisabled={draftLinkAccounts.size === 0}
|
||||||
|
style={
|
||||||
|
isNarrowWidth
|
||||||
|
? {
|
||||||
|
width: '100%',
|
||||||
|
height: '44px',
|
||||||
|
fontSize: '1em',
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Trans>Link accounts</Trans>
|
{label}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</View>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInstitutionName(
|
type ExternalAccount =
|
||||||
externalAccount:
|
| SyncServerGoCardlessAccount
|
||||||
| SyncServerGoCardlessAccount
|
| SyncServerSimpleFinAccount
|
||||||
| SyncServerSimpleFinAccount
|
| SyncServerPluggyAiAccount;
|
||||||
| SyncServerPluggyAiAccount,
|
|
||||||
) {
|
|
||||||
if (typeof externalAccount?.institution === 'string') {
|
|
||||||
return externalAccount?.institution ?? '';
|
|
||||||
} else if (typeof externalAccount.institution?.name === 'string') {
|
|
||||||
return externalAccount?.institution?.name ?? '';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
type TableRowProps = {
|
type SharedAccountRowProps = {
|
||||||
externalAccount:
|
externalAccount: ExternalAccount;
|
||||||
| SyncServerGoCardlessAccount
|
|
||||||
| SyncServerSimpleFinAccount
|
|
||||||
| SyncServerPluggyAiAccount;
|
|
||||||
chosenAccount: { id: string; name: string } | undefined;
|
chosenAccount: { id: string; name: string } | undefined;
|
||||||
unlinkedAccounts: AccountEntity[];
|
unlinkedAccounts: AccountEntity[];
|
||||||
onSetLinkedAccount: (
|
onSetLinkedAccount: (
|
||||||
externalAccount:
|
externalAccount: ExternalAccount,
|
||||||
| SyncServerGoCardlessAccount
|
|
||||||
| SyncServerSimpleFinAccount
|
|
||||||
| SyncServerPluggyAiAccount,
|
|
||||||
localAccountId: string | null | undefined,
|
localAccountId: string | null | undefined,
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getAvailableAccountOptions(
|
||||||
|
unlinkedAccounts: AccountEntity[],
|
||||||
|
chosenAccount: { id: string; name: string } | undefined,
|
||||||
|
addOnBudgetAccountOption: { id: string; name: string },
|
||||||
|
addOffBudgetAccountOption: { id: string; name: string },
|
||||||
|
): AutocompleteItem[] {
|
||||||
|
const options: AutocompleteItem[] = [...unlinkedAccounts];
|
||||||
|
if (
|
||||||
|
chosenAccount &&
|
||||||
|
chosenAccount.id !== addOnBudgetAccountOption.id &&
|
||||||
|
chosenAccount.id !== addOffBudgetAccountOption.id
|
||||||
|
) {
|
||||||
|
options.push(chosenAccount);
|
||||||
|
}
|
||||||
|
options.push(addOnBudgetAccountOption, addOffBudgetAccountOption);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableRowProps = SharedAccountRowProps;
|
||||||
|
|
||||||
function TableRow({
|
function TableRow({
|
||||||
externalAccount,
|
externalAccount,
|
||||||
chosenAccount,
|
chosenAccount,
|
||||||
@@ -352,12 +442,12 @@ function TableRow({
|
|||||||
const [focusedField, setFocusedField] = useState<string | null>(null);
|
const [focusedField, setFocusedField] = useState<string | null>(null);
|
||||||
const { addOnBudgetAccountOption, addOffBudgetAccountOption } =
|
const { addOnBudgetAccountOption, addOffBudgetAccountOption } =
|
||||||
useAddBudgetAccountOptions();
|
useAddBudgetAccountOptions();
|
||||||
|
const format = useFormat();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const availableAccountOptions: AutocompleteItem[] = [...unlinkedAccounts];
|
const availableAccountOptions = getAvailableAccountOptions(
|
||||||
if (chosenAccount && chosenAccount.id !== addOnBudgetAccountOption.id) {
|
unlinkedAccounts,
|
||||||
availableAccountOptions.push(chosenAccount);
|
chosenAccount,
|
||||||
}
|
|
||||||
availableAccountOptions.push(
|
|
||||||
addOnBudgetAccountOption,
|
addOnBudgetAccountOption,
|
||||||
addOffBudgetAccountOption,
|
addOffBudgetAccountOption,
|
||||||
);
|
);
|
||||||
@@ -391,7 +481,11 @@ function TableRow({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Field>
|
</Field>
|
||||||
<Field width={80}>
|
<Field width={80}>
|
||||||
<PrivacyFilter>{externalAccount.balance}</PrivacyFilter>
|
<PrivacyFilter>
|
||||||
|
{!isNaN(Number(externalAccount.balance))
|
||||||
|
? format(externalAccount.balance.toString(), 'financial')
|
||||||
|
: t('Unknown')}
|
||||||
|
</PrivacyFilter>
|
||||||
</Field>
|
</Field>
|
||||||
<Field
|
<Field
|
||||||
width="flex"
|
width="flex"
|
||||||
@@ -441,3 +535,161 @@ function TableRow({
|
|||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInstitutionName(
|
||||||
|
externalAccount:
|
||||||
|
| SyncServerGoCardlessAccount
|
||||||
|
| SyncServerSimpleFinAccount
|
||||||
|
| SyncServerPluggyAiAccount,
|
||||||
|
) {
|
||||||
|
if (typeof externalAccount?.institution === 'string') {
|
||||||
|
return externalAccount?.institution ?? '';
|
||||||
|
} else if (typeof externalAccount.institution?.name === 'string') {
|
||||||
|
return externalAccount?.institution?.name ?? '';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountCardProps = SharedAccountRowProps;
|
||||||
|
|
||||||
|
function AccountCard({
|
||||||
|
externalAccount,
|
||||||
|
chosenAccount,
|
||||||
|
unlinkedAccounts,
|
||||||
|
onSetLinkedAccount,
|
||||||
|
}: AccountCardProps) {
|
||||||
|
const [focusedField, setFocusedField] = useState<string | null>(null);
|
||||||
|
const { addOnBudgetAccountOption, addOffBudgetAccountOption } =
|
||||||
|
useAddBudgetAccountOptions();
|
||||||
|
const format = useFormat();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const availableAccountOptions = getAvailableAccountOptions(
|
||||||
|
unlinkedAccounts,
|
||||||
|
chosenAccount,
|
||||||
|
addOnBudgetAccountOption,
|
||||||
|
addOffBudgetAccountOption,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
spacing={2}
|
||||||
|
style={{
|
||||||
|
backgroundColor: theme.tableBackground,
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: '12px 16px',
|
||||||
|
border: `1px solid ${theme.tableBorder}`,
|
||||||
|
minHeight: 'fit-content',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '1.1em',
|
||||||
|
color: theme.pageText,
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
overflowWrap: 'break-word',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{externalAccount.name}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
fontSize: '0.9em',
|
||||||
|
color: theme.pageTextSubdued,
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
overflowWrap: 'break-word',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getInstitutionName(externalAccount)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
fontSize: '0.9em',
|
||||||
|
color: theme.pageTextSubdued,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trans>Balance:</Trans>{' '}
|
||||||
|
<PrivacyFilter>
|
||||||
|
{externalAccount.balance != null
|
||||||
|
? format(externalAccount.balance.toString(), 'financial')
|
||||||
|
: t('Unknown')}
|
||||||
|
</PrivacyFilter>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
spacing={1}
|
||||||
|
style={{
|
||||||
|
fontSize: '0.9em',
|
||||||
|
color: theme.pageTextSubdued,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
<Trans>Linked to:</Trans>
|
||||||
|
</Text>
|
||||||
|
{chosenAccount ? (
|
||||||
|
<Text style={{ color: theme.noticeTextLight, fontWeight: 500 }}>
|
||||||
|
{chosenAccount.name}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Text style={{ color: theme.pageTextSubdued }}>
|
||||||
|
<Trans>Not linked</Trans>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{focusedField === 'account' && (
|
||||||
|
<View style={{ marginBottom: 12 }}>
|
||||||
|
<Autocomplete
|
||||||
|
focused
|
||||||
|
strict
|
||||||
|
highlightFirst
|
||||||
|
suggestions={availableAccountOptions}
|
||||||
|
onSelect={value => {
|
||||||
|
onSetLinkedAccount(externalAccount, value);
|
||||||
|
setFocusedField(null);
|
||||||
|
}}
|
||||||
|
inputProps={{
|
||||||
|
onBlur: () => setFocusedField(null),
|
||||||
|
placeholder: t('Select account...'),
|
||||||
|
}}
|
||||||
|
value={chosenAccount?.id}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View style={{ display: 'flex', justifyContent: 'center' }}>
|
||||||
|
{chosenAccount ? (
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
onSetLinkedAccount(externalAccount, null);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: '8px 16px',
|
||||||
|
fontSize: '0.9em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trans>Remove bank sync</Trans>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
onPress={() => {
|
||||||
|
setFocusedField('account');
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: '8px 16px',
|
||||||
|
fontSize: '0.9em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trans>Link account</Trans>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
6
upcoming-release-notes/5984.md
Normal file
6
upcoming-release-notes/5984.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Enhancements
|
||||||
|
authors: [matt-fidd]
|
||||||
|
---
|
||||||
|
|
||||||
|
Make bank sync accout linking modal mobile responsive
|
||||||
Reference in New Issue
Block a user