Skip to content

CU-868ex18rd Fixing the call images view.#72

Merged
ucswift merged 2 commits intomasterfrom
develop
Sep 9, 2025
Merged

CU-868ex18rd Fixing the call images view.#72
ucswift merged 2 commits intomasterfrom
develop

Conversation

@ucswift
Copy link
Member

@ucswift ucswift commented Sep 9, 2025

Summary by CodeRabbit

  • New Features

    • Full-screen image viewer with pinch-to-zoom, pan, double‑tap zoom, and easy close.
    • Automatic image processing before upload and included filename/note in uploads.
    • Uploaded images now include current location data (when available).
  • UX Improvements

    • Streamlined image selection, preview flow, clearer error states, and fullscreen preview.
  • Localization

    • Added image note and upload success/error messages for English, Arabic, and Spanish.
  • Chores

    • Added image manipulation dependency to support optimized uploads.

@coderabbitai
Copy link

coderabbitai bot commented Sep 9, 2025

Walkthrough

Added expo-image-manipulator dependency. Refactored CallImagesModal to manipulate images (resize/compress), attach location and filename, extract base64 for upload, and show a full-screen preview. Introduced a new FullScreenImageModal component with pinch/pan/double-tap gestures. Added translation keys and adjusted analytics/onClose usage.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Added expo-image-manipulator (~13.1.7) to dependencies.
Call Images Modal Refactor
src/components/calls/call-images-modal.tsx
Reworked image flow to use expo-image + expo-image-manipulator: manipulate (resize 1024w, PNG, compress 0.8), read base64, use selectedImageInfo (uri, filename), include location (lat/long) and note in upload payload, reset state on success, add preview area/fullscreen open, new styles, refactor rendering and error placeholder, simplify analytics and onClose behavior.
New Full-Screen Image Modal
src/components/calls/full-screen-image-modal.tsx
New default-exported FullScreenImageModal component supporting pinch-to-zoom (1–5x), pan with bounds, double-tap toggle (1x/2x), transform reset on open, backdrop/close button, status bar handling, accessibility/testIDs.
Translations
src/translations/en.json, src/translations/es.json, src/translations/ar.json
Added callImages.image_note, callImages.upload_error, and callImages.upload_success translation keys (English, Spanish, Arabic).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant M as CallImagesModal
  participant IM as ImageManipulator
  participant FS as FullScreenImageModal
  participant L as LocationStore
  participant S as UploadService

  U->>M: Select image (camera/gallery)
  M->>IM: Manipulate (resize 1024w, PNG, compress 0.8)
  IM-->>M: Manipulated image URI
  M->>M: Read base64, determine filename
  M->>L: Request location
  L-->>M: lat,long
  M->>S: uploadCallImage({ base64, filename, note, location })
  S-->>M: Success/Failure
  M->>M: Reset selection on success

  U->>M: Tap thumbnail
  M->>FS: Open(fullscreen {imageSource,imageName})
  activate FS
  U->>FS: Pinch / Pan / Double-tap
  FS-->>FS: Apply bounded transforms
  U->>FS: Backdrop press / Close
  FS-->>M: onClose()
  deactivate FS
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hop a pixel, tweak and spin,
Carrot-coded zoom from ear to chin.
Pinch for moons and pan the trail,
Double-tap flips the tiny scale.
With lat-long crumbs I send the file—thump-thump, done with a smile 🥕

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
		  - name: "Undocumented Breaking Changes"
			  mode: "warning"
			  instructions: |
				  Flag potential breaking changes that are not documented:
				  1. Identify changes to public APIs/exports, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints (including removed/renamed items and changes to types, required params, return values, defaults, or behavior).
				  2. Ignore purely internal/private changes (e.g., code not exported from package entry points or marked internal).
				  3. Verify documentation exists: a "Breaking Change" section in the PR description and updates to CHANGELOG.md.

Please share your feedback with us on this Discord post.

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is currently empty and does not provide any information about the changes, making it impossible to understand the purpose or scope of the updates. Please add a descriptive summary outlining the key changes, motivations, and any relevant context to help reviewers understand the intent of this pull request.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly references the ticket and highlights the main change of improving the call images view, which aligns with the primary content of the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

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.

Copy link

@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

Caution

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

⚠️ Outside diff range comments (3)
src/components/calls/call-images-modal.tsx (3)

107-111: Fix permission denial alert.

Use Alert.alert (RN API). alert() isn’t reliable in RN.

Apply:

-      alert(t('common.permission_denied'));
+      Alert.alert(t('common.permission_denied'));

125-129: Fix camera permission denial alert.

Same as above; use RN’s Alert.alert.

Apply:

-      alert(t('common.permission_denied'));
+      Alert.alert(t('common.permission_denied'));

389-390: Avoid random keys in FlatList.

Random keys break recycling and hurt performance. Prefer stable fallbacks.

Apply:

-            keyExtractor={(item) => item?.Id || `image-${Math.random()}`}
+            keyExtractor={(item, index) => item?.Id ?? item?.Url ?? `${item?.Name ?? 'image'}-${index}`}
🧹 Nitpick comments (7)
src/components/calls/full-screen-image-modal.tsx (2)

163-166: Use accessibilityLabel instead of alt.

React Native relies on accessibilityLabel; alt isn’t standard.

Apply:

-              alt={imageName || t('callImages.image_alt')}
+              accessibilityLabel={imageName || t('callImages.image_alt')}

19-21: Handle orientation changes.

Dimensions.get('window') is static here; consider useWindowDimensions() to keep sizing correct after rotation.

src/components/calls/call-images-modal.tsx (5)

2-3: Use the project Image wrapper for consistency.

Import the shared UI Image instead of expo-image directly to keep placeholders, defaults, and future swaps centralized.

Apply:

-import { Image } from 'expo-image';
+import { Image } from '@/components/ui/image';

214-218: Localize the “URL:” label.

Wrap user-facing strings with i18n.

Apply:

-              <Text className="mt-1 px-2 text-center text-xs text-gray-400" numberOfLines={2}>
-                URL: {item.Url}
-              </Text>
+              <Text className="mt-1 px-2 text-center text-xs text-gray-400" numberOfLines={2}>
+                {t('common.url')}: {item.Url}
+              </Text>

60-61: Type the fullscreen image state precisely.

Avoid any; reuse the image source type for safety.

Apply:

-  const [fullScreenImage, setFullScreenImage] = useState<{ source: any; name?: string } | null>(null);
+  const [fullScreenImage, setFullScreenImage] = useState<{ source: import('expo-image').ImageProps['source']; name?: string } | null>(null);

462-468: Style guideline: prefer ternary over && chains.

Repo guidance prefers ?: for conditional rendering.

Example:

{!isAddingImage && !isLoadingImages ? (
  <Button ... />
) : null}

240-257: Optional: extract inline handlers to useCallback.

Inline lambdas in render cause avoidable re-renders; hoist onPress and callbacks into useCallback.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b82329 and af9f7aa.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • package.json (1 hunks)
  • src/components/calls/call-images-modal.tsx (12 hunks)
  • src/components/calls/full-screen-image-modal.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components and favor interfaces for props and state
Avoid using any; use precise types
Use React Navigation for navigation and deep linking following best practices
Handle errors gracefully and provide user feedback
Implement proper offline support (caching, queueing, retries)
Use Expo SecureStore for sensitive data storage
Use zustand for state management
Use react-hook-form for form handling
Use react-query for data fetching and caching
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Ensure support for dark mode and light mode
Handle errors gracefully and provide user feedback
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use functional components and React hooks instead of class components
Use PascalCase for React component names
Use React.FC for defining functional components with props
Minimize useEffect/useState usage and avoid heavy computations during render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Provide getItemLayout to FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers; define callbacks with useCallback or outside render
Use gluestack-ui for styling where available from components/ui; otherwise, style via StyleSheet.create or styled-components
Ensure responsive design across screen sizes and orientations
Use react-native-fast-image for image handling instead of the default Image where appropriate
Wrap all user-facing text in t() from react-i18next for translations
Support dark mode and light mode in UI components
Use @rnmapbox/maps for maps or navigation features
Use lucide-react-native for icons directly; do not use the gluestack-ui icon component
Use conditional rendering with the ternary operator (?:) instead of &&

