Skip to content

fix: new workspace is not saved after reload app - #229

Merged
vitonsky merged 40 commits into
DeepinkApp:masterfrom
katsyuta:225-workspace-not-save-after-reload
Mar 8, 2026
Merged

fix: new workspace is not saved after reload app#229
vitonsky merged 40 commits into
DeepinkApp:masterfrom
katsyuta:225-workspace-not-save-after-reload

Conversation

@katsyuta

@katsyuta katsyuta commented Feb 27, 2026

Copy link
Copy Markdown
Collaborator

Closed #225

Problem:
If the app is reloaded immediately after creating a workspace, it may not yet be saved to the file system, causing the app to try loading a non-existent workspace and resulting in a white screen.
This happens because the database is not synchronized with the file system immediately after creation workspace. We sync changes at intervals, but the user may reload the app before the automatic synchronization occurs.

Fix:

  • Sync immediately after creating a workspace
  • Check if the workspace from the vault state really exists in the database
  • Update the active workspace in Redux only if it exists in Redux

These changes ensure that the workspace is saved after creation and prevent the app from trying to load a non-existent workspace

Changes:

  • Refactor PropertiesForm: move it to the shared components directory and add the isPending prop
  • Update ESLint: enable rules and disallow non-strict equality
ready6.mp4

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced workspace validation to ensure the active workspace exists before use
    • Improved data persistence with automatic synchronization during workspace creation
    • Added guards to prevent duplicate workspace creation submissions
    • Workspace names are now trimmed of whitespace during validation
  • Chores

    • Refactored form components for improved code reusability
    • Updated code quality standards to enforce stricter equality checks

@katsyuta katsyuta changed the title fix: add db sync Bug: If the app is reloaded immediately after creating a workspace, the workspace is not saved Feb 27, 2026
@katsyuta
katsyuta marked this pull request as ready for review February 28, 2026 15:11
Comment thread src/features/App/Profile/index.tsx Outdated
Comment thread src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx Outdated
Comment thread src/state/redux/profiles/profiles.ts Outdated
Comment thread src/state/redux/profiles/profiles.ts Outdated
@katsyuta
katsyuta force-pushed the 225-workspace-not-save-after-reload branch from 51919d4 to 7b668c4 Compare March 2, 2026 14:35
@katsyuta katsyuta changed the title Bug: If the app is reloaded immediately after creating a workspace, the workspace is not saved Fix: If the app is reloaded immediately after creating a workspace, the workspace is not saved Mar 2, 2026
@katsyuta
katsyuta requested a review from vitonsky March 2, 2026 15:56
Comment thread src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx Outdated
Comment thread src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx (1)

51-53: ⚠️ Potential issue | 🟠 Major

Add a synchronous re-entry guard at the top of onCreate.

This previously reported race still exists: fast repeated clicks can enter multiple create flows before the disabled state applies.

Suggested fix
 const onCreate = useCallback(async () => {
+  if (isPending) return;
   setIsPending(true);
   try {
     const { name: validatedName } = workspaceNameValidator.parse({
       name: workspaceName,
     });
@@
   }, [
     db,
     dispatch,
+    isPending,
     workspaceName,
     onClose,
     profileId,
     telemetry,
     updateWorkspaces,
     workspacesManager,
   ]);
#!/bin/bash
set -euo pipefail

FILE="$(fd 'WorkspaceCreatePopup.tsx' | head -n1)"
echo "Inspecting: $FILE"

rg -n -C3 'const onCreate = useCallback|if \(isPending\)|setIsPending\(true\)|isPending,' "$FILE"

Also applies to: 83-92

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx` around
lines 51 - 53, Add a synchronous re-entry guard at the very start of the
onCreate callback to prevent double-entry on fast clicks: check the isPending
flag and immediately return if true before calling setIsPending(true), so the
function never launches a second async flow; update any other create handlers in
the same file (the similar block around the second handler where
isPending/setIsPending are used) to follow the same pattern, referencing the
onCreate function, isPending and setIsPending symbols to locate and apply the
guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx`:
- Around line 51-53: Add a synchronous re-entry guard at the very start of the
onCreate callback to prevent double-entry on fast clicks: check the isPending
flag and immediately return if true before calling setIsPending(true), so the
function never launches a second async flow; update any other create handlers in
the same file (the similar block around the second handler where
isPending/setIsPending are used) to follow the same pattern, referencing the
onCreate function, isPending and setIsPending symbols to locate and apply the
guard.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 366ca8f and 873058d.

📒 Files selected for processing (1)
  • src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx (1)

110-141: Consider adding Enter key submission support.

The input is not wrapped in a <form> element, so users cannot submit by pressing Enter. This is a common UX expectation for single-input forms.

♻️ Optional: Add form wrapper for Enter key support
-					<VStack as={AutoFocusInside} gap="1.5rem" w="100%" minW="350px">
+					<VStack
+						as="form"
+						onSubmit={(e) => {
+							e.preventDefault();
+							onCreate();
+						}}
+						gap="1.5rem"
+						w="100%"
+						minW="350px"
+					>
+						<AutoFocusInside>
 						<VStack as="label" align="start" w="100%" gap="0.3rem">
 							<Text paddingBottom=".2rem">Workspace name</Text>
 							<Input
@@ ... @@
 						</VStack>
+						</AutoFocusInside>

 						<HStack w="100%" justifyContent="end">
 							<Button
 								variant="accent"
-								onClick={onCreate}
+								type="submit"
 								isDisabled={isPending}
 							>
 								Add
 							</Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx` around
lines 110 - 141, Wrap the input and buttons in a form so pressing Enter submits:
change the outer VStack (containing Input, Add and Cancel buttons) into a <form>
with onSubmit that calls onCreate and calls event.preventDefault(); make the
"Add" Button type="submit" and the "Cancel" Button type="button" (still calling
onClose) so Cancel doesn't submit; ensure onCreate remains callable from the
form submit handler and that isPending still disables controls and
setErrorMessage behavior is unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx`:
- Around line 110-141: Wrap the input and buttons in a form so pressing Enter
submits: change the outer VStack (containing Input, Add and Cancel buttons) into
a <form> with onSubmit that calls onCreate and calls event.preventDefault();
make the "Add" Button type="submit" and the "Cancel" Button type="button" (still
calling onClose) so Cancel doesn't submit; ensure onCreate remains callable from
the form submit handler and that isPending still disables controls and
setErrorMessage behavior is unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 873058d and 16abc5c.

📒 Files selected for processing (1)
  • src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx (2)

142-144: Set an explicit non-submit type on the Cancel button.

Because this button is inside a form, making the type explicit avoids accidental submit behavior and improves readability.

Suggested tweak
-<Button onClick={onClose} isDisabled={isPending}>
+<Button type="button" onClick={onClose} isDisabled={isPending}>
   Cancel
 </Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx` around
lines 142 - 144, The Cancel Button in WorkspaceCreatePopup is inside a form and
lacks an explicit type, which can cause accidental form submission; update the
Button element (the one rendering Cancel with props onClose and isPending) to
include an explicit type="button" attribute so it does not act as a submit
button within the form. Ensure you modify the Button component usage in
WorkspaceCreatePopup (the Cancel button that calls onClose) to pass
type="button" while keeping isDisabled={isPending} and the onClick handler
unchanged.

51-55: Clear stale errors at the start of a new submit attempt.

After a failed attempt, the previous error remains visible while the next request is pending. Reset it when a new create starts.

Suggested tweak
 const onCreate = useCallback(async () => {
   if (isPending) return;
+  setErrorMessage(null);
   setIsPending(true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx` around
lines 51 - 55, In onCreate, clear any previous create error at the start of a
new submit so stale errors aren't shown while the new request is pending: in the
useCallback for onCreate (the block that checks isPending and calls
setIsPending(true)), call the error state reset (e.g., setError(null) or
setCreateError(undefined)) immediately when starting the submit attempt (right
after the isPending guard and before/after setIsPending(true)) so the UI no
longer shows the previous error while the new request is in flight.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx`:
- Around line 142-144: The Cancel Button in WorkspaceCreatePopup is inside a
form and lacks an explicit type, which can cause accidental form submission;
update the Button element (the one rendering Cancel with props onClose and
isPending) to include an explicit type="button" attribute so it does not act as
a submit button within the form. Ensure you modify the Button component usage in
WorkspaceCreatePopup (the Cancel button that calls onClose) to pass
type="button" while keeping isDisabled={isPending} and the onClick handler
unchanged.
- Around line 51-55: In onCreate, clear any previous create error at the start
of a new submit so stale errors aren't shown while the new request is pending:
in the useCallback for onCreate (the block that checks isPending and calls
setIsPending(true)), call the error state reset (e.g., setError(null) or
setCreateError(undefined)) immediately when starting the submit attempt (right
after the isPending guard and before/after setIsPending(true)) so the UI no
longer shows the previous error while the new request is in flight.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 782627c4-cf32-44b9-9b5f-e4946271fc70

📥 Commits

Reviewing files that changed from the base of the PR and between 16abc5c and ad9be31.

📒 Files selected for processing (1)
  • src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx

@katsyuta

katsyuta commented Mar 4, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Mar 4, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/features/App/Settings/sections/WorkspaceSettings.tsx (1)

21-24: Extract the shared validator into a schema module.

WorkspaceSettings now depends on WorkspaceCreatePopup just to reuse validation. Please move workspaceNameValidator to a neutral module (e.g., WorkspaceName.schema.ts) and import it from both places to reduce UI-layer coupling.

Also applies to: 70-70

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/App/Settings/sections/WorkspaceSettings.tsx` around lines 21 -
24, WorkspaceSettings currently imports workspaceNameValidator from
WorkspaceCreatePopup creating UI coupling; extract the validator into a neutral
schema module (e.g., create WorkspaceName.schema.ts) that exports
workspaceNameValidator, update WorkspaceCreatePopup to import
workspaceNameValidator from the new module, and update WorkspaceSettings to
import the same symbol instead of importing WorkspaceCreatePopup; ensure the
exported validator name (workspaceNameValidator) is preserved so both
WorkspaceCreatePopup and WorkspaceSettings can reference it without changing
other code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx`:
- Around line 60-79: The catch currently covers both creation
(workspacesManager.create) and post-creation steps (db.sync, updateWorkspaces,
dispatch, telemetry.track, onClose) so failures after a successful create show a
“save failed” and can cause duplicate creates on retry; fix by separating
concerns: wrap only the create call in a try/catch that sets setErrorMessage on
creation failure, then after a successful create run db.sync(),
updateWorkspaces(), dispatch(workspacesApi.setActiveWorkspace({...})),
telemetry.track(...), and onClose() in a separate try/catch that logs or handles
errors without setting the generic save error (or surface a non-blocking
warning) to avoid prompting a retry that would re-create the workspace.

---

Nitpick comments:
In `@src/features/App/Settings/sections/WorkspaceSettings.tsx`:
- Around line 21-24: WorkspaceSettings currently imports workspaceNameValidator
from WorkspaceCreatePopup creating UI coupling; extract the validator into a
neutral schema module (e.g., create WorkspaceName.schema.ts) that exports
workspaceNameValidator, update WorkspaceCreatePopup to import
workspaceNameValidator from the new module, and update WorkspaceSettings to
import the same symbol instead of importing WorkspaceCreatePopup; ensure the
exported validator name (workspaceNameValidator) is preserved so both
WorkspaceCreatePopup and WorkspaceSettings can reference it without changing
other code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51dd8fbb-9fbb-4f0b-97d8-ab95427a078c

📥 Commits

Reviewing files that changed from the base of the PR and between 3039225 and ad9be31.

📒 Files selected for processing (10)
  • eslint.config.ts
  • src/features/App/Profile/index.tsx
  • src/features/App/Profiles/hooks/useProfileContainers.ts
  • src/features/App/Settings/sections/WorkspaceSettings.tsx
  • src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx
  • src/features/NoteEditor/RichEditor/plugins/Formatting/FormattingPlugin.ts
  • src/features/NoteEditor/RichEditor/plugins/Image/ImageNode.tsx
  • src/features/NoteEditor/RichEditor/plugins/Image/ImagesPlugin.tsx
  • src/features/NoteEditor/RichEditor/plugins/Markdown/MarkdownShortcutPlugin.tsx
  • src/state/redux/profiles/profiles.ts

Comment thread src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx Outdated
@katsyuta

katsyuta commented Mar 4, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Mar 4, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx (1)

25-27: Move workspaceNameValidator to a shared non-UI schema module.

Exporting a validator from a popup component file couples settings/business validation to UI code. Consider relocating this schema to a shared workspace validation module and importing it from both screens.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx` around
lines 25 - 27, Move the workspaceNameValidator out of the UI component into a
shared validation module: create a new non-UI file (e.g., workspaceValidators)
that exports workspaceNameValidator (the z.object with name:
z.string().trim().min(1, ...)), update WorkspaceCreatePopup to import
workspaceNameValidator from that module, and update any other screens/components
that rely on the same validation to import it as well so validation logic is
centralized and decoupled from UI code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/features/App/Profiles/hooks/useProfileContainers.ts`:
- Around line 123-126: The call to db.sync() in openProfile can throw and cause
early exit before disposables are registered, leaking resources; update
openProfile so that either (a) disposables for the opened resources (e.g., the
DB and workspaces created via workspaces.create and the db instance) are
registered before calling db.sync(), or (b) wrap the await db.sync() in a
try/catch that on error performs immediate cleanup (dispose/close the db,
rollback or remove the created workspace) and then rethrows; ensure the cleanup
logic targets the exact resources opened by openProfile (the db instance and any
workspaces created) so no resources remain leaked if db.sync() fails.

---

Nitpick comments:
In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx`:
- Around line 25-27: Move the workspaceNameValidator out of the UI component
into a shared validation module: create a new non-UI file (e.g.,
workspaceValidators) that exports workspaceNameValidator (the z.object with
name: z.string().trim().min(1, ...)), update WorkspaceCreatePopup to import
workspaceNameValidator from that module, and update any other screens/components
that rely on the same validation to import it as well so validation logic is
centralized and decoupled from UI code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 371ce179-e000-4238-bf78-6a62e6ca9ec7

📥 Commits

Reviewing files that changed from the base of the PR and between 3039225 and 7638f1d.

📒 Files selected for processing (10)
  • eslint.config.ts
  • src/features/App/Profile/index.tsx
  • src/features/App/Profiles/hooks/useProfileContainers.ts
  • src/features/App/Settings/sections/WorkspaceSettings.tsx
  • src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx
  • src/features/NoteEditor/RichEditor/plugins/Formatting/FormattingPlugin.ts
  • src/features/NoteEditor/RichEditor/plugins/Image/ImageNode.tsx
  • src/features/NoteEditor/RichEditor/plugins/Image/ImagesPlugin.tsx
  • src/features/NoteEditor/RichEditor/plugins/Markdown/MarkdownShortcutPlugin.tsx
  • src/state/redux/profiles/profiles.ts

Comment thread src/features/App/Profiles/hooks/useProfileContainers.ts

@vitonsky vitonsky left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It is unclear for me what's happens in this PR.
Please describe the root cause of a bug and how this changes fix the problem.

Comment thread src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx Outdated
Comment thread src/state/redux/profiles/profiles.ts
@katsyuta katsyuta changed the title fix: workspace not save after reload app fix: new workspace is not saved after reload app Mar 7, 2026
@katsyuta

katsyuta commented Mar 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Mar 7, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
src/features/App/Profiles/hooks/useProfileContainers.ts (1)

124-126: ⚠️ Potential issue | 🟡 Minor

Consider adding error handling for db.sync() to prevent resource leaks.

If db.sync() throws, openProfile exits before db.close() is pushed onto cleanups, leaving the database connection uncleaned. While this won't crash the Electron app (based on unhandled rejection behavior), the resource leak concern remains valid.

A non-fatal catch-and-log pattern would allow the app to continue (the workspace exists in-memory) while ensuring cleanup registration proceeds:

🛡️ Proposed fix
 			await workspaces.create({ name: 'Notes' });

 			// Sync to avoid losing the default workspace if the app closes before the automatic sync
-			await db.sync();
+			await db.sync().catch((err) => {
+				console.error('[openProfile] Failed to sync after creating default workspace', err);
+			});
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/App/Profiles/hooks/useProfileContainers.ts` around lines 124 -
126, openProfile currently awaits db.sync() without error handling so if sync
throws the function exits before registering db.close in cleanups, leaking the
DB connection; wrap the db.sync() call in a try/catch (or push db.close into
cleanups immediately after creating/opening db and before awaiting sync) and on
error log the exception (e.g., processLogger.error or the module logger) but
allow execution to continue so db.close is always registered in cleanups;
reference the openProfile function, the db.sync() call, the db.close method, and
the cleanups array when making the change.
src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx (1)

74-98: ⚠️ Potential issue | 🟠 Major

Catch failures in the create flow.

Line 74 starts a promise chain with .finally() only. If create(), db.sync(), or updateWorkspaces() rejects, the failure is unhandled; if create() throws before returning a promise, setIsPending(false) is skipped and the form can stay stuck. Wrap the sequence in try/catch/finally so the UI always recovers and the failure can be surfaced instead of silently disappearing.

Suggested fix
-							onUpdate={({ name }) => {
+							onUpdate={async ({ name }) => {
 								if (isPending) return;
 								setIsPending(true);
 
-								workspacesManager
-									.create({ name })
-									.then(async (workspaceId) => {
-										// Synchronize immediately after creation to prevent workspace loss
-										// if the user closes the app before the automatic sync
-										await db.sync();
-
-										await updateWorkspaces();
-
-										dispatch(
-											workspacesApi.setActiveWorkspace({
-												workspaceId,
-												profileId,
-											}),
-										);
-
-										telemetry.track(
-											TELEMETRY_EVENT_NAME.WORKSPACE_ADDED,
-										);
-
-										onClose();
-									})
-									.finally(() => {
-										setIsPending(false);
-									});
+								try {
+									const workspaceId = await workspacesManager.create({
+										name,
+									});
+
+									// Synchronize immediately after creation to prevent workspace loss
+									// if the user closes the app before the automatic sync
+									await db.sync();
+									await updateWorkspaces();
+
+									dispatch(
+										workspacesApi.setActiveWorkspace({
+											workspaceId,
+											profileId,
+										}),
+									);
+
+									telemetry.track(
+										TELEMETRY_EVENT_NAME.WORKSPACE_ADDED,
+									);
+									onClose();
+								} catch (error) {
+									console.error(error);
+								} finally {
+									setIsPending(false);
+								}
 							}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx` around
lines 74 - 98, The create flow currently chains
workspacesManager.create(...).then(...).finally(...) which leaves rejections and
synchronous throws unhandled; change the handler to an async function (e.g.,
make the onSubmit/create handler async) and perform the sequence with try {
const workspaceId = await workspacesManager.create({ name }); await db.sync();
await updateWorkspaces(); dispatch(workspacesApi.setActiveWorkspace({
workspaceId, profileId }));
telemetry.track(TELEMETRY_EVENT_NAME.WORKSPACE_ADDED); onClose(); } catch (err)
{ /* surface error to user or logger, e.g., set an error state or call
processLogger.error(err) */ } finally { setIsPending(false); } so
setIsPending(false) always runs and errors from workspacesManager.create,
db.sync, or updateWorkspaces are handled and surfaced.
🧹 Nitpick comments (2)
src/components/PropertiesForm.tsx (2)

52-57: Remove unused setValue from dependency array.

setValue is destructured from useForm but not used in the effect body. Including it in the dependency array is unnecessary.

♻️ Proposed fix
 	useEffect(() => {
 		if (isPending) return;
 		if (isEqual(optionsValues, getValues())) return;

 		reset(optionsValues);
-	}, [getValues, isPending, optionsValues, reset, setValue]);
+	}, [getValues, isPending, optionsValues, reset]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/PropertiesForm.tsx` around lines 52 - 57, The useEffect
subscribed dependencies include an unused symbol setValue; update the dependency
array for the effect that compares isPending, isEqual(optionsValues,
getValues()) and calls reset(optionsValues) to remove setValue so it only
contains [getValues, isPending, optionsValues, reset]; ensure you do not
otherwise reference setValue inside the effect body (leave its external
destructuring as-is if used elsewhere).

66-68: Consider improving type safety for onUpdate callback.

The as any cast bypasses type checking. While the generic T extends OptionObject[] provides some safety, the cast could hide type mismatches at runtime.

A more type-safe approach could be:

♻️ Proposed fix
-			onSubmit={handleSubmit((values) => {
-				onUpdate(values as any);
-			})}
+			onSubmit={handleSubmit((values) => {
+				onUpdate(values as Record<T[number]['id'], string>);
+			})}

Note: This still uses a cast but it's more precise. A fully type-safe solution would require restructuring how react-hook-form infers field names from the options array, which may be complex for this use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/PropertiesForm.tsx` around lines 66 - 68, The current onSubmit
uses an unsafe "as any" cast; replace it with a narrower cast to preserve type
intent by casting through unknown to your generic T (e.g. inside the onSubmit
handler call onUpdate(values as unknown as T)) or, if possible, provide the
generic to react-hook-form's handleSubmit so the handler receives the correct
type; update the onSubmit expression around handleSubmit((values) =>
onUpdate(...)) in the PropertiesForm component to use values as unknown as T (or
supply the handleSubmit generic) instead of as any.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/features/App/Profiles/hooks/useProfileContainers.ts`:
- Around line 124-126: openProfile currently awaits db.sync() without error
handling so if sync throws the function exits before registering db.close in
cleanups, leaking the DB connection; wrap the db.sync() call in a try/catch (or
push db.close into cleanups immediately after creating/opening db and before
awaiting sync) and on error log the exception (e.g., processLogger.error or the
module logger) but allow execution to continue so db.close is always registered
in cleanups; reference the openProfile function, the db.sync() call, the
db.close method, and the cleanups array when making the change.

In `@src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx`:
- Around line 74-98: The create flow currently chains
workspacesManager.create(...).then(...).finally(...) which leaves rejections and
synchronous throws unhandled; change the handler to an async function (e.g.,
make the onSubmit/create handler async) and perform the sequence with try {
const workspaceId = await workspacesManager.create({ name }); await db.sync();
await updateWorkspaces(); dispatch(workspacesApi.setActiveWorkspace({
workspaceId, profileId }));
telemetry.track(TELEMETRY_EVENT_NAME.WORKSPACE_ADDED); onClose(); } catch (err)
{ /* surface error to user or logger, e.g., set an error state or call
processLogger.error(err) */ } finally { setIsPending(false); } so
setIsPending(false) always runs and errors from workspacesManager.create,
db.sync, or updateWorkspaces are handled and surfaced.

---

Nitpick comments:
In `@src/components/PropertiesForm.tsx`:
- Around line 52-57: The useEffect subscribed dependencies include an unused
symbol setValue; update the dependency array for the effect that compares
isPending, isEqual(optionsValues, getValues()) and calls reset(optionsValues) to
remove setValue so it only contains [getValues, isPending, optionsValues,
reset]; ensure you do not otherwise reference setValue inside the effect body
(leave its external destructuring as-is if used elsewhere).
- Around line 66-68: The current onSubmit uses an unsafe "as any" cast; replace
it with a narrower cast to preserve type intent by casting through unknown to
your generic T (e.g. inside the onSubmit handler call onUpdate(values as unknown
as T)) or, if possible, provide the generic to react-hook-form's handleSubmit so
the handler receives the correct type; update the onSubmit expression around
handleSubmit((values) => onUpdate(...)) in the PropertiesForm component to use
values as unknown as T (or supply the handleSubmit generic) instead of as any.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26698df8-93a4-4e53-9a31-fdaf5a3fda6a

📥 Commits

Reviewing files that changed from the base of the PR and between a7186f3 and 3edef10.

📒 Files selected for processing (13)
  • eslint.config.ts
  • src/components/PropertiesForm.tsx
  • src/features/App/Profile/index.tsx
  • src/features/App/Profiles/hooks/useProfileContainers.ts
  • src/features/MainScreen/WorkspacesPanel/WorkspaceCreatePopup.tsx
  • src/features/NoteEditor/EditorPanel/buttons/ImageButton.tsx
  • src/features/NoteEditor/EditorPanel/buttons/LinkButton.tsx
  • src/features/NoteEditor/RichEditor/plugins/ContextMenu/components/ObjectPropertiesEditor.tsx
  • src/features/NoteEditor/RichEditor/plugins/Formatting/FormattingPlugin.ts
  • src/features/NoteEditor/RichEditor/plugins/Image/ImageNode.tsx
  • src/features/NoteEditor/RichEditor/plugins/Image/ImagesPlugin.tsx
  • src/features/NoteEditor/RichEditor/plugins/Markdown/MarkdownShortcutPlugin.tsx
  • src/state/redux/profiles/profiles.ts

@katsyuta
katsyuta requested a review from vitonsky March 7, 2026 13:55
Comment thread src/components/PropertiesForm.tsx Outdated
@vitonsky
vitonsky merged commit 3a449d1 into DeepinkApp:master Mar 8, 2026
9 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 15, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: If the app is reloaded immediately after creating a workspace, the workspace is not saved

2 participants