Skip to content

Improve Emergency Access view loading performance #440

Open
mindmonk wants to merge 2 commits intodevelopfrom
feature/improve-ea-vault-list-loading
Open

Improve Emergency Access view loading performance #440
mindmonk wants to merge 2 commits intodevelopfrom
feature/improve-ea-vault-list-loading

Conversation

@mindmonk
Copy link
Copy Markdown
Contributor

Problem

When opening the Emergency Access view, the frontend made one sequential HTTP request per vault to load its recovery processes (N+1 requests total). This caused unnecessary load time, especially with many vaults.

Additionally, the template structure was missing a proper v-if/v-else-if chain, causing other content (license alerts, banners, etc.) to render alongside the loading indicator instead of being hidden until data is ready.

Solution

A new bulk endpoint loads all recovery processes for the current user's recoverable vaults in a single backend query. On the frontend, this request runs in parallel with the vault list fetch, replacing the sequential loop.
The template was also restructured so that loading, error, and content states are mutually exclusive.

@mindmonk mindmonk requested a review from SailReal April 10, 2026 07:40
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

Walkthrough

This change implements a performance optimization for emergency access recovery processes by adding a batch-retrieval mechanism. A new backend endpoint findAllForRecoverableVaults() fetches all recovery processes for recoverable vaults in a single query, supported by a new repository method findByVaultIds() that filters processes by vault IDs. The frontend service layer exposes this endpoint via findAllProcesses(), and the component logic is refactored to fetch all processes once in parallel with the vault list, then group and map them by vault ID instead of querying per-vault individually. Template control flow for error handling was also consolidated.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Suggested reviewers

  • SailReal
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: improving Emergency Access view loading performance through a bulk endpoint and parallel requests.
Description check ✅ Passed The description clearly explains the problem (N+1 requests and template rendering issues) and the solution (bulk endpoint and template restructuring), directly addressing the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/improve-ea-vault-list-loading

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue (1)

435-437: ⚠️ Potential issue | 🔴 Critical

Don't pass includes unbound to every().

Line 437 will throw a TypeError when the startable filter is selected. When every() invokes processTypes.includes as a plain callback, the this context is lost (set to undefined), and Array.prototype.includes requires a valid this value. Wrap it in a lambda instead.

Suggested fix
-      return !SUPPORTED_PROCESS_TYPES.every(processTypes.includes);
+      return !SUPPORTED_PROCESS_TYPES.every(type => processTypes.includes(type));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue` around
lines 435 - 437, The 'startable' case uses
SUPPORTED_PROCESS_TYPES.every(processTypes.includes) which passes includes
unbound and will throw a TypeError; change the call to provide a bound callback
such as using a lambda that calls processTypes.includes for each type (e.g.,
SUPPORTED_PROCESS_TYPES.every(t => processTypes.includes(t))) so that includes
runs with the correct receiver; update the logic in the case labeled 'startable'
referencing processTypes and SUPPORTED_PROCESS_TYPES accordingly.
🧹 Nitpick comments (1)
frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue (1)

144-148: Drop the redundant pictureUrl guard.

loadMembersForVault() calls backend.vaults.getMembers(vaultId), which already fills missing avatars in the service layer, so this conditional is dead weight here. Render the image unconditionally and rely on the shared contract instead.

Based on learnings, in the frontend codebase, ensure that authority DTOs for users and groups always include a pictureUrl by having AuthorityService.fillInMissingPicture() run at the service layer, and UI components should not implement their own fallback logic for missing images.

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

In `@frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue` around
lines 144 - 148, Remove the redundant guard around the avatar image in
EmergencyAccessVaultList.vue: because loadMembersForVault() uses
backend.vaults.getMembers(vaultId) which relies on
AuthorityService.fillInMissingPicture() to populate pictureUrl, delete the
v-if="member.pictureUrl" condition and render the <img> unconditionally (keep
the :src="member.pictureUrl" and class attributes) so the UI relies on the
service-layer contract rather than duplicating fallback logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue`:
- Line 188: The hard-coded heading "Vault Access" in
EmergencyAccessVaultList.vue should be localized; replace the literal string
inside the <h4> element with a call to the localization function used in this
component (e.g., t('...') or $t('...')) so the heading uses the same i18n key
naming convention as other strings in this file (update the key to something
like 'emergencyAccess.vaultAccess' to match existing keys and add the key/value
to the locale files).

---

Outside diff comments:
In `@frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue`:
- Around line 435-437: The 'startable' case uses
SUPPORTED_PROCESS_TYPES.every(processTypes.includes) which passes includes
unbound and will throw a TypeError; change the call to provide a bound callback
such as using a lambda that calls processTypes.includes for each type (e.g.,
SUPPORTED_PROCESS_TYPES.every(t => processTypes.includes(t))) so that includes
runs with the correct receiver; update the logic in the case labeled 'startable'
referencing processTypes and SUPPORTED_PROCESS_TYPES accordingly.

---

Nitpick comments:
In `@frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue`:
- Around line 144-148: Remove the redundant guard around the avatar image in
EmergencyAccessVaultList.vue: because loadMembersForVault() uses
backend.vaults.getMembers(vaultId) which relies on
AuthorityService.fillInMissingPicture() to populate pictureUrl, delete the
v-if="member.pictureUrl" condition and render the <img> unconditionally (keep
the :src="member.pictureUrl" and class attributes) so the UI relies on the
service-layer contract rather than duplicating fallback logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d9790049-7901-4c5c-9532-d080036eda93

📥 Commits

Reviewing files that changed from the base of the PR and between 6bae0ef and bdcffec.

📒 Files selected for processing (4)
  • backend/src/main/java/org/cryptomator/hub/api/EmergencyAccessResource.java
  • backend/src/main/java/org/cryptomator/hub/entities/EmergencyRecoveryProcess.java
  • frontend/src/common/backend.ts
  • frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue


<!-- VAULT MEMBERS -->
<section class="flex flex-1 min-w-0 flex-col">
<h4 class="mb-2 font-semibold text-gray-700">Vault Access</h4>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Localize this heading.

Vault Access is hard-coded, so it will stay English while the rest of the screen uses t(...).

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

In `@frontend/src/components/emergencyaccess/EmergencyAccessVaultList.vue` at line
188, The hard-coded heading "Vault Access" in EmergencyAccessVaultList.vue
should be localized; replace the literal string inside the <h4> element with a
call to the localization function used in this component (e.g., t('...') or
$t('...')) so the heading uses the same i18n key naming convention as other
strings in this file (update the key to something like
'emergencyAccess.vaultAccess' to match existing keys and add the key/value to
the locale files).

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.

1 participant