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

Duplicating Budget and Budget Snapshots #3689

Open
wants to merge 48 commits into
base: master
Choose a base branch
from

Conversation

tlesicka
Copy link
Contributor

Adding an option from the budget management screen to duplicate the budget file. Also, will allow individual budget on the web-app to create dated snapshots like the Electron app.

Current known issue:
File copy will not allow large files to copy. Reading a file larger than default-db.sqlite will cause a read fault in packages/loot-core/src/platform/server/fs/index.web.ts

Needed:
Write new _copySqlFile function that will copy the database on a block-by-block basis rather than full file basis.

Still has an error in loot-core/src/platform/server/fs/index.web.ts, but will figure that out in a future commit.  Read comment on lines 294 and 152.
@actual-github-bot actual-github-bot bot changed the title Duplicating Budget and Budget Snapshots [WIP] Duplicating Budget and Budget Snapshots Oct 19, 2024
Copy link

netlify bot commented Oct 19, 2024

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit 22e26a0
🔍 Latest deploy log https://app.netlify.com/sites/actualbudget/deploys/67240bd1febeb80008bf1df6
😎 Deploy Preview https://deploy-preview-3689.demo.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

github-actions bot commented Oct 19, 2024

Bundle Stats — desktop-client

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
9 5.32 MB → 5.33 MB (+13.2 kB) +0.24%
Changeset
File Δ Size
src/components/modals/manager/DuplicateFileModal.tsx 🆕 +8.13 kB 0 B → 8.13 kB
src/components/modals/LoadBackupModal.tsx 📈 +2.76 kB (+55.72%) 4.95 kB → 7.71 kB
home/runner/work/actual/actual/packages/loot-core/src/client/actions/budgets.ts 📈 +818 B (+14.65%) 5.45 kB → 6.25 kB
src/components/sidebar/Sidebar.tsx 📈 +596 B (+9.63%) 6.04 kB → 6.62 kB
src/components/manager/BudgetList.tsx 📈 +666 B (+5.75%) 11.32 kB → 11.97 kB
src/components/Modals.tsx 📈 +282 B (+1.79%) 15.39 kB → 15.66 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
static/js/index.js 3.33 MB → 3.35 MB (+13.2 kB) +0.39%

Smaller

No assets were smaller

Unchanged

Asset File Size % Changed
static/js/indexeddb-main-thread-worker-e59fee74.js 13.5 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/usePreviewTransactions.js 1.64 kB 0%
static/js/AppliedFilters.js 21.3 kB 0%
static/js/BackgroundImage.js 122.29 kB 0%
static/js/narrow.js 82.57 kB 0%
static/js/wide.js 232.16 kB 0%
static/js/ReportRouter.js 1.5 MB 0%

Copy link
Contributor

github-actions bot commented Oct 19, 2024

Bundle Stats — loot-core

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
1 1.27 MB → 1.27 MB (+2.35 kB) +0.18%
Changeset
File Δ Size
packages/loot-core/src/server/backups.web.ts 🆕 +7.93 kB 0 B → 7.93 kB
packages/loot-core/src/platform/server/fs/index.web.ts 📈 +1.59 kB (+16.48%) 9.67 kB → 11.26 kB
packages/loot-core/src/server/main.ts 📈 +3.01 kB (+4.89%) 61.46 kB → 64.47 kB
packages/loot-core/src/server/util/budget-name.ts 📉 -10 B (-0.90%) 1.08 kB → 1.07 kB
packages/loot-core/src/server/backups.ts 🔥 -6.63 kB (-100%) 6.63 kB → 0 B
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
kcab.worker.js 1.27 MB → 1.27 MB (+2.35 kB) +0.18%

Smaller

No assets were smaller

Unchanged

No assets were unchanged

@youngcw youngcw linked an issue Oct 19, 2024 that may be closed by this pull request
2 tasks
@tlesicka
Copy link
Contributor Author

Created _copySqlFile to copy the database files on a block-by-block basis.

I would like several people to test the "duplicate" feature from the manage files screen. To test, open inspection panel to the database/storage panel (Application on Chrome). Under IndexedDB there will be a database called "actual" with a table called "files". Each budget file should have 2 entries in this files table:

  • /documents/Actual/budget-name-xxxxxxx/db.sqlite
  • /documents/Actual/budget-name-xxxxxx.metadata.json.

There should be a matching database called:

  • documents-Actual-budget-name-xxxxxx-db.sqlite

Click the budget item menu and select duplicate. Follow the prompts. When a budget is duplicated, all three files should be created. Then on deleting a budget, all three files need to be deleted. I need to know if the database itself fails to delete. This should work for both local and synced budgets.

Let me know what you find out and any other feedback.

@youngcw
Copy link
Member

youngcw commented Oct 20, 2024

I tried to make a backup but got an error. There is an entry in the backup list afterwords. Loading the backup either was really slow, or just not working. I didn't wait long enough to find out. It looked to be stuck at the "Closing" stage. I was testing on Brave, linux, incognito window.

@tlesicka
Copy link
Contributor Author

Thanks for trying that out. Figured there would still be bugs in the backup as that code needs to be split into web/electron platforms.

I think duplicating budget (which is different from backup) should be working correctly. To duplicate, the budget needs to be closed so you can see the list of budgets.

Sorry that I forgot to remove the backup menu item from the budget menu before asking for this test.

@tlesicka tlesicka changed the title [WIP] Duplicating Budget and Budget Snapshots Duplicating Budget and Budget Snapshots Oct 22, 2024
Copy link
Contributor

coderabbitai bot commented Oct 22, 2024

Walkthrough

The pull request introduces several significant changes across multiple components related to budget management in the desktop client. A new modal component, DuplicateFileModal, is added to handle budget file duplication, integrating into the existing modal rendering logic. The BudgetList component is updated to include an optional onDuplicate callback, enabling duplication actions for individual budget files. Additionally, the DuplicateFileModal component manages user input for naming duplicated budgets and includes validation and loading states. The backend functionality is expanded with a new duplicateBudget function, which manages the duplication process and error handling. The backups module is also enhanced with new functions for managing backups, including retrieval, creation, and deletion of backup files. Type definitions are updated across various files for better clarity and type safety. Overall, these changes enhance the functionality and user experience related to budget and file management.

Possibly related PRs

  • Undoable auto transfer notes + auto notes for cover #3411: The changes in the main PR introduce a new modal component, DuplicateFileModal, which is related to the handling of budget duplication. This is relevant to the Undoable auto transfer notes + auto notes for cover PR, which also involves modal management and user interactions related to budget operations.

  • Add UncategorizedButton to mobile budget view #3326: The main PR introduces a new modal for handling duplicate files, while this PR adds a button for uncategorized transactions in the mobile budget view. Both involve user interface components that enhance user interactions with budget-related functionalities.

  • :electron: Move file settings to the file management area #3584: The main PR adds new modals for file management, which relates to the changes in this PR that move file settings to the file management area, indicating a broader restructuring of how file-related functionalities are handled in the application.

  • :electron: Migrations out of sync modal #3600: The main PR introduces a modal for out-of-sync migrations, which is relevant to the changes in this PR that also involve modal management and user notifications regarding application state.

  • Adding a help modal for quick reference to goal template syntax #3691: The main PR adds a help modal for goal template syntax, which is related to the enhancements in user interface components and modals that improve user experience in managing budgeting features.

  • clean up accidental push #3695: The main PR involves cleanup related to accidental commits, which may not directly relate to the functionality changes but indicates ongoing maintenance and improvements in the codebase that could affect modal implementations and overall application stability.

Suggested labels

<:sparkles: Merged>

Suggested reviewers

  • youngcw

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 24

🧹 Outside diff range and nitpick comments (16)
packages/loot-core/src/server/util/budget-name.ts (1)

6-9: Approve changes with a minor suggestion for type safety.

The addition of the initialName parameter with a default value enhances the function's flexibility while maintaining backward compatibility. This change aligns well with the PR objectives, particularly in supporting the new duplicate feature.

Consider adding type annotations to improve type safety:

export async function uniqueFileName(
  existingFiles: { name: string }[],
  initialName: string = 'My Finances'
): Promise<string> {
  // ... rest of the function
}

This change would make the function's interface more explicit and catch potential type-related issues early.

packages/loot-core/src/server/backups.ts (1)

138-138: Add JSDoc comment for the removeAllBackups function.

To improve code documentation and clarify the function's purpose, consider adding a JSDoc comment above the removeAllBackups function.

