Skip to content

Chsami/scrollable accounts#30

Merged
chsami merged 5 commits intomainfrom
chsami/scrollable_accounts
Oct 18, 2025
Merged

Chsami/scrollable accounts#30
chsami merged 5 commits intomainfrom
chsami/scrollable_accounts

Conversation

@chsami
Copy link
Copy Markdown
Owner

@chsami chsami commented Oct 18, 2025

test

Summary by CodeRabbit

  • New Features

    • Added "Refresh accounts" button in the sidebar (accessible label and hover/disabled states) to manually update the account list.
    • App now opens in fullscreen mode by default.
  • Improvements

    • Persist and automatically restore selected account across sessions.
    • Account lists are sorted deterministically (case-insensitive) after updates.
    • Layout and spacing updated for accounts controls.
  • Tests

    • Added automated tests covering the account refresh flow.
  • Chores

    • Version bumped to 3.2.6.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 18, 2025

Walkthrough

Adds an account refresh feature: UI layout and styles for a refresh button, a preload bridge method, an IPC handler that calls oauth-jagex.writeAccountsToFile, renderer persistence/restore and sorting logic, a fullscreen window toggle, tests, and a package version bump.

Changes

Cohort / File(s) Summary
UI Layout & Styling
css/styles.css, index.html
New accounts-refresh-row layout, refresh-accounts-btn styles, and the refresh button element wrapped alongside the accounts-dropdown-container.
IPC Handler
libs/ipc-handlers.js
Added ipcMain.handle('refresh-accounts', ...) to validate/read accounts.json, extract a sessionId, call writeAccountsToFile(sessionId), and return updated accounts or errors.
Account Management Module
libs/oauth-jagex.js
Exported new writeAccountsToFile function (added to module.exports); minor formatting tweaks.
Electron Bridge
preload.js
Exposed refreshAccounts() via contextBridge.exposeInMainWorld('electron', ...) to invoke the new IPC.
Application Logic
renderer.js
Added persistSelectedAccount, restoreSelectedAccountIfAny, setupRefreshAccountsButton, restoringSelectedAccount flag, and case-insensitive alphabetical sorting; wired persistence/restore into flows and refresh button behavior.
Window Configuration
main.js
Set fullScreen: true in BrowserWindow creation options.
Testing
tests/refresh-accounts-ipc.test.js
New tests that mock oauth-jagex, seed a temp accounts.json, and validate success and missing-file error paths for refresh-accounts.
Version Bump
package.json
Updated version from 3.2.5 to 3.2.6.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as Sidebar UI
    participant Renderer
    participant Preload as Preload Bridge
    participant Main as IPC Handler
    participant OAuth as oauth-jagex
    participant FS as File System

    User->>UI: Click refresh button
    UI->>Renderer: click handler
    Renderer->>Preload: electron.refreshAccounts()
    Preload->>Main: invoke 'refresh-accounts'
    Main->>FS: check & read accounts.json
    FS-->>Main: return accounts array
    Main->>OAuth: writeAccountsToFile(sessionId)
    OAuth->>FS: fetch/write updated accounts.json
    FS-->>Main: return updated accounts
    Main-->>Preload: return { success, accounts }
    Preload-->>Renderer: resolved accounts
    Renderer->>Renderer: sort, update UI, persist/restore selection
    Renderer-->>UI: render refreshed accounts
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

codex

Poem

🐇 I hopped to the sidebar with a cheer,

Clicked refresh — new accounts drew near,
Sorted, saved, and nicely restored,
Small rabbit code, quietly adored,
A tiny hop for app-kind cheer.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title "Chsami/scrollable accounts" is misleading and fails to clearly communicate the actual changes in the PR. The title appears to include the branch name ("Chsami/scrollable") rather than describing the main functionality, and the term "scrollable" does not accurately reflect the primary changes, which are adding a refresh button for accounts, persisting/restoring account selection, exposing refresh functionality via IPC and preload APIs, and enabling fullscreen mode for the window. A teammate scanning the commit history would not understand what this PR actually implements based on this title alone. Consider renaming the PR title to something that clearly describes the main changes, such as "Add account refresh functionality with selection persistence" or "Implement refresh button and account state management". The new title should avoid branch name references and clearly convey the primary purpose of the changes to reviewers and future developers.
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5513e5 and d79ccd7.

