Skip to content

Fix audio preview#58

Open
shivankacker wants to merge 10 commits intodevelopfrom
fix-audio-preview
Open

Fix audio preview#58
shivankacker wants to merge 10 commits intodevelopfrom
fix-audio-preview

Conversation

@shivankacker
Copy link
Copy Markdown
Member

@shivankacker shivankacker commented Dec 11, 2025

Needs ohcnetwork/care_scribe#25

Summary by CodeRabbit

  • Refactor

    • More reliable audio type handling for consistent playback.
  • Bug Fixes

    • Improved upload error handling with clearer failure messages.
    • Immediate validation to surface missing upload information.
  • Chores

    • Added file metadata (MIME type and internal filename) to improve audio upload and playback handling.
    • Hardened JSON/error parsing for more robust uploads.

Copilot AI review requested due to automatic review settings December 11, 2025 09:12
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds mime_type: string and internal_name: string to ScribeFileModel. In src/pages/HistoryDetails.tsx the audio element type now uses audio.mime_type directly, removing extension-based MIME derivation. src/utils/upload-utils.ts adds isRecord and safeParseJson, hardens JSON/error parsing, changes uploadFile signature to use headers: Record<string,string> and an onError that accepts an optional message, removes progress callbacks, enforces extension presence and constructs a new File with internal_name in uploadScribeFile, and adjusts upload request error handling and flow.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix audio preview' directly relates to the main change: updating how the audio element's source type is derived by using audio.mime_type directly instead of extension-based fallback logic in HistoryDetails.tsx.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-audio-preview

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an issue with audio preview functionality by using the internal filename instead of parsing the signed URL to determine the audio file type.

  • Updates the ScribeFileModel interface to include an internal_name field
  • Simplifies audio type detection by using the internal_name property

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/types.ts Adds internal_name field to ScribeFileModel interface
src/pages/HistoryDetails.tsx Uses internal_name instead of parsing signed URL for audio type detection

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96784c9 and 3abf397.

📒 Files selected for processing (2)
  • src/pages/HistoryDetails.tsx (1 hunks)
  • src/types.ts (1 hunks)
⏰ 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). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
src/types.ts (1)

235-235: LGTM!

Adding internal_name as a required field is a sound architectural decision. It provides a reliable source for the file extension and avoids the complexity of parsing signed URLs, which may contain query parameters or other URL artifacts.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Dec 11, 2025

Deploying care-scribe-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: c2b2ee5
Status: ✅  Deploy successful!
Preview URL: https://589c4f43.care-scribe-fe.pages.dev
Branch Preview URL: https://fix-audio-preview.care-scribe-fe.pages.dev

View logs

Copy link
Copy Markdown

Copilot AI commented Dec 12, 2025

@gigincg I've opened a new pull request, #59, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/pages/HistoryDetails.tsx (1)

457-464: Fallback MIME derivation is still incorrect for common extensions (e.g., .mp3audio/mpeg, not audio/mp3).
You already prefer audio.mime_type (good), but the extension fallback can still break playback in stricter browsers.

   <source
     src={audio.read_signed_url}
     type={
-      audio.mime_type ||
-      "audio/" + (audio.internal_name.split(".").pop() || "mpeg") // Fallback
+      audio.mime_type ||
+      (() => {
+        const ext = audio.internal_name.split(".").pop()?.toLowerCase();
+        switch (ext) {
+          case "mp3":
+            return "audio/mpeg";
+          case "wav":
+            return "audio/wav";
+          case "m4a":
+          case "mp4":
+            return "audio/mp4";
+          case "ogg":
+            return "audio/ogg";
+          default:
+            return "audio/mpeg";
+        }
+      })()
     }
   />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c64b96 and 7945e9a.

📒 Files selected for processing (3)
  • src/pages/HistoryDetails.tsx (1 hunks)
  • src/types.ts (1 hunks)
  • src/utils/upload-utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/types.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use strict type checking in TypeScript
