Skip to content

Commit

Permalink
Get trend state from React not localStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
idpaterson committed Dec 14, 2023
1 parent 759a09f commit 9045e5c
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 64 deletions.
27 changes: 0 additions & 27 deletions src/shared/lib/__tests__/accounts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
fetchDailyBalancesForAllAccounts,
fetchMonthlyBalances,
fetchNetWorthBalances,
fetchTrendAccounts,
formatBalancesAsCSV,
getAccountTypeFilterForTrend,
} from '../accounts';
Expand Down Expand Up @@ -47,32 +46,6 @@ describe('fetchAccounts', () => {
});
});

describe('fetchTrendAccounts', () => {
it('excludes deselected accounts', async () => {
const allDebtAccounts = await fetchTrendAccounts({
trend: {
reportType: 'DEBTS_TIME',
deselectedAccountIds: [],
fixedFilter: 'CUSTOM',
fromDate: '2020-01-01',
toDate: '2020-01-01',
},
overrideApiKey: TEST_MINT_API_KEY,
});
const accounts = await fetchTrendAccounts({
trend: {
reportType: 'DEBTS_TIME',
deselectedAccountIds: ['43237333_2630847'], // a debt account
fixedFilter: 'CUSTOM',
fromDate: '2020-01-01',
toDate: '2020-01-01',
},
overrideApiKey: TEST_MINT_API_KEY,
});
expect(accounts.length).toEqual(allDebtAccounts.length - 1);
});
});

describe('formatBalancesAsCSV', () => {
it('includes account name if provied', () => {
const result = formatBalancesAsCSV({
Expand Down
28 changes: 3 additions & 25 deletions src/shared/lib/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ type TrendsResponse = {

/** State of user selections on the Mint Trends page */
export type TrendState = {
/** Selected accounts */
accountIds?: string[];
/** Use with {@link deselectedAccountIds } to figure out which accounts to include in the trend */
reportType: ReportType;
/** All accounts eligible for the {@link reportType} that are NOT selected */
deselectedAccountIds?: string[];
/** Semantic representation of the {@link fromDate} {@link toDate} range */
fixedFilter: FixedDateFilter;
/** ISO start date */
Expand Down Expand Up @@ -385,8 +385,7 @@ export const fetchDailyBalancesForTrend = async ({
onProgress?: TrendBalanceHistoryProgressCallback;
overrideApiKey?: string;
}) => {
const accounts = await withRetry(() => fetchTrendAccounts({ trend, overrideApiKey }));
const accountId = accounts.map(({ id }) => id);
const accountId = trend.accountIds;
const { reportType, fromDate, toDate, fixedFilter } = trend;
let interval: Interval;
// ALL_TIME may report a fromDate that is inaccurate by several years (e.g. 2007 when the trend
Expand Down Expand Up @@ -502,27 +501,6 @@ export const fetchAccounts = async ({
return accounts;
};

/**
* Make sense of the {@link TrendState} by determining which accounts are selected.
*/
export const fetchTrendAccounts = async ({
trend,
...options
}: {
trend: TrendState;
} & FetchAccountsOptions) => {
// Mint knows best which accounts are eligible for the trend if nothing is selected
if (!trend.deselectedAccountIds?.length) {
return [];
}
const allAccounts = await fetchAccounts({ offset: 0, ...options });
const accountTypeFilter = getAccountTypeFilterForTrend(trend);

return allAccounts.filter(
({ id, type }) => accountTypeFilter(type) && !trend.deselectedAccountIds?.includes(id),
);
};

/**
* Merges paired API response into a single amount/inverseAmount entry.
*
Expand Down
76 changes: 64 additions & 12 deletions src/shared/lib/trends.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
import { TrendType, TrendState, ReportType } from '../../shared/lib/accounts';
import { TrendState, ReportType, FixedDateFilter } from '../../shared/lib/accounts';

type AccountFilterReactProps = {
children: {
props: {
/** Selected account IDs and categories */
value: string[];
};
};
};

type DatePickerReactProps = {
props: {
children: [
unknown,
{
props: {
/** The ISO date string */
value: string;
};
},
unknown,
];
};
};

type TimeFilterReactProps = {
children: [DatePickerReactProps, DatePickerReactProps];
};

/**
* State for the most recent trend according to localStorage, does not need to be visible
* State for the current visible trend.
*
* This function must be executed in the context of the Mint tab and therefore must be
* self-contained.
Expand All @@ -10,19 +38,43 @@ export const getCurrentTrendState = () => {
if (
// disable when not viewing the Trends page
window.location.pathname.startsWith('/trends') &&
// disable when filtered by category, tag, etc. because this filter is not in the trend state
// disable when filtered by category, tag, etc. because the extension does not support these
!document.querySelector('[data-automation-id="filter-chip"]')
) {
try {
const CURRENT_TREND_TYPE_LOCAL_STORAGE_KEY = 'trends-state';
const currentTrendType = localStorage.getItem(
CURRENT_TREND_TYPE_LOCAL_STORAGE_KEY,
) as TrendType;
const trendState = JSON.parse(
localStorage.getItem(`${CURRENT_TREND_TYPE_LOCAL_STORAGE_KEY}-${currentTrendType}`) ||
'null',
) as TrendState;

// Return React data backing HTML elements
const getReactProps = <Props>(selector: string) => {
const el = document.querySelector(selector);
return el?.[Object.keys(el).find((key) => key.startsWith('__reactProps'))] as Props;
};
const accountState = getReactProps<AccountFilterReactProps>(
'[data-automation-id="filter-accounts"]',
);
// For ALL_TIME charts this time range may be inaccurate (e.g. 2007 when the data only begins
// in 2021) but the extension is better equipped to choose the correct date with the API.
const timeFilterState = getReactProps<TimeFilterReactProps>(
'[data-automation-id="filter-time-custom"]',
);
// ReportType can also be found in react props but it does not update reliably
const reportType = document.querySelector('.trends-sidebar-report-selected-list-item a')
?.id as ReportType;
const fixedFilter = (document.getElementById('select-timeframe') as HTMLSelectElement)
.value as FixedDateFilter;
const accountIds = accountState.children.props.value.filter(
// Only numeric account IDs (ignore selected categories like AllAccounts and BankAccounts
// that will evaluate to NaN)
(id) => +id[0] === +id[0],
) as string[];
// This is a bit much, but can't seem to get the value reliably from child elements
const fromDate = timeFilterState.children[0].props.children[1].props.value;
const toDate = timeFilterState.children[1].props.children[1].props.value;
const trendState: TrendState = {
accountIds,
reportType,
fixedFilter,
fromDate,
toDate,
};
return trendState;
} catch (e) {
// ignore
Expand Down

0 comments on commit 9045e5c

Please sign in to comment.