Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix avatar upload stuck state and update header after changing avatar #9427

Merged

Conversation

shauryag2002
Copy link
Contributor

@shauryag2002 shauryag2002 commented Dec 14, 2024

Proposed Changes

Video of Solution:

fix_uploading_state_and_header.mp4

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate bug / test a new feature.
  • Update product documentation.
  • Ensure that UI text is kept in I18n files.
  • Prep screenshot or demo video for changelog entry, and attach it to issue.
  • Request for Peer Reviews
  • Completion of QA

Summary by CodeRabbit

  • New Features

    • Enhanced error handling for file uploads, including improved notifications for client and network errors.
    • Added an optional refetchUserData parameter to the UserAvatar component to refresh user data after avatar updates.
  • Bug Fixes

    • Improved state management in the AvatarEditModal to ensure UI reflects the correct status during upload attempts.
  • Documentation

    • Updated method signatures to reflect changes in function parameters and return types across components.

@shauryag2002 shauryag2002 requested a review from a team as a code owner December 14, 2024 16:36
Copy link
Contributor

coderabbitai bot commented Dec 14, 2024

Walkthrough

This pull request focuses on improving the file upload and avatar management process across multiple components. The changes primarily address error handling, state management, and user experience during file uploads. The modifications include updating the uploadFile utility function to better handle errors, enhancing the AvatarEditModal to ensure proper state reset, and updating the UserAvatar component to support optional data refetching after avatar changes.

Changes

File Change Summary
src/Utils/request/uploadFile.ts - Converted to async function returning Promise<void>
- Enhanced error handling for client and network errors
- Improved promise rejection mechanism
src/components/Common/AvatarEditModal.tsx - Added try...finally block for state management
- Ensured proper state reset after upload attempt
- Maintained modal closing logic
src/components/Users/UserAvatar.tsx - Added optional refetchUserData prop
- Used optional chaining for data refetching
- Simplified hook destructuring

Assessment against linked issues

Objective Addressed Explanation
Loading state stuck during avatar upload [#9426]
Header not updating after avatar change [#9426]

Possibly related PRs

Suggested labels

tested, needs review, P1

Suggested reviewers

  • rithviknishad
  • Jacobjeevan
  • shivankacker

Poem

🐰 A rabbit's tale of upload might,
Where errors flee and pixels bright,
No more shall loading state be stuck,
With promise's resolve, we've struck luck!
Avatar dancing, header gleams ✨

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. This feature will be included in our Pro Plan when released.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Dec 14, 2024

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit 763f416
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/675dc0604d6d50000879b0af
😎 Deploy Preview https://deploy-preview-9427--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
src/components/Users/UserAvatar.tsx (2)

Line range hint 51-70: Consider adding error boundary for upload failures

While the error handling is delegated to the uploadFile utility, it would be beneficial to add an error boundary to gracefully handle any unhandled exceptions during the upload process.

Consider wrapping the avatar upload functionality in an error boundary component:

class AvatarUploadErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    Notification.Error({ msg: "Failed to upload avatar. Please try again." });
    console.error(error, errorInfo);
  }
  
  render() {
    return this.props.children;
  }
}

Line range hint 73-84: Add loading state for delete operation

The delete operation sets isProcessing but doesn't reflect this in the UI. Consider showing a loading indicator during deletion.

Apply this diff:

   const handleAvatarDelete = async (onError: () => void) => {
     const { res } = await request(routes.deleteProfilePicture, {
       pathParams: { username },
     });
     if (res?.ok) {
       Notification.Success({ msg: "Profile picture deleted" });
       refetchUserData?.();
       setEditAvatar(false);
     } else {
       onError();
     }
   };
src/components/Common/AvatarEditModal.tsx (1)

117-135: LGTM: Robust error handling with guaranteed state cleanup

The try/finally block ensures that loading states are properly reset even if an error occurs during upload.