Here's a suggested JSDoc comment:

/**
 * Removes all backup files associated with the specified budget ID.
 * This function is typically used when deleting a budget to ensure all related backups are also removed.
 * @param {string} id - The ID of the budget whose backups should be removed.
 * @returns {Promise<boolean>} A promise that resolves to true if all backups were successfully removed, false otherwise.
 */
export async function removeAllBackups(id: string): Promise<boolean> {
  // ... function implementation ...
}

This comment provides clear information about the function's purpose, parameters, and return value, which will be helpful for other developers using or maintaining this code.

packages/loot-core/src/client/actions/budgets.ts (1)

151-192: Good implementation with room for improvements

The duplicateBudget function is well-structured and follows the existing patterns in the file. However, there are a few areas that could be improved:

  1. Error handling: Consider adding explicit error handling for the 'duplicate-budget' action to manage potential failures gracefully.

  2. Loading state: When managePage is true, the loading state is not reset. Consider adding a loading state for this case as well.

  3. CloudSync parameter: The purpose of the cloudSync parameter is not clear from this function. Consider adding a comment to explain its usage or impact.

Here's a suggested improvement to address these points:

export function duplicateBudget({
  id,
  cloudId,
  oldName,
  newName,
  managePage,
  cloudSync,
}: {
  id?: string;
  cloudId?: string;
  oldName: string;
  newName: string;
  managePage?: boolean;
  cloudSync?: boolean; // Add a comment explaining the purpose of this parameter
}) {
  return async (dispatch: Dispatch) => {
    try {
      if (!managePage) {
        await dispatch(closeBudget());
        await dispatch(
          setAppState({
            loadingText:
              t('Duplicating:  ') + oldName + t('  --  to:  ') + newName,
          }),
        );
      } else {
        dispatch(setAppState({ loadingText: t('Duplicating budget...') }));
      }

      const newId = await send('duplicate-budget', {
        id,
        cloudId,
        newName,
        cloudSync,
      });

      dispatch(closeModal());

      if (managePage) {
        await dispatch(loadAllFiles());
        await dispatch(loadPrefs());
      } else {
        await dispatch(loadBudget(newId));
      }
    } catch (error) {
      // Handle error appropriately
      console.error('Error duplicating budget:', error);
      dispatch(setAppState({ loadingText: null }));
      // Consider showing an error modal or alert to the user
    } finally {
      dispatch(setAppState({ loadingText: null }));
    }
  };
}

This refactored version adds error handling, manages loading states for both cases, and suggests adding a comment for the cloudSync parameter.

packages/loot-core/src/client/state-types/modals.d.ts (2)

52-52: LGTM! Consider adding JSDoc comment for clarity.

The addition of the budgetId property to the 'load-backup' modal type is a good improvement. It allows for more flexibility in the backup loading process, which aligns with the PR objectives.

Consider adding a JSDoc comment to explain the purpose of the budgetId property and when it might be undefined. For example:

'load-backup': {
  /**
   * The ID of the budget to load a backup for.
   * If undefined, it may indicate loading a backup for the current budget or a user-selected budget.
   */
  budgetId: string | undefined
};

81-82: LGTM! Consider adding JSDoc comment for better documentation.

The addition of the 'duplicate-budget' modal type is well-aligned with the PR objectives. It provides the necessary properties for duplicating a budget file.

To improve clarity and maintainability, consider adding a JSDoc comment to explain the purpose of each property. For example:

'duplicate-budget': {
  /** The budget file to be duplicated */
  file: File;
  /** 
   * Indicates whether the duplication is initiated from a management page.
   * This may affect the behavior or UI of the duplication process.
   */
  managePage?: boolean;
};
packages/loot-core/src/types/server-handlers.d.ts (1)

319-324: LGTM! Consider adding JSDoc comments for clarity.

The new duplicate-budget method aligns well with the PR objectives. The method signature allows for duplicating both local and cloud-synced budgets, which is great for flexibility.

Consider adding JSDoc comments to clarify the purpose of each parameter and the return value. For example:

/**
 * Duplicates a budget file.
 * @param {Object} arg - The arguments for duplicating a budget.
 * @param {string} [arg.id] - The ID of the local budget to duplicate.
 * @param {string} [arg.cloudId] - The ID of the cloud-synced budget to duplicate.
 * @param {string} arg.newName - The name for the duplicated budget.
 * @param {boolean} [arg.cloudSync] - Whether to sync the duplicated budget to the cloud.
 * @returns {Promise<string>} The ID of the newly created budget.
 */
'duplicate-budget': (arg: {
  id?: string;
  cloudId?: string;
  newName: string;
  cloudSync?: boolean;
}) => Promise<string>;

This addition would improve the code's self-documentation and make it easier for other developers to understand and use the method correctly.

packages/desktop-client/src/components/manager/BudgetList.tsx (2)

Line range hint 67-92: LGTM with a suggestion for improved type safety

The changes to the FileMenu component look good. The dynamic generation of menu items based on the presence of the onDuplicate prop is a clean approach.

Consider using a more specific type for the onMenuSelect function parameter:

function onMenuSelect(type: 'delete' | 'duplicate') {
  // ...
}

This will provide better type safety and autocomplete support.


Line range hint 190-265: LGTM with suggestions for improved type safety and clarity

The addition of the onDuplicate functionality to the FileItem component is implemented correctly. The condition 'id' in file ensures that only local files can be duplicated, which is a good safeguard.

Consider the following improvements:

  1. Use type guards to improve type safety:
function isLocalFile(file: File): file is LocalFile {
  return 'id' in file;
}

// In the JSX:
onDuplicate={isLocalFile(file) ? () => onDuplicate(file) : undefined}
  1. Add a comment explaining why only files with 'id' can be duplicated:
// Only local files (with 'id') can be duplicated
onDuplicate={isLocalFile(file) ? () => onDuplicate(file) : undefined}

These changes will enhance type safety and code clarity.

packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1)

90-93: Adjust the style to align with the label in the form error message.

The marginLeft value in the FormError component may not align correctly with the form field label, especially if the label width changes. Consider using the same labelWidth value for consistency.

Apply this diff to use the same labelWidth value:

-    <FormError style={{ marginLeft: 75, color: theme.warningText }}>
+    <FormError style={{ marginLeft: 150, color: theme.warningText }}>

Alternatively, you can define a constant for labelWidth and reuse it.

 const labelWidth = 150;

 // ...

 <InlineField label={t('New Budget Name')} width="100%" labelWidth={labelWidth}>
   {/* ... */}
 </InlineField>

 // ...

 <FormError style={{ marginLeft: labelWidth, color: theme.warningText }}>
   {nameError}
 </FormError>
packages/desktop-client/src/components/sidebar/Sidebar.tsx (1)

Line range hint 150-209: Consider separating concerns within EditableBudgetName

The EditableBudgetName function currently handles displaying the budget name, editing the name, and managing the menu state. To improve code organization and maintainability, consider splitting this function into separate components:

  • BudgetNameDisplay: Responsible for displaying the budget name.
  • EditBudgetName: Manages the editing state and logic.
  • BudgetNameMenu: Handles the menu actions and state.

This separation aligns with previous practices in the project and makes each component focus on a single responsibility.

packages/loot-core/src/server/backups.web.ts (3)

81-103: Add type annotations for function parameters

The updateBackups function lacks type annotations for its backups parameter. Adding type annotations improves code readability and type safety.