Prefer explicit types over inferred types in TypeScript

Files:

  • src/utils/upload-utils.ts
  • src/pages/HistoryDetails.tsx
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{ts,tsx}: Define shared types in src/types.ts
Keep business logic in hooks and pure UI in components
Use absolute imports (configured in vite.config.ts)
Use kebab-case for file names
Include JSDoc comments for complex logic
Follow React hooks rules (dependencies, cleanup, etc.)
Handle cleanup in useEffect hooks
Use React context for state management when needed
Keep state close to where it's used

Files:

  • src/utils/upload-utils.ts
  • src/pages/HistoryDetails.tsx
⏰ 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). (1)
  • GitHub Check: Cloudflare Pages

@shivankacker shivankacker requested a review from gigincg January 13, 2026 12:32
@shivankacker shivankacker requested a review from gigincg February 4, 2026 12:48
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/utils/upload-utils.ts`:
- Around line 59-67: Derive a non-optional mimeType from blob.type once and
reuse it instead of optional-chaining at call sites: compute const mimeType =
blob.type.split(";")[0] (after the existing check that blob.type is truthy),
keep the existing extension logic (extension =
blob.type.split("/")[1]?.split(";")?.[0] can be adjusted to use mimeType where
appropriate), and then pass mimeType into the CreateFileRequest mime_type field
(replace blob.type?.split(";")?.[0] with mimeType) so the type is strictly
string and no optional chaining remains; update any other places (e.g., around
the name and file creation) to use this single mimeType variable.
- Around line 101-112: The headers object lacks an explicit type and the onError
callback passes an optional/undefined parameter directly to Error; update the
headers declaration to an explicit type (e.g., Record<string, string> or a
suitable Headers type) for the constant named headers, and change the uploadFile
onError callback to accept an optional parameter (e.g., e?: unknown) and call
reject(Error(String(e ?? 'Unknown error'))) or similar so undefined errors
produce a clear message instead of "Error(undefined)"; keep the existing
uploadFile call and onLoad callback intact.
- Around line 29-40: Move the HTTP status checks to run before calling the
success callback: in the xhr.onload handler, first inspect xhr.status and handle
error ranges (400–499 and 500–599) by parsing xhr.responseText with
safeParseJson and invoking onError (using isRecord to iterate messages or a
fallback string) and return early; only call onLoad(xhr) when status is a
successful 2xx code. Ensure you do not call onError after onLoad to avoid
double-callbacks and handle 5xx server errors similarly to 4xx by emitting a
generic server error message if parsing fails.

Comment on lines 29 to 40
xhr.onload = () => {
onLoad(xhr);
if (400 <= xhr.status && xhr.status <= 499) {
const error = JSON.parse(xhr.responseText);
if (typeof error === "object" && !Array.isArray(error)) {
const error = safeParseJson(xhr.responseText ?? "");
if (isRecord(error)) {
Object.values(error).forEach((msg) => {
window.alert(msg || "Something went wrong!");
onError(typeof msg === "string" ? msg : "An error occurred.");
});
} else {
window.alert(error || "Something went wrong!");
return;
}
onError(typeof error === "string" ? error : "An error occurred");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine the upload-utils file
fd -t f upload-utils.ts

Repository: ohcnetwork/care_scribe_fe

Length of output: 94


🏁 Script executed:

# View the full file to understand the context
cat -n src/utils/upload-utils.ts

Repository: ohcnetwork/care_scribe_fe

Length of output: 4121


🏁 Script executed:

# Search for uploadFile function calls across the codebase
rg -nP --type=ts --type=tsx 'uploadFile\s*\(' -C 3

Repository: ohcnetwork/care_scribe_fe

Length of output: 96


🏁 Script executed:

# Search for uploadFile usage across the codebase
rg -n 'uploadFile' --type ts -C 3

Repository: ohcnetwork/care_scribe_fe

Length of output: 1056


🏁 Script executed:

# Also search in tsx files (without specifying type to avoid parsing issues)
rg -n 'uploadFile' -C 3 | head -100

Repository: ohcnetwork/care_scribe_fe

Length of output: 1056


🏁 Script executed:

# Search for any imports or usage of uploadFile in the entire codebase
rg -n 'from.*upload-utils' -C 2

Repository: ohcnetwork/care_scribe_fe

Length of output: 774


🏁 Script executed:

# Also check if uploadFile is used anywhere else beyond uploadScribeFile
rg -n 'uploadFile\(' -C 5

Repository: ohcnetwork/care_scribe_fe

Length of output: 681


Move error status checks before onLoad call to prevent double-callback invocation.

The xhr.onload handler currently calls onLoad(xhr) unconditionally (line 30), then checks for 4xx errors and invokes onError afterward. When a 4xx response occurs, both callbacks fire: the caller's onLoad handler (line 110) rejects the Promise on non-2xx status, then onError attempts to reject again. While a Promise only uses the first rejection, this pattern is semantically incorrect and confusing.

The fix should check error status codes first and only invoke onLoad on success (2xx). Additionally, handle 5xx server errors gracefully rather than waiting for network failure.

🔧 Proposed fix
-  xhr.onload = () => {
-    onLoad(xhr);
-    if (400 <= xhr.status && xhr.status <= 499) {
-      const error = safeParseJson(xhr.responseText ?? "");
-      if (isRecord(error)) {
-        Object.values(error).forEach((msg) => {
-          onError(typeof msg === "string" ? msg : "An error occurred.");
-        });
-        return;
-      }
-      onError(typeof error === "string" ? error : "An error occurred");
-    }
-  };
+  xhr.onload = () => {
+    if (400 <= xhr.status && xhr.status <= 499) {
+      const error = safeParseJson(xhr.responseText ?? "");
+      if (isRecord(error)) {
+        Object.values(error).forEach((msg) => {
+          onError(typeof msg === "string" ? msg : "An error occurred.");
+        });
+        return;
+      }
+      onError(typeof error === "string" ? error : "An error occurred");
+      return;
+    }
+    if (xhr.status >= 500) {
+      onError(`Upload failed (${xhr.status})`);
+      return;
+    }
+    onLoad(xhr);
+  };
🤖 Prompt for AI Agents
In `@src/utils/upload-utils.ts` around lines 29 - 40, Move the HTTP status checks
to run before calling the success callback: in the xhr.onload handler, first
inspect xhr.status and handle error ranges (400–499 and 500–599) by parsing
xhr.responseText with safeParseJson and invoking onError (using isRecord to
iterate messages or a fallback string) and return early; only call onLoad(xhr)
when status is a successful 2xx code. Ensure you do not call onError after
onLoad to avoid double-callbacks and handle 5xx server errors similarly to 4xx
by emitting a generic server error message if parsing fails.

Comment on lines +59 to +67
const extension = blob.type.split("/")[1]?.split(";")?.[0];

if (!extension) {
throw new Error(
"Could not determine file extension. Got mime type: " + blob.type,
);
}

const name = `file.${extension}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/utils/upload-utils.ts | head -100

Repository: ohcnetwork/care_scribe_fe

Length of output: 3488


🏁 Script executed:

rg -nP --type=ts -C3 'createFileUpload'

Repository: ohcnetwork/care_scribe_fe

Length of output: 902


🏁 Script executed:

rg -nP --type=ts 'CreateFileRequest' -A10 -B2

Repository: ohcnetwork/care_scribe_fe

Length of output: 1820


🏁 Script executed:

rg -nP --type=ts 'mime_type' src/

Repository: ohcnetwork/care_scribe_fe

Length of output: 300


Derive mimeType once to keep types strict and avoid optional chaining.

The mime_type field in CreateFileRequest is required (string, non-optional), but the current code at line 88 passes blob.type?.split(";")?.[0] which could be undefined. Since the error check at lines 61–65 guarantees blob.type is truthy before reaching line 88, the refactoring is safe and fixes the type mismatch.

♻️ Suggested refactor
-  const extension = blob.type.split("/")[1]?.split(";")?.[0];
+  const mimeType: string = blob.type.split(";")[0];
+  const extension = mimeType.split("/")[1];
@@
-    mime_type: blob.type?.split(";")?.[0],
+    mime_type: mimeType,

Also applies to: 82–89

🤖 Prompt for AI Agents
In `@src/utils/upload-utils.ts` around lines 59 - 67, Derive a non-optional
mimeType from blob.type once and reuse it instead of optional-chaining at call
sites: compute const mimeType = blob.type.split(";")[0] (after the existing
check that blob.type is truthy), keep the existing extension logic (extension =
blob.type.split("/")[1]?.split(";")?.[0] can be adjusted to use mimeType where
appropriate), and then pass mimeType into the CreateFileRequest mime_type field
(replace blob.type?.split(";")?.[0] with mimeType) so the type is strictly
string and no optional chaining remains; update any other places (e.g., around
the name and file creation) to use this single mimeType variable.