📒 Files selected for processing (2)
  • renderer.js (8 hunks)
  • tests/refresh-accounts-ipc.test.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
tests/**/*.test.js

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.test.js: Put tests under tests/ and name them with the .test.js suffix (e.g., feature-name.test.js)
Name test files using kebab or consistent style mirroring feature-name.test.js

Files:

  • tests/refresh-accounts-ipc.test.js
**/*.js

📄 CodeRabbit inference engine (AGENTS.md)

**/*.js: Use 4-space indentation, trailing semicolons, and single quotes in JavaScript files
Prefer const/let over var in JavaScript
Prefer async/await over raw Promises in JavaScript

Files:

  • tests/refresh-accounts-ipc.test.js
  • renderer.js
{main.js,preload.js,renderer.js}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Electron entry points at the repo root: main process in main.js, with preload.js and renderer.js bridging UI logic

Files:

  • renderer.js
🧬 Code graph analysis (2)
tests/refresh-accounts-ipc.test.js (1)
libs/ipc-handlers.js (8)
  • require (18-22)
  • require (23-27)
  • require (75-79)
  • require (364-368)
  • require (379-383)
  • sessionId (101-101)
  • raw (88-88)
  • deps (2-13)
renderer.js (1)
libs/accounts-loader.js (3)
  • accounts (10-10)
  • accounts (66-66)
  • accounts (180-180)
🔇 Additional comments (11)
tests/refresh-accounts-ipc.test.js (4)

1-12: Mock hoisting and setup look good.

The module-level mock with placeholder implementations and the use of module variables to share state between setup and mock logic correctly addresses the previous review feedback.


19-72: Test setup is well-structured.

The beforeEach correctly sequences temp directory creation, mock configuration, and module loading. The mock implementation properly appends a second account for testing the refresh flow.


74-86: Cleanup logic is solid.

The afterEach properly removes temporary directories and clears mock state. The try/catch wrapper with console warning ensures tests don't fail due to cleanup issues, which is appropriate for this scenario.


88-103: Test coverage is appropriate.

The tests cover both the success path (account refresh adds new account) and error handling (missing accounts.json). The assertions are specific and validate the expected behavior effectively.

renderer.js (7)

2-2: Guard flag prevents circular updates.

The restoringSelectedAccount flag is a reasonable pattern to prevent re-persisting the account selection during restoration, avoiding infinite loops or redundant writes.


248-249: Restoration after file changes is well-placed.

Calling restoreSelectedAccountIfAny() after detecting account file changes ensures the persisted selection is rehydrated when accounts are modified externally.


357-359: Guard prevents circular persistence.

The conditional check prevents re-persisting the account selection during restoration, which correctly avoids infinite loops or redundant writes to the properties file.


885-925: Refresh button logic is well-structured with proper restoration.

The function correctly handles the refresh flow, including sorting accounts, updating UI, and restoring the persisted selection. The restoration calls at lines 908 and 916 address the previous review feedback.

Note: The duplicate sorting logic at lines 899-905 is addressed in the comment for lines 114-125.


1205-1222: Persistence logic is sound.

The function correctly handles both saving and clearing the selected account property, only writing when the value changes. Error handling logs issues without throwing, which is appropriate for a non-critical persistence feature.


1224-1242: Restoration logic handles edge cases properly.

The function validates that the persisted account still exists before restoring, clears stale state if the account was deleted, and uses the guard flag to prevent circular updates. The restoration of both selection and profile is complete.


1261-1261: Initialization calls are correctly placed.

Both restoreSelectedAccountIfAny() and setupRefreshAccountsButton() are appropriately positioned in the initialization flow, ensuring accounts are loaded before restoration and the refresh button is wired up during UI setup.

Also applies to: 1279-1279


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: 6

Caution

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

⚠️ Outside diff range comments (1)
package.json (1)

35-35: Upgrade axios from 1.7.2 to address CVE-2024-39338 SSRF vulnerability.

Axios 1.7.2 contains CVE-2024-39338, a high-severity SSRF vulnerability where path-relative URLs can be treated as protocol-relative URLs, allowing server-side request forgery. Fixes are available in later 1.7.x and 1.8.x releases. Update package.json to a patched version.

🧹 Nitpick comments (3)
libs/ipc-handlers.js (2)

82-93: Consider using async filesystem operations.

The handler uses synchronous filesystem operations (fs.existsSync, fs.readFileSync) which can block the main process. Since this is already an async handler, consider using the async equivalents (fs.promises.access, fs.promises.readFile) for better performance.

-const accountsPath = path.join(microbotDir, 'accounts.json');
-if (!fs.existsSync(accountsPath)) {
-    return { error: 'accounts.json does not exist' };
-}
-
-let accountsData = [];
-try {
-    const raw = fs.readFileSync(accountsPath, 'utf8');
-    accountsData = JSON.parse(raw);
-} catch (err) {
-    log.error('Failed to read accounts.json for refresh:', err.message);
-    return { error: 'Failed to read accounts.json' };
-}
+const accountsPath = path.join(microbotDir, 'accounts.json');
+let accountsData = [];
+try {
+    const raw = await fs.promises.readFile(accountsPath, 'utf8');
+    accountsData = JSON.parse(raw);
+} catch (err) {
+    if (err.code === 'ENOENT') {
+        return { error: 'accounts.json does not exist' };
+    }
+    log.error('Failed to read accounts.json for refresh:', err.message);
+    return { error: 'Failed to read accounts.json' };
+}

99-104: Document the sessionId sharing assumption.

The comment states "assuming all accounts share the same session scope." This is a critical assumption for the refresh logic. Consider adding validation or error handling if accounts have different sessionIds, or document this constraint more explicitly in the function documentation.

tests/refresh-accounts-ipc.test.js (1)

75-93: Consider adding more test coverage.

The current tests cover the happy path and missing file case, but could benefit from additional scenarios:

  • Empty accounts array (should return error)
  • Accounts without sessionId (should return error)
  • Verify the correct sessionId is passed to writeAccountsToFile
  • Multiple accounts with different sessionIds

Example test for missing sessionId:

test('refresh-accounts errors when sessionId is missing', async () => {
    const accountsFile = path.join(tempDir, 'accounts.json');
    fs.writeFileSync(accountsFile, JSON.stringify([
        { accountId: 'acc1', displayName: 'No Session' }
    ]));
    const handler = registeredHandlers['refresh-accounts'];
    const result = await handler();
    expect(result.error).toMatch(/No sessionId found/);
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b78bdd and 23bc5d6.

📒 Files selected for processing (9)
  • css/styles.css (2 hunks)
  • index.html (1 hunks)
  • libs/ipc-handlers.js (1 hunks)
  • libs/oauth-jagex.js (3 hunks)
  • main.js (1 hunks)
  • package.json (1 hunks)
  • preload.js (1 hunks)
  • renderer.js (8 hunks)
  • tests/refresh-accounts-ipc.test.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
{main.js,preload.js,renderer.js}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Electron entry points at the repo root: main process in main.js, with preload.js and renderer.js bridging UI logic

Files:

  • main.js
  • preload.js
  • renderer.js
**/*.js

📄 CodeRabbit inference engine (AGENTS.md)

**/*.js: Use 4-space indentation, trailing semicolons, and single quotes in JavaScript files
Prefer const/let over var in JavaScript
Prefer async/await over raw Promises in JavaScript

Files:

  • main.js
  • preload.js
  • libs/ipc-handlers.js
  • renderer.js
  • libs/oauth-jagex.js
  • tests/refresh-accounts-ipc.test.js
libs/**

📄 CodeRabbit inference engine (AGENTS.md)

libs/**: Place shared functionality (IPC handlers, utilities) under libs/
Keep Electron IPC handlers isolated within libs/

Files:

  • libs/ipc-handlers.js
  • libs/oauth-jagex.js
libs/**/*.js

📄 CodeRabbit inference engine (AGENTS.md)

Name shared modules in camelCase (e.g., libs/memoryUtils.js)

Files:

  • libs/ipc-handlers.js
  • libs/oauth-jagex.js
package.json

📄 CodeRabbit inference engine (AGENTS.md)

package.json: Versioning follows package.json; update it when releasing
When changing auto-update endpoints or build metadata, update build.publish.url in package.json

Files:

  • package.json
index.html

📄 CodeRabbit inference engine (AGENTS.md)

Keep the main HTML entry at index.html in the project root

Files:

  • index.html
tests/**/*.test.js

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.test.js: Put tests under tests/ and name them with the .test.js suffix (e.g., feature-name.test.js)
Name test files using kebab or consistent style mirroring feature-name.test.js

Files:

  • tests/refresh-accounts-ipc.test.js
css/**

📄 CodeRabbit inference engine (AGENTS.md)

Store renderer CSS assets under css/

Files:

  • css/styles.css
css/**/*.css

📄 CodeRabbit inference engine (AGENTS.md)

Name renderer CSS files in kebab-case (e.g., css/app-shell.css)

Files:

  • css/styles.css
🧬 Code graph analysis (3)
libs/ipc-handlers.js (3)
libs/oauth-jagex.js (5)
  • require (1-1)
  • require (7-7)
  • path (5-5)
  • fs (4-4)
  • log (6-6)
main.js (8)
  • require (1-1)
  • require (6-6)
  • require (8-8)
  • require (11-11)
  • require (12-16)
  • path (2-2)
  • fs (3-3)
  • log (7-7)
libs/dir-module.js (5)
  • path (2-2)
  • microbotDir (15-15)
  • fs (84-84)
  • fs (111-111)
  • fs (192-192)
renderer.js (1)
libs/accounts-loader.js (3)
  • accounts (10-10)
  • accounts (66-66)
  • accounts (180-180)
tests/refresh-accounts-ipc.test.js (3)
libs/oauth-jagex.js (5)
  • fs (4-4)
  • require (1-1)
  • require (7-7)
  • path (5-5)
  • result (127-127)
main.js (9)
  • fs (3-3)
  • require (1-1)
  • require (6-6)
  • require (8-8)
  • require (11-11)
  • require (12-16)
  • path (2-2)
  • deps (37-52)
  • handler (36-36)
libs/ipc-handlers.js (11)
  • require (18-22)
  • require (23-27)
  • require (75-79)
  • require (364-368)
  • require (379-383)
  • accountsPath (81-81)
  • sessionId (101-101)
  • raw (88-88)
  • deps (2-13)
  • result (292-296)
  • result (345-345)
🔇 Additional comments (12)
package.json (1)

3-3: LGTM!

Version bump to 3.2.6 is appropriate for the new account refresh feature.

preload.js (1)

76-76: LGTM!

The refreshAccounts API method follows the established pattern and correctly bridges the IPC call to the main process.

index.html (1)

120-127: LGTM!

The UI changes are well-structured with proper accessibility attributes (aria-label, aria-live, title). The horizontal layout with the refresh button is intuitive.

libs/oauth-jagex.js (1)

419-419: LGTM!

Exporting writeAccountsToFile enables the account refresh functionality while maintaining the existing implementation. The function is well-tested through the new IPC handler tests.

css/styles.css (1)

220-265: LGTM!

The styling for the refresh button is well-implemented with:

  • Proper accessibility states (focus-visible, disabled, hover)
  • Consistent color scheme matching the existing design
  • Flexible layout that integrates smoothly with the accounts dropdown
renderer.js (7)

2-2: LGTM! Flag prevents infinite loops.

The restoringSelectedAccount flag correctly prevents the character change handler from persisting during restoration, avoiding circular updates.


248-249: Verify restoration logic doesn't override intentional selection changes.

After detecting file changes and updating the selection (lines 228-246), the code immediately attempts to restore the persisted account. This could override the selection that was just updated based on account additions/deletions.

For example:

  • If a new account is added and line 231 selects it (updateCharacterSelection(latestAccount.accountId))
  • Line 249 immediately calls restoreSelectedAccountIfAny() which might restore a different account

Verify this is the intended behavior and that restoration should take precedence over automatic selection of new accounts.


357-359: LGTM! Correctly prevents circular updates.

The conditional check ensures that persistence only occurs during user-initiated changes, not during programmatic restoration, which prevents infinite loops between the character change handler and the restoration logic.


1203-1220: LGTM! Efficient persistence logic with proper error handling.

The function correctly:

  • Clears the persisted selection when 'none' is selected
  • Only writes to properties when the value changes (avoiding unnecessary I/O)
  • Handles errors gracefully with logging

1222-1240: LGTM! Robust restoration logic with stale data cleanup.

The function correctly:

  • Validates that the saved account still exists before restoring
  • Cleans up stale references when accounts are deleted
  • Uses the restoration flag to prevent circular updates
  • Handles errors gracefully

1259-1259: LGTM! Correct placement of restoration during initialization.

Calling restoreSelectedAccountIfAny() after accounts are loaded and the sidebar is set up ensures the persisted selection can be properly restored on application startup.


1276-1277: LGTM! Refresh button setup correctly placed during initialization.

Calling setupRefreshAccountsButton() at the end of UI initialization ensures the handler is registered after the DOM is ready.

Comment thread libs/ipc-handlers.js
Comment on lines +109 to +116
try {
const updatedRaw = fs.readFileSync(accountsPath, 'utf8');
const updatedAccounts = JSON.parse(updatedRaw);
return { success: true, accounts: updatedAccounts };
} catch (err) {
log.error('Failed to read updated accounts.json after refresh:', err.message);
return { success: true };
}
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

Inconsistent error handling after refresh.

If re-reading the updated accounts.json fails after a successful refresh, the handler returns { success: true } without the updated accounts list. This could cause the UI to not reflect the refreshed accounts. Consider either:

  • Returning the error and requiring a full page refresh
  • Keeping the accounts in memory before refresh as a fallback
-try {
-    const updatedRaw = fs.readFileSync(accountsPath, 'utf8');
-    const updatedAccounts = JSON.parse(updatedRaw);
-    return { success: true, accounts: updatedAccounts };
-} catch (err) {
-    log.error('Failed to read updated accounts.json after refresh:', err.message);
-    return { success: true };
-}
+try {
+    const updatedRaw = fs.readFileSync(accountsPath, 'utf8');
+    const updatedAccounts = JSON.parse(updatedRaw);
+    return { success: true, accounts: updatedAccounts };
+} catch (err) {
+    log.error('Failed to read updated accounts.json after refresh:', err.message);
+    return { error: 'Refresh completed but failed to read updated accounts. Please reload.' };
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const updatedRaw = fs.readFileSync(accountsPath, 'utf8');
const updatedAccounts = JSON.parse(updatedRaw);
return { success: true, accounts: updatedAccounts };
} catch (err) {
log.error('Failed to read updated accounts.json after refresh:', err.message);
return { success: true };
}
try {
const updatedRaw = fs.readFileSync(accountsPath, 'utf8');
const updatedAccounts = JSON.parse(updatedRaw);
return { success: true, accounts: updatedAccounts };
} catch (err) {
log.error('Failed to read updated accounts.json after refresh:', err.message);
return { error: 'Refresh completed but failed to read updated accounts. Please reload.' };
}
🤖 Prompt for AI Agents
In libs/ipc-handlers.js around lines 109-116, the catch block after re-reading
accounts.json returns { success: true } without accounts, causing the UI to lose
refreshed data; update the handler to either return a failure with the error
(e.g. { success: false, error: err.message }) so callers can trigger a full
refresh, or keep a previously-read accounts snapshot before the refresh and
return it as a fallback (e.g. { success: true, accounts: previousAccounts });
ensure you capture previousAccounts before performing the refresh and log the
full error message when returning failure.

Comment thread main.js Outdated
Comment thread renderer.js
Comment on lines +114 to +125
// Sort alphabetically by displayName (case-insensitive); fallback to accountId
try {
result.sort((a, b) => {
const nameA = (a?.displayName || a?.accountId || '').toString().trim().toLowerCase();
const nameB = (b?.displayName || b?.accountId || '').toString().trim().toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
} catch (_) {
// Ignore sort errors
}
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.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicate sorting logic and avoid silent error swallowing.

This sorting logic is duplicated in setupRefreshAccountsButton() (lines 899-905). Additionally, the catch block silently ignores all errors, which could hide bugs.

Extract the sorting logic into a helper function:

+function sortAccountsAlphabetically(accountsList) {
+    if (!Array.isArray(accountsList)) return accountsList;
+    try {
+        accountsList.sort((a, b) => {
+            const nameA = (a?.displayName || a?.accountId || '').toString().trim().toLowerCase();
+            const nameB = (b?.displayName || b?.accountId || '').toString().trim().toLowerCase();
+            if (nameA < nameB) return -1;
+            if (nameA > nameB) return 1;
+            return 0;
+        });
+    } catch (err) {
+        window.electron?.logError?.(`Failed to sort accounts: ${err?.message || err}`);
+    }
+    return accountsList;
+}
+
 async function safeReadAccounts() {
     const result = await window.electron.readAccounts();
     if (result?.error) {
         reportAccountsError(`Failed to load accounts: ${result.error}`);
         return null;
     }
 
     if (!Array.isArray(result)) {
         reportAccountsError('Accounts data is in an unexpected format.');
         return null;
     }
 
     lastAccountsReadError = null;
-    // Sort alphabetically by displayName (case-insensitive); fallback to accountId
-    try {
-        result.sort((a, b) => {
-            const nameA = (a?.displayName || a?.accountId || '').toString().trim().toLowerCase();
-            const nameB = (b?.displayName || b?.accountId || '').toString().trim().toLowerCase();
-            if (nameA < nameB) return -1;
-            if (nameA > nameB) return 1;
-            return 0;
-        });
-    } catch (_) {
-        // Ignore sort errors
-    }
-    return result;
+    return sortAccountsAlphabetically(result);
 }

Then update setupRefreshAccountsButton() to use the same helper (lines 898-905):

             if (Array.isArray(updated)) {
-                // Sort updated accounts alphabetically
-                accounts = [...updated].sort((a, b) => {
-                    const nameA = (a?.displayName || a?.accountId || '').toString().trim().toLowerCase();
-                    const nameB = (b?.displayName || b?.accountId || '').toString().trim().toLowerCase();
-                    if (nameA < nameB) return -1;
-                    if (nameA > nameB) return 1;
-                    return 0;
-                });
+                accounts = sortAccountsAlphabetically([...updated]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Sort alphabetically by displayName (case-insensitive); fallback to accountId
try {
result.sort((a, b) => {
const nameA = (a?.displayName || a?.accountId || '').toString().trim().toLowerCase();
const nameB = (b?.displayName || b?.accountId || '').toString().trim().toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
} catch (_) {
// Ignore sort errors
}
function sortAccountsAlphabetically(accountsList) {
if (!Array.isArray(accountsList)) return accountsList;
try {
accountsList.sort((a, b) => {
const nameA = (a?.displayName || a?.accountId || '').toString().trim().toLowerCase();
const nameB = (b?.displayName || b?.accountId || '').toString().trim().toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
} catch (err) {
console.error('Failed to sort accounts:', err);
}
return accountsList;
}
async function safeReadAccounts() {
const result = await window.electron.readAccounts();
if (result?.error) {
reportAccountsError(`Failed to load accounts: ${result.error}`);
return null;
}
if (!Array.isArray(result)) {
reportAccountsError('Accounts data is in an unexpected format.');
return null;
}
lastAccountsReadError = null;
return sortAccountsAlphabetically(result);
}
🤖 Prompt for AI Agents
In renderer.js around lines 114-125 (and duplicate at ~898-905), the
alphabetical sort logic is duplicated and its try/catch silently swallows
errors; extract this into a single helper function (e.g.,
sortAccounts(accounts)) that encapsulates the case-insensitive
displayName/accountId comparison and returns the sorted array (document whether
it sorts in-place or returns a new array), then replace both existing blocks to
call that helper; remove the empty catch and instead let the helper either
propagate errors or catch and log them (use console.error or a logger) and
return the original array as a safe fallback.

Comment thread renderer.js
Comment thread tests/refresh-accounts-ipc.test.js Outdated
Comment thread tests/refresh-accounts-ipc.test.js
@chsami chsami merged commit 0bf1dcc into main Oct 18, 2025
1 check passed
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