Skip to content

image update for profile - #24

Merged
OumB2021 merged 2 commits into
mainfrom
avatar_url
Jul 5, 2026
Merged

image update for profile#24
OumB2021 merged 2 commits into
mainfrom
avatar_url

Conversation

@OumB2021

@OumB2021 OumB2021 commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added profile avatar selection and upload support on the edit profile screen, including image preview and an edit/add avatar action.
    • Updated profile saving so a newly selected avatar is uploaded before the profile is saved.
  • Bug Fixes
    • Improved avatar display in post cards for better image positioning and fit.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds avatar selection and upload capability to the edit-profile screen, including image picking, MIME-type normalization, a signed-upload flow (sign, PUT upload, publicUrl assignment), and preview UI. Also adjusts avatar image rendering in PostCard (contentFit and alignment class).

Changes

Avatar upload feature

Layer / File(s) Summary
Avatar types and imports
apps/mobile/app/edit-profile.tsx
Adds imports for expo-image, expo-image-picker, and Feather icons, plus a supported MIME-type set and a SignUploadResponse type for the signed-upload payload.
Avatar state and picker logic
apps/mobile/app/edit-profile.tsx
Adds avatarUrl/avatarLocalUri/avatarMimeType state, derives avatarPreviewUri, and adds pickAvatar to request permissions, launch the picker, and normalize MIME type.
Signed upload and save integration
apps/mobile/app/edit-profile.tsx
Extends handleSave to sign an upload, PUT the file to the signed URL, and pass the returned publicUrl as avatar_url to updateProfile, with error handling for signing/upload failures.
Avatar picker UI and PostCard rendering
apps/mobile/app/edit-profile.tsx, apps/mobile/components/PostCard.tsx
Adds a tappable avatar preview with edit badge in the edit-profile screen and updates PostCard avatar image contentFit ("contain") plus object-center alignment class.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EditProfileScreen
  participant UploadsAPI
  participant SignedUrlStorage
  participant ProfileAPI

  User->>EditProfileScreen: pickAvatar()
  EditProfileScreen->>EditProfileScreen: store avatarLocalUri, avatarMimeType
  User->>EditProfileScreen: handleSave()
  EditProfileScreen->>UploadsAPI: POST /uploads/sign (contentType)
  UploadsAPI-->>EditProfileScreen: signedUrl, publicUrl
  EditProfileScreen->>SignedUrlStorage: PUT file blob to signedUrl
  EditProfileScreen->>ProfileAPI: updateProfile(avatar_url=publicUrl)
  ProfileAPI-->>EditProfileScreen: success
Loading

Possibly related PRs

  • OumB2021/Journal#6: Both PRs modify avatar rendering in apps/mobile/components/PostCard.tsx, with this PR adjusting contentFit and alignment introduced there.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the changes but too vague to clearly describe the avatar upload and profile image work. Use a more specific title like "Add avatar upload to profile editing" or similar.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch avatar_url

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 `@apps/mobile/app/edit-profile.tsx`:
- Around line 150-160: The avatar upload flow in the edit-profile screen
currently uses fetch(avatarLocalUri).blob() followed by a PUT, which can buffer
the full file in JS and is brittle in React Native. Replace that path in the
avatar upload logic with a native file-stream upload API such as
expo-file-system uploadAsync targeting the signedUrl, and preserve the existing
content type and error handling around the upload step.
- Around line 104-113: The avatar selection flow in edit-profile.tsx is falling
back to image/jpeg for unsupported picker formats without changing the
underlying file bytes. In the image-picker handling logic around
setAvatarLocalUri and setAvatarMimeType, either transcode unsupported assets to
a real JPEG before uploading or reject them instead of relabeling. Make sure the
upload path uses a contentType that matches the actual bytes so the storage
extension derived by /uploads/sign stays consistent.

In `@apps/mobile/components/PostCard.tsx`:
- Around line 59-60: The avatar image in PostCard should use a fill style
instead of contain so the fixed circular frame does not letterbox non-square
images; update the Image/Avatar usage in PostCard to use contentFit="cover" and
remove the redundant className="object-center", matching the other avatar image
patterns in this component.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ce689937-0899-4fcc-b0e5-66716b476b7e

📥 Commits

Reviewing files that changed from the base of the PR and between 9f046af and e091d27.

📒 Files selected for processing (2)
  • apps/mobile/app/edit-profile.tsx
  • apps/mobile/components/PostCard.tsx

Comment on lines +104 to +113
if (!result.canceled && result.assets[0]) {
const mimeType = result.assets[0].mimeType;
setAvatarLocalUri(result.assets[0].uri);
setAvatarMimeType(
mimeType && SUPPORTED_IMAGE_TYPES.has(mimeType)
? mimeType
: "image/jpeg",
);
}
}, [isSubmitting]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

expo-image-picker allowsEditing mimeType heic jpeg output format iOS Android

💡 Result:

