Skip to content

Commit

Permalink
Merge branch 'master' into SpendingFeature
Browse files Browse the repository at this point in the history
  • Loading branch information
carkom authored Oct 21, 2024
2 parents 1dc6576 + 715bc00 commit c62aafb
Show file tree
Hide file tree
Showing 359 changed files with 3,817 additions and 2,415 deletions.
22 changes: 21 additions & 1 deletion packages/desktop-client/e2e/accounts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,27 @@ test.describe('Accounts', () => {

test('import csv file twice', async () => {
await importCsv(false);
await importCsv(true);

const fileChooserPromise = page.waitForEvent('filechooser');
await accountPage.page.getByRole('button', { name: 'Import' }).click();

const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(join(__dirname, 'data/test.csv'));

await expect(page).toMatchThemeScreenshots();

const importButton = accountPage.page.getByRole('button', {
name: /Import \d+ transactions/,
});

await expect(importButton).toBeDisabled();
await expect(await importButton.innerText()).toMatch(
/Import 0 transactions/,
);

await accountPage.page.getByRole('button', { name: 'Close' }).click();

await expect(importButton).not.toBeVisible();
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/desktop-client/globals.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
import { type CSSObject } from '@emotion/css/dist/declarations/src/create-instance';

// Allow images to be imported
declare module '*.png';

declare module 'react' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-empty-object-type
interface CSSProperties extends CSSObject {}
}
4 changes: 2 additions & 2 deletions packages/desktop-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"build"
],
"devDependencies": {
"@emotion/css": "^11.13.4",
"@fontsource/redacted-script": "^5.0.21",
"@juggle/resize-observer": "^3.4.0",
"@playwright/test": "1.41.1",
Expand Down Expand Up @@ -36,7 +37,6 @@
"debounce": "^1.2.1",
"downshift": "7.6.2",
"focus-visible": "^4.1.5",
"glamor": "^2.20.40",
"i18next": "^23.11.5",
"i18next-parser": "^9.0.0",
"i18next-resources-to-backend": "^1.2.1",
Expand Down Expand Up @@ -77,7 +77,7 @@
"terser-webpack-plugin": "^5.3.10",
"usehooks-ts": "^3.0.1",
"uuid": "^9.0.1",
"vite": "^5.2.11",
"vite": "^5.2.14",
"vite-plugin-pwa": "^0.20.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
Expand Down
5 changes: 0 additions & 5 deletions packages/desktop-client/src/browser-preload.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,5 @@ document.addEventListener('keydown', e => {
window.__actionsForMenu.undo();
}
}
} else if (e.key === '?') {
if (inputFocused(e)) {
return;
}
window.__actionsForMenu.pushModal('keyboard-shortcuts');
}
});
5 changes: 2 additions & 3 deletions packages/desktop-client/src/components/AnimatedRefresh.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// @ts-strict-ignore
import React from 'react';
import React, { type CSSProperties } from 'react';

import { keyframes } from 'glamor';
import { keyframes } from '@emotion/css';

import { SvgRefresh } from '../icons/v1';
import { type CSSProperties } from '../style';

import { View } from './common/View';

Expand Down
6 changes: 3 additions & 3 deletions packages/desktop-client/src/components/AppBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { useSelector } from 'react-redux';
import { useTransition, animated } from 'react-spring';

import { css } from 'glamor';
import { css } from '@emotion/css';

import { AnimatedLoading } from '../icons/AnimatedLoading';
import { theme } from '../style';
Expand Down Expand Up @@ -33,7 +33,7 @@ export function AppBackground({ isLoading }: AppBackgroundProps) {
transitions((style, item) => (
<animated.div key={item} style={style}>
<View
className={`${css({
className={css({
position: 'absolute',
top: 0,
left: 0,
Expand All @@ -42,7 +42,7 @@ export function AppBackground({ isLoading }: AppBackgroundProps) {
paddingTop: 200,
color: theme.pageText,
alignItems: 'center',
})}`}
})}
>
<Block style={{ marginBottom: 20, fontSize: 18 }}>
{loadingText}
Expand Down
3 changes: 1 addition & 2 deletions packages/desktop-client/src/components/FixedSizeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import {
type Ref,
type MutableRefObject,
type UIEvent,
type CSSProperties,
} from 'react';