- export async function updateBackups(backups) {
+ export async function updateBackups(backups: BackupWithDate[]) {

15-15: Specify the type for serviceInterval

Currently, serviceInterval is initialized to null without a specific type, which may lead to type inconsistencies. Explicitly specifying its type enhances code clarity and type safety.

- let serviceInterval = null;
+ let serviceInterval: NodeJS.Timeout | null = null;

115-117: Ensure uniqueness of backup filenames

While the current implementation uses milliseconds to ensure unique filenames, there is still a slight chance of collisions if backups are created rapidly. Consider adding a unique identifier or UUID to the backup filenames.

- const backupId = `${currentTime.toISOString().replace(/[-:.]/g, '')}-db.backup.sqlite`;
+ const uniqueId = Math.random().toString(36).substr(2, 9);
+ const backupId = `${dateFns.format(currentTime, 'yyyyMMddHHmmssSSS')}-${uniqueId}-db.backup.sqlite`;
packages/loot-core/src/platform/server/fs/index.web.ts (2)

91-93: Use TextDecoder for efficient decoding of ArrayBuffers

Instead of manually converting ArrayBuffers to strings using String.fromCharCode and Uint16Array, leverage the TextDecoder API for better performance and readability.

Apply this change:

if (opts?.encoding === 'utf8' && ArrayBuffer.isView(item.contents)) {
-  return String.fromCharCode.apply(
-    null,
-    new Uint16Array(item.contents.buffer),
-  );
+  const decoder = new TextDecoder('utf-8');
+  return decoder.decode(item.contents);
}

Line range hint 113-149: Ensure consistent error handling in _writeFile function

The _writeFile function always returns true, even when exceptions may occur during execution. It's recommended to allow exceptions to propagate or handle them appropriately, ensuring consistent return behavior.

Consider removing the return true; statement and letting the function implicitly return void, or handle errors and return false when exceptions occur.

packages/loot-core/src/server/main.ts (1)

1810-1882: Add unit tests for duplicate-budget handler

To ensure the new duplicate-budget functionality works as intended across different scenarios, consider adding unit tests. Test cases could include duplicating budgets with unique names, handling name collisions, error handling during file operations, and verifying cloud synchronization behavior.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a91a885 and f74a910.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/3689.md is excluded by !**/*.md
📒 Files selected for processing (12)
  • packages/desktop-client/src/components/Modals.tsx (2 hunks)
  • packages/desktop-client/src/components/manager/BudgetList.tsx (8 hunks)
  • packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1 hunks)
  • packages/desktop-client/src/components/sidebar/Sidebar.tsx (3 hunks)
  • packages/loot-core/src/client/actions/budgets.ts (1 hunks)
  • packages/loot-core/src/client/state-types/modals.d.ts (2 hunks)
  • packages/loot-core/src/platform/server/fs/index.web.ts (9 hunks)
  • packages/loot-core/src/server/backups.ts (1 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
  • packages/loot-core/src/server/main.ts (3 hunks)
  • packages/loot-core/src/server/util/budget-name.ts (1 hunks)
  • packages/loot-core/src/types/server-handlers.d.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/desktop-client/src/components/sidebar/Sidebar.tsx (2)
Learnt from: tlesicka
PR: actualbudget/actual#3593
File: packages/desktop-client/src/components/sidebar/Sidebar.tsx:112-116
Timestamp: 2024-10-10T02:29:05.655Z
Learning: In `packages/desktop-client/src/components/sidebar/BudgetName.tsx`, the `BudgetName` component consists of three parts: `BudgetName`, `EditBudgetName`, and the Menu. Keeping `EditBudgetName` as a separate component helps maintain cleaner code by separating concerns.
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/SidebarGroup.tsx:69-76
Timestamp: 2024-10-13T11:17:59.711Z
Learning: In the `SidebarGroup` component (`packages/desktop-client/src/components/budget/SidebarGroup.tsx`), context menu state management is handled in another file, ensuring that only one context menu is open at a time.
🔇 Additional comments (16)
packages/loot-core/src/server/backups.ts (1)

138-144: Overall assessment of the new removeAllBackups function.

The addition of the removeAllBackups function aligns well with the PR objectives for managing backups, particularly in the context of budget deletion. The function is appropriately placed within the backups module and follows the existing coding patterns.

While the basic functionality is sound, implementing the suggested improvements for error handling, success feedback, and documentation will enhance the robustness and maintainability of this feature.

packages/desktop-client/src/components/manager/BudgetList.tsx (3)

Line range hint 97-129: LGTM: FileMenuButton changes are consistent and correct

The modifications to the FileMenuButton component correctly incorporate the new onDuplicate functionality. The prop is properly passed down to the FileMenu component, maintaining consistency with the earlier changes.


278-284: LGTM: BudgetFiles changes are consistent

The addition of the onDuplicate prop to the BudgetFiles component and its propagation to each FileItem is implemented correctly. This change maintains consistency with the earlier modifications and ensures that the duplication functionality is available for each file in the list.


Line range hint 1-549: Summary: Duplication feature well-implemented with minor suggestions

The new duplication functionality has been successfully integrated into the BudgetList component and its child components. The implementation is consistent and aligns well with the PR objectives.

Key points:

  1. The FileMenu, FileMenuButton, FileItem, and BudgetFiles components have been updated to support the new duplication feature.
  2. The changes maintain consistency across components and follow React best practices.
  3. The integration with the existing modal system for handling duplication is appropriate.

Suggestions for improvement:

  1. Enhance type safety in several components using more specific types and type guards.
  2. Add error handling and validation when dispatching the duplication action.
  3. Consider adding comments to clarify the logic behind certain conditions (e.g., why only files with 'id' can be duplicated).

Overall, the implementation is solid, and these minor improvements will further enhance the code quality and maintainability.

packages/desktop-client/src/components/Modals.tsx (3)

47-47: LGTM: New import statement for DuplicateFileModal

The import statement for DuplicateFileModal is correctly added and follows the existing import style in the file.


582-589: LGTM: New case for 'duplicate-budget' modal

The new case for 'duplicate-budget' is correctly implemented:

  • It's properly placed within the switch statement.
  • The DuplicateFileModal component is rendered with the correct props:
    • file prop is passed as required.
    • managePage prop is correctly passed as an optional prop using the optional chaining operator.

This implementation aligns well with the existing structure and handles both required and optional props appropriately.


47-47: Summary: Successfully integrated DuplicateFileModal

The changes in this file successfully integrate the new DuplicateFileModal component into the existing modal system:

  1. The import statement is correctly added.
  2. The new case in the switch statement properly renders the DuplicateFileModal with the correct props.

These changes enable the application to display a modal for duplicating budget files, aligning with the PR objective of allowing users to duplicate budget files directly from the budget management screen.

Also applies to: 582-589

packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (2)

34-34: Confirm the logic for determining if a file is a cloud file.

The condition 'cloudFileId' in file && file.state !== 'broken' is used to determine if the file is a cloud file. Ensure that this logic correctly identifies cloud files in all scenarios.

If there's any doubt, you can verify the usage of isCloudFile throughout the codebase to ensure consistency.


61-64: ⚠️ Potential issue

Ensure consistent usage of localization in the modal header.

In the ModalHeader, the title is localized using t, but the rightContent component label is not localized. Ensure all user-facing text is localized.

If the ModalCloseButton component contains user-facing text, ensure it is localized within that component. If it doesn't, this comment can be disregarded.

packages/desktop-client/src/components/sidebar/Sidebar.tsx (6)

3-3: Importing useSelector for accessing Redux state

Good incorporation of useSelector from react-redux to access the Redux store's state. This allows you to retrieve allFiles later in the code.


8-12: Adding necessary actions for modal management

The addition of closeBudget, replaceModal, and pushModal actions enhances the component's ability to manage modals effectively.


14-19: Importing file types for type safety

Including the file types File, LocalFile, SyncableLocalFile, and SyncedLocalFile helps in maintaining type safety and clarity when working with different file states.


183-187: Effective use of TypeScript type guard

The isNonRemoteFile function correctly implements a type guard, enhancing type safety by narrowing down the file type. This ensures that only non-remote files are processed in subsequent code.


206-209: Handling the absence of budgetFile when duplicating

Good practice in checking if budgetFile exists before attempting to dispatch the pushModal action for duplication. This prevents potential errors if budgetFile is null or undefined.


225-225: Conditional inclusion of 'Duplicate budget' menu item

Including the 'Duplicate budget' option conditionally ensures that the menu displays relevant actions based on the availability of budgetFile. This enhances the user experience by preventing unnecessary options.

packages/loot-core/src/platform/server/fs/index.web.ts (1)

Line range hint 22-113: Excellent enhancement of type safety with added type annotations

The addition of type annotations to function parameters and return types throughout the codebase significantly improves code clarity and helps catch type-related errors during development.

packages/loot-core/src/server/backups.ts Outdated Show resolved Hide resolved
packages/loot-core/src/platform/server/fs/index.web.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/main.ts Show resolved Hide resolved
packages/loot-core/src/server/main.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/main.ts Show resolved Hide resolved
packages/loot-core/src/server/main.ts Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (8)
packages/desktop-client/src/components/modals/LoadBackupModal.tsx (3)

36-36: Consider adding a tooltip for better UX.

The "Revert to Latest" text might not be immediately clear to users. Consider adding a tooltip to explain what this action means.

 <Cell
   width="flex"
-  value={backup.date ? backup.date : t('Revert to Latest')}
+  value={
+    <View
+      title={t('Revert to the most recent version of your budget')}
+    >
+      {backup.date ? backup.date : t('Revert to Latest')}
+    </View>
+  }
   valueStyle={{ paddingLeft: 20 }}
 />

59-59: Enhance type safety with const assertion.

Consider using a const assertion to make the loading state types more type-safe.

-const [loading, setLoading] = useState<'revert' | 'backup' | null>(null);
+const [loading, setLoading] = useState<const LoadingState>(null);
+type LoadingState = 'revert' | 'backup' | null;

Line range hint 165-167: Add confirmation before loading backups.

Loading a backup is a significant action that should require user confirmation to prevent accidental clicks.

 <BackupTable
   backups={previousBackups}
-  onSelect={id => dispatch(loadBackup(budgetIdToLoad, id))}
+  onSelect={async id => {
+    const confirmed = window.confirm(
+      t('Are you sure you want to load this backup? This will replace your current budget.')
+    );
+    if (!confirmed) return;
+    try {
+      await dispatch(loadBackup(budgetIdToLoad, id));
+    } catch (error) {
+      console.error('Failed to load backup:', error);
+    }
+  }}
 />
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (3)

6-6: Consider using a more specific import path for the File type.

The import path loot-core/src/types/file seems to reach into the source directory. Consider importing from a public API path if available.


35-35: Localize the " - copy" suffix.

The " - copy" suffix should be localized to support different languages.

-  const [newName, setNewName] = useState(file.name + ' - copy');
+  const [newName, setNewName] = useState(file.name + t(' - copy'));

29-33: Add JSDoc documentation for the component.

Consider adding JSDoc documentation to describe the component's purpose, props, and usage examples.

/**
 * Modal component for duplicating budget files.
 * Supports both cloud and local file duplication.
 *
 * @param {Object} props
 * @param {File} props.file - The budget file to duplicate
 * @param {boolean} [props.managePage] - Whether to return to manage page after duplication
 * @param {'none' | 'original' | 'copy'} [props.loadBudget='none'] - Which budget to load after duplication
 *
 * @example
 * <DuplicateFileModal
 *   file={budgetFile}
 *   managePage={true}
 *   loadBudget="copy"
 * />
 */
packages/loot-core/src/server/backups.web.ts (1)

15-15: Add cleanup handler for backup service

The backup service uses a global variable for the interval. Consider adding a cleanup handler to ensure the service is properly stopped when the application exits.

Add a cleanup handler in the main application:

process.on('exit', () => {
  if (serviceInterval) {
    stopBackupService();
  }
});

Also applies to: 232-254

packages/loot-core/src/platform/server/fs/index.web.ts (1)

152-193: Enhance error logging in _copySqlFile.

While the implementation is solid, the error message could be more descriptive to aid debugging.

Consider updating the error logging to include more context:

-    console.error('Failed to copy database file', error);
+    console.error(
+      `Failed to copy database file from ${fromDbPath} to ${toDbPath}:`,
+      error,
+      '\nBlock size:',
+      blockSize,
+      'File size:',
+      fileSize
+    );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f275142 and 8e81f44.

📒 Files selected for processing (6)
  • packages/desktop-client/src/components/modals/LoadBackupModal.tsx (7 hunks)
  • packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1 hunks)
  • packages/desktop-client/src/components/sidebar/Sidebar.tsx (3 hunks)
  • packages/loot-core/src/platform/server/fs/index.web.ts (8 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
  • packages/loot-core/src/server/main.ts (3 hunks)
🧰 Additional context used
📓 Learnings (4)
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:181-196
Timestamp: 2024-10-22T11:55:03.192Z
Learning: After refactoring `nameError` in `DuplicateFileModal.tsx`, it's not necessary to use `setNameError` in the `onPress` handlers when validation fails.
packages/desktop-client/src/components/sidebar/Sidebar.tsx (2)
Learnt from: tlesicka
PR: actualbudget/actual#3593
File: packages/desktop-client/src/components/sidebar/Sidebar.tsx:112-116
Timestamp: 2024-10-10T02:29:05.655Z
Learning: In `packages/desktop-client/src/components/sidebar/BudgetName.tsx`, the `BudgetName` component consists of three parts: `BudgetName`, `EditBudgetName`, and the Menu. Keeping `EditBudgetName` as a separate component helps maintain cleaner code by separating concerns.
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/SidebarGroup.tsx:69-76
Timestamp: 2024-10-13T11:17:59.711Z
Learning: In the `SidebarGroup` component (`packages/desktop-client/src/components/budget/SidebarGroup.tsx`), context menu state management is handled in another file, ensuring that only one context menu is open at a time.
packages/loot-core/src/server/backups.web.ts (2)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:203-207
Timestamp: 2024-10-24T05:09:44.115Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, developers prefer to keep the error handling for cloud storage uploads inline rather than extracting it into a separate function. Avoid suggesting this refactoring in future reviews.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:204-208
Timestamp: 2024-10-24T05:05:06.973Z
Learning: In `backups.web.ts`, prefer to keep the `try-catch` blocks for `cloudStorage.upload()` as is, rather than refactoring into a helper function.
packages/loot-core/src/server/main.ts (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1800-1805
Timestamp: 2024-10-23T07:50:40.515Z
Learning: Paths are not fully defined on initialization; they become fully defined when a budget is loaded. Therefore, loading and closing the budget before deleting is necessary to ensure that paths are properly resolved.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1873-1877
Timestamp: 2024-10-23T07:39:50.053Z
Learning: In the `duplicate-budget` function in `packages/loot-core/src/server/main.ts`, when `loadBudget` returns an object with an `error` property where `error` is a string, returning `error` directly is consistent with the function's declared return type `Promise<string>`.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1807-1809
Timestamp: 2024-10-23T07:38:25.699Z
Learning: In the function `delete-budget` in `packages/loot-core/src/server/main.ts`, the function `removeAllBackups(id)` handles its own errors internally, so additional error handling when calling it is unnecessary.
🔇 Additional comments (14)
packages/desktop-client/src/components/modals/LoadBackupModal.tsx (1)

Line range hint 1-173: Verify backup functionality implementation.

The implementation looks solid overall, but let's verify the backup-related functionality across the codebase.

✅ Verification successful

Let me analyze the results and generate additional verification scripts to check backup functionality implementation.

The previous results showed backup-related translations and cleanup functions, but we need to verify the core backup functionality, error handling, and event handling to ensure proper implementation.


Based on the verification results, I can now provide a final response about the backup functionality implementation:

Backup functionality is properly implemented with appropriate error handling

The implementation shows a robust backup system with:

  1. Core backup operations:

    • makeBackup and loadBackup functions in both web and electron environments
    • Proper event handling with backups-get and backups-updated events
    • Error handling for backup operations with try-catch blocks
  2. UI Integration:

    • LoadBackupModal properly dispatches backup actions
    • Real-time backup updates through event listeners
    • Loading states and error handling for user feedback
  3. Architecture:

    • Clean separation between client actions and server handlers
    • Consistent backup operations across web and electron platforms
    • Proper cleanup with removeAllBackups functionality

The implementation aligns with the PR objectives and includes proper error handling, loading states, and user feedback mechanisms.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify backup functionality implementation

# Test 1: Check for consistent error handling patterns
echo "Checking error handling patterns in backup-related code..."
rg -l "try\s*{.*?}\s*catch\s*\(.*?\)\s*{.*?}\s*finally\s*{.*?}" --type ts

# Test 2: Check for backup-related translations
echo "Checking backup-related translations..."
rg "Trans|useTranslation.*?backup" --type ts

# Test 3: Check for proper cleanup of backup files
echo "Checking backup cleanup implementation..."
rg "removeAllBackups|deleteBackup" --type ts

Length of output: 200623


Script:

#!/bin/bash
# Description: Verify backup functionality implementation

# Test 1: Check backup-related handlers and actions
echo "Checking backup-related handlers and actions..."
rg "loadBackup|makeBackup" --type ts -A 2

# Test 2: Check backup file operations
echo "Checking backup file operations..."
rg "backups-get|backups-updated" --type ts -A 2

# Test 3: Check backup error handling
echo "Checking backup error handling..."
rg "catch.*error.*backup" --type ts -A 2

Length of output: 6046

packages/desktop-client/src/components/sidebar/Sidebar.tsx (4)

3-15: LGTM: Import statements are well-organized

The new imports for Redux and File types are properly structured and necessary for the added functionality.


176-187: LGTM: Budget file state management is well-implemented

The state management for budget files is clean and efficient, using appropriate hooks and selectors.


196-200: LGTM: Backup menu handling is properly guarded

The backup functionality is well-implemented with appropriate null checking for budgetId.


216-216: 🛠️ Refactor suggestion

Consider conditional rendering of backup menu item

Based on the PR discussions, the backup menu item should potentially be conditionally rendered or removed from this menu. Consider adding a condition to show this option only when appropriate.

  const items = [
    { name: 'rename', text: t('Rename budget') },
-    { name: 'backups', text: t('Backups') },
+    ...(budgetFile ? [{ name: 'backups', text: t('Backups') }] : []),
    { name: 'settings', text: t('Settings') },
    { name: 'close', text: t('Close file') },
  ];
packages/loot-core/src/server/backups.web.ts (1)

1-1: Consider removing @ts-strict-ignore

The presence of @ts-strict-ignore suggests potential type safety issues. Consider addressing these issues to enable strict type checking, which would help catch type-related bugs early in development.

packages/loot-core/src/platform/server/fs/index.web.ts (1)

22-22: LGTM: Type annotations enhance code clarity and safety.

The addition of TypeScript type annotations to function signatures improves code maintainability and helps catch potential type-related issues at compile time.

Also applies to: 26-26, 50-50, 70-70, 104-104, 113-113, 195-195, 319-322, 343-343, 347-347, 351-351

packages/loot-core/src/server/main.ts (7)

38-38: LGTM: Clean import addition

The addition of removeAllBackups to the existing backup-related imports is well-organized and properly grouped with related functionality.


1800-1809: LGTM: Proper web platform handling

The implementation correctly handles backup cleanup for web platform. The loading and closing of the budget before deletion is a necessary workaround to ensure paths are properly resolved, as confirmed by previous discussions.


1858-1881: LGTM: Robust error handling and cleanup

The implementation includes proper error handling with cleanup of partially created files in case of failure. This ensures no orphaned files are left in case of errors during the duplication process.


1890-1898: LGTM: Cloud sync implementation

The cloud sync implementation properly handles offline scenarios by catching and logging upload errors without blocking the local file creation.


1900-1903: LGTM: Flexible budget loading options

The implementation provides good flexibility in handling which budget to open after duplication through the open parameter.


2001-2002: LGTM: Improved type safety

The addition of the string type annotation for the id parameter enhances type safety and code clarity.


1816-1823: ⚠️ Potential issue

Add input validation for newName parameter

The function should validate the newName parameter to ensure it's not empty or contains invalid characters.

Apply this diff:

+  if (!newName?.trim()) {
+    throw new Error('Budget name is required and cannot be empty');
+  }
+  if (!/^[a-zA-Z0-9\s-_()]+$/.test(newName)) {
+    throw new Error('Budget name contains invalid characters');
+  }

Likely invalid or redundant comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (9)
packages/desktop-client/src/components/modals/LoadBackupModal.tsx (2)

24-24: Consider adding aria-label for better accessibility.

The translation implementation looks good. Consider adding an aria-label to improve accessibility for screen readers.

 <Row
   key={backup.id}
   collapsed={idx !== 0}
   onClick={() => onSelect(backup.id)}
   style={{ cursor: 'pointer' }}
+  aria-label={t('Select backup from {{date}}', { date: backup.date || t('latest version') })}
 >

Also applies to: 36-36


86-86: Consider adding explicit translation keys for better maintainability.

While the translation implementation is functional, using explicit keys can help prevent conflicts and make maintenance easier.

-<Trans>You are currently working from a backup.</Trans>
+<Trans i18nKey="backup.current_state">You are currently working from a backup.</Trans>

-<Trans>You can load a different backup or revert to the original version below.</Trans>
+<Trans i18nKey="backup.instructions">You can load a different backup or revert to the original version below.</Trans>

Also applies to: 102-107, 132-141, 166-166

packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (3)

33-50: Consider localizing the "copy" suffix in the initial name.

The initial name is set with a hardcoded " - copy" suffix. Consider using the translation function for better internationalization support.

-  const [newName, setNewName] = useState(file.name + ' - copy');
+  const [newName, setNewName] = useState(file.name + t(' - copy'));

51-74: Document the regex pattern for valid characters.

The regex pattern /^[a-zA-Z0-9 .\-_()]+$/ for valid characters should be documented to explain which characters are allowed and why.

+  // Only allow alphanumeric characters, spaces, dots, hyphens, underscores, and parentheses
+  // This ensures compatibility across different file systems and prevents special characters
+  // that might cause issues
   if (!/^[a-zA-Z0-9 .\-_()]+$/.test(trimmedName)) {

231-233: Make button text more consistent.

The button text combines two translation components, which could lead to inconsistent translations or word order issues in different languages.

-                    <Trans>Duplicate budget</Trans>
-                    {isCloudFile && <Trans> locally only</Trans>}
+                    <Trans>
+                      {isCloudFile ? 'Duplicate budget locally only' : 'Duplicate budget'}
+                    </Trans>
packages/loot-core/src/server/backups.web.ts (2)

22-74: Consider adding retry logic for file operations.

The functions handle basic error cases well, but file operations might fail due to temporary file system issues. Consider adding retry logic for critical operations.

Example implementation:

async function withRetry<T>(operation: () => Promise<T>, retries = 3): Promise<T> {
  let lastError;
  for (let i = 0; i < retries; i++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error;
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
    }
  }
  throw lastError;
}

76-139: Add JSDoc comments for backup retention policy.

The backup retention logic (3 backups for current day, 1 for other days, max 10 total) should be documented using JSDoc for better maintainability.

Add documentation like this:

/**
 * Updates the list of backups according to retention policy:
 * - Keeps 3 backups for the current day
 * - Keeps 1 backup per day for previous days
 * - Maintains a maximum of 10 backups total
 * @param backups Array of backup objects with date information
 * @returns Array of backup IDs that should be removed
 */
export async function updateBackups(backups) {
packages/loot-core/src/client/state-types/modals.d.ts (1)

87-116: LGTM: Well-structured type definition with suggested improvements

The type definition is well-documented and provides proper type safety for the budget duplication feature. Consider these optional improvements for better maintainability:

  1. Extract shared types:
type LoadBudgetOption = 'none' | 'original' | 'copy';
type DuplicateStatus = 'success' | 'failed' | 'canceled';

type DuplicateBudgetModal = {
  file: File;
  managePage?: boolean;
  loadBudget?: LoadBudgetOption;
  onComplete?: (event: {
    status: DuplicateStatus;
    error?: Error;
  }) => void;
};
  1. Consider adding a type guard or validation for the File type to ensure it's a budget file:
type BudgetFile = File & {
  readonly extension: '.budget';
};
packages/loot-core/src/server/main.ts (1)

1848-1863: Consider using a whitelist approach for metadata cleanup.

The current blacklist approach for metadata cleanup could be made more maintainable by explicitly listing the properties to keep.

-  [
-    'cloudFileId',
-    'groupId',
-    'lastUploaded',
-    'encryptKeyId',
-    'lastSyncedTimestamp',
-  ].forEach(item => {
-    if (metadata[item]) delete metadata[item];
-  });
+  const newMetadata = {
+    id: newId,
+    budgetName: budgetName,
+    // Add other properties that should be preserved
+    ...Object.fromEntries(
+      Object.entries(metadata).filter(([key]) => 
+        !['cloudFileId', 'groupId', 'lastUploaded', 'encryptKeyId', 'lastSyncedTimestamp'].includes(key)
+      )
+    )
+  };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8e81f44 and 0b226b8.

📒 Files selected for processing (6)
  • packages/desktop-client/src/components/Modals.tsx (2 hunks)
  • packages/desktop-client/src/components/modals/LoadBackupModal.tsx (7 hunks)
  • packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1 hunks)
  • packages/loot-core/src/client/state-types/modals.d.ts (2 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
  • packages/loot-core/src/server/main.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/desktop-client/src/components/Modals.tsx
🧰 Additional context used
📓 Learnings (3)
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:144-156
Timestamp: 2024-10-25T04:49:31.861Z
Learning: In `packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx`, styles for buttons may differ for each button in the future, so avoid suggesting extraction of common styles in this file.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:181-196
Timestamp: 2024-10-22T11:55:03.192Z
Learning: After refactoring `nameError` in `DuplicateFileModal.tsx`, it's not necessary to use `setNameError` in the `onPress` handlers when validation fails.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:181-196
Timestamp: 2024-10-22T11:55:03.192Z
Learning: In `DuplicateFileModal.tsx`, `trim()` is performed during the blur event when validating `newName`, so additional trimming is not necessary in the `onPress` handlers.
packages/loot-core/src/server/backups.web.ts (2)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:203-207
Timestamp: 2024-10-24T05:09:44.115Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, developers prefer to keep the error handling for cloud storage uploads inline rather than extracting it into a separate function. Avoid suggesting this refactoring in future reviews.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:204-208
Timestamp: 2024-10-24T05:05:06.973Z
Learning: In `backups.web.ts`, prefer to keep the `try-catch` blocks for `cloudStorage.upload()` as is, rather than refactoring into a helper function.
packages/loot-core/src/server/main.ts (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1800-1805
Timestamp: 2024-10-23T07:50:40.515Z
Learning: Paths are not fully defined on initialization; they become fully defined when a budget is loaded. Therefore, loading and closing the budget before deleting is necessary to ensure that paths are properly resolved.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1873-1877
Timestamp: 2024-10-23T07:39:50.053Z
Learning: In the `duplicate-budget` function in `packages/loot-core/src/server/main.ts`, when `loadBudget` returns an object with an `error` property where `error` is a string, returning `error` directly is consistent with the function's declared return type `Promise<string>`.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1807-1809
Timestamp: 2024-10-23T07:38:25.699Z
Learning: In the function `delete-budget` in `packages/loot-core/src/server/main.ts`, the function `removeAllBackups(id)` handles its own errors internally, so additional error handling when calling it is unnecessary.
🪛 GitHub Check: lint
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx

[warning] 97-97:
Delete ·

🔇 Additional comments (14)
packages/desktop-client/src/components/modals/LoadBackupModal.tsx (3)

2-2: LGTM: i18n imports are properly organized.

The addition of i18n imports is well-structured and follows good practices.


56-60: LGTM: Well-structured state management.

The loading state implementation is clean and follows React best practices. The union type for loading states provides good type safety.


113-124: Verify handling of large backup files.

Given the PR objectives mention issues with large file handling, ensure that the backup loading and creation operations can handle timeouts and large file operations gracefully.

Also applies to: 148-157

packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1)

1-31: LGTM! Well-structured imports and type definitions.

The imports are organized logically, and the DuplicateFileProps type is well-defined with clear documentation of optional properties.

packages/loot-core/src/server/backups.web.ts (3)

1-21: LGTM! Well-structured type definitions and imports.

The type definitions provide a clear structure for backup data, and the imports cover all necessary dependencies.


241-263: LGTM! Well-implemented backup service.

The backup service implementation includes proper error handling, cleanup, and clear logging. The interval management is robust.


164-239: Verify atomic operations during backup loading.

The sequence of file operations (copy, remove) could lead to inconsistent state if interrupted. Consider implementing atomic operations or a rollback mechanism.

packages/loot-core/src/client/state-types/modals.d.ts (1)

52-58: LGTM: Well-documented type enhancement for load-backup modal

The addition of the budgetId parameter with proper JSDoc documentation improves type safety and clarity. The optional nature (string | undefined) appropriately handles both specific budget loading and user selection scenarios.

packages/loot-core/src/server/main.ts (6)

38-38: LGTM: Import of removeAllBackups is appropriate.

The addition of removeAllBackups to the imports aligns with the PR's backup management requirements.


1800-1809: LGTM: Proper handling of budget deletion with path resolution.

The implementation correctly:

  1. Loads and closes the budget to ensure proper path resolution (necessary workaround as confirmed by learnings)
  2. Handles backup cleanup for web platform

1823-1828: LGTM: Strong input validation for budget name.

The validation ensures:

  1. Non-empty trimmed name
  2. Only allows alphanumeric characters, spaces, and basic punctuation

1864-1887: LGTM: Robust error handling with cleanup.

The implementation properly:

  1. Wraps file operations in try-catch
  2. Cleans up partially created files on error
  3. Provides clear error messages

1896-1904: LGTM: Appropriate cloud sync handling.

The cloud sync implementation:

  1. Makes sync optional
  2. Handles errors gracefully
  3. Logs warnings for sync failures

2007-2008: LGTM: Added type safety to loadBudget.

The addition of the string type annotation for the id parameter improves type safety and code clarity.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (1)
packages/desktop-client/src/components/modals/LoadBackupModal.tsx (1)

Line range hint 28-46: Consider enhancing accessibility for the backup table rows.

While the translation implementation is correct, the table rows could benefit from improved accessibility.

Add ARIA attributes to make the table more accessible:

 <Row
   key={backup.id}
   collapsed={idx !== 0}
   onClick={() => onSelect(backup.id)}
-  style={{ cursor: 'pointer' }}
+  style={{ cursor: 'pointer' }}
+  role="button"
+  aria-label={t('Load backup from {{date}}', { date: backup.date || t('latest version') })}
+  tabIndex={0}
+  onKeyPress={(e) => e.key === 'Enter' && onSelect(backup.id)}
 >
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0b226b8 and ef9eb70.

📒 Files selected for processing (2)
  • packages/desktop-client/src/components/modals/LoadBackupModal.tsx (6 hunks)
  • packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:144-156
Timestamp: 2024-10-25T04:49:31.861Z
Learning: In `packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx`, styles for buttons may differ for each button in the future, so avoid suggesting extraction of common styles in this file.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:181-196
Timestamp: 2024-10-22T11:55:03.192Z
Learning: After refactoring `nameError` in `DuplicateFileModal.tsx`, it's not necessary to use `setNameError` in the `onPress` handlers when validation fails.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx:181-196
Timestamp: 2024-10-22T11:55:03.192Z
Learning: In `DuplicateFileModal.tsx`, `trim()` is performed during the blur event when validating `newName`, so additional trimming is not necessary in the `onPress` handlers.
🔇 Additional comments (3)
packages/desktop-client/src/components/modals/LoadBackupModal.tsx (2)

63-64: LGTM: Well-implemented loading state management.

The loading state implementation using discriminated union type is clean and type-safe.


113-145: Consider adding a confirmation dialog for revert action.

The revert action is irreversible and could benefit from a confirmation step.

This was already suggested in a previous review comment and should be implemented.

packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx (1)

1-31: LGTM! Well-organized imports and type definitions.

The imports are logically organized, and the DuplicateFileProps type is well-defined with clear optional properties and callback types.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
packages/loot-core/src/server/backups.d.ts (1)

1-22: Consider scalability aspects of backup management.

The type system and function declarations provide a good foundation for backup management. However, given the PR objective's mention of issues with large files, consider these architectural points:

  1. Progress Tracking: Consider adding types for progress tracking during backup operations, especially for large files.
  2. Chunked Operations: The type system could benefit from supporting chunked/streamed backup operations.
  3. Error States: Consider extending the types to include specific error states or validation results.

Example enhancement for progress tracking:

export interface BackupProgress {
  totalBytes: number;
  processedBytes: number;
  status: 'preparing' | 'copying' | 'finalizing';
}

export function makeBackup(id: string): Promise<void> & {
  progress: () => BackupProgress;
};
🧰 Tools
🪛 Biome

[error] 4-4: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 6-6: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 8-8: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 16-16: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 18-18: 'async' modifier cannot be used in an ambient context.

(parse)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ef9eb70 and 66760ad.

📒 Files selected for processing (4)
  • packages/desktop-client/src/components/modals/LoadBackupModal.tsx (6 hunks)
  • packages/loot-core/src/server/backups.d.ts (1 hunks)
  • packages/loot-core/src/server/backups.electron.ts (4 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/desktop-client/src/components/modals/LoadBackupModal.tsx
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/backups.web.ts (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:141-162
Timestamp: 2024-10-25T06:04:25.867Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, the `removeAllBackups` function should remove backups sequentially to prevent database errors. Parallel execution in this context should be avoided.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:203-207
Timestamp: 2024-10-24T05:09:44.115Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, developers prefer to keep the error handling for cloud storage uploads inline rather than extracting it into a separate function. Avoid suggesting this refactoring in future reviews.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:204-208
Timestamp: 2024-10-24T05:05:06.973Z
Learning: In `backups.web.ts`, prefer to keep the `try-catch` blocks for `cloudStorage.upload()` as is, rather than refactoring into a helper function.
🪛 Biome
packages/loot-core/src/server/backups.d.ts

[error] 4-4: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 6-6: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 8-8: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 16-16: 'async' modifier cannot be used in an ambient context.

(parse)


[error] 18-18: 'async' modifier cannot be used in an ambient context.

(parse)

🔇 Additional comments (11)
packages/loot-core/src/server/backups.d.ts (1)

1-2: Well-structured type definitions!

The discriminated union pattern with LatestBackup having isLatest: true provides good type safety and clear semantics.

packages/loot-core/src/server/backups.electron.ts (3)

10-10: LGTM: Type safety improvements

The addition of explicit types for Backup and LatestBackup, along with the more precise typing of serviceInterval, enhances type safety and maintainability.

Also applies to: 18-18


258-261: LGTM: Improved service cleanup

The changes to stopBackupService improve robustness by adding a null check and properly cleaning up the interval reference.


72-78: Verify date formatting in UI components

The commented-out code previously handled date formatting for display. Removing this formatting logic shifts the responsibility to the UI components.

Let's verify that the UI components properly handle the date formatting:

✅ Verification successful

Based on the search results, I can now provide a definitive response about the date formatting concern:

Date formatting is properly handled in UI components

The removal of date formatting from getAvailableBackups is safe because:

  • The LoadBackupModal component (packages/desktop-client/src/components/modals/LoadBackupModal.tsx) properly formats backup dates using dateFns.format(backup.date, 'yyyy-MM-dd H:mm')
  • This maintains the same format that was previously in the commented-out code
  • The change appropriately moves presentation logic to the UI layer while keeping the raw Date object in the data layer
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if date formatting is handled in UI components that consume backup dates

# Search for components that render backup dates
rg -A 5 "backup.*date|backup.*Date" --type ts --type tsx

# Search for date formatting logic in these components
rg -A 5 "format.*date|format.*Date" --type ts --type tsx

Length of output: 173


Script:

#!/bin/bash
# Let's try different search patterns and file extensions

# Search for backup date usage in TypeScript files
rg -A 5 "backup.*date|backup.*Date" --type typescript

# Search for date formatting in TypeScript files
rg -A 5 "format.*date|format.*Date" --type typescript

# Search for backup-related components and their imports
rg -A 5 "import.*backup|from.*backup" --type typescript

# Search specifically in the UI/client code for backup-related code
fd -e ts -e tsx . packages/loot-core/src/client | xargs rg -l "backup"

Length of output: 544


Script:

#!/bin/bash
# Let's examine the backup-related files we found

# Check the backup actions implementation
cat packages/loot-core/src/client/actions/backups.ts

# Check modal types for backup-related components
cat packages/loot-core/src/client/state-types/modals.d.ts

# Let's also try searching with ripgrep without file type specification
rg -A 5 "backup.*date|backup.*Date"

# Search for date formatting patterns across all files
rg -A 5 "format.*date|format.*Date"

Length of output: 62235

packages/loot-core/src/server/backups.web.ts (7)

22-48: Robust error handling and date parsing in getBackups function

The getBackups function correctly handles potential invalid dates and filters out null backups. The use of dateFns.parse ensures proper date parsing, and the backups are sorted in descending order of their timestamps.


79-103: Confirm backup removal logic in updateBackups function

The logic for retaining backups seems appropriate, but please ensure that it aligns with the desired backup retention policy:

  • Keeps up to 3 backups for the current day.
  • Keeps 1 backup for each previous day.
  • Limits the total number of backups to 10, removing the oldest ones beyond this limit.

Please verify that this logic meets the project's backup retention requirements and consider adding unit tests to validate this behavior.


105-145: Efficient backup creation in makeBackup function

The makeBackup function efficiently creates backups and cleans up old backups according to the specified rules. The use of a try-catch-finally block ensures that the database connection is properly closed even if an error occurs during message cleanup.


153-168: Sequential removal of backups in removeAllBackups function

The function correctly removes backups sequentially to prevent database errors, adhering to best practices noted in previous learnings.


211-216: Maintain inline error handling for cloud storage uploads

The try-catch blocks for cloudStorage.upload() are appropriately kept inline, matching the project's preferred coding style as noted in previous learnings.

Also applies to: 233-236


247-264: Proper error handling in the backup service

The startBackupService function correctly handles errors that may occur during the periodic backup creation, ensuring that exceptions are caught and logged.


170-245: ⚠️ Potential issue

Add validation for backup file existence in loadBackup function

Currently, there's no check to verify if the specified backup file exists before attempting to load it. To prevent potential errors, please add a validation step for backupId when it's not the latest backup.

Apply this diff to add the validation:

 export async function loadBackup(id: string, backupId: string) {
   const budgetDir = fs.getBudgetDir(id);

+  const backupPath = fs.join(budgetDir, backupId);
+
+  if (backupId !== LATEST_BACKUP_FILENAME && !(await fs.exists(backupPath))) {
+    throw new Error(`Backup file ${backupId} does not exist`);
+  }

   if (!(await fs.exists(fs.join(budgetDir, LATEST_BACKUP_FILENAME)))) {
     // If this is the first time we're loading a backup, save the

Likely invalid or redundant comment.

packages/loot-core/src/server/backups.d.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/backups.electron.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (4)
packages/loot-core/src/server/backups.d.ts (2)

1-2: Add JSDoc documentation and consider type refinement.

The type definitions would benefit from documentation explaining their purpose and usage. Additionally, consider making the types more explicit:

+/**
+ * Represents a backup entry with either a date (regular backup) or null date (latest backup)
+ */
-export type Backup = { id: string; date: Date; isLatest?: boolean } | LatestBackup;
+export type RegularBackup = {
+  id: string;
+  date: Date;
+  isLatest?: false;
+};
+export type Backup = RegularBackup | LatestBackup;

+/**
+ * Represents the latest backup entry with a null date
+ */
 export type LatestBackup = { id: string; date: null; isLatest: boolean };
🧰 Tools
🪛 GitHub Check: lint

[warning] 1-1:
Replace ·{·id:·string;·date:·Date;·isLatest?:·boolean·} with ⏎··|·{·id:·string;·date:·Date;·isLatest?:·boolean·}⏎·


4-8: Add JSDoc documentation for consistency.

Only removeAllBackups has JSDoc documentation. For consistency and maintainability, add documentation to all exported functions.

Example documentation:

/**
 * Retrieves all available backups for a specific budget
 * @param {string} id - The ID of the budget
 * @returns {Promise<Backup[]>} List of available backups
 */
export function getAvailableBackups(id: string): Promise<Backup[]>;

/**
 * Updates the list of backups
 * @param {Backup[]} backups - The backups to update
 * @returns {Promise<Backup[]>} Updated list of backups
 */
export function updateBackups(backups: Backup[]): Promise<Backup[]>;

Also applies to: 16-22

packages/loot-core/src/server/backups.web.ts (2)

34-34: Use dateFns.isValid for date validation

Instead of comparing the date string to 'Invalid Date', you can use dateFns.isValid(date) for more reliable date validation.

Apply this diff to improve date validation:

- if (date.toString() === 'Invalid Date') return null;
+ if (!dateFns.isValid(date)) return null;

43-43: Refine type guard in validBackups filter

To ensure TypeScript correctly infers the type of validBackups as Backup[], you can use a type predicate in the filter function.

Apply this diff to enhance type inference:

- const validBackups = backups.filter(backup => backup !== null);
+ const validBackups = backups.filter((backup): backup is Backup => backup !== null);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 66760ad and f70a44a.

📒 Files selected for processing (3)
  • packages/loot-core/src/server/backups.d.ts (1 hunks)
  • packages/loot-core/src/server/backups.electron.ts (4 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/backups.web.ts (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:141-162
Timestamp: 2024-10-25T06:04:25.867Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, the `removeAllBackups` function should remove backups sequentially to prevent database errors. Parallel execution in this context should be avoided.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:203-207
Timestamp: 2024-10-24T05:09:44.115Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, developers prefer to keep the error handling for cloud storage uploads inline rather than extracting it into a separate function. Avoid suggesting this refactoring in future reviews.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:204-208
Timestamp: 2024-10-24T05:05:06.973Z
Learning: In `backups.web.ts`, prefer to keep the `try-catch` blocks for `cloudStorage.upload()` as is, rather than refactoring into a helper function.
🪛 GitHub Check: lint
packages/loot-core/src/server/backups.d.ts

[warning] 1-1:
Replace ·{·id:·string;·date:·Date;·isLatest?:·boolean·} with ⏎··|·{·id:·string;·date:·Date;·isLatest?:·boolean·}⏎·

🔇 Additional comments (6)
packages/loot-core/src/server/backups.electron.ts (5)

10-10: LGTM: Type safety improvements

The addition of proper type imports and the explicit NodeJS.Timeout type for serviceInterval enhances type safety and maintainability.

Also applies to: 18-18


Line range hint 20-72: LGTM: Clean implementation of backup retrieval

The updated return type and removal of commented code improve code clarity while maintaining correct functionality for backup retrieval and sorting.


75-77: LGTM: Robust handling of backup dates

The addition of type annotation and filtering of null dates demonstrates good defensive programming, preventing potential runtime errors when processing backup dates.


134-165: LGTM: Well-implemented backup cleanup function

The new removeAllBackups function is thoroughly implemented with:

  • Proper error handling and logging
  • Handling of both regular and latest backups
  • Clear success/failure indication
  • Good documentation

This implementation successfully addresses the previous review comment about cleaning up LATEST_BACKUP_FILENAME.


256-259: LGTM: Improved service cleanup

The enhanced interval cleanup with proper null checking and reset prevents potential issues and memory leaks.

packages/loot-core/src/server/backups.web.ts (1)

185-186: Verify necessity of multiple prefs.loadPrefs(id) calls

The prefs.loadPrefs(id); function is called twice in loadBackup, at lines 185 and 218. Verify if both calls are necessary or if one can be removed to prevent redundant operations and potential side effects.

Also applies to: 218-219

packages/loot-core/src/server/backups.d.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/backups.d.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/backups.web.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/backups.web.ts Outdated Show resolved Hide resolved
packages/loot-core/src/server/backups.web.ts Show resolved Hide resolved
packages/loot-core/src/server/backups.web.ts Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
packages/loot-core/src/server/backups.web.ts (1)

203-205: Add error handling when removing latest backup files

When removing the latest backup files (db.latest.sqlite and metadata.latest.json), any errors during fs.removeFile could lead to unhandled exceptions. Consider wrapping these calls in a try-catch block to handle potential file system errors and enhance robustness.

Apply this diff to add error handling:

+try {
 await fs.removeFile(fs.join(budgetDir, LATEST_BACKUP_FILENAME));
 await fs.removeFile(fs.join(budgetDir, 'metadata.latest.json'));
+} catch (error) {
+  console.error('Error removing latest backup files:', error);
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f70a44a and 4168aac.

📒 Files selected for processing (3)
  • packages/loot-core/src/server/backups.d.ts (1 hunks)
  • packages/loot-core/src/server/backups.electron.ts (5 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/loot-core/src/server/backups.d.ts
  • packages/loot-core/src/server/backups.electron.ts
🧰 Additional context used
📓 Learnings (1)
packages/loot-core/src/server/backups.web.ts (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:141-162
Timestamp: 2024-10-25T06:04:25.867Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, the `removeAllBackups` function should remove backups sequentially to prevent database errors. Parallel execution in this context should be avoided.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:203-207
Timestamp: 2024-10-24T05:09:44.115Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, developers prefer to keep the error handling for cloud storage uploads inline rather than extracting it into a separate function. Avoid suggesting this refactoring in future reviews.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:204-208
Timestamp: 2024-10-24T05:05:06.973Z
Learning: In `backups.web.ts`, prefer to keep the `try-catch` blocks for `cloudStorage.upload()` as is, rather than refactoring into a helper function.

packages/loot-core/src/server/backups.web.ts Show resolved Hide resolved
packages/loot-core/src/server/backups.web.ts Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
packages/loot-core/src/server/backups.web.ts (1)

33-33: Consider providing more context for invalid dates.

When a date is invalid, it would be helpful to log the problematic path to assist with debugging.

-      if (date.toString() === 'Invalid Date') return null;
+      if (date.toString() === 'Invalid Date') {
+        console.warn(`Invalid date format in backup file: ${path}`);
+        return null;
+      }
packages/loot-core/src/server/main.ts (1)

1896-1904: Consider enhancing cloud sync error visibility

While the error handling is functional, consider logging additional details about sync failures to help with debugging.

     try {
       await cloudStorage.upload();
     } catch (error) {
-      console.warn('Failed to sync duplicated budget to cloud:', error);
+      console.warn('Failed to sync duplicated budget to cloud:', {
+        error,
+        budgetId: newId,
+        timestamp: new Date().toISOString()
+      });
       // Ignore any errors uploading. If they are offline they should
       // still be able to create files.
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 4168aac and 9b4d6ab.

📒 Files selected for processing (4)
  • packages/loot-core/src/server/backups.d.ts (1 hunks)
  • packages/loot-core/src/server/backups.electron.ts (7 hunks)
  • packages/loot-core/src/server/backups.web.ts (1 hunks)
  • packages/loot-core/src/server/main.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/loot-core/src/server/backups.d.ts
  • packages/loot-core/src/server/backups.electron.ts
🧰 Additional context used
📓 Learnings (2)
packages/loot-core/src/server/backups.web.ts (5)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:112-118
Timestamp: 2024-10-27T11:27:58.922Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, during backup creation, only the `.latest.` backup requires copying the `metadata.json` file; other backups do not need to include `metadata.json`.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:236-240
Timestamp: 2024-10-27T11:27:51.797Z
Learning: When loading a backup in `backups.web.ts`, only the `.latest.` backup requires restoring `metadata.json`; other backups do not need to restore `metadata.json`.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:141-162
Timestamp: 2024-10-25T06:04:25.867Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, the `removeAllBackups` function should remove backups sequentially to prevent database errors. Parallel execution in this context should be avoided.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:203-207
Timestamp: 2024-10-24T05:09:44.115Z
Learning: In `packages/loot-core/src/server/backups.web.ts`, developers prefer to keep the error handling for cloud storage uploads inline rather than extracting it into a separate function. Avoid suggesting this refactoring in future reviews.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/backups.web.ts:204-208
Timestamp: 2024-10-24T05:05:06.973Z
Learning: In `backups.web.ts`, prefer to keep the `try-catch` blocks for `cloudStorage.upload()` as is, rather than refactoring into a helper function.
packages/loot-core/src/server/main.ts (3)
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1800-1805
Timestamp: 2024-10-23T07:50:40.515Z
Learning: Paths are not fully defined on initialization; they become fully defined when a budget is loaded. Therefore, loading and closing the budget before deleting is necessary to ensure that paths are properly resolved.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1873-1877
Timestamp: 2024-10-23T07:39:50.053Z
Learning: In the `duplicate-budget` function in `packages/loot-core/src/server/main.ts`, when `loadBudget` returns an object with an `error` property where `error` is a string, returning `error` directly is consistent with the function's declared return type `Promise<string>`.
Learnt from: tlesicka
PR: actualbudget/actual#3689
File: packages/loot-core/src/server/main.ts:1807-1809
Timestamp: 2024-10-23T07:38:25.699Z
Learning: In the function `delete-budget` in `packages/loot-core/src/server/main.ts`, the function `removeAllBackups(id)` handles its own errors internally, so additional error handling when calling it is unnecessary.
🔇 Additional comments (8)
packages/loot-core/src/server/backups.web.ts (3)

1-20: LGTM! Well-structured imports and constants.

The imports are properly organized, and constants are well-defined with clear purposes.


71-145: LGTM! Robust backup management implementation.

The code implements a well-thought-out retention policy and includes proper error handling for database operations. The sequential backup removal, as preferred, prevents database errors.


146-168: LGTM! Well-documented and robust backup removal.

The implementation follows best practices with proper error handling and sequential removal of backups.

packages/loot-core/src/server/main.ts (5)

1787-1787: LGTM: Clean integration of backup service shutdown

The stopBackupService() is properly integrated into the budget closing sequence, ensuring proper cleanup of backup-related resources.


1807-1809: LGTM: Proper platform-specific backup cleanup

The web platform check and backup removal is correctly implemented, ensuring proper cleanup of backups for web users.


1822-1828: LGTM: Robust input validation

The input validation is thorough, checking for required fields and valid characters in the budget name.


1864-1887: LGTM: Well-implemented error handling with cleanup

The implementation includes proper error handling with cleanup of partially created files in case of failure.


2007-2008: LGTM: Improved type safety

The addition of the string type annotation to the loadBudget function parameter improves type safety and code clarity.

packages/loot-core/src/server/backups.web.ts Show resolved Hide resolved
packages/loot-core/src/server/backups.web.ts Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature] duplicate file
2 participants