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

Optimize media library handling and photo grid rendering in Expo app #386

Merged
merged 4 commits into from
Jan 14, 2025

Conversation

jaronheard
Copy link
Owner

@jaronheard jaronheard commented Jan 14, 2025

  • ♻️ optimize photo grid performance with memoization and image caching
  • ♻️ refactor(media): improve media library hook with better error handling and DRY

Summary by CodeRabbit

  • New Features

    • Added media library access hook for retrieving recent photos.
    • Enhanced photo grid component with optimized rendering and memoization.
    • Improved event preview image handling with advanced caching and transition settings.
    • Introduced a new GridItem component for individual photo grid items.
  • Performance Improvements

    • Optimized photo grid rendering with memoized components and list properties.
    • Implemented efficient photo URI prefetching.
    • Enhanced image loading and caching strategies.
    • Simplified component structure and layout for better performance.

The commit improves performance by:
- Adding memoization for grid items and layout calculations
- Implementing FlatList performance optimizations
- Enhancing image caching and transition settings
- Adding photo prefetching
Copy link

vercel bot commented Jan 14, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
soonlist-turbo ⬜️ Skipped (Inspect) Jan 14, 2025 11:02am

Copy link

linear bot commented Jan 14, 2025

Copy link
Contributor

coderabbitai bot commented Jan 14, 2025

Walkthrough

This pull request introduces several improvements to media library handling and photo grid rendering in an Expo-based application. The changes focus on optimizing performance, enhancing image loading, and refactoring media library access. A new useMediaLibrary hook is introduced, which streamlines photo retrieval and prefetching. The PhotoGrid component receives performance optimizations through memoization and rendering improvements, while the EventPreview component updates image handling properties. Additionally, the CaptureEventButton component's structure is simplified for better readability.

Changes

File Change Summary
apps/expo/src/hooks/useMediaLibrary.ts - Added loadRecentPhotos function
- Refactored photo loading logic
- Improved media library state management
apps/expo/src/app/new.tsx - Integrated useMediaLibrary hook
apps/expo/src/components/PhotoGrid.tsx - Added memoized GridItem component
- Optimized rendering with useMemo
- Enhanced FlatList performance props
apps/expo/src/components/EventPreview.tsx - Updated ExpoImage properties
- Modified transition and caching settings
apps/expo/src/components/CaptureEventButton.tsx - Restructured JSX layout
- Simplified component hierarchy and styling

Possibly related PRs


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.

@coderabbitai coderabbitai bot changed the title @coderabbitai Optimize media library handling and photo grid rendering in Expo app Jan 14, 2025
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

🧹 Nitpick comments (3)
apps/expo/src/hooks/useMediaLibrary.ts (1)

Line range hint 15-35: Enhance error handling for better user experience.

The parallel prefetching optimization is excellent for performance. However, the error handling could be more user-friendly.

 const loadRecentPhotos = useCallback(async () => {
   try {
     const { assets } = await MediaLibrary.getAssetsAsync({
       first: 15,
       sortBy: MediaLibrary.SortBy.creationTime,
       mediaType: [MediaLibrary.MediaType.photo],
     });
     const photos = assets.map((asset) => ({
       id: asset.id,
       uri: asset.uri,
     }));

     // Prefetch all photos in parallel
     await Promise.all(photos.map((photo) => Image.prefetch(photo.uri)));

     setRecentPhotos(photos);
   } catch (error) {
-    console.error("Error loading recent photos:", error);
+    console.error("Error loading recent photos:", error);
+    toast.error("Failed to load recent photos. Please try again.");
+    setRecentPhotos([]);
   }
 }, [setRecentPhotos]);
apps/expo/src/components/PhotoGrid.tsx (2)

29-117: Well-structured memoized GridItem component!

The component effectively separates rendering logic and implements performance optimizations.

Consider removing the recyclingKey prop.

Based on previous feedback, the recyclingKey prop isn't necessary for small lists of recent photos.

 <ExpoImage
   source={{ uri: item.uri }}
   style={{
     width: "100%",
     height: "100%",
   }}
   contentFit="cover"
   contentPosition="center"
   transition={200}
   cachePolicy="memory-disk"
-  recyclingKey={item.id}
   placeholder={null}
   placeholderContentFit="cover"
   className="bg-muted/10"
 />

232-236: Remove unnecessary FlatList optimizations.

Based on previous feedback, these performance optimizations aren't necessary for iOS implementations with a fixed/limited number of items.

 <FlatList
   data={gridData}
   renderItem={renderItem}
   numColumns={4}
   showsVerticalScrollIndicator={false}
   contentContainerStyle={{
     paddingBottom: 100,
   }}
   keyExtractor={(item) => item.id}
   horizontal={false}