import memoizeOne from 'memoize-one';

import { type CSSProperties } from '../style';

import { View } from './common/View';

const IS_SCROLLING_DEBOUNCE_INTERVAL = 150;
Expand Down
116 changes: 116 additions & 0 deletions packages/desktop-client/src/components/HelpMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { forwardRef, useRef } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { Trans, useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';

import { useToggle } from 'usehooks-ts';

import { pushModal } from 'loot-core/client/actions/modals';

import { SvgHelp } from '../icons/v2/Help';
import { openUrl } from '../util/router-tools';

import { Button } from './common/Button2';
import { Menu } from './common/Menu';
import { Popover } from './common/Popover';
import { SpaceBetween } from './common/SpaceBetween';

type HelpMenuItem = 'docs' | 'keyboard-shortcuts';

type HelpButtonProps = {
onPress?: () => void;
};

const HelpButton = forwardRef<HTMLButtonElement, HelpButtonProps>(
({ onPress }, ref) => {
const size = 15;
return (
<Button
variant="bare"
ref={ref}
onPress={onPress}
style={{
display: 'flex',
alignItems: 'center',
gap: 4,
}}
>
<SvgHelp width={size} height={size} />
<Trans>Help</Trans>
</Button>
);
},
);

HelpButton.displayName = 'HelpButton';

const getPageDocs = (page: string) => {
switch (page) {
case '/budget':
return 'https://actualbudget.org/docs/getting-started/envelope-budgeting';
case '/reports':
return 'https://actualbudget.org/docs/reports/';
case '/schedules':
return 'https://actualbudget.org/docs/schedules';
case '/payees':
return 'https://actualbudget.org/docs/transactions/payees';
case '/rules':
return 'https://actualbudget.org/docs/budgeting/rules';
case '/settings':
return 'https://actualbudget.org/docs/settings';
default:
// All pages under /accounts, plus any missing pages
return 'https://actualbudget.org/docs';
}
};

export const HelpMenu = () => {
const { t } = useTranslation();
const [isMenuOpen, toggleMenuOpen, setMenuOpen] = useToggle();
const menuButtonRef = useRef(null);

const dispatch = useDispatch();
const page = useLocation().pathname;

const handleItemSelect = (item: HelpMenuItem) => {
switch (item) {
case 'docs':
openUrl(getPageDocs(page));
break;
case 'keyboard-shortcuts':
dispatch(pushModal('keyboard-shortcuts'));
break;
}
};

useHotkeys('shift+?', () => setMenuOpen(true));

return (
<SpaceBetween>
<HelpButton ref={menuButtonRef} onPress={toggleMenuOpen} />

<Popover
placement="bottom end"
offset={8}
triggerRef={menuButtonRef}
isOpen={isMenuOpen}
onOpenChange={() => setMenuOpen(false)}
>
<Menu
onMenuSelect={item => {
setMenuOpen(false);
handleItemSelect(item);
}}
items={[
{
name: 'docs',
text: t('Documentation'),
},
{ name: 'keyboard-shortcuts', text: t('Keyboard shortcuts') },
]}
/>
</Popover>
</SpaceBetween>
);
};
5 changes: 3 additions & 2 deletions packages/desktop-client/src/components/LoggedInUser.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// @ts-strict-ignore
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, type CSSProperties } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import { type State } from 'loot-core/src/client/state-types';

import { useActions } from '../hooks/useActions';
import { theme, styles, type CSSProperties } from '../style';
import { theme, styles } from '../style';

