Click-to-Call: Dial Phone Numbers Directly from Any App - #3325
Click-to-Call: Dial Phone Numbers Directly from Any App#3325jeanfbrito wants to merge 61 commits into
Conversation
Register Rocket.Chat as OS handler for callto: and tel: URL schemes on Windows, macOS, and Linux. When a telephony link is clicked in any app, RC launches or focuses and dispatches a typed IPC event to the server webview with the parsed phone number. - Register callto/tel schemes in electron-builder.json (all platforms) - Add parseTelephonyLink() with number normalization and callto:// support - Add performTelephonyCall() with multi-server dialog + remember choice - Expose onTelephonyCallRequested callback on RocketChatDesktop API - Persist telephonyPreferredServer via selectPersistableValues - IPC listener registered before onReady to avoid cold-start race
Tests cover parseTelephonyLink (tel:/callto: protocols, number normalization, callto:// double-slash format, extension syntax, edge cases) and performTelephonyCall (0/1/2+ servers, preferred server persistence, dialog remember checkbox).
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds tel:/callto: deep-link handling: builder protocol config, multi-scheme registration, telephony URI parsing, server-selection and preference persistence, preload IPC plumbing to deliver telephony requests to renderers, UI settings and i18n, supported-version git-hash handling, and Jest tests. ChangesTelephony Deep Links Support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labelstype: feature 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/telephony/preload.ts (1)
18-29: ⚡ Quick winGuard
listenToTelephonyRequestsagainst duplicate registration.Calling
listenToTelephonyRequests()more than once (e.g., in a hot-reload dev cycle or defensive initialization) stacks multipleipcRenderer.onhandlers. Each subsequenttelephony/call-requestedevent would fire all of them, invokingtelephonyCallbackmultiple times or repeatedly overwritingpendingPayload.♻️ Proposed fix — idempotency guard
+let isListening = false; + export const listenToTelephonyRequests = (): void => { + if (isListening) return; + isListening = true; ipcRenderer.on( 'telephony/call-requested', (_event, payload: TelephonyPayload) => {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/telephony/preload.ts` around lines 18 - 29, listenToTelephonyRequests currently registers an ipcRenderer.on handler every time it's called, causing duplicate handlers; make it idempotent by guarding registration: add a module-level flag (e.g., isTelephonyListenerRegistered) or remove existing listeners for 'telephony/call-requested' before adding, then only call ipcRenderer.on if not already registered; keep the handler logic using telephonyCallback and pendingPayload unchanged and set the flag to true after successful registration (or rely on removeAllListeners + add to ensure a single handler).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/deepLinks/main.ts`:
- Around line 110-117: The dialog.showMessageBox call is missing the parent
window argument so the prompt isn't attached as a modal; update the call in
src/deepLinks/main.ts to pass the app's root window as the first argument (same
pattern used by askForServerAddition and warnAboutInvalidServerUrl) so the
dialog becomes a sheet/modal attached to the main window, keeping the existing
options ({ type, title, message, buttons, checkboxLabel, checkboxChecked }) as
the second parameter.
---
Nitpick comments:
In `@src/telephony/preload.ts`:
- Around line 18-29: listenToTelephonyRequests currently registers an
ipcRenderer.on handler every time it's called, causing duplicate handlers; make
it idempotent by guarding registration: add a module-level flag (e.g.,
isTelephonyListenerRegistered) or remove existing listeners for
'telephony/call-requested' before adding, then only call ipcRenderer.on if not
already registered; keep the handler logic using telephonyCallback and
pendingPayload unchanged and set the flag to true after successful registration
(or rely on removeAllListeners + add to ensure a single handler).
🪄 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: 705c516b-f939-4295-87b5-cd4aab10137e
📒 Files selected for processing (12)
electron-builder.jsonsrc/app/main/app.tssrc/app/selectors.tssrc/deepLinks/main.spec.tssrc/deepLinks/main.tssrc/preload.tssrc/servers/preload/api.tssrc/store/actions.tssrc/store/rootReducer.tssrc/telephony/actions.tssrc/telephony/preload.tssrc/telephony/reducers.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: check (ubuntu-latest)
- GitHub Check: check (windows-latest)
- GitHub Check: check (macos-latest)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript for all new code in this codebase unless explicitly told otherwise
Use Fuselage components from@rocket.chat/fuselagefor all UI work — only create custom components when Fuselage doesn't provide the needed functionality
Check Theme.d.ts for valid color tokens when working with Fuselage components
Use optional chaining with fallbacks for platform-specific APIs instead of mocks (e.g., process.getuid?.() ?? 1000) to ensure code works across all platforms without requiring mocks
TypeScript code must use strict mode
Use React functional components with hooks instead of class components
Redux actions must follow the FSA (Flux Standard Action) pattern
Use camelCase for file naming
Use PascalCase for component file names (React components)
Write self-documenting code through clear naming — avoid unnecessary comments
Files:
src/store/actions.tssrc/telephony/reducers.tssrc/app/selectors.tssrc/telephony/preload.tssrc/preload.tssrc/store/rootReducer.tssrc/telephony/actions.tssrc/app/main/app.tssrc/servers/preload/api.tssrc/deepLinks/main.spec.tssrc/deepLinks/main.ts
**/*.spec.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use *.spec.ts file naming convention for Renderer process tests
Files:
src/deepLinks/main.spec.ts
**/*.{spec,main.spec}.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{spec,main.spec}.ts: Tests must run and pass on Windows, macOS, and Linux CI environments — always verify cross-platform compatibility
Only mock Linux-only APIs (process.getuid(), process.getgid(), process.geteuid(), process.getegid()) when defensive coding with optional chaining isn't possible
Files:
src/deepLinks/main.spec.ts
🔇 Additional comments (10)
src/telephony/actions.ts (1)
1-5: Clean action typing for preferred server state.The constant and payload map are consistent and correctly model set/clear (
string | null) behavior.src/store/actions.ts (1)
12-12: Root action map integration is correct.Including the telephony payload map in the global action type composition is done cleanly and keeps
RootActiontyping coherent.Also applies to: 30-31
src/telephony/reducers.ts (1)
10-21: Reducer logic is straightforward and correct.Initial state, set behavior, and reset support via
nullpayload are all implemented as expected.src/store/rootReducer.ts (1)
21-21: Root reducer wiring for telephony state looks good.The new slice is properly registered and will be reflected in
RootState.Also applies to: 122-122
src/app/selectors.ts (1)
86-87: Persistable selector update is aligned with state changes.Including
telephonyPreferredServerhere keeps persisted state in sync with the new reducer slice.electron-builder.json (1)
5-8: Protocol packaging metadata update is consistent.The new telephony schemes are correctly declared both in app protocol config and Linux MIME handlers.
Also applies to: 126-126
src/preload.ts (1)
11-11: Preload telephony listener wiring is solid.The listener is integrated at an appropriate point in the preload startup sequence.
Also applies to: 68-68
src/app/main/app.ts (1)
43-47: Multi-scheme protocol registration is implemented correctly.Flattening all configured schemes and registering each one at startup matches the new
electron-builder.jsonprotocol format.Also applies to: 90-92
src/servers/preload/api.ts (1)
17-17: LGTM!Clean extension of
ExtendedIRocketChatDesktopfollowing the established pattern. The type signature correctly matchessrc/telephony/preload.ts's export.Also applies to: 53-55, 102-102
src/deepLinks/main.spec.ts (1)
131-335: LGTM! Thorough coverage across all server-selection branches.The
performTelephonyCalltests correctly sequencemockReturnValueOncecalls to separately control theserversandtelephonyPreferredServerselectors, and the polling test placesjest.useRealTimers()before the assertions so timers are always restored.
…duplicate IPC listener - Pass getRootWindow() as first argument to dialog.showMessageBox so the server selection prompt appears as a modal sheet attached to the main window (consistent with all other dialogs in the codebase) - Add idempotency guard to listenToTelephonyRequests to prevent duplicate IPC handler registration during hot-reload dev cycles
Add GitNexus section with impact analysis, query, and context tools. Gitignore .gitnexus index directory.
Dispatch WEBVIEW_GIT_COMMIT_HASH_CHANGED from server info response. Match supportedVersions exceptions using sha:<hash> prefix against the server's git commit hash for per-build version overrides.
Add TelephonyServer component to Settings > General tab with a Select dropdown to choose which server handles tel:/callto: links. Hidden when only one server exists. "Auto (ask each time)" option clears the preference and reverts to dialog behavior.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/servers/supportedVersions/main.ts (2)
237-238: 💤 Low valueRemove redundant trim() call.
The
gitCommitHashvariable is already trimmed on line 232, so the.trim()call on line 238 is redundant.♻️ Proposed cleanup
const normalizedGitCommitHash = gitCommitHash - .trim() .replace(/^sha-/, '') .toLowerCase();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/servers/supportedVersions/main.ts` around lines 237 - 238, The assignment to normalizedGitCommitHash unnecessarily calls .trim() again; remove the redundant .trim() so normalizedGitCommitHash is set directly from the already-trimmed gitCommitHash (update the line that defines normalizedGitCommitHash in main.ts to use gitCommitHash without calling .trim()).
237-240: ⚡ Quick winConsider case-insensitive prefix removal for robustness.
While
server.gitCommitHashis unlikely to have an uppercase "SHA-" prefix in practice, using a case-insensitive regex makes the code more defensive and consistent with Git's case-insensitive treatment of commit hashes.♻️ Proposed improvement
const normalizedGitCommitHash = gitCommitHash .trim() - .replace(/^sha-/, '') + .replace(/^sha-/i, '') .toLowerCase();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/servers/supportedVersions/main.ts` around lines 237 - 240, Update the normalization of gitCommitHash in normalizedGitCommitHash to remove a leading "sha-" prefix case-insensitively: change the regex used on gitCommitHash.trim().replace(/^sha-/, '') to a case-insensitive variant (e.g., use the /i flag) so any "SHA-", "Sha-", etc. prefixes are stripped consistently before toLowerCase(); keep the rest of the flow intact.src/servers/supportedVersions/main.main.spec.ts (1)
838-908: ⚡ Quick winConsider adding test coverage for case variations.
While the current tests cover the core sha-prefix functionality, adding tests for case variations (e.g., "SHA-bb83777" or uppercase commit hashes) would improve robustness—especially if the case-sensitivity issues in
main.tsare addressed.📋 Suggested test cases
it('should support uppercase SHA- prefix in exception versions', async () => { const futureDate = new Date(Date.now() + 86400000); const supportedVersions: SupportedVersions = { enforcementStartDate: new Date(Date.now() - 86400000).toISOString(), timestamp: new Date().toISOString(), versions: [{ version: '8.4.0', expiration: futureDate }], exceptions: { domain: 'open.rocket.chat', uniqueId: 'test-unique-id', versions: [{ version: 'SHA-bb83777', expiration: futureDate }], }, }; const result = await isServerVersionSupported( { url: 'https://open.rocket.chat/', version: '8.5', title: 'Rocket.Chat Open', gitCommitHash: 'bb83777b51a42d', } as any, supportedVersions ); expect(result.supported).toBe(true); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/servers/supportedVersions/main.main.spec.ts` around lines 838 - 908, Add tests that cover case variations for SHA-prefixed exception matching: create additional it(...) cases in the same spec that pass exception versions like 'SHA-bb83777' and commit hashes in uppercase (e.g., 'BB83777B51A42D') to ensure isServerVersionSupported correctly normalizes/matches case; reference the existing test setup (the SupportedVersions object and the isServerVersionSupported call) and duplicate the pattern used in the two existing tests but change the exception version prefix casing and commit-hash casing, asserting supported === true for matching variations and supported === false for malformed/non-matching variations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/servers/supportedVersions/main.ts`:
- Line 221: The current if check using
trimmedExceptionVersion.startsWith('sha-') is case-sensitive and will miss
prefixes like "SHA-"; update the condition in the if that references
trimmedExceptionVersion to perform a case-insensitive check (e.g., compare
trimmedExceptionVersion.toLowerCase().startsWith('sha-') or use a
case-insensitive regex) so any "sha-" prefix in any case is recognized as a
git-hash exception.
In `@src/ui/components/SettingsView/features/TelephonyServer.tsx`:
- Around line 37-43: The options useMemo is calling new URL(s.url).hostname
which can throw on malformed URLs and crash rendering; update the mapping inside
useMemo (or compute options after the component's early return) to safely
extract hostname by catching URL parsing errors or validating s.url first—e.g.,
wrap the hostname extraction in a try/catch (or use a small helper like
safeHostname(s.url)) and fall back to s.url or an empty string if parsing fails;
ensure this change references the existing useMemo/options mapping and s.url to
avoid crashing the component.
---
Nitpick comments:
In `@src/servers/supportedVersions/main.main.spec.ts`:
- Around line 838-908: Add tests that cover case variations for SHA-prefixed
exception matching: create additional it(...) cases in the same spec that pass
exception versions like 'SHA-bb83777' and commit hashes in uppercase (e.g.,
'BB83777B51A42D') to ensure isServerVersionSupported correctly
normalizes/matches case; reference the existing test setup (the
SupportedVersions object and the isServerVersionSupported call) and duplicate
the pattern used in the two existing tests but change the exception version
prefix casing and commit-hash casing, asserting supported === true for matching
variations and supported === false for malformed/non-matching variations.
In `@src/servers/supportedVersions/main.ts`:
- Around line 237-238: The assignment to normalizedGitCommitHash unnecessarily
calls .trim() again; remove the redundant .trim() so normalizedGitCommitHash is
set directly from the already-trimmed gitCommitHash (update the line that
defines normalizedGitCommitHash in main.ts to use gitCommitHash without calling
.trim()).
- Around line 237-240: Update the normalization of gitCommitHash in
normalizedGitCommitHash to remove a leading "sha-" prefix case-insensitively:
change the regex used on gitCommitHash.trim().replace(/^sha-/, '') to a
case-insensitive variant (e.g., use the /i flag) so any "SHA-", "Sha-", etc.
prefixes are stripped consistently before toLowerCase(); keep the rest of the
flow intact.
🪄 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: b3c2f774-98af-42e8-bd5b-bfd1d5a03ff7
📒 Files selected for processing (7)
.gitignoreCLAUDE.mdsrc/i18n/en.i18n.jsonsrc/servers/supportedVersions/main.main.spec.tssrc/servers/supportedVersions/main.tssrc/ui/components/SettingsView/GeneralTab.tsxsrc/ui/components/SettingsView/features/TelephonyServer.tsx
✅ Files skipped from review due to trivial changes (3)
- src/ui/components/SettingsView/GeneralTab.tsx
- .gitignore
- src/i18n/en.i18n.json
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: check (macos-latest)
- GitHub Check: check (ubuntu-latest)
- GitHub Check: check (windows-latest)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Fuselage components for all UI work; only create custom components when Fuselage doesn't provide what's needed. Import from
@rocket.chat/fuselageCheck Theme.d.ts for valid color tokens when using Fuselage
Use optional chaining with fallbacks for platform-specific APIs instead of mocking (e.g., const uid = process.getuid?.() ?? 1000)
Use TypeScript strict mode
Use React functional components with hooks
Use camelCase for file naming
No unnecessary comments — self-documenting code through clear naming
Files:
src/ui/components/SettingsView/features/TelephonyServer.tsxsrc/servers/supportedVersions/main.tssrc/servers/supportedVersions/main.main.spec.ts
**/*[A-Z]*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use PascalCase for component files
Files:
src/ui/components/SettingsView/features/TelephonyServer.tsx
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript for all new code unless explicitly told otherwise
Files:
src/servers/supportedVersions/main.tssrc/servers/supportedVersions/main.main.spec.ts
**/*.spec.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Name test files with *.spec.ts for renderer process tests
Files:
src/servers/supportedVersions/main.main.spec.ts
**/*.main.spec.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Name test files with *.main.spec.ts for main process tests
Files:
src/servers/supportedVersions/main.main.spec.ts
**/*.{spec,main.spec}.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Tests run on Windows, macOS, AND Linux CI — always verify cross-platform compatibility
Files:
src/servers/supportedVersions/main.main.spec.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: NEVER run `yarn build` directly in workspace directories — always use root commands
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: After building desktop-release-action, remove nested dist with: rm -rf workspaces/desktop-release-action/dist/dist
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Code signing for Windows builds uses Google Cloud KMS in two phases: build packages without signing first, then sign built packages using jsign
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Prefer editing existing files over creating new ones
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: NEVER commit or push without explicit user permission — 'fix this' does NOT mean 'commit it'
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: NEVER commit directly to master or dev — create a branch, test, open a PR
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Use worktrees to avoid disrupting the user's working directory when making changes
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Understand WHY code is written that way before changing it — working code is correct until proven otherwise
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Verify your work by running tests, checking types with npx tsc --noEmit, and demonstrating correctness
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Diagnose before iterating — analyze WHY approaches fail before trying the next one
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Always verify libraries by checking official docs and .d.ts files in node_modules/ — never assume props, tokens, or APIs
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Avoid subjective descriptors like 'smart', 'excellent', 'dumb' — use measurable descriptions in writing
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: Never invent metrics in PR descriptions or documentation — only include numbers from actual logs, error messages, or documented sources
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: PR descriptions should use straightforward language and focus on what changed and why
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: MUST run impact analysis with gitnexus_impact before editing any symbol
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: MUST run gitnexus_detect_changes before committing to verify changes only affect expected symbols
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: MUST warn the user if impact analysis returns HIGH or CRITICAL risk before proceeding with edits
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: NEVER edit a function, class, or method without first running gitnexus_impact on it
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: NEVER ignore HIGH or CRITICAL risk warnings from impact analysis
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-09T02:27:13.470Z
Learning: NEVER rename symbols with find-and-replace — use gitnexus_rename which understands the call graph
🪛 LanguageTool
CLAUDE.md
[style] ~118-~118: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...pected symbols and execution flows. - MUST warn the user if impact analysis retu...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[style] ~126-~126: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...L risk warnings from impact analysis. - NEVER rename symbols with find-and-replace — ...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
[style] ~127-~127: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...mewhich understands the call graph. - NEVER commit changes without runninggitnexu...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🔇 Additional comments (9)
CLAUDE.md (1)
107-149: Solid addition: clear operational guardrails for GitNexus usage.This section is actionable and well-structured (required steps, anti-patterns, and quick links), and it aligns with the workflow expectations for safe symbol changes and commit validation.
src/servers/supportedVersions/main.ts (3)
22-22: LGTM!Import of
WEBVIEW_GIT_COMMIT_HASH_CHANGEDis properly used in the dispatch call at line 392.
390-398: LGTM!The conditional dispatch of
WEBVIEW_GIT_COMMIT_HASH_CHANGEDcorrectly uses optional chaining to check forinfo.commit?.hashbefore dispatching, and the payload structure is consistent with Redux action patterns.
312-312: LGTM!The refactoring to use
isVersionExceptionForServerhelper improves code organization and enables the new git-hash exception matching functionality.src/servers/supportedVersions/main.main.spec.ts (5)
14-14: LGTM!Import is correctly used in the test assertion at line 197.
179-203: LGTM!Comprehensive test coverage for the git commit hash dispatch behavior. The test properly mocks server info with a commit hash and verifies the action payload structure.
796-796: LGTM!Changing
expirationto aDateinstance improves type consistency with the expectedSupportedVersionsinterface.
838-872: LGTM!Excellent test coverage for the sha-prefixed exception matching feature. The test correctly verifies that a server with matching git commit hash is recognized as supported when an exception entry with
sha-prefix exists.
874-908: LGTM!Good negative test case verifying that malformed (empty) exception versions don't incorrectly match.
electron-builder v26 rejects MimeType as a direct child of linux.desktop — only desktopActions and entry are valid properties. Move it inside desktop.entry where it belongs.
Replace hardcoded English strings in the telephony dialog with i18n t() calls and add telephonySelectServer translation keys to all 22 locale files.
PR 3325 TEL-QA matrix status - 2026-05-25
All mOSdat-configured OS targets for the PR 3325 TEL-QA matrix are passing. |
…selection Replace dialog.showMessageBox with an in-app modal that shows server favicons, names and hostnames — matching the sidebar appearance. Scales to many servers via a scrollable list and includes a "remember this choice" checkbox. Also hardens the telephony flow: - Mutex prevents concurrent tel: links from opening duplicate modals - 120s timeout on modal promise prevents hanging if renderer crashes - 10s timeout on webContents polling prevents infinite loop if server is removed between selection and view creation
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/deepLinks/main.ts`:
- Around line 83-85: The code currently normalizes the telephony URI using the
raw value from url.pathname/url.href but doesn't decode percent-encoded
characters; update the logic that builds raw/phoneNumber so that you first run
decodeURIComponent on the raw input (derived from url.pathname or
url.href.slice(url.protocol.length)) before applying replace(/^\/+/, '') and
.replace(/[\s\-().]/g, '') so percent-encoded characters like %2B become "+";
adjust variables raw and phoneNumber in src/deepLinks/main.ts accordingly.
In `@src/ui/components/TelephonyServerSelectModal/index.tsx`:
- Around line 65-75: The checkbox label isn't programmatically associated with
the control: add an id to the CheckBox (e.g., "telephony-server-remember") and
set that id as htmlFor on the Box (which is acting as the label) so assistive
tech recognizes the relationship; then remove the Box onClick toggle (leave the
CheckBox onChange using setRememberChoice and keep the checked={rememberChoice})
to avoid double-toggling while preserving the existing rememberChoice and
setRememberChoice state handlers.
In `@src/ui/components/TelephonyServerSelectModal/ServerItem.tsx`:
- Around line 39-55: The interactive row in ServerItem is not
keyboard-accessible; update the Tile element (in ServerItem) to render as a
native button by adding is='button' (and type='button') so it receives keyboard
focus and activates with Enter/Space, keep the existing onClick={handleClick}
and hover handlers (setIsHovered/isHovered) intact, and ensure any custom styles
don't remove the native focus outline so keyboard users can see focus.
🪄 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: d79ae606-5abc-4b78-babf-4f36179624b2
📒 Files selected for processing (7)
src/deepLinks/main.spec.tssrc/deepLinks/main.tssrc/ui/actions.tssrc/ui/components/Shell/index.tsxsrc/ui/components/TelephonyServerSelectModal/ServerItem.tsxsrc/ui/components/TelephonyServerSelectModal/index.tsxsrc/ui/reducers/dialogs.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/deepLinks/main.spec.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
MANDATORY: Use Fuselage components from
@rocket.chat/fuselagefor all UI work. Only create custom components when Fuselage doesn't provide what's needed.Check Theme.d.ts for valid color tokens when using Fuselage components.
Files:
src/ui/components/TelephonyServerSelectModal/index.tsxsrc/ui/components/Shell/index.tsxsrc/ui/actions.tssrc/ui/components/TelephonyServerSelectModal/ServerItem.tsxsrc/ui/reducers/dialogs.tssrc/deepLinks/main.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use optional chaining with fallbacks for platform-specific APIs to ensure cross-platform compatibility without mocks: e.g.,
process.getuid?.() ?? 1000Use TypeScript strict mode.
Use camelCase for file naming.
No unnecessary comments — self-documenting code through clear naming.
Always verify libraries — check official docs and
.d.tsfiles innode_modules/. Never assume props, tokens, or APIs work without verification.Avoid subjective descriptors ('smart', 'excellent', 'dumb') in code comments and documentation.
Use measurable descriptions in documentation and comments: 'reduced memory usage', 'improved by X%' instead of subjective terms.
Never invent metrics in code comments or documentation — no estimated time spent, no speculated user counts. Only include numbers from actual logs, error messages, or documented sources.
Files:
src/ui/components/TelephonyServerSelectModal/index.tsxsrc/ui/components/Shell/index.tsxsrc/ui/actions.tssrc/ui/components/TelephonyServerSelectModal/ServerItem.tsxsrc/ui/reducers/dialogs.tssrc/deepLinks/main.ts
**/*.tsx
📄 CodeRabbit inference engine (CLAUDE.md)
Use React functional components with hooks.
Use PascalCase for component files.
Files:
src/ui/components/TelephonyServerSelectModal/index.tsxsrc/ui/components/Shell/index.tsxsrc/ui/components/TelephonyServerSelectModal/ServerItem.tsx
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
TypeScript codebase. Use TypeScript for all new code unless explicitly told otherwise.
Files:
src/ui/actions.tssrc/ui/reducers/dialogs.tssrc/deepLinks/main.ts
**/*action*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Redux actions must follow FSA (Flux Standard Action) pattern.
Files:
src/ui/actions.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: NEVER run `yarn build` directly in workspace directories — always use root commands to avoid creating incorrect output structures.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: After building `desktop-release-action`, remove the nested dist: `rm -rf workspaces/desktop-release-action/dist/dist` — the action only needs `workspaces/desktop-release-action/dist/index.js`.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: NEVER add `ewsjs/xhr` patches to `patches/` directory — use `.yarn/patches/` instead as configured in package.json to avoid CI failures due to conflicts.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Use patch-package for patching `kayahr/jest-electron-runner` with patches stored in `patches/` directory.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Always include all architectures for Windows builds: x64, ia32, arm64.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Code signing uses Google Cloud KMS in two phases: 1) Build packages without signing (empty env vars), 2) Sign built packages using jsign with Google Cloud KMS to prevent MSI build failures.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Prefer editing existing files over creating new ones.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: NEVER commit or push without explicit user permission — 'fix this' does NOT mean 'commit it'.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: NEVER commit directly to master or dev — create a branch, test, open a PR.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Read operations on git (status, diff, log) are always fine.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Show what will be committed before committing.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Use git worktrees to avoid disrupting the user's working directory when creating feature branches.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Understand before changing — understand WHY code is written that way. Working code is correct until proven otherwise. If unsure, ASK.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Verify your work — run tests, check types (`npx tsc --noEmit`), demonstrate correctness. Never mark a task done without proving it works.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Diagnose before iterating — when approaches fail, analyze WHY before trying the next one. Don't cycle through 3+ approaches blindly.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: PR descriptions should use straightforward language and focus on what changed and why.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: MUST run impact analysis before editing any symbol using gitnexus_impact to understand blast radius.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: MUST run `gitnexus_detect_changes()` before committing to verify changes only affect expected symbols.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: MUST warn the user if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: Use gitnexus_query to find execution flows instead of grepping when exploring unfamiliar code.
Learnt from: CR
Repo: RocketChat/Rocket.Chat.Electron
Timestamp: 2026-05-12T00:50:52.404Z
Learning: NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
🔇 Additional comments (3)
src/ui/actions.ts (1)
159-160: LGTM!Also applies to: 312-319
src/ui/components/Shell/index.tsx (1)
20-20: LGTM!Also applies to: 106-106
src/ui/reducers/dialogs.ts (1)
4-9: LGTM!Also applies to: 24-33, 35-40, 65-83, 100-108
Drop Margins wrapper from the modal and the Tile container from rows. Title→message margin x8→x4, message→list x16→x12, rows now use paddingBlock x6 / paddingInline x8 instead of Tile padding x12.
…ver modal Adds 20 tests across three new spec files covering the telephony deep-link runtime path that was previously only validated by deepLinks/main.spec.ts. - src/telephony/renderer/preload.spec.ts (6 tests): IPC bridge state machine — listenToTelephonyRequests guard, pendingPayload buffer/replay, callback replacement, ipcRenderer.on registration. - src/ui/components/SettingsView/features/TelephonyServer.spec.tsx (8 tests): Settings dropdown — hide when servers.length <= 1, option generation (auto + per-server), value binding to telephonyPreferredServer, dispatch of TELEPHONY_PREFERRED_SERVER_SET (null for auto, URL string otherwise), hostname fallback when server title missing. - src/ui/components/TelephonyServerSelectModal/index.spec.tsx (6 tests): Modal flow — visibility gating on dialogs.telephonyServerSelect.isOpen, ServerItem rendering per server, dispatch payload shape on click with rememberChoice on/off, close dispatch with null payload, rememberChoice reset after close. Adds @testing-library/react, @testing-library/jest-dom, and @testing-library/dom (peer) as devDependencies. Fuselage Select and Dialog are mocked at module level since they rely on React-Aria and native <dialog>.showModal() respectively, which don't drive cleanly in @kayahr/jest-electron-runner's renderer environment. Spec paths follow the existing renderer testMatch convention: src/<module>/<subdir>/<file>.spec.tsx — a flat src/telephony/preload.spec.ts would be silently dropped by jest's testMatch globs.
tel:%2B15551234 left %2B encoded, producing phoneNumber '%2B15551234' instead of '+15551234'. decodeURIComponent runs before strip pass; malformed escapes return null (treated same as other invalid input).
…tive Git commit hashes are conventionally case-insensitive. SHA-bb83777 should match same as sha-bb83777.
- TelephonyServer: extract hostname via safeHostname helper to prevent settings page crash on malformed server URLs (new URL() throws). - TelephonyServerSelectModal: associate 'Remember this choice' label with checkbox via htmlFor/id for assistive tech. - ServerItem: render Tile as native button (is='button' type='button') so keyboard users get Tab focus and Enter/Space activation.
* feat(telephony): add global shortcut to dial clipboard number * fix(telephony): harden global shortcut handling * refactor(telephony): share dialpad opener * test(telephony): stabilize shortcut notification click
Linux installer download |
… do not crowd the text
…in policy flag Windows blocks user-mode writes to UserChoice (UCPD since March 2024) so the installer cannot make Rocket.Chat the default tel:/callto: handler on its own. Ship the canonical DefaultAssociations XML alongside the app and expose a new MSI public property SET_DEFAULT_ASSOCIATIONS=1 that, when explicitly passed, writes the GPO-equivalent registry value (HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\System!DefaultAssociationsConfiguration) pointing at the bundled XML. A sentinel under HKLM\\SOFTWARE\\Rocket.Chat\\InstallState lets uninstall remove just the value we wrote without touching other policies in that key. Documents GPO / Intune / DISM paths so admins who already manage default associations centrally use those channels instead of the installer flag (real AD GPOs win at the next gpupdate cycle anyway). A small spec guards against the XML and installer ProgIds drifting apart.
… upgrades The cleanup CA also fires during RemoveExistingProducts on a major upgrade, which would wipe HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\System!DefaultAssociationsConfiguration and the sentinel before the new MSI installs. The new MSI only rewrites when SET_DEFAULT_ASSOCIATIONS=1 is re-passed, and admins typically forget that on routine upgrades — so policy would silently disappear after a version bump. Gate the uninstall condition on UPGRADINGPRODUCTCODE="" so cleanup runs only on real uninstalls. Add an automated WiX-injection spec covering the property declaration, deferred + Impersonate="no" CA attributes, type-51 immediate setters, install/uninstall scheduling conditions (including the new upgrade guard), placement of CustomAction/Property elements as children of <Product>, and a regression check that backslashes in VBScript registry paths render as single backslashes after JS template-literal expansion.
Read Windows UserChoiceLatest when UserChoice is absent and treat explicit Windows handler choices as authoritative so another app cannot be reported as pass through protocol registration fallback. Hide the default-app CTA when diagnostics are healthy, add per-check settings actions for actionable failures, and cover the Windows/Linux diagnostic flows with focused tests.
Keep the telephony server selector aligned with the settings select width while preventing mid-word wrapping, and rename the prompt option to describe the ask-each-time behavior.
…server, link formats, cold start Five new functional scenarios fill the coverage gaps versus RocketChat/Rocket.Chat.Electron#3325 (Click-to-Call): - 3325-master-toggle.yaml: kill switch — OFF default, toggle ON, registration cycle, xdg-mime checks per state, sub-controls disabled when off. - 3325-global-shortcut.yaml: configurable shortcut field, capture, Save/Clear, trigger from unfocused window, clipboard ≥3-digit prefill rule, empty dial pad on non-phone text, unregistration when master toggle disabled. - 3325-auto-and-single-server.yaml: "Auto (ask each time)" resets persisted preference (modal reappears); single-server config hides dropdown and skips modal entirely. - 3325-link-formats.yaml: tel: E.164, callto: dashed, callto:// authority form, tel:(049) 123-456.78 parens+dots, tel: with ;ext=, malformed tel:abc graceful handling. - 3325-cold-start.yaml: RC fully quit, tel: cold-launches RC and queues number until UI ready; toggle-OFF negative path verifies RC starts but ignores the deep-link arg. All five use the canonical printf-array JSON pattern (not heredocs) for servers.json/config.json to keep YAML scalar indentation valid, the canonical TERM-loop pkill cleanup, and localize: + click: true for Settings UI elements rather than hardcoded coordinates.
Covers PR RocketChat/Rocket.Chat.Electron#3325 QA flow TEL-QA-004 (telephony diagnostics panel). End-to-end AT-SPI: cleanup → userData pre-stage → inline launch → server picker → kebab → Settings → Voice & Video → enable Telephony (probe-by-modal) → expand diagnostics → Refresh → Copy → clipboard JSON assertion. Live-verified PASS 27/27 against ubuntu2204@192.168.13.81 with the fresh feat/telephony-deeplink PR build deployed. - shared/scenarios/functional/3325-diagnostics-panel.yaml: new scenario (272 LOC). Inline-shell launch (routine bypass), userData inlined, probe-by-modal for Fuselage ToggleSwitch state (Got it modal fires only on OFF→ON), frame wait_for relaxed to role-only since post- pre-stage frame name is the workspace title. - shared/routines/launch-rocketchat.yaml: XAUTH fallback chain (mutter → gdm → \$HOME/.Xauthority → bare DISPLAY=:0). Routine previously assumed Wayland mutter-Xwaylandauth file; silently failed on Xorg. - docs/KNOWN_ISSUES.md: two new entries — Wayland-only XAUTH path and Fuselage ToggleSwitch action_name decoupled from React state. - AGENTS.md, CLAUDE.md: GitNexus stats counter refresh (auto-regen after swiss-knife epic reindex). Test counts: 1056 passed / 5 skipped / 3 xfailed / 0 failed.
The TelephonyServerSelectModal kept `rememberChoice` local state alive across close/reopen cycles when the modal closed via a Redux state update (e.g., external dispatch) rather than the local close handlers, leaking the prior `true` value into the next dispatched payload. Add a useEffect keyed on `isVisible` that resets `rememberChoice` when the modal becomes hidden. The existing in-handler resets stay in place for stores that do not propagate state changes (notably the stub reducers in unit tests). Fixes the failing `rememberChoice resets when the dialog is closed by state update` spec that blocked all 6 PR #3325 CI jobs.
…link # Conflicts: # .gitignore # src/app/main/app.ts # src/app/selectors.ts # src/deepLinks/main.spec.ts # src/deepLinks/main.ts # src/i18n/en.i18n.json # src/servers/supportedVersions/main.ts # src/store/rootReducer.ts # src/telephony/actions.ts # src/telephony/reducers.ts # src/ui/components/SettingsView/GeneralTab.tsx # src/ui/components/SettingsView/features/TelephonyServer.tsx
A deeplink targeting a workspace without VoIP never registers an onTelephonyCallRequested callback, so the buffered pendingPayload would sit in the frame indefinitely and could surface a stale number on a later unrelated remount. Drop the payload silently after a 120s TTL; the timer is cleared on flush so a consumed payload never re-fires.
extractClipboardPhoneNumber returned the raw trimmed clipboard text, so pasted content like "Call (800) 555-0199 now" reached the dial pad with surrounding words and formatting intact. Strip everything that is not a dialable character ([^\d+*#]) and keep + only as a leading prefix; still require at least 3 digits.
…3370) * feat: register callto:/tel: deep link handlers (DAMOVO-1) Register Rocket.Chat as OS handler for callto: and tel: URL schemes on Windows, macOS, and Linux. When a telephony link is clicked in any app, RC launches or focuses and dispatches a typed IPC event to the server webview with the parsed phone number. - Register callto/tel schemes in electron-builder.json (all platforms) - Add parseTelephonyLink() with number normalization and callto:// support - Add performTelephonyCall() with multi-server dialog + remember choice - Expose onTelephonyCallRequested callback on RocketChatDesktop API - Persist telephonyPreferredServer via selectPersistableValues - IPC listener registered before onReady to avoid cold-start race * test: add unit tests for telephony deep link parsing and routing Tests cover parseTelephonyLink (tel:/callto: protocols, number normalization, callto:// double-slash format, extension syntax, edge cases) and performTelephonyCall (0/1/2+ servers, preferred server persistence, dialog remember checkbox). * fix: attach server selection dialog to root window and guard against duplicate IPC listener - Pass getRootWindow() as first argument to dialog.showMessageBox so the server selection prompt appears as a modal sheet attached to the main window (consistent with all other dialogs in the codebase) - Add idempotency guard to listenToTelephonyRequests to prevent duplicate IPC handler registration during hot-reload dev cycles * chore: add GitNexus code intelligence config to CLAUDE.md Add GitNexus section with impact analysis, query, and context tools. Gitignore .gitnexus index directory. * feat: support sha-prefixed exception versions by git commit hash Dispatch WEBVIEW_GIT_COMMIT_HASH_CHANGED from server info response. Match supportedVersions exceptions using sha:<hash> prefix against the server's git commit hash for per-build version overrides. * feat: add telephony preferred server settings UI Add TelephonyServer component to Settings > General tab with a Select dropdown to choose which server handles tel:/callto: links. Hidden when only one server exists. "Auto (ask each time)" option clears the preference and reverts to dialog behavior. * fix: move MimeType into desktop.entry for electron-builder v26 compat electron-builder v26 rejects MimeType as a direct child of linux.desktop — only desktopActions and entry are valid properties. Move it inside desktop.entry where it belongs. * i18n: add translations for telephony server selection dialog Replace hardcoded English strings in the telephony dialog with i18n t() calls and add telephonySelectServer translation keys to all 22 locale files. * feat: replace native dialog with Fuselage modal for telephony server selection Replace dialog.showMessageBox with an in-app modal that shows server favicons, names and hostnames — matching the sidebar appearance. Scales to many servers via a scrollable list and includes a "remember this choice" checkbox. Also hardens the telephony flow: - Mutex prevents concurrent tel: links from opening duplicate modals - 120s timeout on modal promise prevents hanging if renderer crashes - 10s timeout on webContents polling prevents infinite loop if server is removed between selection and view creation * refactor(telephony-modal): tighten vertical spacing Drop Margins wrapper from the modal and the Tile container from rows. Title→message margin x8→x4, message→list x16→x12, rows now use paddingBlock x6 / paddingInline x8 instead of Tile padding x12. * test(telephony): add coverage for preload, settings dropdown, and server modal Adds 20 tests across three new spec files covering the telephony deep-link runtime path that was previously only validated by deepLinks/main.spec.ts. - src/telephony/renderer/preload.spec.ts (6 tests): IPC bridge state machine — listenToTelephonyRequests guard, pendingPayload buffer/replay, callback replacement, ipcRenderer.on registration. - src/ui/components/SettingsView/features/TelephonyServer.spec.tsx (8 tests): Settings dropdown — hide when servers.length <= 1, option generation (auto + per-server), value binding to telephonyPreferredServer, dispatch of TELEPHONY_PREFERRED_SERVER_SET (null for auto, URL string otherwise), hostname fallback when server title missing. - src/ui/components/TelephonyServerSelectModal/index.spec.tsx (6 tests): Modal flow — visibility gating on dialogs.telephonyServerSelect.isOpen, ServerItem rendering per server, dispatch payload shape on click with rememberChoice on/off, close dispatch with null payload, rememberChoice reset after close. Adds @testing-library/react, @testing-library/jest-dom, and @testing-library/dom (peer) as devDependencies. Fuselage Select and Dialog are mocked at module level since they rely on React-Aria and native <dialog>.showModal() respectively, which don't drive cleanly in @kayahr/jest-electron-runner's renderer environment. Spec paths follow the existing renderer testMatch convention: src/<module>/<subdir>/<file>.spec.tsx — a flat src/telephony/preload.spec.ts would be silently dropped by jest's testMatch globs. * fix(telephony): decode percent-encoded URI before sanitization tel:%2B15551234 left %2B encoded, producing phoneNumber '%2B15551234' instead of '+15551234'. decodeURIComponent runs before strip pass; malformed escapes return null (treated same as other invalid input). * fix(supportedVersions): make sha- exception prefix check case-insensitive Git commit hashes are conventionally case-insensitive. SHA-bb83777 should match same as sha-bb83777. * fix(telephony-ui): harden URL parsing and improve modal accessibility - TelephonyServer: extract hostname via safeHostname helper to prevent settings page crash on malformed server URLs (new URL() throws). - TelephonyServerSelectModal: associate 'Remember this choice' label with checkbox via htmlFor/id for assistive tech. - ServerItem: render Tile as native button (is='button' type='button') so keyboard users get Tab focus and Enter/Space activation. * Feat/telephony shortcut main process (#3334) * feat(telephony): add global shortcut to dial clipboard number * fix(telephony): harden global shortcut handling * refactor(telephony): share dialpad opener * test(telephony): stabilize shortcut notification click (#3331) * test(telephony): stabilize shortcut notification click (#3333) * Add telephony clipboard dial shortcut (#3330) * feat(telephony): add global shortcut to dial clipboard number * fix(telephony): harden global shortcut handling * refactor(telephony): share dialpad opener * test(telephony): stabilize shortcut notification click * fix telephony deeplink edge cases * chore: format telephony PR lint fixes * refactor: add marginBlock to Field components in SettingsView features Updated the AvailableBrowsers, TelephonyGlobalShortcut, TelephonyServer, and ThemeAppearance components to include a marginBlock of 'x16' on the Field components for improved spacing and layout consistency. * chore: polish telephony settings copy * chore: add telephony settings translations * feat(telephony): add master toggle and gate runtime registration Add `isTelephonyEnabled` setting (default off) to gate the telephony feature end-to-end: - New persisted `isTelephonyEnabled` boolean with action, reducer, and selector entry; surfaces as a master toggle in Settings > General. - `TelephonyServer` and `TelephonyGlobalShortcut` controls remain visible but disabled while the master toggle is off. - Global shortcut config selector returns the disabled config when the master toggle is off, so the existing watcher auto-unregisters any active accelerator on toggle-off. - `tel:`/`callto:` deep links short-circuit when the master toggle is off. - OS-level protocol registration for `tel`/`callto` moves out of the unconditional startup loop into a new reactive `setupTelephonyProtocolHandlers`, which calls `setAsDefaultProtocolClient` / `removeAsDefaultProtocolClient` in response to toggle changes. `rocketchat:` continues to register at startup unchanged. The macOS `Info.plist` and Linux `.desktop` files declared by electron-builder will still list the app as a candidate handler for `tel`/`callto`, but it will never be set as default unless the user opts in at runtime. * feat(telephony): prompt user about default handler conflicts on opt-in DMV-1 calls for a first-run prompt warning the user that Teams, Zoom, or Skype may already own the tel:/callto: handler and that they must confirm Rocket.Chat as default in OS settings themselves. Windows 10/11 hash-protects UserChoice so `setAsDefaultProtocolClient` only registers the app as a candidate; without the prompt, users have no way to know the OS silently kept the prior default. Trigger every off->on transition of the master `isTelephonyEnabled` toggle (not literal first run — the toggle is opt-in and is the natural moment of user intent). Seed the transition tracker via a synchronous `select` before subscribing, so returning users who reopen the app with telephony already enabled are not re-prompted. - New `TelephonyDefaultHandlerPromptModal` Fuselage modal (title, two body paragraphs, "Open System Settings" + "Got it" buttons), mounted in Shell next to the existing telephony modal. - Three new void actions (`_OPEN`, `_CLOSE`, `_OPEN_SETTINGS_CLICKED`) and a `telephonyDefaultHandlerPrompt` sub-reducer in `dialogs.ts`. - Main-process `setupTelephonyDefaultHandlerPrompt` watches the master toggle and dispatches OPEN on each off->on flip. Listens for the settings-button click and routes per platform: - Windows: `shell.openExternal('ms-settings:defaultapps')` - macOS: opens FaceTime preferences (where tel: default lives) - Linux: spawns `gnome-control-center default-apps` or `kcmshell5/6 componentchooser` based on `XDG_CURRENT_DESKTOP`; unknown DEs log a tip and rely on the modal's verbal instructions. - i18n keys under `telephony.defaultHandlerPrompt`. - 13 new tests covering transition detection, idempotency, teardown, and each platform branch. * test(app): nest PersistableValues spec into __tests__ The renderer Jest project's `testMatch` requires at least one subdirectory between `src/<module>/` and the spec, so `src/app/PersistableValues.spec.ts` was silently skipped. Moved the file under `src/app/__tests__/`, fixed the relative import, and expanded the migration assertion to cover `isTelephonyEnabled`. Documented the constraint in `CLAUDE.md` so future renderer specs are placed correctly. * feat(telephony): add Voice & Video settings tab and polish diagnostics UI Split telephony, video-call and screen-capture controls out of the General settings tab into a dedicated Voice & Video tab so the telephony stack has room to grow without crowding the rest of the settings. Diagnostics UI now collapses by default behind an Accordion with a status Tag summary in the title (pass/issues/warnings/checking). Per-check rows use Tag variants for status (primary/danger/warning) with flexShrink guards so the badge does not collapse to an ellipsis on narrow widths. Check labels were rewritten in user-facing terms (Click-to-call, Click-to-conference) and platform names are humanized (darwin -> macOS). Long handler paths are stripped from the inline details (full path still ships in the copy-diagnostics JSON) so the row layout stays clean. * feat(telephony): expose diagnostics IPC and Windows capabilities registration Adds telephony/get-diagnostics IPC channel and runtime module, wires TelephonyDiagnostics into the settings panel, and registers Rocket.Chat in the Windows RegisteredApplications/Capabilities surface so Default Apps exposes it for tel and callto. * fix(telephony): target app-scoped Windows default-apps deep link and skip darwin Detects per-user vs per-machine installs from process.execPath and opens ms-settings:defaultapps?registeredApp{User,Machine}=Rocket.Chat so the Default Apps page lands on the app-specific surface. macOS has no equivalent settings pane, so the open-settings handler is now a no-op and the modal hides body2 plus the Open Settings button on darwin. * fix(store): snapshot prev before invoking watcher to avoid stale re-entry If a watcher synchronously dispatches an action that re-triggers the same subscription, the recursive call previously saw a stale prev value. Capture prev into a local before assigning curr to it, so the recursive watcher invocation observes the freshly applied state. * refactor(telephony): localize shortcut display and tighten telephony copy Introduces formatAcceleratorForDisplay so the shortcut input and validation error render Cmd/Ctrl labels (with macOS-aware overrides) instead of leaking the raw Electron accelerator syntax. The input becomes capture-only (readOnly) so manual typing cannot desync from the stored value, and the reserved-accelerator key is renamed to reservedByApp with a new reservedByOS sibling. Several telephony strings (modal, settings descriptions, diagnostics labels, select server dialog) are rewritten for clarity and consistency. * chore: ignore local OpenWolf tooling state * chore: apply prettier formatting to VoiceVideoTab accordion items * fix(telephony): use Fuselage default color token for server title in select modal * fix(telephony): switch active workspace to resolved server before placing call openTelephonyDialpad sent telephony/call-requested to the resolved server's webContents but never updated currentView, so the call landed in a workspace the user was not looking at. Dispatch DEEP_LINKS_SERVER_FOCUSED (same action the rocketchat:// deep-link path uses) with the resolved URL before contacting the webview so the visible view follows the call across the single-server, preferred-server, and modal-selection paths. * fix(telephony): check Windows UserChoice ProgId for isDefault diagnostic app.isDefaultProtocolClient on Windows reports true when the RocketChat.tel / RocketChat.callto ProgIDs are registered, regardless of which handler the user actually picked via Default Apps. This made isDefault.tel pass even when Windows Settings still showed "Choose a default" for tel. Read the authoritative HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\URLAssociations\\<scheme>\\UserChoice ProgId and compare it to RocketChat.<scheme> instead. Non-Windows platforms keep using isDefaultProtocolClient. * docs(telephony): clarify Windows default-handler prompt and diagnostic messages Windows blocks apps from writing the UserChoice ProgId, so the user has to pick Rocket.Chat per scheme on the Default Apps page. Reword the default-handler modal to spell out that each link type (tel and callto) must be picked individually, mention that Windows itself prevents apps from setting it, and rename the action button to point at the Rocket.Chat default-apps page. Diagnostic details for the isDefault check now read as user-facing guidance instead of registry jargon: "Windows has not been told which app to use..." when UserChoice is missing, and "Currently handled by <app>. Open default apps to switch to Rocket.Chat." when another handler is set. * docs(telephony): split default-handler modal copy by platform body2 and the action button render on both Windows and Linux, so the prior Windows-specific text leaked onto Linux installs. Split into bodyWindows / bodyLinux and openSettingsWindows / openSettingsLinux keys and pick the right pair in the modal based on process.platform. * fix(telephony): widen body margin in default-handler modal so buttons do not crowd the text * feat(telephony): ship Windows default-app associations XML + MSI opt-in policy flag Windows blocks user-mode writes to UserChoice (UCPD since March 2024) so the installer cannot make Rocket.Chat the default tel:/callto: handler on its own. Ship the canonical DefaultAssociations XML alongside the app and expose a new MSI public property SET_DEFAULT_ASSOCIATIONS=1 that, when explicitly passed, writes the GPO-equivalent registry value (HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\System!DefaultAssociationsConfiguration) pointing at the bundled XML. A sentinel under HKLM\\SOFTWARE\\Rocket.Chat\\InstallState lets uninstall remove just the value we wrote without touching other policies in that key. Documents GPO / Intune / DISM paths so admins who already manage default associations centrally use those channels instead of the installer flag (real AD GPOs win at the next gpupdate cycle anyway). A small spec guards against the XML and installer ProgIds drifting apart. * fix(installer): preserve default-associations policy across MSI major upgrades The cleanup CA also fires during RemoveExistingProducts on a major upgrade, which would wipe HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\System!DefaultAssociationsConfiguration and the sentinel before the new MSI installs. The new MSI only rewrites when SET_DEFAULT_ASSOCIATIONS=1 is re-passed, and admins typically forget that on routine upgrades — so policy would silently disappear after a version bump. Gate the uninstall condition on UPGRADINGPRODUCTCODE="" so cleanup runs only on real uninstalls. Add an automated WiX-injection spec covering the property declaration, deferred + Impersonate="no" CA attributes, type-51 immediate setters, install/uninstall scheduling conditions (including the new upgrade guard), placement of CustomAction/Property elements as children of <Product>, and a regression check that backslashes in VBScript registry paths render as single backslashes after JS template-literal expansion. * docs(windows): split default-app-associations into its own shareable file * fix(telephony): tighten default handler diagnostics Read Windows UserChoiceLatest when UserChoice is absent and treat explicit Windows handler choices as authoritative so another app cannot be reported as pass through protocol registration fallback. Hide the default-app CTA when diagnostics are healthy, add per-check settings actions for actionable failures, and cover the Windows/Linux diagnostic flows with focused tests. * fix(telephony): keep server selector text readable Keep the telephony server selector aligned with the settings select width while preventing mid-word wrapping, and rename the prompt option to describe the ask-each-time behavior. * fix(i18n): complete Brazilian Portuguese translations * fix(i18n): complete German translations * docs(telephony): clarify default handler diagnostics * fix(installer): register telephony associations in MSI * Fix telephony release blockers * Add telephony QA flows * Make telephony QA flows Qase-ready * Harden QA flow authoring guidance * Add reusable Desktop QA flow skill * Tighten telephony QA coverage * chore: add telephony picker diagnostics * fix: filter deep link process arguments * fix(telephony): reset rememberChoice on state-driven modal close The TelephonyServerSelectModal kept `rememberChoice` local state alive across close/reopen cycles when the modal closed via a Redux state update (e.g., external dispatch) rather than the local close handlers, leaking the prior `true` value into the next dispatched payload. Add a useEffect keyed on `isVisible` that resets `rememberChoice` when the modal becomes hidden. The existing in-handler resets stay in place for stores that do not propagate state changes (notably the stub reducers in unit tests). Fixes the failing `rememberChoice resets when the dialog is closed by state update` spec that blocked all 6 PR #3325 CI jobs. * fix(telephony): expire buffered deeplink after 120s TTL A deeplink targeting a workspace without VoIP never registers an onTelephonyCallRequested callback, so the buffered pendingPayload would sit in the frame indefinitely and could surface a stale number on a later unrelated remount. Drop the payload silently after a 120s TTL; the timer is cleared on flush so a consumed payload never re-fires. * fix(telephony): strip non-phone debris from clipboard dial shortcut extractClipboardPhoneNumber returned the raw trimmed clipboard text, so pasted content like "Call (800) 555-0199 now" reached the dial pad with surrounding words and formatting intact. Strip everything that is not a dialable character ([^\d+*#]) and keep + only as a leading prefix; still require at least 3 digits. * chore: bump version to 4.15.0 * CORE-2201 Rewrite App settings panel copy and group into sections Rename the panel to "App settings" and restructure the General tab from a flat toggle list into five sections (App UI, System UI, System behavior, Calling, Other & technical), reordered per platform. Rewrite every label to sentence case and add a plain-language description to each setting, using "workspace" instead of "server" in user-facing strings. Add a macOS "Menu bar extra" variant for the tray icon, a "Bounce dock icon" label for flash frame, and a disabled-state hint for Minimize on close. Strings only; every toggle and input keeps the same preference key and behavior. * Rename Settings menu entries to App settings Update the workspace-bar overflow menu and the native app menu item that open the settings panel so their labels match the renamed "App settings" panel. * Hide telephony preferred server setting from App settings Remove the TelephonyServer picker from the settings panel so the unreleased click-to-call feature stays hidden on master. The full feature, including this UI, lands via PR #3325. Backend telephony code is left in place but dormant. * Translate App settings rewrite into 15 locales Apply the CORE-2201 settings copy rewrite to de-DE, es, fi, fr, hu, ja, no, pl, pt-BR, ru, sv, tr-TR, uk-UA, zh-CN and zh-TW: rename the panel to App settings, add the five section headings, retranslate changed labels and descriptions (workspace terminology, sentence case), and add the macOS menu-bar-extra and bounce-dock-icon variants plus the minimize-on-close hint where the parent key exists. Sparse stub locales (ar, it-IT, nb-NO, nn, se, zh) are left to fall back to en-US, matching their existing settings coverage. * fix: share main webview session with internal video chat window Internal conferences opened via openInternalVideoChatWindow ran in a webview hardcoded to the isolated `persist:jitsi-session` partition, so they did not share cookies or localStorage with the server webview they were opened from. Electron scopes cookies/localStorage to the (partition, origin) pair, so a same-origin conference loaded unauthenticated (the login token lives in localStorage under the server origin). Load the call webview in the originating server's partition (`persist:<serverUrl>`, resolved from the caller webContents) so the call shares the main webview's session. Because session-level handlers are per-session, sharing the session means the call window's handlers would otherwise clobber the main webview's: - Screen sharing now uses a single unified display-media handler that routes by originating frame (in-call requests open the picker in the call window; main-app requests fall back to the server-view picker), and the plain server-view handler is restored when the call closes. - The teardown permission-handler reset is skipped on a shared session so it can't disable permissions on the live main webview. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: harden video call window session sharing for production Make the POC session-share fix correct for all users. - Thread per-window state (ActiveCall record) instead of reading the mutable pendingVideoCallPartition global at lifecycle points that span async ticks; narrow the global to renderer-handshake reads only. - Restore the server-view display-media AND permission handlers from every teardown path (closed, cleanup, render-process-gone) so a shared session can't leave the main webview's screen sharing or permission prompts disabled after a call ends. Restore is idempotent. - Fix isSharedSession staleness: snapshot the per-window record at webview attach and evaluate routing at display-media request time. - Guard terminal state null-out with identity (=== capturedCall) so a stale prior-window teardown can't wipe a newer call's state. - Serialize the open-window handler via a promise-chain mutex to close a rapid-double-open race that orphaned a window with leaked handlers. - Retain persist:jitsi-session as the unresolved-server fallback so such calls keep stable isolated storage. Add ipc.main.spec.ts covering restore (shared/fallback/destroyed/ idempotent), partition assignment, render-process-gone, the null-out guard, and the serialization race. * fix: address CodeRabbit review feedback on #3370 Hardening fixes from CodeRabbit review (verified against code, false positives rejected): Security / stability: - videoCallWindow/ipc.ts: validate URL protocol (http/https) before the g.co external-open escape hatch (was reachable via ftp://g.co/...); tighten host match to exact g.co / *.g.co (was overmatching evilg.co); guard getRootWindow() rejection in restoreServerViewHandler so teardown can't produce an unhandled rejection (display-media restore still runs). - serverView/index.ts: gate the 'Permission request' debug log behind NODE_ENV==='development' (was logging payloads in production); wrap isProtocolAllowed() in try/catch so the openExternal permission callback always fires even on malformed URLs. - telephony/main.ts: handle shell.openExternal() promise rejection with .catch(); attach 'error' listeners to gnome/kcmshell spawns before unref() so a missing executable can't throw unhandled. - screenSharing/serverViewScreenSharing.ts: reset cached init state on provider init failure so later requests can retry instead of reusing a rejected promise until restart. - telephony/dialpad.ts: guard webContents.send against a destroyed handle. Data integrity: - PersistableValues.ts: preserve persisted telephonyGlobalShortcutConfig in the >=4.14.0 migration instead of resetting it to defaults on upgrade. UI / i18n: - TelephonyDiagnostics.tsx: catch rejected get-diagnostics IPC and set a controlled empty state instead of leaking an unhandled rejection. - i18n/ar, i18n/es: split reservedAccelerator into reservedByApp / reservedByOS to match the runtime keys (other locales fall back to en). QA tooling / installer: - validate-flows.mjs: enforce qase_id PRESENCE (key may be null per the flow contract) rather than truthiness, which would have rejected every existing flow; handle CRLF frontmatter delimiters. - export-qase-csv.mjs: normalize CRLF on read for cross-platform parsing. - package.json: declare yaml as a devDependency (used by export-qase-csv). - msiProjectCreated.js: fail fast on telephony RegWrite errors in the WriteTelephonyCapabilities custom action. Tests: - main.spec.ts: mock shell.openExternal as a resolved promise. - dialpad.spec.ts / deepLinks/main.spec.ts: add isDestroyed() to webContents mocks for the new destroyed-handle guard. - TelephonyGlobalShortcut.spec.tsx: add configurable:true so the process.platform restore in afterAll doesn't throw. Rejected: - Rename telephony/main.spec.ts -> main.main.spec.ts: false positive. jest testMatch already routes src/**/main.spec.ts to the main-process project; the rename would break discovery. Verified: tsc clean, lint clean, full suite 456 passed / 2 skipped / 0 failed, validate-flows passes all 14 telephony flows. * fix(i18n): complete settings option keys across locales CodeRabbit flagged locales missing settings.options keys introduced by the settings UX rewrite, causing controls to fall back to English. - Add missing settings.options keys (debugLogging, e2ePdfPreviewSizeLimit, detailedEventsLogging, outlookCalendarSyncInterval, verboseOutlookLogging, telephonyServer, and more) to de-DE, fi, hu, no, pt-BR, sv, uk-UA - Add missing settings.general tab label to ja and zh-CN - Translate settings.general no: General -> Generelt All 15 locales now match en.i18n.json (22 settings.options keys). * feat: openInMainWindow bridge to navigate the main window from the video call window The standalone internal video-chat window is its own BrowserWindow with no window.opener, so the web app's window.open/opener trick can't reach the main window — it just spawns another window. Add an IPC path so the conference page can ask the main app window to focus itself and navigate to an in-app route. Caller (video-chat window): - window.videoCallWindow.openInMainWindow(path) — validates that path is an in-app relative route ("/..."), rejecting absolute/protocol-relative/scheme URLs, then invokes 'video-call-window/open-in-main-window'. Main process: - New handler resolves the target server webview (caller's own server, else the active currentView.url), shows/restores/focuses the main window, and emits 'navigate-to-route' (payload: path) to that server webview's webContents. It intentionally does NOT loadURL — that would hard-reload the SPA. No-ops safely with a warning when no window/webview is found, and re-validates the path. Receiver (server webview is contextIsolated, so a raw send lands in the preload, not the page): - New navigateToRoute preload relay listens on 'navigate-to-route' and forwards to a RocketChatDesktop.onNavigateToRoute(callback) the web client registers, buffering the latest path if it arrives before registration (mirrors the telephony relay). Also: Cmd/Ctrl+Shift+D ("Toggle Developer Tools") now targets the focused window (falling back to the main window), so it can open DevTools for the video call window instead of always the main one. Web-repo follow-up (out of scope here): the web client must call RocketChatDesktop.onNavigateToRoute(path => router.navigate(path)) for the route change to take effect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: route open-in-main-window to the call's origin server When the standalone video call window asks the main window to navigate, the handler resolved the target server from the caller webContents and fell back to whichever server was active in the main window. In a multi-workspace setup that could navigate a *different* server than the one the call belongs to. Resolve in priority order: caller's own server, then the active call's origin server (authoritative, via activeCall.serverWebContentsId), then the active view as a last-resort guess (now logged as ambiguous). Also log in the preload bridge when a non-relative path is rejected, for parity with the main-process handler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: add videoCallWindow.close() bridge to close the video call window The internal video-chat window is a BrowserWindow created by the main process, so the renderer's own window.close() can't close it. Expose a close() method on the window.videoCallWindow bridge that asks the main process to do it. - Preload: close: () => ipcRenderer.send('video-call-window/close') (no payload). - Main: ipcMain.on('video-call-window/close') resolves the window from the sender (with a hostWebContents fallback for the webview-guest sender) and calls win.close() when it's live. Resolving from the sender means a renderer can only close its own window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: focus the existing video call window when reopening the same conference Clicking "join" again from the main window while the video call window was already open tore the window down and recreated it. When the requested conference URL matches the one already open, focus the existing window instead (restore if minimized, show, focus) and return early, leaving activeCall, provider, credentials and partition untouched. A different URL still closes + recreates as before. Track the conference URL on activeCall so the decision uses lifecycle state rather than the renderer-handshake globals. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: open external links from the video call window in the system browser The internal video-chat window didn't route external links to the system browser like the main window does, so target="_blank" / window.open links from the conference chat (which run in the webview guest, whose setWindowOpenHandler was unset) spawned a new Electron window instead. Set the guest webview's window-open handler on attach: http(s) popups return { action: 'deny' } and open via the system browser (openExternal), smb:// is denied, anything else stays in-app. Also add a will-navigate handler that sends external-scheme target="_self" navigations (mailto:, tel:, custom schemes) to the browser, while leaving http(s) self-navigations in the webview so the conference's own flows (auth redirects, etc.) keep working. Extract the shared deny/openExternal policy into a helper reused by the host window's existing handler so host and guest behave identically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review feedback on video call session sharing - media/openExternal permission branches in serverView now catch rejections and deny instead of leaving the request hanging - video call window-open policy denies popups by default, allowing only about:/blob: in-app schemes (closes javascript:/data:/file:/smb:) - install the media permission handler on the conference webview's partition session (isolated fallback only) so mic/cam requests route through handleMediaPermissionRequest instead of Electron's default - resolve the partition / set activeCall only inside the window-creating branch, after URL validation and the g.co redirect, so a bailed-out open can't leave stale state for teardown to misread - swallow the fire-and-forget open-in-main-window invoke rejection - drop no-op awaits on synchronous getNormalBounds()/getURL() - add regression tests for popup scheme policy and the fallback-session permission handler * CORE-2201 Apply UXDQA feedback to App settings panel Address designer review on the General tab: - Hide section heading labels (keep the grouped layout/order) - Stack select/input controls below their description, full-width - Move per-control caveats ("Requires app restart", "Reloads app on change") into dim c1 hint sublines instead of inline sentences - Drop the redundant "System default uses..." browser sentence - Render the video-calls description without bold product names - Make Clear screen capture permissions a secondary danger button - Use smart quotes around "do not ask again" in calling copy - Group the PDF preview size limit next to Hardware acceleration Full-width controls: SettingField wraps the control in a flex row so Fuselage Select/InputBox (flex-grow:1) fill the column; drop the maxWidth caps. Caveat sublines use fontScale c1 (regular) not micro (which is bold). Strings updated across all populated locales. * CORE-2201 Use Fuselage 3-tier field layout for App settings Route every settings row through Fuselage's canonical FieldLabel / FieldDescription / FieldHint stack instead of misusing FieldHint for description text and hand-rolling hints as <Box fontScale='c1'>. - Add shared ToggleField component (label+toggle row, then FieldDescription, optional FieldHint, children escape hatch) - Add description prop to SettingField (Select/Input rows) - Migrate all 13 toggle and 5 select feature components - Split the "App restarts when this option is changed" sentence out of videoCallScreenCaptureFallback.description into a dedicated hint key, across all 22 locales - Backfill option keys missing from ar, it-IT, nb-NO, nn, se, zh - ScreenCaptureFallback: replace Math.random id with useId * fix: use bare 'default' Fuselage color token instead of 'font-default' Box color= prepends the font- prefix internally, so passing color='font-default' produced an invalid token and logged 'invalid color: font-default' on every render. Use the bare 'default' token at all 5 call sites (MarkdownContent, DocumentViewer, PdfContent, TopBar). * fix: memoize DownloadsManagerView selectors and label back buttons - Wrap serverFilterOptions and the filtered downloads list in useMemo over a stable downloads slice, eliminating the react-redux 'Selector returned a different result' rerender warning - Add aria-label to the icon-only arrow-back IconButton in DownloadsManagerView and SettingsView for screen-reader access * fix: remove voice/video settings duplicated in General tab The master merge added ScreenCaptureFallback, InternalVideoChatWindow, and VideoCallWindowPersistence to GeneralTab while the new Voice & Video tab already rendered them alongside TelephonyServer and ClearPermittedScreenCaptureServers. Remove all five from GeneralTab so the Voice & Video tab is their single canonical home. --------- Co-authored-by: Rodrigo Nascimento <rodrigoknascimento@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Click-to-Call: Dial Phone Numbers Directly from Any App
Summary
You can now click phone number links (
tel:andcallto:) in any application — browser, email client, CRM, PDF viewer — and have Rocket.Chat open with the number ready to dial. The feature is fully opt-in: a master toggle in Settings > Voice & Video controls everything. When enabled, a new global keyboard shortcut lets you dial whatever phone number is on your clipboard from anywhere on your system.What's New
Added
tel:andcallto:links, in all common formats — international, dashed, parenthesizedImproved
Fixed
%2Bfor+) now decode correctly before being passed to the dial padPlatform Notes
tel:links. Rocket.Chat'sInfo.plistdeclarestel/calltoso macOS lists it as a candidate even before the toggle is enabled — but it only becomes the default handler once the user enables TelephonysetAsDefaultProtocolClientcall fortel/calltoonly fires when the toggle is on. Works from Outlook, Edge, Chrome, and other appsxdg-openon both X11 and Wayland. The.desktopMimeType list is baked at install time, so the OS may list the app as a candidate before the toggle is enabledHow to Test
Voice & Video Tab
Master Toggle (kill switch)
tel:link from another app — verify Rocket.Chat does not open the dial padHKEY_CURRENT_USER\Software\Classes\tel— verify Rocket.Chat is not registered. On Linux, runxdg-mime query default x-scheme-handler/teland verify it does not return Rocket.Chattel:link click and protocol-handler checks — verify the dial pad opens and the OS now lists Rocket.Chat as the default handlertel:clicks are ignoredConflict Detection
tel:handlerClick-to-Call (App Running)
tel:+1234567890in the address bar) and click itClick-to-Call (App Closed)
tel:orcallto:link in any applicationVarious Link Formats
tel:+491234567890— international formatcallto:+1-800-555-0199— dashed formatcallto://+491234567890— authority format (double-slash)tel:(049) 123-456.78— formatted with parens and dotsMulti-Workspace Selection
Global Shortcut
Preferred Workspace
tel:link — verify it goes directly to that workspace without showing the selection dialogDefault-Handler Diagnostics
Related