**/*.tsx: Use functional components and hooks over class components
Ensure components are modular, reusable, and maintainable
Ensure all components are mobile-friendly, responsive, and support both iOS and Android
Use PascalCase for component names
Utilize React.FC for defining functional components with props
Minimize useEffect, useState, and heavy computations inside render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Use getItemLayout for FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers to prevent re-renders
Ensure responsive design for different screen sizes and orientations
Optimize image handling using rea...

Files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
src/**

📄 CodeRabbit inference engine (.cursorrules)

src/**: Organize files by feature, grouping related components, hooks, and styles
Directory and file names should be lowercase and hyphenated (e.g., user-profile)

Files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
🧠 Learnings (9)
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use react-native-fast-image for image handling instead of the default Image where appropriate

Applied to files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Optimize image handling using react-native-fast-image

Applied to files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use lucide-react-native for icons directly; do not use the gluestack-ui icon component

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use lucide-react-native for icons directly in markup; do not use gluestack-ui icon component

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use React Navigation for navigation and deep linking following best practices

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps, mapping, or vehicle navigation

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Provide getItemLayout to FlatList when items have consistent size

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use getItemLayout for FlatList when items have consistent size

Applied to files:

  • src/components/calls/call-images-modal.tsx
🧬 Code graph analysis (2)
src/components/calls/full-screen-image-modal.tsx (1)
src/components/ui/image.tsx (1)
  • Image (12-14)
src/components/calls/call-images-modal.tsx (3)
__mocks__/react-native.ts (1)
  • StyleSheet (48-50)
src/stores/calls/detail-store.ts (1)
  • useCallDetailStore (41-206)
src/components/ui/image.tsx (1)
  • Image (12-14)
⏰ 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: test
🔇 Additional comments (1)
package.json (1)

112-112: No action needed: expo-image-manipulator@~13.1.7 is the bundled version for SDK 53, matching Expo’s v53 docs. (docs.expo.dev)

@ucswift
Copy link
Member Author

ucswift commented Sep 9, 2025

Approve

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift ucswift merged commit 3feb5cb into master Sep 9, 2025
10 of 12 checks passed
Copy link

@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: 2

Caution

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

⚠️ Outside diff range comments (6)
src/translations/en.json (2)

127-144: Add missing key used by FullScreenImageModal.

FullScreenImageModal calls t('callImages.image_alt'), but image_alt is not defined under callImages. Add it to avoid fallback-to-key.

   "callImages": {
     "add": "Add Image",
     "add_new": "Add New Image",
     "default_name": "Untitled Image",
     "error": "Error getting images",
     "failed_to_load": "Failed to load image",
     "image_name": "Image Name",
+    "image_alt": "Full-screen image",
     "image_note": "Image Note",
     "loading": "Loading...",

335-369: Define common.permission_denied and common.uploading used in UI.

call-images-modal.tsx uses t('common.permission_denied') and t('common.uploading'), but these keys don't exist in common. Add them to prevent untranslated strings.

   "common": {
     "active": "Active",
     ...
     "loading": "Loading",
+    "uploading": "Uploading...",
+    "permission_denied": "Permission denied",
     "navigation": "Navigation",
src/translations/ar.json (2)

127-144: إضافة مفتاح image_alt المستخدم في العارض كامل الشاشة.

المكوّن يستدعي callImages.image_alt لكنه غير معرّف. الرجاء إضافته.

   "callImages": {
     "add": "إضافة صورة",
     ...
     "image_name": "اسم الصورة",
+    "image_alt": "صورة بالحجم الكامل",
     "image_note": "ملاحظة الصورة",

335-369: تعريف مفاتيح عامة ناقصة.

permission_denied وuploading مستخدمتان من common وغير موجودتين. أضفهما لتفادي السلاسل غير المترجمة.

   "common": {
     "active": "نشط",
     ...
     "loading": "جاري التحميل...",
+    "uploading": "جاري الرفع...",
+    "permission_denied": "تم رفض الإذن",
     "navigation": "التنقل",
src/translations/es.json (2)

127-144: Añadir clave image_alt usada por el visor a pantalla completa.

Se llama a t('callImages.image_alt'), pero la clave no existe. Añádela para evitar el fallback.

   "callImages": {
     "add": "Añadir imagen",
     ...
     "image_name": "Nombre de la imagen",
+    "image_alt": "Imagen a pantalla completa",
     "image_note": "Nota de imagen",

335-369: Definir common.permission_denied y common.uploading.

El código usa estas claves y no existen en common. Añádelas.

   "common": {
     "active": "Activo",
     ...
     "loading": "Cargando...",
+    "uploading": "Subiendo...",
+    "permission_denied": "Permiso denegado",
     "navigation": "Navegación",
♻️ Duplicate comments (2)
src/components/calls/full-screen-image-modal.tsx (2)

4-4: Type imageSource with expo-image’s source type for correctness.

Your UI Image wraps expo-image; prefer its source type to avoid mismatches with RN’s ImageSourcePropType.

-import { Dimensions, type ImageSourcePropType, StatusBar, TouchableOpacity } from 'react-native';
+import { Dimensions, StatusBar, TouchableOpacity } from 'react-native';
+import type { ImageProps as ExpoImageProps } from 'expo-image';

12-17: Update prop type accordingly.

 interface FullScreenImageModalProps {
   isOpen: boolean;
   onClose: () => void;
-  imageSource: ImageSourcePropType;
+  imageSource: ExpoImageProps['source'];
   imageName?: string;
 }
🧹 Nitpick comments (14)
src/components/calls/full-screen-image-modal.tsx (3)

6-6: Remove unused import.

runOnJS is not used anymore.

-import Animated, { interpolate, runOnJS, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
+import Animated, { interpolate, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';

102-102: Simplify gesture composition.

Nesting Simultaneous is redundant.

-  const composedGesture = Gesture.Simultaneous(Gesture.Simultaneous(pinchGesture, panGesture), doubleTapGesture);
+  const composedGesture = Gesture.Simultaneous(pinchGesture, panGesture, doubleTapGesture);

19-21: Optional: handle orientation changes.

Using Dimensions.get('window') locks values; useWindowDimensions() will adapt on rotate.

Would you like a small patch to switch to useWindowDimensions()?

src/components/calls/call-images-modal.tsx (11)

2-3: Use the shared Image wrapper for consistency and caching.

Elsewhere you use @/components/ui/image. Prefer that over raw expo-image to unify behavior/placeholders.

-import { Image } from 'expo-image';
+import { Image } from '@/components/ui/image';

8-8: Align image source typing with expo-image.

Use ExpoImageProps['source'] instead of RN’s ImageSourcePropType to match the wrapper’s underlying lib.

-import { Alert, Dimensions, FlatList, type ImageSourcePropType, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';
+import { Alert, Dimensions, FlatList, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';
+import type { ImageProps as ExpoImageProps } from 'expo-image';

62-63: Update fullScreenImage state type accordingly.

-  const [fullScreenImage, setFullScreenImage] = useState<{ source: ImageSourcePropType; name?: string } | null>(null);
+  const [fullScreenImage, setFullScreenImage] = useState<{ source: ExpoImageProps['source']; name?: string } | null>(null);

121-123: Match filename extension to chosen format.

If saving as JPEG, default to .jpg to align name with content type.

-      const filename = asset.fileName || `image_${Date.now()}.png`;
+      const filename = asset.fileName || `image_${Date.now()}.jpg`;

138-139: Same here for camera capture: use .jpg.

-      const filename = `camera_${Date.now()}.png`;
+      const filename = `camera_${Date.now()}.jpg`;

374-376: Use ternary instead of && for conditional rendering (repo guideline).

-            <Button className="mt-2 w-full" size="sm" onPress={handleUploadImage} isDisabled={isUploading} testID="upload-button">
-              <ButtonText>{isUploading ? t('common.uploading') : t('callImages.upload')}</ButtonText>
-            </Button>
+            {true ? (
+              <Button className="mt-2 w/full" size="sm" onPress={handleUploadImage} isDisabled={isUploading} testID="upload-button">
+                <ButtonText>{isUploading ? t('common.uploading') : t('callImages.upload')}</ButtonText>
+              </Button>
+            ) : null}

466-471: Replace && with ternary as per coding guidelines.

-              {!isAddingImage && !isLoadingImages && (
-                <Button size="sm" variant="outline" onPress={() => setIsAddingImage(true)}>
-                  <ButtonIcon as={PlusIcon} />
-                  <ButtonText>{t('callImages.add')}</ButtonText>
-                </Button>
-              )}
+              {!isAddingImage && !isLoadingImages ? (
+                <Button size="sm" variant="outline" onPress={() => setIsAddingImage(true)}>
+                  <ButtonIcon as={PlusIcon} />
+                  <ButtonText>{t('callImages.add')}</ButtonText>
+                </Button>
+              ) : null}

214-226: Localize the static “URL:” label.

Wrap in t() (e.g., add common.url) for consistency with i18n.

Want me to add common.url to all locales?


1-9: Remove unused import.

Alert isn’t used.

-import { Alert, Dimensions, FlatList, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';
+import { Dimensions, FlatList, Platform, StyleSheet, TouchableOpacity, View } from 'react-native';

269-274: Stabilize callbacks to reduce re-renders.

Wrap handleViewableItemsChanged, handlePrevious, handleNext with useCallback. Minor perf win on large lists.


479-481: Defensive default for imageSource.

Passing { uri: '' } can still error on some implementations. Consider guarding render until fullScreenImage is set.

-      <FullScreenImageModal isOpen={!!fullScreenImage} onClose={() => setFullScreenImage(null)} imageSource={fullScreenImage?.source || { uri: '' }} imageName={fullScreenImage?.name} />
+      {fullScreenImage ? (
+        <FullScreenImageModal isOpen onClose={() => setFullScreenImage(null)} imageSource={fullScreenImage.source} imageName={fullScreenImage.name} />
+      ) : null}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af9f7aa and 90f2da8.

📒 Files selected for processing (5)
  • src/components/calls/call-images-modal.tsx (11 hunks)
  • src/components/calls/full-screen-image-modal.tsx (1 hunks)
  • src/translations/ar.json (1 hunks)
  • src/translations/en.json (1 hunks)
  • src/translations/es.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**

📄 CodeRabbit inference engine (.cursorrules)

src/**: Organize files by feature, grouping related components, hooks, and styles
Directory and file names should be lowercase and hyphenated (e.g., user-profile)

Files:

  • src/translations/ar.json
  • src/translations/en.json
  • src/translations/es.json
  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
src/translations/**/*.json