-  windowSize={5}
-  maxToRenderPerBatch={16}
-  updateCellsBatchingPeriod={50}
-  initialNumToRender={12}
-  removeClippedSubviews={true}
   getItemLayout={getItemLayout}
 />
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 78b83c6 and 1e808cb.

📒 Files selected for processing (4)
  • apps/expo/src/app/new.tsx (2 hunks)
  • apps/expo/src/components/EventPreview.tsx (1 hunks)
  • apps/expo/src/components/PhotoGrid.tsx (4 hunks)
  • apps/expo/src/hooks/useMediaLibrary.ts (5 hunks)
🧰 Additional context used
📓 Learnings (1)
apps/expo/src/components/PhotoGrid.tsx (2)
Learnt from: jaronheard
PR: jaronheard/soonlist-turbo#375
File: apps/expo/src/components/PhotoGrid.tsx:93-171
Timestamp: 2025-01-13T03:13:45.462Z
Learning: FlatList performance optimizations (initialNumToRender, maxToRenderPerBatch, windowSize, removeClippedSubviews) are not necessary for iOS implementations with a fixed/limited number of items, as iOS has better performance characteristics for FlatList rendering.
Learnt from: jaronheard
PR: jaronheard/soonlist-turbo#375
File: apps/expo/src/components/PhotoGrid.tsx:150-160
Timestamp: 2025-01-13T03:23:22.636Z
Learning: The PhotoGrid component in the Expo app intentionally omits the recyclingKey prop for ExpoImage components as it's designed for smaller lists of recent photos, where the benefits of recycling don't outweigh potential complexities.
🪛 GitHub Check: lint
apps/expo/src/components/PhotoGrid.tsx

[failure] 150-150:
Unexpected any. Specify a different type

🪛 GitHub Actions: CI
apps/expo/src/components/PhotoGrid.tsx

[error] 150-150: Unexpected any. Specify a different type (@typescript-eslint/no-explicit-any)

🔇 Additional comments (5)
apps/expo/src/hooks/useMediaLibrary.ts (2)

Line range hint 37-76: Well-implemented permission handling and subscription management!

The implementation follows React best practices:

  • Uses useFocusEffect for permission checks
  • Properly cleans up subscription
  • Efficiently handles photo library changes

Line range hint 79-89: Clean implementation of the refresh mechanism!

The useEffect hook correctly handles the refresh flag and dependencies.

apps/expo/src/components/EventPreview.tsx (1)

65-69: Good image loading optimizations!

The changes enhance the image loading experience:

  • Smoother transitions with increased duration
  • Efficient caching with memory-disk policy
  • Better visual feedback during loading states
apps/expo/src/components/PhotoGrid.tsx (1)

140-146: Efficient grid data memoization!

The useMemo implementation correctly optimizes the grid data array creation.

apps/expo/src/app/new.tsx (1)

16-16: Clean integration of useMediaLibrary hook!

The hook is properly integrated for its side effects.

Also applies to: 28-28

Comment on lines 149 to 156
const getItemLayout = useMemo(
() => (_: any, index: number) => ({
length: imageSize,
offset: imageSize * Math.floor(index / columns),
index,
}),
[imageSize, columns],
);
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

Fix TypeScript any type.

Replace the any type with proper typing to resolve the TypeScript error.

-    const getItemLayout = useMemo(
-      () => (_: any, index: number) => ({
+    const getItemLayout = useMemo(
+      () => (_: unknown[], index: number) => ({
         length: imageSize,
         offset: imageSize * Math.floor(index / columns),
         index,
       }),
       [imageSize, columns],
     );
📝 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 getItemLayout = useMemo(
() => (_: any, index: number) => ({
length: imageSize,
offset: imageSize * Math.floor(index / columns),
index,
}),
[imageSize, columns],
);
const getItemLayout = useMemo(
() => (_: unknown[], index: number) => ({
length: imageSize,
offset: imageSize * Math.floor(index / columns),
index,
}),
[imageSize, columns],
);
🧰 Tools
🪛 GitHub Check: lint

[failure] 150-150:
Unexpected any. Specify a different type

🪛 GitHub Actions: CI

[error] 150-150: Unexpected any. Specify a different type (@typescript-eslint/no-explicit-any)

The commit improves the UI by fixing the capture button position, adjusting its
styling, and optimizing the photo grid layout with proper bottom padding and
TypeScript types
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

🧹 Nitpick comments (5)
apps/expo/src/components/CaptureEventButton.tsx (2)

34-34: Consider using design tokens for colors.

Instead of hardcoding hex values, consider using design tokens or theme variables for colors to maintain consistency and make theme changes easier.

-        <Sparkles size={20} color={isDisabled ? "#627496" : "#5A32FB"} />
+        <Sparkles size={20} color={isDisabled ? colors.neutral[2] : colors.primary} />

35-42: Maintain consistency with color tokens.

Similar to the icon, consider using design tokens for text colors to ensure consistency throughout the application.

-            isDisabled ? "text-neutral-2" : "text-[#5A32FB]",
+            isDisabled ? "text-neutral-2" : "text-primary",
apps/expo/src/components/PhotoGrid.tsx (3)

103-103: Remove the recyclingKey prop.

Based on previous learnings, the recyclingKey prop should be omitted for this component as it's designed for smaller lists of recent photos.

-          recyclingKey={item.id}

51-69: Simplify the plus button press handler logic.

The nested conditional logic can be simplified for better readability.

-          onPress={() => {
-            if (hasMediaPermission && !hasFullPhotoAccess) {
-              ActionSheetIOS.showActionSheetWithOptions(
-                {
-                  options: ["Select More Photos", "Change Settings", "Cancel"],
-                  cancelButtonIndex: 2,
-                },
-                (buttonIndex) => {
-                  if (buttonIndex === 0) {
-                    void MediaLibrary.presentPermissionsPickerAsync();
-                  } else if (buttonIndex === 1) {
-                    void Linking.openSettings();
-                  }
-                },
-              );
-            } else {
-              onMorePhotos();
-            }
-          }}
+          onPress={() => {
+            if (!hasMediaPermission || hasFullPhotoAccess) {
+              onMorePhotos();
+              return;
+            }
+            
+            ActionSheetIOS.showActionSheetWithOptions(
+              {
+                options: ["Select More Photos", "Change Settings", "Cancel"],
+                cancelButtonIndex: 2,
+              },
+              (buttonIndex) => {
+                switch (buttonIndex) {
+                  case 0:
+                    void MediaLibrary.presentPermissionsPickerAsync();
+                    break;
+                  case 1:
+                    void Linking.openSettings();
+                    break;
+                }
+              },
+            );
+          }}

228-232: Remove unnecessary FlatList performance optimizations.

Based on previous learnings, these performance optimizations (windowSize, maxToRenderPerBatch, updateCellsBatchingPeriod, initialNumToRender, removeClippedSubviews) are not necessary for iOS implementations with a fixed/limited number of items, as iOS has better performance characteristics for FlatList rendering.

-            windowSize={5}
-            maxToRenderPerBatch={16}
-            updateCellsBatchingPeriod={50}
-            initialNumToRender={12}
-            removeClippedSubviews={true}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1e808cb and 6f13a34.

📒 Files selected for processing (3)
  • apps/expo/src/app/new.tsx (3 hunks)
  • apps/expo/src/components/CaptureEventButton.tsx (1 hunks)
  • apps/expo/src/components/PhotoGrid.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/expo/src/app/new.tsx
🧰 Additional context used
📓 Learnings (1)
apps/expo/src/components/PhotoGrid.tsx (2)
Learnt from: jaronheard
PR: jaronheard/soonlist-turbo#375
File: apps/expo/src/components/PhotoGrid.tsx:93-171
Timestamp: 2025-01-13T03:13:45.462Z
Learning: FlatList performance optimizations (initialNumToRender, maxToRenderPerBatch, windowSize, removeClippedSubviews) are not necessary for iOS implementations with a fixed/limited number of items, as iOS has better performance characteristics for FlatList rendering.
Learnt from: jaronheard
PR: jaronheard/soonlist-turbo#375
File: apps/expo/src/components/PhotoGrid.tsx:150-160
Timestamp: 2025-01-13T03:23:22.636Z
Learning: The PhotoGrid component in the Expo app intentionally omits the recyclingKey prop for ExpoImage components as it's designed for smaller lists of recent photos, where the benefits of recycling don't outweigh potential complexities.
🔇 Additional comments (3)
apps/expo/src/components/CaptureEventButton.tsx (1)

26-33: Well-structured button implementation with proper accessibility!

The Pressable component implementation follows React Native best practices with:

  • Proper disabled state handling
  • Consistent styling patterns using utility classes
  • Good accessibility considerations
apps/expo/src/components/PhotoGrid.tsx (2)

137-143: LGTM! Effective memoization of grid data.

The memoization of gridData is well implemented with appropriate dependencies.


149-149: Fix the type for _data parameter.

The type for the _data parameter should be more specific.

-          _data: ArrayLike<RecentPhoto & { id: string }> | null | undefined,
+          _: unknown[],

@jaronheard jaronheard merged commit 9f281ee into main Jan 14, 2025
8 checks passed
@jaronheard jaronheard deleted the jaronheard/tim-823-slow-photo-loading-rendering branch January 14, 2025 11:07
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