diff --git a/.eslintrc.js b/.eslintrc.js index 66d41071cf2..bf1b787c898 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -230,13 +230,22 @@ module.exports = { './packages/desktop-client/src/components/UpdateNotification.*', './packages/desktop-client/src/components/accounts/Header.*', './packages/desktop-client/src/components/alerts.*', + './packages/desktop-client/src/components/budget/BudgetCategories.*', + './packages/desktop-client/src/components/budget/BudgetTotals.*', + './packages/desktop-client/src/components/budget/ExpenseGroup.*', + './packages/desktop-client/src/components/budget/IncomeGroup.*', './packages/desktop-client/src/components/budget/MobileBudget.*', './packages/desktop-client/src/components/budget/MobileBudgetTable.*', './packages/desktop-client/src/components/budget/MobileTable.*', './packages/desktop-client/src/components/budget/MonthCountSelector.*', './packages/desktop-client/src/components/budget/MonthPicker.*', + './packages/desktop-client/src/components/budget/RenderMonths.*', + './packages/desktop-client/src/components/budget/SidebarCategory.*', + './packages/desktop-client/src/components/budget/SidebarGroup.*', './packages/desktop-client/src/components/budget/constants.*', './packages/desktop-client/src/components/budget/misc.*', + './packages/desktop-client/src/components/budget/report/BudgetSummary.*', + './packages/desktop-client/src/components/budget/report/components.*', './packages/desktop-client/src/components/budget/rollover/BudgetSummary.*', './packages/desktop-client/src/components/budget/rollover/rollover-components.*', './packages/desktop-client/src/components/budget/util.*', diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..c4f207e77fa --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +# Funding policies: https://actualbudget.org/docs/contributing/leadership/funding +open_collective: actual diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index e452d08506c..1d48d375a87 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -10,11 +10,11 @@ concurrency: cancel-in-progress: true jobs: - functional: - name: Functional + netlify: + name: Wait for Netlify build to finish runs-on: ubuntu-latest - container: - image: mcr.microsoft.com/playwright:v1.37.0-jammy + outputs: + netlify_url: ${{ steps.netlify.outputs.url }} steps: - uses: actions/checkout@v3 - name: Set up environment @@ -25,10 +25,21 @@ jobs: COMMIT_SHA: ${{ github.event.pull_request.head.sha }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ./.github/actions/netlify-wait-for-build + + functional: + name: Functional + needs: netlify + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/playwright:v1.37.0-jammy + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup - name: Run E2E Tests on Netlify URL run: yarn e2e env: - E2E_START_URL: ${{ steps.netlify.outputs.url }} + E2E_START_URL: ${{ needs.netlify.outputs.netlify_url }} - uses: actions/upload-artifact@v3 if: always() with: @@ -37,6 +48,7 @@ jobs: retention-days: 30 vrt: name: Visual regression + needs: netlify runs-on: ubuntu-latest container: image: mcr.microsoft.com/playwright:v1.37.0-jammy @@ -44,16 +56,10 @@ jobs: - uses: actions/checkout@v3 - name: Set up environment uses: ./.github/actions/setup - - name: Wait for Netlify build to finish - id: netlify - env: - COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./.github/actions/netlify-wait-for-build - name: Run VRT Tests on Netlify URL run: yarn vrt env: - E2E_START_URL: ${{ steps.netlify.outputs.url }} + E2E_START_URL: ${{ needs.netlify.outputs.netlify_url }} - uses: actions/upload-artifact@v3 if: always() with: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000000..8dd92eda29e --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,16 @@ +name: 'Close stale PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' + close-pr-message: 'This PR was closed because it has been stalled for 5 days with no activity.' + days-before-stale: 30 + days-before-close: 5 + days-before-issue-stale: -1 diff --git a/bin/package-electron b/bin/package-electron index 9e2e0d89d72..0caece7e196 100755 --- a/bin/package-electron +++ b/bin/package-electron @@ -35,8 +35,6 @@ if [ "$OSTYPE" == "msys" ]; then fi fi -yarn patch-package - yarn rebuild-electron yarn workspace loot-core build:node diff --git a/package.json b/package.json index 5b495a1faeb..e0f7ebe2f93 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "scripts": { "start": "yarn start:browser", - "start:desktop": "npm-run-all --parallel 'start:desktop-*'", + "start:desktop": "yarn rebuild-electron && npm-run-all --parallel 'start:desktop-*'", "start:desktop-node": "yarn workspace loot-core watch:node", "start:desktop-client": "yarn workspace @actual-app/web watch", "start:desktop-electron": "yarn workspace desktop-electron watch", @@ -39,8 +39,7 @@ "lint": "eslint . --max-warnings 0", "lint:verbose": "DEBUG=eslint:cli-engine eslint . --max-warnings 0", "typecheck": "yarn tsc", - "jq": "./node_modules/node-jq/bin/jq", - "postinstall": "patch-package" + "jq": "./node_modules/node-jq/bin/jq" }, "devDependencies": { "cross-env": "^7.0.3", @@ -53,7 +52,6 @@ "eslint-plugin-rulesdir": "^0.2.2", "node-jq": "^4.0.1", "npm-run-all": "^4.1.3", - "patch-package": "^6.1.2", "prettier": "2.8.2", "react-refresh": "^0.14.0", "source-map-support": "^0.5.21", diff --git a/packages/api/package.json b/packages/api/package.json index 24263923f7f..69c1653731d 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -16,7 +16,7 @@ "build": "rm -rf dist && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db" }, "dependencies": { - "better-sqlite3": "^8.2.0", + "better-sqlite3": "^8.6.0", "node-fetch": "^3.3.2", "uuid": "^9.0.0" }, diff --git a/packages/crdt/jest.config.js b/packages/crdt/jest.config.js index a3b8912d852..0a6a68f0762 100644 --- a/packages/crdt/jest.config.js +++ b/packages/crdt/jest.config.js @@ -1,4 +1,6 @@ module.exports = { - preset: 'ts-jest/presets/js-with-ts-esm', testEnvironment: 'node', + transform: { + '^.+\\.(t|j)sx?$': '@swc/jest', + }, }; diff --git a/packages/crdt/package.json b/packages/crdt/package.json index 4e7f3967605..010f8b89c40 100644 --- a/packages/crdt/package.json +++ b/packages/crdt/package.json @@ -20,10 +20,11 @@ "uuid": "^9.0.0" }, "devDependencies": { + "@swc/core": "^1.3.82", + "@swc/jest": "^0.2.29", "@types/jest": "^27.5.0", "@types/uuid": "^9.0.2", "jest": "^27.0.0", - "ts-jest": "^27.0.0", "ts-protoc-gen": "^0.15.0", "typescript": "^5.0.2" } diff --git a/packages/desktop-client/.swcrc b/packages/desktop-client/.swcrc new file mode 100644 index 00000000000..d9e2a5e424a --- /dev/null +++ b/packages/desktop-client/.swcrc @@ -0,0 +1,16 @@ +{ + "jsc": { + "target": "es2022", + "transform": { + "react": { + "runtime": "automatic" + } + }, + "externalHelpers": true, + "parser": { + "syntax": "typescript", + "tsx": true + } + }, + "sourceMaps": true +} diff --git a/packages/desktop-client/config-overrides.js b/packages/desktop-client/config-overrides.js deleted file mode 100644 index 5fe0dd3301b..00000000000 --- a/packages/desktop-client/config-overrides.js +++ /dev/null @@ -1,77 +0,0 @@ -const path = require('path'); - -const chokidar = require('chokidar'); -const { - addWebpackPlugin, - addWebpackResolve, - babelInclude, - override, - overrideDevServer, -} = require('customize-cra'); -const { IgnorePlugin } = require('webpack'); -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); - -if (process.env.CI) { - process.env.DISABLE_ESLINT_PLUGIN = 'true'; -} - -// Forward Netlify env variables -if (process.env.REVIEW_ID) { - process.env.REACT_APP_REVIEW_ID = process.env.REVIEW_ID; -} - -module.exports = { - webpack: override( - babelInclude([path.resolve('src'), path.resolve('../loot-core')]), - addWebpackResolve({ - extensions: [ - ...(process.env.IS_GENERIC_BROWSER - ? ['.browser.js', '.browser.ts', '.browser.tsx'] - : []), - '.web.js', - '.web.ts', - '.web.tsx', - '.js', - '.ts', - '.tsx', - ], - }), - addWebpackPlugin( - new BundleAnalyzerPlugin({ - analyzerMode: 'disabled', - generateStatsFile: true, - }), - ), - // Pikaday throws a warning if Moment.js is not installed however it doesn't - // actually require it to be installed. As we don't use Moment.js ourselves - // then we can just silence this warning. - addWebpackPlugin( - new IgnorePlugin({ - contextRegExp: /pikaday$/, - resourceRegExp: /moment$/, - }), - ), - ), - devServer: overrideDevServer(config => { - return { - ...config, - onBeforeSetupMiddleware(server) { - chokidar - .watch([ - path.resolve('../loot-core/lib-dist/*.js'), - path.resolve('../loot-core/lib-dist/browser/*.js'), - ]) - .on('all', function () { - for (const ws of server.webSocketServer.clients) { - ws.send(JSON.stringify({ type: 'static-changed' })); - } - }); - }, - headers: { - ...config.headers, - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'require-corp', - }, - }; - }), -}; diff --git a/packages/desktop-client/craco.config.ts b/packages/desktop-client/craco.config.ts new file mode 100644 index 00000000000..42a428979a2 --- /dev/null +++ b/packages/desktop-client/craco.config.ts @@ -0,0 +1,116 @@ +const path = require('path'); + +const { + loaderByName, + removeLoaders, + addAfterLoader, + addPlugins, +} = require('@craco/craco'); +const chokidar = require('chokidar'); +const TerserPlugin = require('terser-webpack-plugin'); +const { IgnorePlugin } = require('webpack'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); + +if (process.env.CI) { + process.env.DISABLE_ESLINT_PLUGIN = 'true'; +} + +// Forward Netlify env variables +if (process.env.REVIEW_ID) { + process.env.REACT_APP_REVIEW_ID = process.env.REVIEW_ID; +} + +module.exports = { + webpack: { + configure: (webpackConfig, { env, paths }) => { + webpackConfig.mode = + process.env.NODE_ENV === 'development' ? 'development' : 'production'; + + // swc-loader + addAfterLoader(webpackConfig, loaderByName('babel-loader'), { + test: /\.m?[tj]sx?$/, + exclude: /node_modules/, + loader: require.resolve('swc-loader'), + }); + + // remove the babel loaders + removeLoaders(webpackConfig, loaderByName('babel-loader')); + + addPlugins(webpackConfig, [ + new BundleAnalyzerPlugin({ + analyzerMode: 'disabled', + generateStatsFile: true, + }), + // Pikaday throws a warning if Moment.js is not installed however it doesn't + // actually require it to be installed. As we don't use Moment.js ourselves + // then we can just silence this warning. + new IgnorePlugin({ + contextRegExp: /pikaday$/, + resourceRegExp: /moment$/, + }), + ]); + + webpackConfig.resolve.extensions = [ + '.web.js', + '.web.jsx', + '.web.ts', + '.web.tsx', + '.js', + '.jsx', + '.ts', + '.tsx', + ...webpackConfig.resolve.extensions, + ]; + + if (process.env.IS_GENERIC_BROWSER) { + webpackConfig.resolve.extensions = [ + '.browser.js', + '.browser.jsx', + '.browser.ts', + '.browser.tsx', + ...webpackConfig.resolve.extensions, + ]; + } + + webpackConfig.optimization = { + ...webpackConfig.optimization, + minimize: + process.env.CI === 'true' || process.env.NODE_ENV !== 'development', + minimizer: [ + new TerserPlugin({ + minify: TerserPlugin.swcMinify, + // `terserOptions` options will be passed to `swc` (`@swc/core`) + // Link to options - https://swc.rs/docs/config-js-minify + terserOptions: { + compress: false, + mangle: true, + }, + }), + ], + }; + + return webpackConfig; + }, + }, + devServer: (devServerConfig, { env, paths, proxy, allowedHost }) => { + devServerConfig.onBeforeSetupMiddleware = server => { + chokidar + .watch([ + path.resolve('../loot-core/lib-dist/*.js'), + path.resolve('../loot-core/lib-dist/browser/*.js'), + ]) + .on('all', function () { + for (const ws of server.webSocketServer.clients) { + ws.send(JSON.stringify({ type: 'static-changed' })); + } + }); + }; + devServerConfig.headers = { + ...devServerConfig.headers, + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }; + + return devServerConfig; + }, +}; diff --git a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png index 2134f58218e..1855d820966 100644 Binary files a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png and b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png index 412753803fa..861f3c8cc8b 100644 Binary files a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png and b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-closes-an-account-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-creates-a-new-account-and-views-the-initial-balance-transaction-1-chromium-linux.png b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-creates-a-new-account-and-views-the-initial-balance-transaction-1-chromium-linux.png index f91b967e009..a186c1bbb8d 100644 Binary files a/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-creates-a-new-account-and-views-the-initial-balance-transaction-1-chromium-linux.png and b/packages/desktop-client/e2e/accounts.test.js-snapshots/Accounts-creates-a-new-account-and-views-the-initial-balance-transaction-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-renders-the-summary-information-available-funds-overspent-budgeted-and-for-next-month-1-chromium-linux.png b/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-renders-the-summary-information-available-funds-overspent-budgeted-and-for-next-month-1-chromium-linux.png index 8eeb47f9866..1685e7e51d7 100644 Binary files a/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-renders-the-summary-information-available-funds-overspent-budgeted-and-for-next-month-1-chromium-linux.png and b/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-renders-the-summary-information-available-funds-overspent-budgeted-and-for-next-month-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-transfer-funds-to-another-category-1-chromium-linux.png b/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-transfer-funds-to-another-category-1-chromium-linux.png index 9e0dc62aa70..2646cc85e09 100644 Binary files a/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-transfer-funds-to-another-category-1-chromium-linux.png and b/packages/desktop-client/e2e/budget.test.js-snapshots/Budget-transfer-funds-to-another-category-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-checks-that-settings-page-can-be-opened-2-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-checks-that-settings-page-can-be-opened-2-chromium-linux.png index 9e420b859e8..443581d98f5 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-checks-that-settings-page-can-be-opened-2-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-checks-that-settings-page-can-be-opened-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png index 722e9b77a35..8898769c0ba 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-from-accounts-id-page-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png index aa0d6f8c864..0cfdffe0d74 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png index d8a7a694ec1..08644ff8375 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png index cb2f7338578..2dd2bdc648c 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-creates-a-transaction-via-footer-button-3-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png index 2fc9d8e70a6..4e52ef1c12c 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png index 06a183b0da8..7fcb3d43a44 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-3-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-3-chromium-linux.png index d239e73fe15..f4481614526 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-3-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-individual-account-page-and-checks-that-filtering-is-working-3-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-the-accounts-page-and-asserts-on-balances-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-the-accounts-page-and-asserts-on-balances-1-chromium-linux.png index 207a3d53444..900a67f119f 100644 Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-the-accounts-page-and-asserts-on-balances-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-opens-the-accounts-page-and-asserts-on-balances-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-1-chromium-linux.png index 2ff0f44b32c..9f850670dff 100644 Binary files a/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-2-chromium-linux.png b/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-2-chromium-linux.png index b37b61f98d2..ed62336bdf9 100644 Binary files a/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-2-chromium-linux.png and b/packages/desktop-client/e2e/onboarding.test.js-snapshots/Onboarding-checks-the-page-visuals-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/page-models/account-page.js b/packages/desktop-client/e2e/page-models/account-page.js index 9f0127bd075..cabd83f9535 100644 --- a/packages/desktop-client/e2e/page-models/account-page.js +++ b/packages/desktop-client/e2e/page-models/account-page.js @@ -20,9 +20,11 @@ export class AccountPage { name: 'Menu', }); - this.transactionTableRow = this.page - .getByTestId('table') - .getByTestId('row'); + this.transactionTable = this.page.getByTestId('transaction-table'); + this.transactionTableRow = this.transactionTable.getByTestId('row'); + + this.filterButton = this.page.getByRole('button', { name: 'Filter' }); + this.filterSelectTooltip = this.page.getByTestId('filters-select-tooltip'); } /** @@ -94,6 +96,26 @@ export class AccountPage { ); } + /** + * Open the filtering popover. + */ + async filterBy(field) { + await this.filterButton.click(); + await this.filterSelectTooltip.getByRole('button', { name: field }).click(); + + return new FilterTooltip(this.page.getByTestId('filters-menu-tooltip')); + } + + /** + * Remove the nth filter + */ + async removeFilter(idx) { + await this.page + .getByRole('button', { name: 'Delete filter' }) + .nth(idx) + .click(); + } + async _fillTransactionFields(transactionRow, transaction) { if (transaction.payee) { await transactionRow.getByTestId('payee').click(); @@ -131,3 +153,10 @@ export class AccountPage { } } } + +class FilterTooltip { + constructor(page) { + this.page = page; + this.applyButton = page.getByRole('button', { name: 'Apply' }); + } +} diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png index f2eaf9cd861..6648df99dd5 100644 Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png index 48bd21d4b6c..b99cb9a5dbe 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png index 551c1cc27d4..5965ecaa59d 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png index 7fe56db1abd..0105e69eb51 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png index d671dfdf221..9397a30e057 100644 Binary files a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png index 6ce00637e0a..b1b23b44ac4 100644 Binary files a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png index ef78d4a770e..bd6e38b4485 100644 Binary files a/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png and b/packages/desktop-client/e2e/schedules.test.js-snapshots/Schedules-creates-a-new-schedule-posts-the-transaction-and-later-completes-it-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png index 0beaee67daf..b25a320d879 100644 Binary files a/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js b/packages/desktop-client/e2e/transactions.test.js index a9e4975febc..b715d687c2a 100644 --- a/packages/desktop-client/e2e/transactions.test.js +++ b/packages/desktop-client/e2e/transactions.test.js @@ -31,6 +31,62 @@ test.describe('Transactions', () => { await expect(page).toHaveScreenshot(screenshotConfig(page)); }); + test.describe('filters transactions', () => { + // Reset filters + test.afterEach(async () => { + await accountPage.removeFilter(0); + }); + + test('by date', async () => { + const filterTooltip = await accountPage.filterBy('Date'); + await expect(filterTooltip.page).toHaveScreenshot(screenshotConfig(page)); + + // Open datepicker + await page.keyboard.press('Space'); + const datepicker = page.getByTestId('date-select-tooltip'); + await expect(datepicker).toHaveScreenshot(screenshotConfig(page)); + + // Select "is xxxxx" + await datepicker.getByRole('button', { name: '20' }).click(); + await filterTooltip.applyButton.click(); + + // Assert that there are no transactions + await expect(accountPage.transactionTable).toHaveText('No transactions'); + await expect(page).toHaveScreenshot(screenshotConfig(page)); + }); + + test('by category', async () => { + const filterTooltip = await accountPage.filterBy('Category'); + await expect(filterTooltip.page).toHaveScreenshot(screenshotConfig(page)); + + // Type in the autocomplete box + const autocomplete = page.getByTestId('autocomplete'); + await expect(autocomplete).toHaveScreenshot(screenshotConfig(page)); + + // Select the active item + await page.getByRole('button', { name: 'Clothing' }).click(); + await filterTooltip.applyButton.click(); + + // Assert that there are only clothing transactions + await expect(accountPage.getNthTransaction(0).category).toHaveText( + 'Clothing', + ); + await expect(accountPage.getNthTransaction(1).category).toHaveText( + 'Clothing', + ); + await expect(accountPage.getNthTransaction(2).category).toHaveText( + 'Clothing', + ); + await expect(accountPage.getNthTransaction(3).category).toHaveText( + 'Clothing', + ); + await expect(accountPage.getNthTransaction(4).category).toHaveText( + 'Clothing', + ); + await expect(page).toHaveScreenshot(screenshotConfig(page)); + }); + }); + test('creates a test transaction', async () => { await accountPage.createSingleTransaction({ payee: 'Home Depot', diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-checks-the-page-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-checks-the-page-visuals-1-chromium-linux.png index fa44cbe5258..93cd5f3ea05 100644 Binary files a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-checks-the-page-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-checks-the-page-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-split-test-transaction-1-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-split-test-transaction-1-chromium-linux.png index ad871979445..a82cb3c9d48 100644 Binary files a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-split-test-transaction-1-chromium-linux.png and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-split-test-transaction-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-test-transaction-1-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-test-transaction-1-chromium-linux.png index b4e96689750..90f1ce321fc 100644 Binary files a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-test-transaction-1-chromium-linux.png and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-creates-a-test-transaction-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-1-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-1-chromium-linux.png new file mode 100644 index 00000000000..52dd13a7850 Binary files /dev/null and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-2-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-2-chromium-linux.png new file mode 100644 index 00000000000..06acef3e546 Binary files /dev/null and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-3-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-3-chromium-linux.png new file mode 100644 index 00000000000..aa59979a25a Binary files /dev/null and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-category-3-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-1-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-1-chromium-linux.png new file mode 100644 index 00000000000..c20a4937f12 Binary files /dev/null and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-2-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-2-chromium-linux.png new file mode 100644 index 00000000000..a5b72beb8df Binary files /dev/null and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-3-chromium-linux.png b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-3-chromium-linux.png new file mode 100644 index 00000000000..76543890428 Binary files /dev/null and b/packages/desktop-client/e2e/transactions.test.js-snapshots/Transactions-filters-transactions-by-date-3-chromium-linux.png differ diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json index 6c082c4cf01..b8dc8f84238 100644 --- a/packages/desktop-client/package.json +++ b/packages/desktop-client/package.json @@ -1,20 +1,24 @@ { "name": "@actual-app/web", - "version": "23.8.1", + "version": "23.9.0", "license": "MIT", "files": [ "build" ], "devDependencies": { + "@craco/craco": "^7.1.0", + "@craco/types": "^7.1.0", "@juggle/resize-observer": "^3.1.2", "@playwright/test": "^1.37.1", "@reach/listbox": "^0.18.0", - "@react-aria/focus": "^3.8.0", - "@react-aria/listbox": "^3.6.1", - "@react-aria/utils": "^3.13.3", - "@react-stately/collections": "^3.4.3", - "@react-stately/list": "^3.5.3", + "@react-aria/focus": "^3.14.0", + "@react-aria/listbox": "^3.10.1", + "@react-aria/utils": "^3.19.0", + "@react-stately/collections": "^3.10.0", + "@react-stately/list": "^3.9.1", "@svgr/cli": "^8.0.1", + "@swc/core": "^1.3.82", + "@swc/helpers": "^0.5.1", "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.4.3", "@types/react": "^18.2.0", @@ -26,7 +30,6 @@ "@types/webpack-bundle-analyzer": "^4.6.0", "chokidar": "^3.5.3", "cross-env": "^7.0.3", - "customize-cra": "^1.0.0", "date-fns": "^2.29.3", "debounce": "^1.2.0", "downshift": "7.6.0", @@ -36,42 +39,43 @@ "inter-ui": "^3.19.3", "jest": "^27.0.0", "jest-watch-typeahead": "^2.2.2", + "mdast-util-newline-to-break": "^2.0.0", "memoize-one": "^6.0.0", "pikaday": "1.8.0", "react": "18.2.0", - "react-app-rewired": "^2.2.1", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "18.2.0", "react-error-boundary": "^4.0.11", + "react-markdown": "^8.0.7", "react-merge-refs": "^1.1.0", "react-modal": "3.16.1", "react-redux": "7.2.1", "react-router-dom": "6.11.2", "react-scripts": "^5.0.1", + "react-simple-pull-to-refresh": "^1.3.3", "react-spring": "^9.7.1", "react-virtualized-auto-sizer": "^1.0.2", "redux": "^4.0.5", "redux-thunk": "^2.3.0", + "remark-gfm": "^3.0.1", "sass": "^1.63.6", + "swc-loader": "^0.2.3", + "terser-webpack-plugin": "^5.3.9", "uuid": "^9.0.0", "victory": "^36.6.8", - "webpack-bundle-analyzer": "^4.9.0" + "webpack-bundle-analyzer": "^4.9.1", + "xml2js": "^0.6.2" }, "scripts": { - "start": "cross-env PORT=3001 react-app-rewired start", + "start": "cross-env PORT=3001 craco start", "start:browser": "cross-env ./bin/watch-browser", "watch": "cross-env BROWSER=none yarn start", - "build": "cross-env INLINE_RUNTIME_CHUNK=false react-app-rewired build", + "build": "cross-env INLINE_RUNTIME_CHUNK=false craco build", "build:browser": "cross-env ./bin/build-browser", "generate:icons": "rm src/icons/*/*.js; cd src/icons && svgr --expand-props start --ext js -d . .", - "test": "react-app-rewired test", + "test": "craco test", "e2e": "npx playwright test --browser=chromium", "vrt": "cross-env VRT=true npx playwright test --browser=chromium" - }, - "jest": { - "setupFilesAfterEnv": [ - "/src/setupTests.js" - ] } } diff --git a/packages/desktop-client/playwright.config.js b/packages/desktop-client/playwright.config.js index b3c14554173..87b854e79ab 100644 --- a/packages/desktop-client/playwright.config.js +++ b/packages/desktop-client/playwright.config.js @@ -26,14 +26,4 @@ export default defineConfig({ trace: 'on-first-retry', ignoreHTTPSErrors: true, }, - expect: { - toHaveScreenshot: { - // Approx. 2% difference (1000 pixels) - maxDiffPixels: 1000, - }, - toMatchSnapshot: { - // 2% difference is acceptable - maxDiffPixelRatio: 0.02, - }, - }, }); diff --git a/packages/desktop-client/src/browser-preload.browser.js b/packages/desktop-client/src/browser-preload.browser.js index ddb1928b7f3..35b714c3889 100644 --- a/packages/desktop-client/src/browser-preload.browser.js +++ b/packages/desktop-client/src/browser-preload.browser.js @@ -1,5 +1,7 @@ import { initBackend as initSQLBackend } from 'absurd-sql/dist/indexeddb-main-thread'; +import * as Platform from 'loot-core/src/client/platform'; + import packageJson from '../package.json'; const backendWorkerUrl = new URL('./browser-server.js', import.meta.url); @@ -10,7 +12,7 @@ const backendWorkerUrl = new URL('./browser-server.js', import.meta.url); // everything else. let IS_DEV = process.env.NODE_ENV === 'development'; -let ACTUAL_VERSION = packageJson.version; +let ACTUAL_VERSION = Platform.isPlaywright ? '99.9.9' : packageJson.version; // *** Start the backend *** let worker; @@ -40,7 +42,6 @@ createBackendWorker(); global.Actual = { IS_DEV, ACTUAL_VERSION, - IS_FAKE_WEB: true, logToTerminal: (...args) => { console.log(...args); diff --git a/packages/desktop-client/src/components/AnimatedRefresh.tsx b/packages/desktop-client/src/components/AnimatedRefresh.tsx index 731016bfa5f..b80bd955a71 100644 --- a/packages/desktop-client/src/components/AnimatedRefresh.tsx +++ b/packages/desktop-client/src/components/AnimatedRefresh.tsx @@ -1,8 +1,9 @@ -import React, { type CSSProperties } from 'react'; +import React from 'react'; import { keyframes } from 'glamor'; import Refresh from '../icons/v1/Refresh'; +import { type CSSProperties } from '../style'; import View from './common/View'; @@ -22,7 +23,7 @@ export default function AnimatedRefresh({ }: AnimatedRefreshProps) { return ( diff --git a/packages/desktop-client/src/components/AppBackground.tsx b/packages/desktop-client/src/components/AppBackground.tsx index 1af44ea58b3..b23e84af6ab 100644 --- a/packages/desktop-client/src/components/AppBackground.tsx +++ b/packages/desktop-client/src/components/AppBackground.tsx @@ -21,7 +21,7 @@ function AppBackground({ initializing, loadingText }: AppBackgroundProps) { {(loadingText != null || initializing) && ( {loadingText} diff --git a/packages/desktop-client/src/components/DevelopmentTopBar.tsx b/packages/desktop-client/src/components/DevelopmentTopBar.tsx index 581f06f79c5..ca90ee74a27 100644 --- a/packages/desktop-client/src/components/DevelopmentTopBar.tsx +++ b/packages/desktop-client/src/components/DevelopmentTopBar.tsx @@ -6,19 +6,17 @@ import View from './common/View'; export default function DevelopmentTopBar() { return ( This is a demo build of Actual. diff --git a/packages/desktop-client/src/components/LoggedInUser.tsx b/packages/desktop-client/src/components/LoggedInUser.tsx index e92c4a53283..4f432c8077f 100644 --- a/packages/desktop-client/src/components/LoggedInUser.tsx +++ b/packages/desktop-client/src/components/LoggedInUser.tsx @@ -1,8 +1,8 @@ -import React, { useState, useEffect, type CSSProperties } from 'react'; +import React, { useState, useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useActions } from '../hooks/useActions'; -import { colors, styles } from '../style'; +import { colors, styles, type CSSProperties } from '../style'; import Button from './common/Button'; import Menu from './common/Menu'; @@ -77,14 +77,12 @@ export default function LoggedInUser({ if (loading && serverUrl) { return ( Connecting... @@ -92,7 +90,7 @@ export default function LoggedInUser({ } return ( - + diff --git a/packages/desktop-client/src/components/ManageRules.js b/packages/desktop-client/src/components/ManageRules.js index 98b433d4fec..11c9e3737ad 100644 --- a/packages/desktop-client/src/components/ManageRules.js +++ b/packages/desktop-client/src/components/ManageRules.js @@ -14,6 +14,7 @@ import * as undo from 'loot-core/src/platform/client/undo'; import { mapField, friendlyOp } from 'loot-core/src/shared/rules'; import { describeSchedule } from 'loot-core/src/shared/schedules'; +import useCategories from '../hooks/useCategories'; import useSelected, { SelectedProvider } from '../hooks/useSelected'; import { theme } from '../style'; @@ -88,12 +89,19 @@ function ManageRulesContent({ isModal, payeeId, setLoading }) { let dispatch = useDispatch(); let { data: schedules } = SchedulesQuery.useQuery(); - let filterData = useSelector(state => ({ + let { list: categories } = useCategories(); + let state = useSelector(state => ({ payees: state.queries.payees, - categories: state.queries.categories.list, accounts: state.queries.accounts, schedules, })); + let filterData = useMemo( + () => ({ + ...state, + categories, + }), + [state, categories], + ); let filteredRules = useMemo( () => diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index 9524ef06f67..2dc2423bd3c 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -1,9 +1,11 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; import { send } from 'loot-core/src/platform/client/fetch'; import { useActions } from '../hooks/useActions'; +import useCategories from '../hooks/useCategories'; import useSyncServerStatus from '../hooks/useSyncServerStatus'; import { type CommonModalProps } from '../types/modals'; @@ -33,12 +35,18 @@ export default function Modals() { const modalStack = useSelector(state => state.modals.modalStack); const isHidden = useSelector(state => state.modals.isHidden); const accounts = useSelector(state => state.queries.accounts); - const categoryGroups = useSelector(state => state.queries.categories.grouped); - const categories = useSelector(state => state.queries.categories.list); + const { grouped: categoryGroups, list: categories } = useCategories(); const budgetId = useSelector( state => state.prefs.local && state.prefs.local.id, ); const actions = useActions(); + const location = useLocation(); + + useEffect(() => { + if (modalStack.length > 0) { + actions.closeModal(); + } + }, [location]); const syncServerStatus = useSyncServerStatus(); diff --git a/packages/desktop-client/src/components/NotesButton.tsx b/packages/desktop-client/src/components/NotesButton.tsx index 3b46eb879d7..ac46eed8bb4 100644 --- a/packages/desktop-client/src/components/NotesButton.tsx +++ b/packages/desktop-client/src/components/NotesButton.tsx @@ -1,19 +1,88 @@ import React, { createRef, useState, useEffect } from 'react'; +import ReactMarkdown from 'react-markdown'; -import { type CSSProperties, css } from 'glamor'; +import { css } from 'glamor'; +import remarkGfm from 'remark-gfm'; import q from 'loot-core/src/client/query-helpers'; import { useLiveQuery } from 'loot-core/src/client/query-hooks'; import { send } from 'loot-core/src/platform/client/fetch'; import CustomNotesPaper from '../icons/v2/CustomNotesPaper'; -import { colors } from '../style'; +import { type CSSProperties, colors } from '../style'; +import { remarkBreaks, sequentialNewlinesPlugin } from '../util/markdown'; import Button from './common/Button'; import Text from './common/Text'; import View from './common/View'; import { Tooltip, useTooltip } from './tooltips'; +const remarkPlugins = [sequentialNewlinesPlugin, remarkGfm, remarkBreaks]; + +const markdownStyles = css({ + display: 'block', + maxWidth: 350, + padding: 8, + overflowWrap: 'break-word', + '& p': { + margin: 0, + ':not(:first-child)': { + marginTop: '0.25rem', + }, + }, + '& ul, & ol': { + listStylePosition: 'inside', + margin: 0, + paddingLeft: 0, + }, + '&>* ul, &>* ol': { + marginLeft: '1.5rem', + }, + '& li>p': { + display: 'contents', + }, + '& blockquote': { + paddingLeft: '0.75rem', + borderLeft: '3px solid ' + colors.p6, + margin: 0, + }, + '& hr': { + borderTop: 'none', + borderLeft: 'none', + borderRight: 'none', + borderBottom: '1px solid ' + colors.p9, + }, + '& code': { + backgroundColor: colors.p10, + padding: '0.1rem 0.5rem', + borderRadius: '0.25rem', + }, + '& pre': { + padding: '0.5rem', + backgroundColor: colors.p10, + borderRadius: '0.5rem', + margin: 0, + ':not(:first-child)': { + marginTop: '0.25rem', + }, + '& code': { + background: 'inherit', + padding: 0, + borderRadius: 0, + }, + }, + '& table, & th, & td': { + border: '1px solid ' + colors.p9, + }, + '& table': { + borderCollapse: 'collapse', + wordBreak: 'break-word', + }, + '& td': { + padding: '0.25rem 0.75rem', + }, +}); + type NotesTooltipProps = { editable?: boolean; defaultNotes?: string; @@ -40,27 +109,24 @@ function NotesTooltip({ {editable ? (