import { Button } from './common/Button2';
import { Menu } from './common/Menu';
Expand Down Expand Up @@ -108,6 +108,7 @@ export function LoggedInUser({
</Button>

<Popover
offset={8}
triggerRef={triggerRef}
isOpen={menuOpen}
onOpenChange={() => setMenuOpen(false)}
Expand Down
11 changes: 4 additions & 7 deletions packages/desktop-client/src/components/ManageRules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,14 @@ export function ManageRules({
const { list: categories } = useCategories();
const payees = usePayees();
const accounts = useAccounts();
const state = {
payees,
accounts,
schedules,
};
const filterData = useMemo(
() => ({
...state,
payees,
accounts,
schedules,
categories,
}),
[state, categories],
[payees, accounts, schedules, categories],
);

const filteredRules = useMemo(
Expand Down
12 changes: 3 additions & 9 deletions packages/desktop-client/src/components/Modals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { ImportYNAB5Modal } from './modals/manager/ImportYNAB5Modal';
import { ManageRulesModal } from './modals/ManageRulesModal';
import { MergeUnusedPayeesModal } from './modals/MergeUnusedPayeesModal';
import { NotesModal } from './modals/NotesModal';
import { OutOfSyncMigrationsModal } from './modals/OutOfSyncMigrationsModal';
import { PayeeAutocompleteModal } from './modals/PayeeAutocompleteModal';
import { ScheduledTransactionMenuModal } from './modals/ScheduledTransactionMenuModal';
import { SelectLinkedAccountsModal } from './modals/SelectLinkedAccountsModal';
Expand Down Expand Up @@ -591,15 +592,8 @@ export function Modals() {
return <ImportYNAB5Modal key={name} />;
case 'import-actual':
return <ImportActualModal key={name} />;
case 'manager-load-backup':
return (
<LoadBackupModal
key={name}
budgetId={options.budgetId}
backupDisabled={true}
watchUpdates={false}
/>
);
case 'out-of-sync-migrations':
return <OutOfSyncMigrationsModal key={name} />;

default:
throw new Error('Unknown modal');
Expand Down
12 changes: 6 additions & 6 deletions packages/desktop-client/src/components/Notes.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// @ts-strict-ignore
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useRef, type CSSProperties } from 'react';
import ReactMarkdown from 'react-markdown';

import { css } from 'glamor';
import { css } from '@emotion/css';
import remarkGfm from 'remark-gfm';

import { useResponsive } from '../ResponsiveProvider';
import { type CSSProperties, theme } from '../style';
import { theme } from '../style';
import { remarkBreaks, sequentialNewlinesPlugin } from '../util/markdown';

import { Text } from './common/Text';
Expand Down Expand Up @@ -110,22 +110,22 @@ export function Notes({
return editable ? (
<textarea
ref={textAreaRef}
className={`${css({
className={css({
border: '1px solid ' + theme.buttonNormalBorder,
padding: 7,
...(!isNarrowWidth && { minWidth: 350, minHeight: 120 }),
outline: 'none',
backgroundColor: theme.tableBackground,
color: theme.tableText,
...getStyle?.(editable),
})}`}
})}
value={notes || ''}
onChange={e => onChange?.(e.target.value)}
onBlur={e => onBlur?.(e.target.value)}
placeholder="Notes (markdown supported)"
/>
) : (
<Text {...markdownStyles} style={{ ...getStyle?.(editable) }}>
<Text className={css([markdownStyles, getStyle?.(editable)])}>
<ReactMarkdown remarkPlugins={remarkPlugins} linkTarget="_blank">
{notes}
</ReactMarkdown>
Expand Down
10 changes: 8 additions & 2 deletions packages/desktop-client/src/components/NotesButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React, { useEffect, useRef, useState, type ComponentProps } from 'react';
import React, {
useEffect,
useRef,
useState,
type ComponentProps,
type CSSProperties,
} from 'react';

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

import { useNotes } from '../hooks/useNotes';
import { SvgCustomNotesPaper } from '../icons/v2';
import { type CSSProperties, theme } from '../style';
import { theme } from '../style';

import { Button } from './common/Button2';
import { Popover } from './common/Popover';
Expand Down
Loading

0 comments on commit c62aafb

Please sign in to comment.