From 871de93f2d818aa0bb88c6fafa2e27c05bec4bf0 Mon Sep 17 00:00:00 2001 From: Koen van Staveren Date: Sun, 3 Nov 2024 18:51:34 +0100 Subject: [PATCH 01/16] ci: /update-vrt (#3764) * ci: /update-vrt * chore: release note * chore: code rabbit feedback. * chore: code rabbit feedback. * chore: code rabbit feedback. --- .github/workflows/update-vrt.yml | 50 ++++++++++++++++++++++++++++++++ upcoming-release-notes/3764.md | 6 ++++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/update-vrt.yml create mode 100644 upcoming-release-notes/3764.md diff --git a/.github/workflows/update-vrt.yml b/.github/workflows/update-vrt.yml new file mode 100644 index 00000000000..1806255dd52 --- /dev/null +++ b/.github/workflows/update-vrt.yml @@ -0,0 +1,50 @@ +name: /update-vrt +on: + issue_comment: + types: [ created ] + +permissions: + pull-requests: write + contents: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.issue.number }} + cancel-in-progress: true + +jobs: + update-vrt: + name: Update VRT + runs-on: ubuntu-latest + if: | + github.event.issue.pull_request && + contains(github.event.comment.body, '/update-vrt') + steps: + - name: Get PR branch + uses: xt0rted/pull-request-comment-branch@v2 + id: comment-branch + - uses: actions/checkout@v4 + with: + ref: ${{ steps.comment-branch.outputs.head_ref }} + - name: Set up environment + uses: ./.github/actions/setup + - name: Wait for Netlify build to finish + id: netlify + env: + COMMIT_SHA: ${{ steps.comment-branch.outputs.head_sha }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./.github/actions/netlify-wait-for-build + - name: Run VRT Tests on Netlify URL + run: yarn vrt --update-snapshots + env: + E2E_START_URL: ${{ steps.netlify.outputs.url }} + - name: Commit and push changes + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add "**/*.png" + if git diff --staged --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Update VRT" + git push origin HEAD:${{ steps.comment-branch.outputs.head_ref }} diff --git a/upcoming-release-notes/3764.md b/upcoming-release-notes/3764.md new file mode 100644 index 00000000000..e61ae13018c --- /dev/null +++ b/upcoming-release-notes/3764.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [UnderKoen] +--- + +Add `/update-vrt` as a command for PR's. From 6f07894be7417079aff2c90cb33300d92ad66230 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Sun, 3 Nov 2024 13:00:12 -0500 Subject: [PATCH 02/16] Auto-reload on app updates (#3693) * Auto-reload on app updates * update #1 * test * wip * green * wip * ux * green * red * cleanup * Add release notes * Unique notification name * Missing awaits * Try to fix Electron app * Simplify update checking * PR feedback --- .../src/browser-preload.browser.js | 23 +++++++++++++- .../desktop-client/src/components/App.tsx | 30 ++++++++++++++----- .../src/components/FinancesApp.tsx | 23 ++++++++++++++ packages/desktop-client/src/index.tsx | 7 ++++- packages/desktop-client/vite.config.mts | 2 +- packages/desktop-electron/preload.ts | 4 +++ packages/loot-core/src/client/actions/app.ts | 2 +- upcoming-release-notes/3693.md | 6 ++++ 8 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 upcoming-release-notes/3693.md diff --git a/packages/desktop-client/src/browser-preload.browser.js b/packages/desktop-client/src/browser-preload.browser.js index 4a04b0e23c0..95c048247c8 100644 --- a/packages/desktop-client/src/browser-preload.browser.js +++ b/packages/desktop-client/src/browser-preload.browser.js @@ -1,4 +1,5 @@ import { initBackend as initSQLBackend } from 'absurd-sql/dist/indexeddb-main-thread'; +import { registerSW } from 'virtual:pwa-register'; import * as Platform from 'loot-core/src/client/platform'; @@ -39,6 +40,19 @@ function createBackendWorker() { createBackendWorker(); +let isUpdateReadyForDownload = false; +let markUpdateReadyForDownload; +const isUpdateReadyForDownloadPromise = new Promise(resolve => { + markUpdateReadyForDownload = () => { + isUpdateReadyForDownload = true; + resolve(true); + }; +}); +const updateSW = registerSW({ + immediate: true, + onNeedRefresh: markUpdateReadyForDownload, +}); + global.Actual = { IS_DEV, ACTUAL_VERSION, @@ -140,7 +154,14 @@ global.Actual = { window.open(url, '_blank'); }, onEventFromMain: () => {}, - applyAppUpdate: () => {}, + isUpdateReadyForDownload: () => isUpdateReadyForDownload, + waitForUpdateReadyForDownload: () => isUpdateReadyForDownloadPromise, + applyAppUpdate: async () => { + updateSW(); + + // Wait for the app to reload + await new Promise(() => {}); + }, updateAppMenu: () => {}, ipcConnect: () => {}, diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx index f7785d58df4..75841207b0c 100644 --- a/packages/desktop-client/src/components/App.tsx +++ b/packages/desktop-client/src/components/App.tsx @@ -51,8 +51,20 @@ function AppInner() { const { showBoundary: showErrorBoundary } = useErrorBoundary(); const dispatch = useDispatch(); + const maybeUpdate = async (cb?: () => T): Promise => { + if (global.Actual.isUpdateReadyForDownload()) { + dispatch( + setAppState({ + loadingText: t('Downloading and applying update...'), + }), + ); + await global.Actual.applyAppUpdate(); + } + return cb?.(); + }; + async function init() { - const socketName = await global.Actual.getServerSocket(); + const socketName = await maybeUpdate(() => global.Actual.getServerSocket()); dispatch( setAppState({ @@ -86,14 +98,16 @@ function AppInner() { loadingText: t('Retrieving remote files...'), }), ); - send('get-remote-files').then(files => { - if (files) { - const remoteFile = files.find(f => f.fileId === cloudFileId); - if (remoteFile && remoteFile.deleted) { - dispatch(closeBudget()); - } + + const files = await send('get-remote-files'); + if (files) { + const remoteFile = files.find(f => f.fileId === cloudFileId); + if (remoteFile && remoteFile.deleted) { + dispatch(closeBudget()); } - }); + } + + await maybeUpdate(); } } diff --git a/packages/desktop-client/src/components/FinancesApp.tsx b/packages/desktop-client/src/components/FinancesApp.tsx index b585d5d30ee..387b8bbcac5 100644 --- a/packages/desktop-client/src/components/FinancesApp.tsx +++ b/packages/desktop-client/src/components/FinancesApp.tsx @@ -100,6 +100,29 @@ export function FinancesApp() { }, 100); }, []); + useEffect(() => { + async function run() { + await global.Actual.waitForUpdateReadyForDownload(); + dispatch( + addNotification({ + type: 'message', + title: t('A new version of Actual is available!'), + message: t('Click the button below to reload and apply the update.'), + sticky: true, + id: 'update-reload-notification', + button: { + title: t('Update now'), + action: async () => { + await global.Actual.applyAppUpdate(); + }, + }, + }), + ); + } + + run(); + }, []); + useEffect(() => { async function run() { const latestVersion = await getLatestVersion(); diff --git a/packages/desktop-client/src/index.tsx b/packages/desktop-client/src/index.tsx index c6800e70473..8d3e585a1c4 100644 --- a/packages/desktop-client/src/index.tsx +++ b/packages/desktop-client/src/index.tsx @@ -58,7 +58,12 @@ function rootReducer(state, action) { return appReducer(state, action); } -const store = createStore(rootReducer, undefined, applyMiddleware(thunk)); +const compose = window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] || (f => f); +const store = createStore( + rootReducer, + undefined, + compose(applyMiddleware(thunk)), +); const boundActions = bindActionCreators( actions, store.dispatch, diff --git a/packages/desktop-client/vite.config.mts b/packages/desktop-client/vite.config.mts index ca7a93a61e7..0f921416bea 100644 --- a/packages/desktop-client/vite.config.mts +++ b/packages/desktop-client/vite.config.mts @@ -152,7 +152,7 @@ export default defineConfig(async ({ mode }) => { mode === 'desktop' ? undefined : VitePWA({ - registerType: 'autoUpdate', + registerType: 'prompt', workbox: { globPatterns: [ '**/*.{js,css,html,txt,wasm,sql,sqlite,ico,png,woff2,webmanifest}', diff --git a/packages/desktop-electron/preload.ts b/packages/desktop-electron/preload.ts index c6f8327ebf5..1f98a39096a 100644 --- a/packages/desktop-electron/preload.ts +++ b/packages/desktop-electron/preload.ts @@ -66,6 +66,10 @@ contextBridge.exposeInMainWorld('Actual', { ipcRenderer.send('update-menu', budgetId); }, + // No auto-updates in the desktop app + isUpdateReadyForDownload: () => false, + waitForUpdateReadyForDownload: () => new Promise(() => {}), + getServerSocket: () => { return null; }, diff --git a/packages/loot-core/src/client/actions/app.ts b/packages/loot-core/src/client/actions/app.ts index 6f322b196c6..b2a707ffb26 100644 --- a/packages/loot-core/src/client/actions/app.ts +++ b/packages/loot-core/src/client/actions/app.ts @@ -18,7 +18,7 @@ export function setAppState(state: Partial): SetAppStateAction { export function updateApp() { return async (dispatch: Dispatch) => { - global.Actual.applyAppUpdate(); + await global.Actual.applyAppUpdate(); dispatch(setAppState({ updateInfo: null })); }; } diff --git a/upcoming-release-notes/3693.md b/upcoming-release-notes/3693.md new file mode 100644 index 00000000000..c6c3969d814 --- /dev/null +++ b/upcoming-release-notes/3693.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [jfdoming] +--- + +Auto-reload on app updates if possible, and show a notification if not possible From 41d59226358c00e968f4dd97e2c7fdff1fc8fc9f Mon Sep 17 00:00:00 2001 From: Koen van Staveren Date: Sun, 3 Nov 2024 20:26:53 +0100 Subject: [PATCH 03/16] Add context menu's (#3381) * feat: context menu on transactions * feat: context menu's on budget page * chore: release note * fix: losing focus on context menu * feat: schedules context menu * feat: payees context menu * feat: rules context menu * chore: update release note * chore: lint * fix: broken balance movement menu * fix: placement on context menu to be closer to cursor * feat: context menu on budget field * chore: lint * Update packages/desktop-client/src/components/transactions/TransactionsTable.jsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: fix merge * fix: e2e test * fix: moving of the popover in the sidebar * chore: lint * chore: add feature flag * chore: fix tsc * chore: fix test * Update packages/desktop-client/src/components/settings/Experimental.tsx Co-authored-by: Julian Dominguez-Schatz * fix: to budget button next steps didn't work * chore: lint --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Julian Dominguez-Schatz --- .../src/components/ManageRules.tsx | 8 + .../src/components/accounts/Account.tsx | 9 + .../src/components/budget/SidebarCategory.tsx | 12 +- .../src/components/budget/SidebarGroup.tsx | 12 +- .../budget/envelope/BalanceMovementMenu.tsx | 18 +- .../envelope/EnvelopeBudgetComponents.tsx | 15 ++ .../envelope/budgetsummary/ToBudget.tsx | 112 +++++---- .../envelope/budgetsummary/ToBudgetAmount.tsx | 5 +- .../src/components/common/Popover.tsx | 23 +- .../src/components/payees/ManagePayees.tsx | 9 +- .../src/components/payees/PayeeTable.tsx | 96 ++++---- .../src/components/payees/PayeeTableRow.tsx | 70 +++++- .../src/components/rules/RuleRow.tsx | 66 ++++- .../src/components/rules/RulesList.tsx | 3 + .../components/schedules/SchedulesTable.tsx | 226 +++++++++++------- .../src/components/settings/Experimental.tsx | 6 + .../transactions/TransactionList.jsx | 14 ++ .../transactions/TransactionMenu.tsx | 139 +++++++++++ .../transactions/TransactionsTable.jsx | 84 +++++++ .../transactions/TransactionsTable.test.jsx | 3 + .../src/hooks/useFeatureFlag.ts | 1 + packages/loot-core/src/types/prefs.d.ts | 3 +- upcoming-release-notes/3381.md | 6 + 23 files changed, 742 insertions(+), 198 deletions(-) create mode 100644 packages/desktop-client/src/components/transactions/TransactionMenu.tsx create mode 100644 upcoming-release-notes/3381.md diff --git a/packages/desktop-client/src/components/ManageRules.tsx b/packages/desktop-client/src/components/ManageRules.tsx index f3eb583005a..bb9ce717ae5 100644 --- a/packages/desktop-client/src/components/ManageRules.tsx +++ b/packages/desktop-client/src/components/ManageRules.tsx @@ -204,6 +204,13 @@ export function ManageRules({ setLoading(false); } + async function onDeleteRule(id: string) { + setLoading(true); + await send('rule-delete', id); + await loadRules(); + setLoading(false); + } + const onEditRule = useCallback(rule => { dispatch( pushModal('edit-rule', { @@ -306,6 +313,7 @@ export function ManageRules({ hoveredRule={hoveredRule} onHover={onHover} onEditRule={onEditRule} + onDeleteRule={rule => onDeleteRule(rule.id)} /> )} diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx index 1a8009db0b5..37936e10c7d 100644 --- a/packages/desktop-client/src/components/accounts/Account.tsx +++ b/packages/desktop-client/src/components/accounts/Account.tsx @@ -1803,6 +1803,15 @@ class AccountInternal extends PureComponent< sortField={this.state.sort?.field} ascDesc={this.state.sort?.ascDesc} onChange={this.onTransactionsChange} + onBatchDelete={this.onBatchDelete} + onBatchDuplicate={this.onBatchDuplicate} + onBatchLinkSchedule={this.onBatchLinkSchedule} + onBatchUnlinkSchedule={this.onBatchUnlinkSchedule} + onCreateRule={this.onCreateRule} + onScheduleAction={this.onScheduleAction} + onMakeAsNonSplitTransactions={ + this.onMakeAsNonSplitTransactions + } onRefetch={this.refetchTransactions} onCloseAddTransaction={() => this.setState({ isAdding: false }) diff --git a/packages/desktop-client/src/components/budget/SidebarCategory.tsx b/packages/desktop-client/src/components/budget/SidebarCategory.tsx index 7e20348176b..4d3b53e7a04 100644 --- a/packages/desktop-client/src/components/budget/SidebarCategory.tsx +++ b/packages/desktop-client/src/components/budget/SidebarCategory.tsx @@ -7,6 +7,7 @@ import { type CategoryEntity, } from 'loot-core/src/types/models'; +import { useFeatureFlag } from '../../hooks/useFeatureFlag'; import { SvgCheveronDown } from '../../icons/v1'; import { theme } from '../../style'; import { Button } from '../common/Button2'; @@ -51,6 +52,7 @@ export function SidebarCategory({ const temporary = category.id === 'new'; const [menuOpen, setMenuOpen] = useState(false); const triggerRef = useRef(null); + const contextMenusEnabled = useFeatureFlag('contextMenus'); const displayed = ( { + if (!contextMenusEnabled) return; + e.preventDefault(); + setMenuOpen(true); }} >
{category.name}
- +