In expo-image-picker, the allowsEditing property enables a system UI for cropping (iOS and Android) and rotating (Android only) [1][2][3]. Its interaction with output formats like HEIC and JPEG depends on how the picker processes the asset [2][4]. Since Expo SDK 54, the default configuration (allowsEditing: false, videoExportPreset: 'Passthrough') is designed to return the original asset instantly, which preserves HEIC or AVIF formats if selected [2][4][5]. However, when allowsEditing is enabled (set to true), the library invokes system editing tools that typically transcode or compress the image, which often results in a JPEG output [2][4]. Key points regarding mimeType and format: 1. Format Conversion: When allowsEditing is true, the native OS cropping/editing flow frequently forces a conversion to a widely supported format like JPEG [2][4]. 2. HEIC Handling: Even when the original asset is HEIC, if the picker performs processing (such as through allowsEditing), the resulting file is commonly output as a JPEG [6][7]. 3. MimeType Accuracy: There have been documented cases where the returned asset's mimeType property incorrectly reports image/jpeg even when the underlying file remains HEIC, or when the system has not successfully transcoded it, leading to inconsistencies in how developers handle the returned URI [7][8]. 4. iOS Specifics: On iOS, cropping a.bmp image will convert it to.png [3]. For other types, editing typically results in a JPEG [6]. If your application requires a specific format (e.g., ensuring an image is JPEG), it is recommended to perform manual image processing using a library like expo-image-manipulator after the picker returns the URI, rather than relying on the native picker's editing capabilities to guarantee a specific output mimeType [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the edit-profile screen and the upload helper it calls.
git ls-files 'apps/mobile/app/edit-profile.tsx' 'apps/mobile/**/*' | sed -n '1,120p'

echo '--- edit-profile.tsx (relevant range) ---'
cat -n apps/mobile/app/edit-profile.tsx | sed -n '1,220p'

echo '--- search for upload/content-type handling ---'
rg -n "Content-Type|contentType|mimeType|avatarMimeType|SUPPORTED_IMAGE_TYPES|upload" apps/mobile -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

Repository: OumB2021/Journal

Length of output: 15907


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the exact upload call path and any server-side handling that might rely on MIME type.
rg -n "avatarMimeType|SUPPORT|contentType|Content-Type|putObject|signed upload|presign|presigned|multipart" . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

Repository: OumB2021/Journal

Length of output: 2271


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- apps/api/src/services/storageService.ts ---'
cat -n apps/api/src/services/storageService.ts

echo '--- packages/shared/src/index.ts (relevant upload schema) ---'
cat -n packages/shared/src/index.ts | sed -n '1,120p'

echo '--- apps/mobile/components/AddPostPanel.tsx (relevant picker/upload range) ---'
cat -n apps/mobile/components/AddPostPanel.tsx | sed -n '290,340p'

Repository: OumB2021/Journal

Length of output: 7789


Don't relabel unsupported picker formats as JPEG without re-encoding. When mimeType falls back to image/jpeg, the app still uploads the original bytes. Since /uploads/sign derives the storage extension from contentType, a HEIC/other source can end up stored as .jpg with a JPEG content-type, which breaks the upload contract. Transcode first or reject unsupported formats.

🤖 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 `@apps/mobile/app/edit-profile.tsx` around lines 104 - 113, The avatar
selection flow in edit-profile.tsx is falling back to image/jpeg for unsupported
picker formats without changing the underlying file bytes. In the image-picker
handling logic around setAvatarLocalUri and setAvatarMimeType, either transcode
unsupported assets to a real JPEG before uploading or reject them instead of
relabeling. Make sure the upload path uses a contentType that matches the actual
bytes so the storage extension derived by /uploads/sign stays consistent.

Comment on lines +150 to +160
const fileRes = await fetch(avatarLocalUri);
const blob = await fileRes.blob();

const uploadRes = await fetch(signedUrl, {
method: "PUT",
headers: { "Content-Type": contentType },
body: blob,
});
if (!uploadRes.ok) {
throw new Error("Failed to upload avatar. Please try again.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

expo react native upload local file signed url PUT blob vs FileSystem.uploadAsync

💡 Result:

When uploading a file to a signed URL using a PUT request in an Expo/React Native environment, you should generally prefer expo-file-system's UploadTask over standard fetch/blob approaches to ensure memory efficiency and reliability, particularly for large files [1][2][3]. Key Considerations for PUT vs. Multipart Uploads: 1. expo-file-system (Recommended for Performance): - Use FileSystem.createUploadTask(url, fileUri, options) for the most robust implementation [1]. - Configure options with httpMethod: 'PUT' and uploadType: FileSystem.FileSystemUploadType.BINARY_CONTENT [4][1][5]. This method reads directly from the device filesystem, avoiding the "bridge" performance bottlenecks and potential out-of-memory (OOM) errors associated with loading large files into memory as Blobs or Base64 strings [2][3]. - UploadTask supports an onProgress callback, which is essential for user feedback during file uploads [4][1]. 2. Handling Signed URLs: - When using a signed PUT URL (e.g., for AWS S3 or Google Cloud Storage), ensure that your headers (specifically Content-Type) match exactly what was used to generate the signed URL [5][6]. - If the signed URL generation requires specific headers, pass them in the headers object within your UploadOptions [1][6]. 3. Common Pitfalls: - Versioning and Platform Consistency: Issues with FileSystem.uploadAsync sometimes emerge on specific iOS versions or SDK releases (e.g., empty body issues or background session failures) [7][8][9]. Always check the current Expo SDK documentation, as these are frequently addressed in patches [1][8]. - Binary vs. Multipart: Do not use FileSystemUploadType.MULTIPART for a simple PUT request. A PUT request typically expects a binary stream; using multipart headers will often cause the signature validation to fail because the request body format will not match what the signed URL provider expects [5][2]. Summary: For standard signed PUT uploads, use FileSystem.createUploadTask with BINARY_CONTENT. This avoids the memory overhead of converting files to Blobs and allows for native-level file streaming, which is significantly more stable for production applications [1][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the edit-profile screen around the upload logic
sed -n '120,190p' apps/mobile/app/edit-profile.tsx

# Check Expo-related dependencies to see whether expo-file-system is available
cat package.json 2>/dev/null || true
cat apps/mobile/package.json 2>/dev/null || true

# Search for other upload implementations or file-system usage in the mobile app
rg -n "uploadAsync|createUploadTask|FileSystem\.|fetch\(.*\.blob\(\)|blob\(\)" apps/mobile -t ts -t tsx

Repository: OumB2021/Journal

Length of output: 4317


🌐 Web query:

React Native Hermes fetch local file uri blob upload signed url reliability expo-file-system uploadAsync

💡 Result:

For reliable file uploads in React Native using Hermes, direct standard fetch-based Blob uploads are often problematic due to inconsistencies in how local file system URIs are handled across Android and iOS [1][2]. Reliability Recommendations: 1. Use specialized libraries for production-grade uploads. Libraries like react-native-blob-util provide a robust, cross-platform mechanism to wrap local file paths and stream them directly to a destination, avoiding the memory overhead of converting files to Blobs or Base64 [3]. This is the industry-standard approach for handling S3 pre-signed URLs reliably [4]. 2. Expo File System's uploadAsync is designed for native efficiency but has historically faced platform-specific bugs, particularly with multipart uploads on iOS and background session types [5][6][7][8]. If you choose this route, ensure you are on the latest Expo SDK version, as significant fixes for iOS multipart uploads were implemented in recent SDK releases [6][7]. 3. Avoid the common "fetch blob" anti-pattern. Many older tutorials suggest creating a Blob via XMLHttpRequest to then use in a standard fetch PUT request [4][2]. This approach often fails on Android (e.g., status code 0 errors) or causes high memory pressure [2][9]. Native fetch in React Native does not natively stream files from the local file system in the same way it handles standard network requests [1]. 4. Prefer direct file system access. When using pre-signed URLs, the most reliable pattern is to stream the file directly from its local path [4][3]. Libraries like react-native-blob-util allow you to use a.wrap function (e.g., RNBlobUtil.wrap(filePath)) to identify the file to the native layer, allowing the platform's networking stack to handle the file upload efficiently without loading the entire content into JavaScript memory [3]. 5. Workflow best practices: - Always validate the existence of the file URI before initiating an upload [9]. - If using pre-signed URLs, ensure your backend correctly handles the Content-Type and CORS settings to match what the mobile app sends [4][10]. - For large files, avoid background upload types if possible, as they are susceptible to connection loss and have platform-specific constraints on file size and session persistence [8]. In summary, for maximum reliability, leverage react-native-blob-util for direct file streaming [3]. If you must use Expo's ecosystem, prefer FileSystem.uploadAsync but be prepared to manage potential edge cases in multipart handling, and avoid the standard fetch-to-blob conversion pattern entirely [2][8][9].

Citations:


Use a native file-stream upload here The fetch(avatarLocalUri).blob()PUT path is fragile in React Native and can buffer the whole file in JS; use a native upload API such as expo-file-system’s uploadAsync for this signed URL instead.

🤖 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 `@apps/mobile/app/edit-profile.tsx` around lines 150 - 160, The avatar upload
flow in the edit-profile screen currently uses fetch(avatarLocalUri).blob()
followed by a PUT, which can buffer the full file in JS and is brittle in React
Native. Replace that path in the avatar upload logic with a native file-stream
upload API such as expo-file-system uploadAsync targeting the signedUrl, and
preserve the existing content type and error handling around the upload step.

Comment thread apps/mobile/components/PostCard.tsx Outdated
@OumB2021
OumB2021 merged commit 7be35c6 into main Jul 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant