-
Notifications
You must be signed in to change notification settings - Fork 7
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 minor issue #79
Fix minor issue #79
Conversation
Warning Rate limit exceeded@cp-pratik-k has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 18 minutes and 9 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (4)
WalkthroughThis pull request introduces comprehensive changes to media handling, image preview, and cache management across multiple components of the application. The modifications focus on improving how media files are stored, retrieved, and processed, with particular emphasis on cache directory usage, media deletion events, and image preview functionality. The changes span various files in the UI, domain, and repository layers, introducing more robust and centralized approaches to media management. Changes
Sequence DiagramsequenceDiagram
participant User
participant MediaPreviewScreen
participant MediaProcessRepo
participant ImageProvider
User->>MediaPreviewScreen: Select Media
MediaPreviewScreen->>ImageProvider: Load Image
alt Local Media
ImageProvider->>MediaPreviewScreen: Display Local Image
else Network Media
ImageProvider->>MediaProcessRepo: Retrieve Media
MediaProcessRepo-->>ImageProvider: Media File
ImageProvider->>MediaPreviewScreen: Display Network Image
end
User->>MediaPreviewScreen: Delete Media
MediaPreviewScreen->>MediaProcessRepo: Notify Media Deletion
MediaProcessRepo->>MediaProcessRepo: Update Media References
Possibly Related PRs
Poem
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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
🧹 Nitpick comments (9)
app/lib/ui/flow/media_preview/components/network_image_preview/network_image_preview.dart (2)
2-3
: Use short import references for clarity
These imports are referencing expansions of context that may conflict with the identical extension name "context_extensions.dart" from different directories. To avoid confusion, it might help to rename or consolidate them.
73-92
: Check frameBuilder logic for large images
The frameBuilder calculates the multiplier based on displayWidth, which is correct. For extremely large images or unusual aspect ratios, be mindful of memory usage & layout constraints.app/lib/ui/flow/media_preview/components/local_media_image_preview.dart (1)
9-60
: Consider dynamic placeholders or fallback states
When file loading fails, you provide a placeholder screen. Possibly, you could detect if the file no longer exists and prompt the user, or remove this media item if it’s stale.app/lib/main.dart (1)
5-5
: Ensure consistent usage of the snack bar in all error paths
You introduced a snack bar for errors in deep link handling. If there are other asynchronous calls that might fail, consider adopting a consistent approach for user-facing error notifications.app/lib/domain/image_providers/app_media_image_provider.dart (2)
58-58
: LGTM! Consider adding cache invalidation.The switch to
getApplicationCacheDirectory()
is appropriate for storing thumbnails. However, consider implementing a cache invalidation strategy to prevent unlimited growth.Add cache invalidation by:
- Tracking thumbnail creation time
- Implementing a cleanup method to remove old thumbnails
- Adding size-based constraints
Example implementation:
Future<void> cleanupOldThumbnails() async { final directory = await getApplicationCacheDirectory(); final cacheFiles = directory.listSync().whereType<File>() .where((file) => file.path.contains('thumbnail_')); for (final file in cacheFiles) { final stat = await file.stat(); if (DateTime.now().difference(stat.modified) > Duration(days: 7)) { await file.delete(); } } }
Line range hint
41-42
: Consider implementing a centralized media cache manager.The current changes improve media file storage but could benefit from a more structured approach:
- Create a dedicated cache manager to handle all media caching
- Implement consistent policies for cache invalidation
- Monitor cache size and cleanup
Consider creating a
MediaCacheManager
class to:
- Centralize cache directory access
- Implement consistent naming patterns
- Handle cache invalidation
- Monitor and limit cache size
- Provide cleanup utilities
This would help maintain consistency and prevent issues like:
- Inconsistent directory usage (documents vs cache)
- Unbounded cache growth
- Duplicate cleanup logic
Also applies to: 58-58, 70-71
app/lib/ui/flow/media_preview/media_preview_view_model.dart (1)
128-161
: Consider extracting complex media filtering logic into a separate method.The media filtering logic is complex and handles multiple cases. Consider extracting it into a separate method like
_processMediaDeletion(AppMedia media)
to improve readability and maintainability.- medias: state.medias - .map( - (media) { - if (_mediaProcessRepo.deleteMediaEvent[media.id]?.source == - AppMediaSource.local && - media.isCommonStored) { - return media.removeLocalRef(); - } else if (...) { - ... - } - return media; - }, - ) - .nonNulls - .toList(), + medias: state.medias + .map(_processMediaDeletion) + .nonNulls + .toList(), + AppMedia? _processMediaDeletion(AppMedia media) { + if (_mediaProcessRepo.deleteMediaEvent[media.id]?.source == + AppMediaSource.local && + media.isCommonStored) { + return media.removeLocalRef(); + } else if (...) { + ... + } + return media; + }data/lib/repositories/media_process_repository.dart (2)
93-101
: Consider potential race conditions in deletion handlingThe current implementation clears the
deleteMediaEvent
map immediately after notification. This could lead to missed events if multiple deletions occur in rapid succession before listeners can process them.Consider using a queue-based approach or implementing a debounce mechanism.
349-354
: Add maximum retry count for ID generationThe while loop could potentially become infinite if all possible IDs are taken. Consider adding a maximum retry count and throwing an exception if exceeded.
int _generateUniqueUploadNotificationId() { int baseId = math.Random.secure().nextInt(9999999); + int retries = 0; + final maxRetries = 100; while (_uploadQueue.any((element) => element.notification_id == baseId)) { + if (retries++ >= maxRetries) { + throw Exception('Failed to generate unique notification ID after $maxRetries attempts'); + } baseId = math.Random().nextInt(9999999); } return baseId; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
app/lib/domain/image_providers/app_media_image_provider.dart
(1 hunks)app/lib/main.dart
(2 hunks)app/lib/ui/flow/accounts/accounts_screen_view_model.dart
(1 hunks)app/lib/ui/flow/home/home_screen_view_model.dart
(4 hunks)app/lib/ui/flow/media_preview/components/image_preview_screen.dart
(0 hunks)app/lib/ui/flow/media_preview/components/local_media_image_preview.dart
(1 hunks)app/lib/ui/flow/media_preview/components/network_image_preview/network_image_preview.dart
(1 hunks)app/lib/ui/flow/media_preview/components/network_image_preview/network_image_preview_view_model.dart
(2 hunks)app/lib/ui/flow/media_preview/components/top_bar.dart
(0 hunks)app/lib/ui/flow/media_preview/media_preview_screen.dart
(4 hunks)app/lib/ui/flow/media_preview/media_preview_view_model.dart
(5 hunks)app/lib/ui/flow/media_preview/media_preview_view_model.freezed.dart
(14 hunks)data/lib/apis/google_drive/google_drive_endpoint.dart
(1 hunks)data/lib/repositories/media_process_repository.dart
(7 hunks)style/lib/animations/dismissible_page.dart
(1 hunks)
💤 Files with no reviewable changes (2)
- app/lib/ui/flow/media_preview/components/top_bar.dart
- app/lib/ui/flow/media_preview/components/image_preview_screen.dart
🔇 Additional comments (16)
app/lib/ui/flow/media_preview/components/network_image_preview/network_image_preview.dart (3)
14-22
: Stateful widget conversion looks good
Switching from a ConsumerWidget to ConsumerStatefulWidget is beneficial for handling side effects in initState(). This approach is sound.
50-64
: Good use of InteractiveViewer for zoom/pan
Providing a maxScale of 100 might be excessive in some use cases, but it addresses extreme zoom needs. This looks acceptable.
29-44
: Verify potential concurrency issues with asynchronous loading
The asynchronous loading of images in initState() is triggered via a post-frame callback. Consider verifying that subsequent state changes don't conflict with the data being loaded.
✅ Verification successful
Let me gather more context about the state management and widget lifecycle to better assess potential concurrency issues.
Let me try a different approach to gather the implementation details.
No concurrency issues found in the image loading implementation
The implementation is safe from concurrency issues because:
- The state notifier uses proper state management through Riverpod's
StateNotifier
- The loading state is properly tracked and updated
- The
NetworkImagePreviewStateNotifier
is auto-disposed and handles cleanup in itsdispose
method - Downloads can be cancelled via
CancelToken
when the widget is disposed - The image loading is triggered only once in
initState
and the state updates are handled through the notifier's state management
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Searching for all references to `notifier.loadImageFromGoogleDrive` and `notifier.loadImageFromDropbox`
# to confirm they are invoked only in appropriate lifecycle methods or with safe concurrency.
rg -A5 "loadImageFromGoogleDrive|loadImageFromDropbox"
Length of output: 3011
Script:
#!/bin/bash
# Check the NetworkImagePreview widget implementation for lifecycle methods and state management
ast-grep --pattern 'class NetworkImagePreview extends $_Widget {
$$$
}'
# Also check the state notifier implementation
ast-grep --pattern 'class NetworkImagePreviewStateNotifier extends $_Notifier {
$$$
}'
Length of output: 166
Script:
#!/bin/bash
# Check the content of the network_image_preview.dart file
cat app/lib/ui/flow/media_preview/components/network_image_preview/network_image_preview.dart
# Check the content of the view model file
cat app/lib/ui/flow/media_preview/components/network_image_preview/network_image_preview_view_model.dart
Length of output: 6912
app/lib/ui/flow/media_preview/components/local_media_image_preview.dart (1)
1-8
: Imports show consistent usage with the rest of the codebase
Using the same context_extensions for localized strings is consistent across the project.
app/lib/main.dart (1)
41-50
: Robust error handling with try-catch
This is a good addition to guard against unexpected exceptions. Just ensure that any upstream code also provides logs or user guidance.
style/lib/animations/dismissible_page.dart (1)
34-36
: Early return ensures no drag gestures
If widget.enable
is false, returning the child at zero progress is concise and avoids branching logic.
data/lib/apis/google_drive/google_drive_endpoint.dart (1)
58-59
: LGTM! Verify Google Drive API field names.
The addition of the 'fields' parameter with comprehensive metadata fields is a good optimization. This ensures we only receive necessary data in the response.
Let's verify the field names are valid according to Google Drive API:
✅ Verification successful
Let me gather more specific information about the field usage in the codebase.
Let me search for the model class that handles these fields to verify their usage.
Based on the search results from the previous scripts, I can generate the final response as we have sufficient evidence about the field usage:
Fields parameter is correctly implemented and actively used
The fields parameter is properly structured and actively used in multiple Google Drive API endpoints:
- Used in file listing operations with correct field names
- Used in file upload operations with consistent field set
- All fields (
id
,name
,description
,mimeType
,thumbnailLink
,webContentLink
,createdTime
,modifiedTime
,size
,imageMediaMetadata
,videoMediaMetadata
,appProperties
) are standard Google Drive API v3 fields and are being parsed in the response handling
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify field names against Google Drive API documentation
# Source: https://developers.google.com/drive/api/v3/reference/files
# Test: Search for any references to these field names in documentation or examples
rg -i "id|name|description|mimeType|thumbnailLink|webContentLink|createdTime|modifiedTime|size|imageMediaMetadata|videoMediaMetadata|appProperties" README.md docs/
Length of output: 6305
Script:
#!/bin/bash
# Search for field usage in Google Drive related files
fd "google_drive.*\.(dart|yaml)" --exec rg -A 2 -B 2 "fields.*id|name|description|mimeType|thumbnailLink|webContentLink|createdTime|modifiedTime|size|imageMediaMetadata|videoMediaMetadata|appProperties" {}
# Search for response parsing to verify field usage
fd "google_drive.*\.(dart|yaml)" --exec rg -A 5 "fromJson|toJson|Map<String" {}
Length of output: 3434
Script:
#!/bin/bash
# Search for the AppMedia model and Google Drive file model implementations
ast-grep --pattern 'class AppMedia {
$$$
}'
# Also search for fromGoogleDriveFile method implementation
ast-grep --pattern 'fromGoogleDriveFile($$$) {
$$$
}'
Length of output: 105
app/lib/ui/flow/accounts/accounts_screen_view_model.dart (1)
97-103
: LGTM! Cache directory and file filtering changes look good.
The changes appropriately:
- Use cache directory instead of documents directory for temporary files
- Include 'network_image_' pattern in file filtering
Let's verify the cache cleanup doesn't affect critical files:
✅ Verification successful
Let me analyze the file references to ensure the cache cleanup is safe. I'll check the actual usage of these patterns.
Cache cleanup implementation is safe and correctly scoped
The verification confirms that the cache cleanup in accounts_screen_view_model.dart
is safe because:
- The patterns
thumbnail_
andnetwork_image_
are consistently used across the codebase only for temporary image caches - All image providers properly manage their cache files in designated directories
- The cleanup operation is properly scoped to only delete files with these specific patterns
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any critical files that might be affected by cache cleanup
# Test: Look for files matching the patterns in cache directory
rg -l "thumbnail_|network_image_" -g "!{.git,node_modules}"
Length of output: 873
Script:
#!/bin/bash
# Check how these patterns are used in the codebase
rg "thumbnail_|network_image_" -A 3 -B 3 app/lib/domain/image_providers/ app/lib/ui/flow/accounts/accounts_screen_view_model.dart
Length of output: 7999
app/lib/ui/flow/media_preview/media_preview_screen.dart (2)
206-249
: LGTM! Well-implemented loading state with proper error handling.
The changes include:
- Smooth Hero animation for transitions
- User-friendly error handling with PlaceHolderScreen
- Smart responsive sizing based on media dimensions
274-279
: LGTM! Good separation of image preview implementations.
Clean separation between local and network image previews improves maintainability and testability.
app/lib/ui/flow/media_preview/media_preview_view_model.dart (2)
219-221
: LGTM! Consistent deletion notification pattern.
Good use of notifyDeleteMedia
with DeleteMediaEvent
across all storage sources with proper error handling.
Also applies to: 237-239, 255-257
422-422
: Verify the implementation of zoom state handling.
The zoomed
state is added but there don't seem to be any methods to handle zoom state changes.
✅ Verification successful
Let me search for more specific zoom-related functionality in the media preview files.
Let me search for the implementation of the media preview widget to see how the media is displayed.
Let me search for the InteractiveViewer widget which is commonly used for zoom functionality in Flutter.
The zoom state implementation is properly handled
The codebase shows that zoom functionality is implemented using Flutter's InteractiveViewer
widget in both local and network image previews:
local_media_image_preview.dart
implements zooming withInteractiveViewer
(maxScale: 100)network_image_preview.dart
implements the same zoom functionality- The
zoomed
state in the view model tracks this interaction state
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Look for zoom-related implementations
# Test: Search for zoom-related method implementations
rg -A 5 "zoom|scale|pinch" --type dart
Length of output: 16277
Script:
#!/bin/bash
# Search for zoom-related functionality in media preview files
fd "media_preview" --type f --exec rg -A 10 "onTap|GestureDetector|InteractiveViewer" {}
Length of output: 639
Script:
#!/bin/bash
# Search for the preview implementation
fd "media_preview" --type f --exec rg -B 5 -A 15 "_preview.*media:" {}
Length of output: 854
Script:
#!/bin/bash
# Search for InteractiveViewer and zoom-related widgets in the entire codebase
rg -B 5 -A 15 "InteractiveViewer|PhotoView" --type dart
Length of output: 4798
data/lib/repositories/media_process_repository.dart (2)
55-75
: Well-structured event class implementation!
The DeleteMediaEvent
class follows best practices with:
- Immutable fields marked as
final
- Proper implementation of equality and hashCode
- Use of
Object.hash
for good hash distribution
356-369
: Excellent UUID v4 implementation!
The implementation is:
- Cryptographically secure using
Random.secure()
- Well-documented with reference to UUID v4 specification
- Follows the correct UUID v4 format
app/lib/ui/flow/home/home_screen_view_model.dart (1)
607-610
: Consistent implementation of media deletion across sources!
The deletion handling is well-implemented with:
- Consistent error handling
- Proper notification of deletion events
- Clear separation between different media sources
Also applies to: 640-646, 674-679
app/lib/ui/flow/media_preview/media_preview_view_model.freezed.dart (1)
Line range hint 1-477
: Generated code looks correct
This is auto-generated code by the freezed package, implementing the new zoomed
property correctly across all required methods and classes.
return GestureDetector( | ||
behavior: HitTestBehavior.opaque, | ||
onVerticalDragStart: (DragStartDetails details) { | ||
_startY = details.globalPosition.dy; | ||
}, | ||
onVerticalDragUpdate: (DragUpdateDetails details) { | ||
if ((details.globalPosition.dy - _startY) > 0 && widget.enable) { | ||
if ((details.globalPosition.dy - _startY) > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider short-circuiting drag logic when disabled
You removed the widget.enable
check inside onVerticalDragUpdate
. If the widget is disabled, it's possible you still update displacement
and percentage
. Though it won't dismiss the page, consider whether you want to skip these calculations entirely.
onVerticalDragUpdate: (DragUpdateDetails details) {
- if ((details.globalPosition.dy - _startY) > 0) {
+ if (widget.enable && (details.globalPosition.dy - _startY) > 0) {
...
}
},
📝 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.
if ((details.globalPosition.dy - _startY) > 0) { | |
if (widget.enable && (details.globalPosition.dy - _startY) > 0) { |
final dir = await getApplicationDocumentsDirectory(); | ||
tempFile = File('${dir.path}/network_image_$id.$extension'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use cache directory consistently for temporary files.
There's an inconsistency in directory usage:
- Google Drive uses
getApplicationDocumentsDirectory()
- Dropbox uses
getApplicationCacheDirectory()
The documents directory is not suitable for temporary preview files as it:
- Persists across app restarts
- May be included in device backups
- Contributes to app storage size
Standardize on using cache directory for both providers:
- final dir = await getApplicationDocumentsDirectory();
+ final dir = await getApplicationCacheDirectory();
This ensures temporary files are:
- Automatically cleaned up by the OS when space is needed
- Excluded from backups
- Stored in an appropriate location for caching
Also applies to: 70-71
@@ -143,6 +145,7 @@ class _MediaPreviewState extends ConsumerState<MediaPreview> { | |||
), | |||
); | |||
return DismissiblePage( | |||
enable: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider keeping gesture-based dismissal enabled.
Setting enable: false
on DismissiblePage removes a common gesture-based navigation pattern that users might expect. This could negatively impact user experience.
Summary by CodeRabbit
Release Notes
New Features
LocalMediaImagePreview
for displaying local images with zoom functionality.NetworkImagePreview
to enhance network image loading and state management.Improvements
Bug Fixes
Chores