📄 CodeRabbit inference engine (.cursorrules)

Store translation dictionary files under src/translations as JSON resources

Files:

  • src/translations/ar.json
  • src/translations/en.json
  • src/translations/es.json
src/translations/**/*.{ts,tsx,json}

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

Store translation dictionary files in src/translations

Files:

  • src/translations/ar.json
  • src/translations/en.json
  • src/translations/es.json
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components and favor interfaces for props and state
Avoid using any; use precise types
Use React Navigation for navigation and deep linking following best practices
Handle errors gracefully and provide user feedback
Implement proper offline support (caching, queueing, retries)
Use Expo SecureStore for sensitive data storage
Use zustand for state management
Use react-hook-form for form handling
Use react-query for data fetching and caching
Use react-native-mmkv for local storage
Use axios for API requests

**/*.{ts,tsx}: Write concise, type-safe TypeScript code
Use camelCase for variable and function names
Use TypeScript for all components, favoring interfaces for props and state
Avoid using any; strive for precise types
Ensure support for dark mode and light mode
Handle errors gracefully and provide user feedback
Use react-query for data fetching
Use react-i18next for internationalization
Use react-native-mmkv for local storage
Use axios for API requests

Files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: Use functional components and React hooks instead of class components
Use PascalCase for React component names
Use React.FC for defining functional components with props
Minimize useEffect/useState usage and avoid heavy computations during render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Provide getItemLayout to FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers; define callbacks with useCallback or outside render
Use gluestack-ui for styling where available from components/ui; otherwise, style via StyleSheet.create or styled-components
Ensure responsive design across screen sizes and orientations
Use react-native-fast-image for image handling instead of the default Image where appropriate
Wrap all user-facing text in t() from react-i18next for translations
Support dark mode and light mode in UI components
Use @rnmapbox/maps for maps or navigation features
Use lucide-react-native for icons directly; do not use the gluestack-ui icon component
Use conditional rendering with the ternary operator (?:) instead of &&

**/*.tsx: Use functional components and hooks over class components
Ensure components are modular, reusable, and maintainable
Ensure all components are mobile-friendly, responsive, and support both iOS and Android
Use PascalCase for component names
Utilize React.FC for defining functional components with props
Minimize useEffect, useState, and heavy computations inside render
Use React.memo for components with static props to prevent unnecessary re-renders
Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize
Use getItemLayout for FlatList when items have consistent size
Avoid anonymous functions in renderItem or event handlers to prevent re-renders
Ensure responsive design for different screen sizes and orientations
Optimize image handling using rea...

Files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Optimize image handling using react-native-fast-image
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use react-native-fast-image for image handling instead of the default Image where appropriate

Applied to files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Optimize image handling using react-native-fast-image

Applied to files:

  • src/components/calls/full-screen-image-modal.tsx
  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use lucide-react-native for icons directly; do not use the gluestack-ui icon component

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use lucide-react-native for icons directly in markup; do not use gluestack-ui icon component

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps, mapping, or vehicle navigation

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Use rnmapbox/maps for maps or navigation features

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.{ts,tsx} : Use React Navigation for navigation and deep linking following best practices

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Optimize FlatList with removeClippedSubviews, maxToRenderPerBatch, and windowSize

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:46:42.743Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .cursorrules:0-0
Timestamp: 2025-08-21T02:46:42.743Z
Learning: Applies to **/*.tsx : Provide getItemLayout to FlatList when items have consistent size

Applied to files:

  • src/components/calls/call-images-modal.tsx
📚 Learning: 2025-08-21T02:47:24.081Z
Learnt from: CR
PR: Resgrid/Responder#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-21T02:47:24.081Z
Learning: Applies to **/*.tsx : Use getItemLayout for FlatList when items have consistent size

Applied to files:

  • src/components/calls/call-images-modal.tsx
🧬 Code graph analysis (2)
src/components/calls/full-screen-image-modal.tsx (2)
src/components/ui/modal/index.tsx (3)
  • Modal (228-228)
  • ModalBackdrop (228-228)
  • ModalContent (228-228)
src/components/ui/image.tsx (1)
  • Image (12-14)
src/components/calls/call-images-modal.tsx (4)
__mocks__/react-native.ts (1)
  • StyleSheet (48-50)
src/stores/toast/store.ts (1)
  • useToastStore (18-41)
src/stores/calls/detail-store.ts (1)
  • useCallDetailStore (41-206)
src/components/ui/image.tsx (1)
  • Image (12-14)
🔇 Additional comments (1)
src/components/calls/call-images-modal.tsx (1)

111-113: Translation key not found: common.permission_denied.

Until the key is added to translations, this will show the raw key. Either add the key (preferred; see translation comments) or temporarily reuse an existing string.

Do you want me to open a follow-up PR adding common.permission_denied across locales?

Comment on lines +148 to +156
// Manipulate image to ensure PNG format and proper compression
const manipulatedImage = await ImageManipulator.manipulateAsync(
selectedImageInfo.uri,
[{ resize: { width: 1024 } }], // Resize to max width of 1024px while maintaining aspect ratio
{
compress: 0.8,
format: ImageManipulator.SaveFormat.PNG, // Ensure PNG format
}
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid extra file I/O; ask manipulator for base64 directly and prefer JPEG for size.

Reading back via FileSystem is slower and PNG ignores compress. Request base64 from manipulator and save as JPEG (~smaller uploads).

-      const manipulatedImage = await ImageManipulator.manipulateAsync(
+      const manipulatedImage = await ImageManipulator.manipulateAsync(
         selectedImageInfo.uri,
         [{ resize: { width: 1024 } }], // Resize to max width of 1024px while maintaining aspect ratio
         {
-          compress: 0.8,
-          format: ImageManipulator.SaveFormat.PNG, // Ensure PNG format
+          compress: 0.8,
+          format: ImageManipulator.SaveFormat.JPEG,
+          base64: true,
         }
       );
📝 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
// Manipulate image to ensure PNG format and proper compression
const manipulatedImage = await ImageManipulator.manipulateAsync(
selectedImageInfo.uri,
[{ resize: { width: 1024 } }], // Resize to max width of 1024px while maintaining aspect ratio
{
compress: 0.8,
format: ImageManipulator.SaveFormat.PNG, // Ensure PNG format
}
);
// Manipulate image to ensure PNG format and proper compression
const manipulatedImage = await ImageManipulator.manipulateAsync(
selectedImageInfo.uri,
[{ resize: { width: 1024 } }], // Resize to max width of 1024px while maintaining aspect ratio
{
compress: 0.8,
format: ImageManipulator.SaveFormat.JPEG,
base64: true,
}
);
🤖 Prompt for AI Agents
In src/components/calls/call-images-modal.tsx around lines 148 to 156, the code
manipulates the image to PNG then later reads it back from disk which is slow
and ignores compress for PNG; instead call ImageManipulator.manipulateAsync with
{ base64: true, compress: 0.8, format: ImageManipulator.SaveFormat.JPEG } (and
still resize as you have) so you get a compressed JPEG base64 string directly,
then use manipulatedImage.base64 for upload or to write the file instead of
performing extra FileSystem.readAsStringAsync and avoid saving/reading PNG
entirely.

Comment on lines +158 to 161
const base64Image = await FileSystem.readAsStringAsync(manipulatedImage.uri, {
encoding: FileSystem.EncodingType.Base64,
});

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use manipulator’s base64; remove FileSystem read.

-      // Read the manipulated image as base64
-      const base64Image = await FileSystem.readAsStringAsync(manipulatedImage.uri, {
-        encoding: FileSystem.EncodingType.Base64,
-      });
+      // Use base64 returned by manipulator
+      const base64Image = manipulatedImage.base64!;
🤖 Prompt for AI Agents
In src/components/calls/call-images-modal.tsx around lines 158 to 161, the code
reads the manipulated image from disk with FileSystem.readAsStringAsync instead
of using the base64 already returned by the image manipulator; change the image
manipulation call to request base64 (set base64: true in manipulateAsync options
if not present), then use the returned result.base64 directly as the base64Image
value, and remove the FileSystem.readAsStringAsync call and any related
imports/usages.

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