Comment on lines 101 to 112
const headers = {
"Content-type": newFile?.type?.split(";")?.[0],
"Content-type": newFile.type.split(";")[0],
"Content-disposition": "inline",
};

uploadFile(
url || "",
url,
newFile,
"PUT",
headers,
(xhr: XMLHttpRequest) => (xhr.status === 200 ? resolve() : reject()),
null,
reject,
(e) => reject(Error(e)),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and view the specific lines
wc -l src/utils/upload-utils.ts

Repository: ohcnetwork/care_scribe_fe

Length of output: 98


🏁 Script executed:

# Read the relevant section of the file
sed -n '90,120p' src/utils/upload-utils.ts

Repository: ohcnetwork/care_scribe_fe

Length of output: 860


🏁 Script executed:

# Find the uploadFile function definition to understand the callback signatures
rg -A 10 "function uploadFile|const uploadFile|export.*uploadFile" src/utils/upload-utils.ts

Repository: ohcnetwork/care_scribe_fe

Length of output: 715


Add explicit header typing and handle undefined error parameter.

The onError callback receives an optional parameter that may be undefined, resulting in a confusing error message. Also, the headers object lacks explicit typing despite the coding guideline to prefer explicit types over inferred types.

Suggested fix
-    const headers = {
+    const headers: Record<string, string> = {
       "Content-type": newFile.type.split(";")[0],
       "Content-disposition": "inline",
     };
@@
-      (e) => reject(Error(e)),
+      (e) => reject(new Error(e ?? "Upload failed")),
📝 Committable suggestion

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

Suggested change
const headers = {
"Content-type": newFile?.type?.split(";")?.[0],
"Content-type": newFile.type.split(";")[0],
"Content-disposition": "inline",
};
uploadFile(
url || "",
url,
newFile,
"PUT",
headers,
(xhr: XMLHttpRequest) => (xhr.status === 200 ? resolve() : reject()),
null,
reject,
(e) => reject(Error(e)),
);
const headers: Record<string, string> = {
"Content-type": newFile.type.split(";")[0],
"Content-disposition": "inline",
};
uploadFile(
url,
newFile,
headers,
(xhr: XMLHttpRequest) => (xhr.status === 200 ? resolve() : reject()),
(e) => reject(new Error(e ?? "Upload failed")),
);
🤖 Prompt for AI Agents
In `@src/utils/upload-utils.ts` around lines 101 - 112, The headers object lacks
an explicit type and the onError callback passes an optional/undefined parameter
directly to Error; update the headers declaration to an explicit type (e.g.,
Record<string, string> or a suitable Headers type) for the constant named
headers, and change the uploadFile onError callback to accept an optional
parameter (e.g., e?: unknown) and call reject(Error(String(e ?? 'Unknown
error'))) or similar so undefined errors produce a clear message instead of
"Error(undefined)"; keep the existing uploadFile call and onLoad callback
intact.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants