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

SimpleFin #2188

Merged
merged 31 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
000c92d
Some initial UI work for adding SimpleFin.
zachwhelchel Jan 4, 2024
e3161b8
Merge pull request #6 from zachwhelchel/master
zachwhelchel Jan 4, 2024
52a25d1
SimpleFin proof of concept working.
zachwhelchel Jan 7, 2024
b1d59e3
Adds linking & unlinking to existing accounts through the account men…
zachwhelchel Jan 11, 2024
db12f67
Added loading and lint fixes.
zachwhelchel Jan 11, 2024
4c8df96
Merge branch 'master' into zach/simplefin
zachwhelchel Jan 11, 2024
28d8e55
Lint changes.
zachwhelchel Jan 11, 2024
78c232e
Added release notes.
zachwhelchel Jan 11, 2024
80e6c68
Typecheck cleanup.
zachwhelchel Jan 11, 2024
25509b4
Import, lint, typecheck cleanups.
zachwhelchel Jan 11, 2024
f11b9a6
More typecheck cleanup.
zachwhelchel Jan 11, 2024
1c483f4
Refactored language for consistency.
zachwhelchel Jan 11, 2024
be208cc
Added default institution name.
zachwhelchel Jan 11, 2024
74b9cc0
Lint cleanup.
zachwhelchel Jan 11, 2024
853b0db
Addressed change requests.
zachwhelchel Jan 12, 2024
8e49971
Added a default to migration, made variables consistent, added featur…
zachwhelchel Jan 12, 2024
3cca410
Merge branch 'master' into zach/simplefin
zachwhelchel Jan 12, 2024
38db62e
Added account_sync_source to server schema.
zachwhelchel Jan 18, 2024
d83200f
Adds account_sync_source to test.
zachwhelchel Jan 18, 2024
92959c4
Fix for typecheck.
zachwhelchel Jan 18, 2024
058d57b
Attempt to make typecheck happy.
zachwhelchel Jan 18, 2024
3631e36
Added strict ignore.
zachwhelchel Jan 18, 2024
ef1fcd4
Moved account_sync_source to the right model (face palm).
zachwhelchel Jan 18, 2024
606aba4
Merge branch 'master' into zach/simplefin
zachwhelchel Jan 18, 2024
af154ba
Merge branch 'master' into zach/simplefin
zachwhelchel Jan 18, 2024
de85e71
Hotfix for institution format.
zachwhelchel Jan 19, 2024
ae340cd
Lint cleanup.
zachwhelchel Jan 19, 2024
c613c31
Removed unnecessary promise.all.
zachwhelchel Jan 20, 2024
2d9d028
Merge branch 'master' into zach/simplefin
zachwhelchel Jan 20, 2024
9f64ece
Lint cleanup.
zachwhelchel Jan 20, 2024
4c325fa
Merge branch 'zach/simplefin' of https://github.com/zachwhelchel/actu…
zachwhelchel Jan 20, 2024
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
11 changes: 11 additions & 0 deletions packages/desktop-client/src/components/Modals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { PlaidExternalMsg } from './modals/PlaidExternalMsg';
import { ReportBudgetSummary } from './modals/ReportBudgetSummary';
import { RolloverBudgetSummary } from './modals/RolloverBudgetSummary';
import { SelectLinkedAccounts } from './modals/SelectLinkedAccounts';
import { SimpleFinInitialise } from './modals/SimpleFinInitialise';
import { SingleInput } from './modals/SingleInput';
import { SwitchBudgetType } from './modals/SwitchBudgetType';
import { DiscoverSchedules } from './schedules/DiscoverSchedules';
Expand Down Expand Up @@ -79,6 +80,7 @@ export function Modals() {
<CreateAccount
modalProps={modalProps}
syncServerStatus={syncServerStatus}
upgradingAccountId={options?.upgradingAccountId}
/>
);

Expand Down Expand Up @@ -108,6 +110,7 @@ export function Modals() {
requisitionId={options.requisitionId}
localAccounts={accounts.filter(acct => acct.closed === 0)}
actions={actions}
syncSource={options.syncSource}
/>
);

Expand Down Expand Up @@ -195,6 +198,14 @@ export function Modals() {
/>
);

case 'simplefin-init':
return (
<SimpleFinInitialise
modalProps={modalProps}
onSuccess={options.onSuccess}
/>
);

