-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
base: master
Are you sure you want to change the base?
Duplicating Budget and Budget Snapshots #3689
Conversation
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.
✅ Deploy Preview for actualbudget ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Bundle Stats — desktop-clientHey 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
Changeset
View detailed bundle breakdownAdded No assets were added Removed No assets were removed Bigger
Smaller No assets were smaller Unchanged
|
Bundle Stats — loot-coreHey 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
Changeset
View detailed bundle breakdownAdded No assets were added Removed No assets were removed Bigger
Smaller No assets were smaller Unchanged No assets were unchanged |
Created 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:
There should be a matching database called:
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. |
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. |
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. |
Fixed database not deleting new budget file. Removed backups from budget menu and replaced with duplicate budget.
WalkthroughThe pull request introduces several significant changes across multiple components related to budget management in the desktop client. A new modal component, Possibly related PRs
Suggested labels
Suggested reviewers
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 theremoveAllBackups
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 improvementsThe
duplicateBudget
function is well-structured and follows the existing patterns in the file. However, there are a few areas that could be improved:
Error handling: Consider adding explicit error handling for the 'duplicate-budget' action to manage potential failures gracefully.
Loading state: When
managePage
is true, the loading state is not reset. Consider adding a loading state for this case as well.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 safetyThe changes to the
FileMenu
component look good. The dynamic generation of menu items based on the presence of theonDuplicate
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 clarityThe addition of the
onDuplicate
functionality to theFileItem
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:
- 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}
- 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 theFormError
component may not align correctly with the form field label, especially if the label width changes. Consider using the samelabelWidth
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 withinEditableBudgetName
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 parametersThe
updateBackups
function lacks type annotations for itsbackups
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 forserviceInterval
Currently,
serviceInterval
is initialized tonull
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 filenamesWhile 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
: UseTextDecoder
for efficient decoding of ArrayBuffersInstead of manually converting ArrayBuffers to strings using
String.fromCharCode
andUint16Array
, leverage theTextDecoder
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
functionThe
_writeFile
function always returnstrue
, 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 returnvoid
, or handle errors and returnfalse
when exceptions occur.packages/loot-core/src/server/main.ts (1)
1810-1882
: Add unit tests forduplicate-budget
handlerTo 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
⛔ 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 newremoveAllBackups
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 correctThe modifications to the
FileMenuButton
component correctly incorporate the newonDuplicate
functionality. The prop is properly passed down to theFileMenu
component, maintaining consistency with the earlier changes.
278-284
: LGTM: BudgetFiles changes are consistentThe addition of the
onDuplicate
prop to theBudgetFiles
component and its propagation to eachFileItem
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 suggestionsThe 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:
- The
FileMenu
,FileMenuButton
,FileItem
, andBudgetFiles
components have been updated to support the new duplication feature.- The changes maintain consistency across components and follow React best practices.
- The integration with the existing modal system for handling duplication is appropriate.
Suggestions for improvement:
- Enhance type safety in several components using more specific types and type guards.
- Add error handling and validation when dispatching the duplication action.
- 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 DuplicateFileModalThe import statement for
DuplicateFileModal
is correctly added and follows the existing import style in the file.
582-589
: LGTM: New case for 'duplicate-budget' modalThe 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 DuplicateFileModalThe changes in this file successfully integrate the new
DuplicateFileModal
component into the existing modal system:
- The import statement is correctly added.
- 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 issueEnsure consistent usage of localization in the modal header.
In the
ModalHeader
, the title is localized usingt
, but therightContent
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
: ImportinguseSelector
for accessing Redux stateGood incorporation of
useSelector
fromreact-redux
to access the Redux store's state. This allows you to retrieveallFiles
later in the code.
8-12
: Adding necessary actions for modal managementThe addition of
closeBudget
,replaceModal
, andpushModal
actions enhances the component's ability to manage modals effectively.
14-19
: Importing file types for type safetyIncluding the file types
File
,LocalFile
,SyncableLocalFile
, andSyncedLocalFile
helps in maintaining type safety and clarity when working with different file states.
183-187
: Effective use of TypeScript type guardThe
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 ofbudgetFile
when duplicatingGood practice in checking if
budgetFile
exists before attempting to dispatch thepushModal
action for duplication. This prevents potential errors ifbudgetFile
isnull
orundefined
.
225-225
: Conditional inclusion of 'Duplicate budget' menu itemIncluding 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 annotationsThe 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/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Outdated
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Outdated
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Outdated
Show resolved
Hide resolved
…esicka/actual into snapshot-and-duplicate-budget
There was a problem hiding this 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 serviceThe 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
📒 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:
Core backup operations:
makeBackup
andloadBackup
functions in both web and electron environments- Proper event handling with
backups-get
andbackups-updated
events- Error handling for backup operations with try-catch blocks
UI Integration:
- LoadBackupModal properly dispatches backup actions
- Real-time backup updates through event listeners
- Loading states and error handling for user feedback
Architecture:
- Clean separation between client actions and server handlers
- Consistent backup operations across web and electron platforms
- Proper cleanup with
removeAllBackups
functionalityThe 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 tsLength 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 2Length of output: 6046
packages/desktop-client/src/components/sidebar/Sidebar.tsx (4)
3-15
: LGTM: Import statements are well-organizedThe 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-implementedThe state management for budget files is clean and efficient, using appropriate hooks and selectors.
196-200
: LGTM: Backup menu handling is properly guardedThe backup functionality is well-implemented with appropriate null checking for
budgetId
.
216-216
: 🛠️ Refactor suggestionConsider 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 additionThe addition of
removeAllBackups
to the existing backup-related imports is well-organized and properly grouped with related functionality.
1800-1809
: LGTM: Proper web platform handlingThe 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 cleanupThe 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 implementationThe 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 optionsThe implementation provides good flexibility in handling which budget to open after duplication through the
open
parameter.
2001-2002
: LGTM: Improved type safetyThe addition of the string type annotation for the
id
parameter enhances type safety and code clarity.
1816-1823
:⚠️ Potential issueAdd 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.
packages/desktop-client/src/components/modals/LoadBackupModal.tsx
Outdated
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Outdated
Show resolved
Hide resolved
…esicka/actual into snapshot-and-duplicate-budget
There was a problem hiding this 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 improvementsThe type definition is well-documented and provides proper type safety for the budget duplication feature. Consider these optional improvements for better maintainability:
- 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; };
- 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
📒 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 modalThe 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 ofremoveAllBackups
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:
- Loads and closes the budget to ensure proper path resolution (necessary workaround as confirmed by learnings)
- Handles backup cleanup for web platform
1823-1828
: LGTM: Strong input validation for budget name.The validation ensures:
- Non-empty trimmed name
- Only allows alphanumeric characters, spaces, and basic punctuation
1864-1887
: LGTM: Robust error handling with cleanup.The implementation properly:
- Wraps file operations in try-catch
- Cleans up partially created files on error
- Provides clear error messages
1896-1904
: LGTM: Appropriate cloud sync handling.The cloud sync implementation:
- Makes sync optional
- Handles errors gracefully
- 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.
packages/desktop-client/src/components/modals/LoadBackupModal.tsx
Outdated
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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
📒 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.
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Show resolved
Hide resolved
packages/desktop-client/src/components/modals/manager/DuplicateFileModal.tsx
Show resolved
Hide resolved
There was a problem hiding this 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:
- Progress Tracking: Consider adding types for progress tracking during backup operations, especially for large files.
- Chunked Operations: The type system could benefit from supporting chunked/streamed backup operations.
- 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
📒 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
havingisLatest: true
provides good type safety and clear semantics.packages/loot-core/src/server/backups.electron.ts (3)
10-10
: LGTM: Type safety improvementsThe addition of explicit types for
Backup
andLatestBackup
, along with the more precise typing ofserviceInterval
, enhances type safety and maintainability.Also applies to: 18-18
258-261
: LGTM: Improved service cleanupThe changes to
stopBackupService
improve robustness by adding a null check and properly cleaning up the interval reference.
72-78
: Verify date formatting in UI componentsThe 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 tsxLength 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 ingetBackups
functionThe
getBackups
function correctly handles potential invalid dates and filters out null backups. The use ofdateFns.parse
ensures proper date parsing, and the backups are sorted in descending order of their timestamps.
79-103
: Confirm backup removal logic inupdateBackups
functionThe 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 inmakeBackup
functionThe
makeBackup
function efficiently creates backups and cleans up old backups according to the specified rules. The use of atry-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 inremoveAllBackups
functionThe 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 uploadsThe
try-catch
blocks forcloudStorage.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 serviceThe
startBackupService
function correctly handles errors that may occur during the periodic backup creation, ensuring that exceptions are caught and logged.
170-245
:⚠️ Potential issueAdd validation for backup file existence in
loadBackup
functionCurrently, 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 theLikely invalid or redundant comment.
There was a problem hiding this 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
: UsedateFns.isValid
for date validationInstead of comparing the date string to
'Invalid Date'
, you can usedateFns.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 invalidBackups
filterTo ensure TypeScript correctly infers the type of
validBackups
asBackup[]
, 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
📒 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 improvementsThe 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 retrievalThe 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 datesThe 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 functionThe 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 cleanupThe 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 multipleprefs.loadPrefs(id)
callsThe
prefs.loadPrefs(id);
function is called twice inloadBackup
, 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
There was a problem hiding this 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 filesWhen removing the latest backup files (
db.latest.sqlite
andmetadata.latest.json
), any errors duringfs.removeFile
could lead to unhandled exceptions. Consider wrapping these calls in atry-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
📒 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.
There was a problem hiding this 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 visibilityWhile 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
📒 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 shutdownThe
stopBackupService()
is properly integrated into the budget closing sequence, ensuring proper cleanup of backup-related resources.
1807-1809
: LGTM: Proper platform-specific backup cleanupThe web platform check and backup removal is correctly implemented, ensuring proper cleanup of backups for web users.
1822-1828
: LGTM: Robust input validationThe input validation is thorough, checking for required fields and valid characters in the budget name.
1864-1887
: LGTM: Well-implemented error handling with cleanupThe implementation includes proper error handling with cleanup of partially created files in case of failure.
2007-2008
: LGTM: Improved type safetyThe addition of the string type annotation to the
loadBudget
function parameter improves type safety and code clarity.
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 inpackages/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.