Skip to content

Fix: markdown download cancellation logic#3348

Open
tyroneyeh wants to merge 3 commits into
RocketChat:masterfrom
tyroneyeh:master_fixMarkdownOnlyView
Open

Fix: markdown download cancellation logic#3348
tyroneyeh wants to merge 3 commits into
RocketChat:masterfrom
tyroneyeh:master_fixMarkdownOnlyView

Conversation

@tyroneyeh

@tyroneyeh tyroneyeh commented May 28, 2026

Copy link
Copy Markdown

Closes #3345

image

Summary by CodeRabbit

  • Bug Fixes

    • Improved markdown download handling: non-download links are no longer canceled or opened in the document viewer; only viewer-eligible markdown downloads are intercepted and handled.
  • Tests

    • Added unit tests validating detection logic for markdown viewer download URLs, covering query parameter variants and non-markdown cases.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c9ac798-e790-4950-bfd7-df1677572624

📥 Commits

Reviewing files that changed from the base of the PR and between 572781d and b494117.

📒 Files selected for processing (3)
  • src/ui/main/serverView/index.ts
  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.ts
✅ Files skipped from review due to trivial changes (1)
  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/ui/main/serverView/index.ts
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use TypeScript for all new code unless explicitly told otherwise
Use optional chaining with fallbacks for platform-specific APIs instead of mocking when possible. Example: const uid = process.getuid?.() ?? 1000;

Files:

  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,ts}: MANDATORY: Use Fuselage components for all UI work. Only create custom components when Fuselage doesn't provide what's needed
Import UI components from @rocket.chat/fuselage and check Theme.d.ts for valid color tokens
Use React functional components with hooks
Use PascalCase for component file names

Files:

  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.spec.ts file naming for Renderer process tests

Files:

  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