case 'gocardless-external-msg':
return (
<GoCardlessExternalMsg
Expand Down
4 changes: 3 additions & 1 deletion packages/desktop-client/src/components/accounts/Account.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,9 @@ class AccountInternal extends PureComponent {

switch (item) {
case 'link':
authorizeBank(this.props.pushModal, { upgradingAccountId: accountId });
this.props.pushModal('add-account', {
upgradingAccountId: accountId,
});
break;
case 'unlink':
this.props.unlinkAccount(accountId);
Expand Down
182 changes: 145 additions & 37 deletions packages/desktop-client/src/components/modals/CreateAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React, { useEffect, useState } from 'react';

import { send } from 'loot-core/src/platform/client/fetch';

import { authorizeBank } from '../../gocardless';
import { useActions } from '../../hooks/useActions';
import { useFeatureFlag } from '../../hooks/useFeatureFlag';
import { useGoCardlessStatus } from '../../hooks/useGoCardlessStatus';
import { useSimpleFinStatus } from '../../hooks/useSimpleFinStatus';
import { type SyncServerStatus } from '../../hooks/useSyncServerStatus';
import { theme } from '../../style';
import { type CommonModalProps } from '../../types/modals';
Expand All @@ -16,23 +20,75 @@ import { View } from '../common/View';
type CreateAccountProps = {
modalProps: CommonModalProps;
syncServerStatus: SyncServerStatus;
upgradingAccountId?: string;
};

export function CreateAccount({
modalProps,
syncServerStatus,
upgradingAccountId,
}: CreateAccountProps) {
const actions = useActions();
const [isGoCardlessSetupComplete, setIsGoCardlessSetupComplete] =
useState(null);
const [isSimpleFinSetupComplete, setIsSimpleFinSetupComplete] =
useState(null);

const onConnect = () => {
const onConnectGoCardless = () => {
if (!isGoCardlessSetupComplete) {
onGoCardlessInit();
return;
}

authorizeBank(actions.pushModal);
if (upgradingAccountId == null) {
authorizeBank(actions.pushModal);
} else {
authorizeBank(actions.pushModal, {
upgradingAccountId,
});
}
};

const onConnectSimpleFin = async () => {
if (!isSimpleFinSetupComplete) {
onSimpleFinInit();
return;
}

if (loadingSimpleFinAccounts) {
return;
}

setLoadingSimpleFinAccounts(true);

const results = await send('simplefin-accounts');

const newAccounts = [];

type NormalizedAccount = {
account_id: string;
name: string;
institution: string;
orgDomain: string;
};

for (const oldAccount of results.accounts) {
const newAccount: NormalizedAccount = {
account_id: oldAccount.id,
name: oldAccount.name,
institution: oldAccount.org.name,
orgDomain: oldAccount.org.domain,
};

newAccounts.push(newAccount);
}

actions.pushModal('select-linked-accounts', {
accounts: newAccounts,
syncSource: 'simpleFin',
});

setLoadingSimpleFinAccounts(false);
};

const onGoCardlessInit = () => {
Expand All @@ -41,45 +97,68 @@ export function CreateAccount({
});
};

const onSimpleFinInit = () => {
actions.pushModal('simplefin-init', {
onSuccess: () => setIsSimpleFinSetupComplete(true),
});
};

const onCreateLocalAccount = () => {
actions.pushModal('add-local-account');
};

const { configured } = useGoCardlessStatus();
const { configuredGoCardless } = useGoCardlessStatus();
useEffect(() => {
setIsGoCardlessSetupComplete(configured);
}, [configured]);
setIsGoCardlessSetupComplete(configuredGoCardless);
}, [configuredGoCardless]);

const { configuredSimpleFin } = useSimpleFinStatus();
useEffect(() => {
setIsSimpleFinSetupComplete(configuredSimpleFin);
}, [configuredSimpleFin]);

let title = 'Add Account';
const [loadingSimpleFinAccounts, setLoadingSimpleFinAccounts] =
useState(false);

if (upgradingAccountId != null) {
title = 'Link Account';
}

const simpleFinSyncFeatureFlag = useFeatureFlag('simpleFinSync');

return (
<Modal title="Add Account" {...modalProps}>
<Modal title={title} {...modalProps}>
{() => (
<View style={{ maxWidth: 500, gap: 30, color: theme.pageText }}>
<View style={{ gap: 10 }}>
<Button
type="primary"
style={{
padding: '10px 0',
fontSize: 15,
fontWeight: 600,
}}
onClick={onCreateLocalAccount}
>
Create local account
</Button>
<View style={{ lineHeight: '1.4em', fontSize: 15 }}>
<Text>
<strong>Create a local account</strong> if you want to add
transactions manually. You can also{' '}
<ExternalLink
to="https://actualbudget.org/docs/transactions/importing"
linkColor="muted"
>
import QIF/OFX/QFX files into a local account
</ExternalLink>
.
</Text>
{upgradingAccountId == null && (
<View style={{ gap: 10 }}>
<Button
type="primary"
style={{
padding: '10px 0',
fontSize: 15,
fontWeight: 600,
}}
onClick={onCreateLocalAccount}
>
Create local account
</Button>
<View style={{ lineHeight: '1.4em', fontSize: 15 }}>
<Text>
<strong>Create a local account</strong> if you want to add
transactions manually. You can also{' '}
<ExternalLink
to="https://actualbudget.org/docs/transactions/importing"
linkColor="muted"
>
import QIF/OFX/QFX files into a local account
</ExternalLink>
.
</Text>
</View>
</View>
</View>
)}
<View style={{ gap: 10 }}>
{syncServerStatus === 'online' ? (
<>
Expand All @@ -91,17 +170,46 @@ export function CreateAccount({
fontWeight: 600,
flex: 1,
}}
onClick={onConnect}
onClick={onConnectGoCardless}
>
{isGoCardlessSetupComplete
? 'Link bank account with GoCardless'
: 'Set up GoCardless for bank sync'}
</ButtonWithLoading>
<Text style={{ lineHeight: '1.4em', fontSize: 15 }}>
<strong>Link a bank account</strong> to automatically download
transactions. GoCardless provides reliable, up-to-date
information from hundreds of banks.
<strong>
Link a <u>European</u> bank account
</strong>{' '}
to automatically download transactions. GoCardless provides
reliable, up-to-date information from hundreds of banks.
</Text>
{simpleFinSyncFeatureFlag === true && (
<>
<ButtonWithLoading
disabled={syncServerStatus !== 'online'}
loading={loadingSimpleFinAccounts}
style={{
marginTop: '18px',
padding: '10px 0',
fontSize: 15,
fontWeight: 600,
flex: 1,
}}
onClick={onConnectSimpleFin}
>
{isSimpleFinSetupComplete
? 'Link bank account with SimpleFIN'
: 'Set up SimpleFIN for bank sync'}
</ButtonWithLoading>
<Text style={{ lineHeight: '1.4em', fontSize: 15 }}>
<strong>
Link a <u>North American</u> bank account
</strong>{' '}
to automatically download transactions. SimpleFIN provides
reliable, up-to-date information from hundreds of banks.
</Text>
</>
)}
</>
) : (
<>
Expand All @@ -113,15 +221,15 @@ export function CreateAccount({
fontWeight: 600,
}}
>
Set up GoCardless for bank sync
Set up bank sync
</Button>
<Paragraph style={{ fontSize: 15 }}>
Connect to an Actual server to set up{' '}
<ExternalLink
to="https://actualbudget.org/docs/advanced/bank-sync"
linkColor="muted"
>
automatic syncing with GoCardless
automatic syncing.
</ExternalLink>
.
</Paragraph>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,10 @@ export function GoCardlessExternalMsg({
isLoading: isBankOptionsLoading,
isError: isBankOptionError,
} = useAvailableBanks(country);
const { configured: isConfigured, isLoading: isConfigurationLoading } =
useGoCardlessStatus();
const {
configuredGoCardless: isConfigured,
isLoading: isConfigurationLoading,
} = useGoCardlessStatus();

async function onJump() {
setError(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function SelectLinkedAccounts({
externalAccounts,
localAccounts,
actions,
syncSource,
}) {
const [chosenAccounts, setChosenAccounts] = useState(() => {
return Object.fromEntries(
Expand Down Expand Up @@ -49,13 +50,22 @@ export function SelectLinkedAccounts({
}

// Finally link the matched account
actions.linkAccount(
requisitionId,
externalAccount,
chosenLocalAccountId !== addAccountOption.id
? chosenLocalAccountId
: undefined,
);
if (syncSource === "simpleFin") {
actions.linkAccountSimpleFin(
externalAccount,
chosenLocalAccountId !== addAccountOption.id
? chosenLocalAccountId
: undefined,
);
} else {
actions.linkAccount(
requisitionId,
externalAccount,
chosenLocalAccountId !== addAccountOption.id
? chosenLocalAccountId
: undefined,
);
}
},
);

Expand Down
Loading
Loading