However, consider adding a catch block to provide user feedback for unexpected errors:

   try {
     if (!selectedFile) {
       closeModal();
       return;
     }
     setIsProcessing(true);
     setIsCaptureImgBeingUploaded(true);
     await handleUpload(selectedFile, () => {
       setSelectedFile(undefined);
       setPreview(undefined);
       setPreviewImage(null);
       setIsCaptureImgBeingUploaded(false);
       setIsProcessing(false);
     });
+  } catch (error) {
+    Notification.Error({ msg: "An unexpected error occurred while uploading the avatar." });
   } finally {
     setIsCaptureImgBeingUploaded(false);
     setIsProcessing(false);
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8963ad and 130a6d6.

📒 Files selected for processing (3)
  • src/Utils/request/uploadFile.ts (1 hunks)
  • src/components/Common/AvatarEditModal.tsx (1 hunks)
  • src/components/Users/UserAvatar.tsx (4 hunks)
🔇 Additional comments (3)
src/Utils/request/uploadFile.ts (2)

6-14: LGTM: Function signature properly reflects async nature

The updated function signature correctly indicates that this is an asynchronous operation returning a Promise.


46-51: LGTM: Network error handling is complete

The error handling for network failures is comprehensive, including user notification, error callback, and promise rejection.

src/components/Users/UserAvatar.tsx (1)

22-28: LGTM: Props interface properly typed

The optional refetchUserData prop is correctly typed with TypeScript.

Comment on lines 23 to 38
xhr.onload = () => {
onLoad(xhr);
if (400 <= xhr.status && xhr.status <= 499) {
const error = JSON.parse(xhr.responseText);
if (typeof error === "object" && !Array.isArray(error)) {
Object.values(error).forEach((msg) => {
Notification.Error({ msg: msg || "Something went wrong!" });
});
} else {
Notification.Error({ msg: error || "Something went wrong!" });
}
reject(new Error("Client error"));
} else {
Notification.Error({ msg: error || "Something went wrong!" });
resolve();
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance error handling for non-JSON responses

The current implementation assumes that all error responses are JSON-parseable. This could throw an exception if the server returns a plain text error.

Apply this diff to safely handle non-JSON responses:

 xhr.onload = () => {
   onLoad(xhr);
   if (400 <= xhr.status && xhr.status <= 499) {
-    const error = JSON.parse(xhr.responseText);
+    let error;
+    try {
+      error = JSON.parse(xhr.responseText);
+    } catch {
+      error = xhr.responseText;
+    }
     if (typeof error === "object" && !Array.isArray(error)) {
       Object.values(error).forEach((msg) => {
         Notification.Error({ msg: msg || "Something went wrong!" });

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (3)
src/Utils/request/uploadFile.ts (3)

15-21: Consider adding timeout and abort handling

While the XHR setup is correct, consider adding:

  1. Timeout handling to prevent indefinite waiting
  2. AbortController integration to allow cleanup
 const uploadFile = async (
   url: string,
   file: File | FormData,
   reqMethod: string,
   headers: object,
   onLoad: (xhr: XMLHttpRequest) => void,
   setUploadPercent: Dispatch<SetStateAction<number>> | null,
-  onError: () => void,
+  onError: () => void,
+  timeout: number = 30000,
 ): Promise<void> => {
   return new Promise((resolve, reject) => {
     const xhr = new XMLHttpRequest();
+    xhr.timeout = timeout;
     xhr.open(reqMethod, url);

     Object.entries(headers).forEach(([key, value]) => {
       xhr.setRequestHeader(key, value);
     });
+    
+    xhr.ontimeout = () => {
+      Notification.Error({ msg: "Upload timed out. Please try again." });
+      onError();
+      reject(new Error("Timeout"));
+    };

45-48: Consider cleanup for progress handler

While the progress handling works correctly, consider cleaning up the progress handler when the upload is complete or errors out to prevent potential memory leaks or updates after component unmount.

 if (setUploadPercent != null) {
   xhr.upload.onprogress = (event: ProgressEvent) => {
     handleUploadPercentage(event, setUploadPercent);
   };
+  
+  // Clean up progress handler
+  xhr.onloadend = () => {
+    xhr.upload.onprogress = null;
+  };
 }

59-59: Consider adding file type validation

To prevent invalid uploads early, consider adding type validation before sending the file.

+    if (!(file instanceof FormData) && !(file instanceof File)) {
+      reject(new Error("Invalid file type"));
+      return;
+    }
     xhr.send(file);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 130a6d6 and 763f416.

📒 Files selected for processing (1)
  • src/Utils/request/uploadFile.ts (1 hunks)
🔇 Additional comments (4)
src/Utils/request/uploadFile.ts (4)

6-14: LGTM! Good improvement to async/await pattern

The function signature has been properly updated to return a Promise, which better aligns with modern JavaScript practices and makes error handling more straightforward for consumers.


23-43: Well-implemented error handling with proper fallbacks

The error handling implementation is robust:

  • Safely handles both JSON and non-JSON error responses
  • Provides user-friendly error messages
  • Properly resolves/rejects the promise based on response status

51-56: Good error handling implementation

The error handler properly addresses the PR objective of fixing stuck states by:

  • Showing clear user feedback about network issues
  • Calling the error callback to reset UI state
  • Rejecting the promise to allow proper error handling upstream

6-60: Verify integration with avatar components

The changes look good and align with the PR objectives. To ensure complete resolution of the stuck state issue, let's verify the integration with the avatar components.

✅ Verification successful

Avatar upload implementation is properly integrated

The verification shows that both UserAvatar and UserProfile components properly integrate with the uploadFile utility:

  • Both components handle file uploads through FormData
  • Error handling is managed through the onError callback
  • Loading states are tracked in components
  • The uploadFile utility's error notifications will be displayed as implemented

The changes in uploadFile.ts properly address the avatar upload stuck state by:

  1. Properly rejecting promises on errors
  2. Handling network failures
  3. Providing upload progress feedback when requested
  4. Showing appropriate error notifications
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for avatar-related components that use this utility
rg -l "uploadFile.*avatar" --type ts --type tsx

# Check error handling in avatar components
ast-grep --pattern 'try {
  $$$
  uploadFile($$$)
  $$$
} catch {
  $$$
}'

Length of output: 154


Script:

#!/bin/bash
# Search for files containing uploadFile usage
rg -l "uploadFile"

# Search for avatar-related files
rg -l "avatar" --type ts

# Check error handling patterns around uploadFile
rg -B2 -A2 "uploadFile"

Length of output: 6759


Script:

#!/bin/bash
# Check error handling in UserAvatar.tsx
rg -B5 -A5 "uploadFile" src/components/Users/UserAvatar.tsx

# Check error handling in UserProfile.tsx
rg -B5 -A5 "uploadFile" src/components/Users/UserProfile.tsx

# Check state management in these components
rg "useState.*upload|loading|progress" src/components/Users/UserAvatar.tsx src/components/Users/UserProfile.tsx

Length of output: 1908

@nihal467
Copy link
Member

LGTM

@rithviknishad rithviknishad merged commit 1d50f53 into ohcnetwork:develop Dec 18, 2024
25 of 26 checks passed
Copy link

@shauryag2002 Your efforts have helped advance digital healthcare and TeleICU systems. 🚀 Thank you for taking the time out to make CARE better. We hope you continue to innovate and contribute; your impact is immense! 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Avatar upload stuck state and update header after changing avatar in User profile
4 participants