**/*.main.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.main.spec.ts file naming for Main process tests

Files:

  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
**/*.{spec.ts,main.spec.ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Only mock platform-specific APIs when defensive coding isn't possible. Linux-only APIs requiring mocks: process.getuid(), process.getgid(), process.geteuid(), process.getegid()

Files:

  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Redux actions must follow FSA (Flux Standard Action) pattern
Avoid unnecessary comments — write self-documenting code through clear naming
Always verify libraries by checking official docs and .d.ts files in node_modules/. Never assume props, tokens, or APIs work without verification
Avoid subjective descriptors ('smart', 'excellent', 'dumb') in documentation and comments
Use measurable descriptions in code documentation: 'reduced memory usage', 'improved by X%' instead of subjective claims
NEVER invent metrics — don't include estimated time spent or speculated user counts. Only include numbers from actual logs, error messages, or documented sources

Files:

  • src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
🔇 Additional comments (1)
src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts (1)

3-45: LGTM!


Walkthrough

A one-line guard is added to the server view markdown will-download handler: it reads the download URL and returns early unless the URL ends with download=, avoiding cancel/dispatch for non-download markdown requests.

Changes

Markdown Download Fix

Layer / File(s) Summary
isMarkdownViewerDownloadUrl helper and tests
src/ui/main/serverView/isMarkdownViewerDownloadUrl.ts, src/ui/main/serverView/isMarkdownViewerDownloadUrl.main.spec.ts
Adds isMarkdownViewerDownloadUrl(url, filename) which returns true for .md filenames when the URL ends with download=, and a Jest suite covering query-string shapes and extension filtering.
will-download handler uses helper
src/ui/main/serverView/index.ts
Imports and uses isMarkdownViewerDownloadUrl inside the webContents will-download handler; only when the helper returns true does the handler cancel the download and dispatch SERVER_DOCUMENT_VIEWER_OPEN_URL.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

type: bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing markdown download cancellation logic by introducing proper detection of markdown viewer downloads.
Linked Issues check ✅ Passed The PR addresses issue #3345 by distinguishing between markdown view intent and download intent through URL query parameter analysis, allowing downloads to proceed correctly.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing markdown download handling: the helper function, its tests, and the server view integration are all focused on the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/ui/main/serverView/index.ts (1)

176-190: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add explanatory comment for the URL pattern logic.

The code distinguishes between viewing and downloading markdown files based on URL patterns, but there's no explanation of why this specific pattern (endsWith('download=')) is used or what it represents in the Rocket.Chat server API.

📝 Suggested comment
   // Intercept markdown file downloads and open in document viewer
   webviewSession.on('will-download', (_event, item) => {
     if (item.getFilename().endsWith('.md')) {
       const downloadUrl = item.getURL();
+      // Only intercept for viewer URLs; allow normal downloads to proceed.
+      // Rocket.Chat server appends 'download=' for view actions vs. download actions.
       if (!downloadUrl.endsWith('download=')) return;
       item.cancel();

As per coding guidelines, avoid unnecessary comments and write self-documenting code, but this is a necessary comment because the URL pattern distinction is non-obvious and depends on external server API behavior.

🤖 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/ui/main/serverView/index.ts` around lines 176 - 190, Add a brief
explanatory comment above the webviewSession.on('will-download', ...) handler
explaining the Rocket.Chat URL pattern: that markdown file links without query
"download=" open in-browser while links that end with "download=" are the
server's explicit "download" endpoints and should be intercepted/cancelled and
opened in our document viewer; reference the check using
item.getURL().endsWith('download=') and mention that when matched we cancel the
item and dispatch SERVER_DOCUMENT_VIEWER_OPEN_URL with documentFormat 'markdown'
so future readers understand the non-obvious URL-based routing decision.
🤖 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/ui/main/serverView/index.ts`:
- Line 179: Replace the brittle suffix check that tests
downloadUrl.endsWith('download=') with URL parsing: call new URL(item.getURL()),
then inspect url.searchParams (e.g., if (!url.searchParams.has('download'))
return; or check a specific value with url.searchParams.get('download') if
Rocket.Chat uses a value like 'true'/'1' or empty string for the download flag);
update the logic around the downloadUrl/item.getURL() handling and
SERVER_DOCUMENT_VIEWER_OPEN_URL usage to rely on url.searchParams so additional
query params or fragments won’t break the detection.

---

Outside diff comments:
In `@src/ui/main/serverView/index.ts`:
- Around line 176-190: Add a brief explanatory comment above the
webviewSession.on('will-download', ...) handler explaining the Rocket.Chat URL
pattern: that markdown file links without query "download=" open in-browser
while links that end with "download=" are the server's explicit "download"
endpoints and should be intercepted/cancelled and opened in our document viewer;
reference the check using item.getURL().endsWith('download=') and mention that
when matched we cancel the item and dispatch SERVER_DOCUMENT_VIEWER_OPEN_URL
with documentFormat 'markdown' so future readers understand the non-obvious
URL-based routing decision.
🪄 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: 75eefd3d-caf9-42dc-b920-0f32da1d98c8

📥 Commits

Reviewing files that changed from the base of the PR and between 3c89e81 and e4d1fe2.

📒 Files selected for processing (1)
  • src/ui/main/serverView/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use TypeScript for all new code unless explicitly told otherwise
Use optional chaining with fallbacks for platform-specific APIs instead of mocking when possible. Example: const uid = process.getuid?.() ?? 1000;

Files:

  • src/ui/main/serverView/index.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,ts}: MANDATORY: Use Fuselage components for all UI work. Only create custom components when Fuselage doesn't provide what's needed
Import UI components from @rocket.chat/fuselage and check Theme.d.ts for valid color tokens
Use React functional components with hooks
Use PascalCase for component file names

Files:

  • src/ui/main/serverView/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Redux actions must follow FSA (Flux Standard Action) pattern
Avoid unnecessary comments — write self-documenting code through clear naming
Always verify libraries by checking official docs and .d.ts files in node_modules/. Never assume props, tokens, or APIs work without verification
Avoid subjective descriptors ('smart', 'excellent', 'dumb') in documentation and comments
Use measurable descriptions in code documentation: 'reduced memory usage', 'improved by X%' instead of subjective claims
NEVER invent metrics — don't include estimated time spent or speculated user counts. Only include numbers from actual logs, error messages, or documented sources

Files:

  • src/ui/main/serverView/index.ts

Comment thread src/ui/main/serverView/index.ts Outdated
tyroneyeh and others added 2 commits May 28, 2026 08:49
Documents the actual contract: the web client title-link builds its URL
with URLSearchParams (serializes as ?download=), while the download
button concatenates a bare ?download. The trailing = is the only signal
distinguishing view intent from download intent.
@jeanfbrito

Copy link
Copy Markdown
Member

Thanks for the fix @tyroneyeh — the download= vs download distinction is exactly right. I pushed a hardening commit on top of your branch (b494117):

  • Extracted the check into a pure isMarkdownViewerDownloadUrl predicate with unit tests (bare ?download passes through to the download manager, ?download= opens the viewer).
  • Corrected the comment: the server doesn't deliberately append download= for view actions — the title-link is built with URLSearchParams (which serializes the empty value as download=), while the download button concatenates a bare ?download (AttachmentDownloadBase.tsx). The trailing = is the only signal distinguishing the two, so the contract is now documented where the predicate lives.

Follow-ups that make this interception a fallback rather than the primary path: #3354 exposes a supportedDocumentViewerFormats capability on the desktop API, and RocketChat/Rocket.Chat#40915 makes the web client open markdown directly in the document viewer (same flow as PDFs) when that capability is present.

@jeanfbrito jeanfbrito left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Verified against the server code: title-link URLs end with download= (URLSearchParams serialization in GenericFileAttachment.getExternalUrl), download button URLs end with bare ?download (AttachmentDownloadBase), so the guard correctly lets real downloads through while keeping the viewer working for title clicks. Unit tests added on the branch cover both forms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't download markdown files

2 participants