-
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
Implement resumable upload for google drive #117
Conversation
WalkthroughThis pull request introduces comprehensive enhancements to media upload functionality across multiple components of the application. The changes focus on implementing session-based uploads for Google Drive and Dropbox, adding pause and resume capabilities for upload processes, and refining error handling and user feedback mechanisms. The modifications span UI components, data models, services, and localization, providing a more robust and interactive media transfer experience. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI
participant ViewModel
participant Service
participant CloudService
User->>UI: Initiate Upload
UI->>ViewModel: startUploadProcess
ViewModel->>Service: uploadMedia
Service->>CloudService: startUploadSession
CloudService-->>Service: uploadSessionId
Service->>Service: appendUploadSession
Service-->>ViewModel: uploadProgress
ViewModel-->>UI: updateUploadStatus
alt Pause Upload
User->>UI: Pause Upload
UI->>ViewModel: onPauseUploadProcess
ViewModel->>Service: pauseUploadProcess
Service-->>UI: uploadPaused
end
alt Resume Upload
User->>UI: Resume Upload
UI->>ViewModel: onResumeUploadProcess
ViewModel->>Service: resumeUploadProcess
Service->>CloudService: continueUploadSession
Service-->>UI: uploadResumed
end
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: 8
🔭 Outside diff range comments (1)
data/lib/repositories/media_process_repository.dart (1)
Line range hint
721-723
: Fix incorrect method call for updating upload process statusIn the catch block of
_uploadInDropbox
, the methodupdateDownloadProcessStatus
is incorrectly called instead ofupdateUploadProcessStatus
. This needs to be corrected to update the upload queue appropriately upon failure.Apply this diff to correct the method call:
await updateDownloadProcessStatus( + await updateUploadProcessStatus( status: MediaQueueProcessStatus.failed, id: process.id, );
🧹 Nitpick comments (6)
data/lib/models/media_process/media_process.freezed.dart (1)
731-731
: Consider adding documentation for the upload_session_id field.While the toString implementation is correct, it would be helpful to add documentation explaining the purpose and lifecycle of the upload_session_id field in the original class definition.
data/lib/repositories/media_process_repository.dart (1)
454-468
: Prevent multiple resumptions of the same upload processIn
resumeUploadProcess
, ensure that an upload process is not started multiple times ifresumeUploadProcess
is called repeatedly.Add a check to see if the process is already running before initiating the upload.
if (process.status.isUploading) { // The upload is already in progress. return; }data/lib/domain/config.dart (1)
22-28
: LGTM! The configuration values are well-chosen.
uploadRequestByteSize
of 256KB (262144 bytes) is a good balance between network efficiency and memory usage for resumable uploads.processProgressUpdateDuration
of 300ms provides smooth progress updates without overwhelming the UI.Consider making these values configurable through environment variables or build configuration for different environments (development, production) if needed.
app/lib/ui/flow/media_transfer/media_transfer_screen.dart (1)
125-130
: Add error handling for pause/resume actionsWhile the implementation of pause/resume callbacks is correct, consider adding error handling to gracefully handle failures during these operations.
onResumeTap: () { + try { notifier.onResumeUploadProcess(uploadProcesses[index].id); + } catch (e) { + context.showErrorSnackBar(message: 'Failed to resume upload: $e'); + } }, onPausedTap: () { + try { notifier.onPauseUploadProcess(uploadProcesses[index].id); + } catch (e) { + context.showErrorSnackBar(message: 'Failed to pause upload: $e'); + } },data/lib/apis/google_drive/google_drive_endpoint.dart (1)
141-144
: Add validation for required headersConsider adding validation to ensure that required headers (
Content-Type
,Content-Length
,Content-Range
) are not null or empty before making the request.@override Map<String, dynamic> get headers => { + if (content.type.isEmpty) throw ArgumentError('Content-Type cannot be empty'), + if (content.length == null) throw ArgumentError('Content-Length is required'), + if (content.range == null) throw ArgumentError('Content-Range is required for resumable uploads'), 'Content-Type': content.type, 'Content-Length': content.length.toString(), 'Content-Range': content.range, };app/lib/ui/flow/media_transfer/components/transfer_item.dart (1)
141-142
: Consider using localization for the pause message.Other status messages in this method use localization (
context.l10n
), but the pause message is hardcoded. Consider using localization for consistency.- return "Upload paused"; + return context.l10n.upload_status_paused;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
app/lib/ui/flow/media_transfer/components/transfer_item.dart
(4 hunks)app/lib/ui/flow/media_transfer/media_transfer_screen.dart
(1 hunks)app/lib/ui/flow/media_transfer/media_transfer_view_model.dart
(1 hunks)data/.flutter-plugins-dependencies
(1 hunks)data/lib/apis/dropbox/dropbox_content_endpoints.dart
(1 hunks)data/lib/apis/google_drive/google_drive_endpoint.dart
(3 hunks)data/lib/domain/config.dart
(1 hunks)data/lib/models/media_content/media_content.dart
(1 hunks)data/lib/models/media_content/media_content.freezed.dart
(10 hunks)data/lib/models/media_process/media_process.dart
(3 hunks)data/lib/models/media_process/media_process.freezed.dart
(14 hunks)data/lib/models/media_process/media_process.g.dart
(3 hunks)data/lib/repositories/media_process_repository.dart
(12 hunks)data/lib/services/dropbox_services.dart
(5 hunks)data/lib/services/google_drive_service.dart
(6 hunks)
✅ Files skipped from review due to trivial changes (1)
- data/.flutter-plugins-dependencies
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (21)
data/lib/models/media_process/media_process.freezed.dart (2)
417-417
: LGTM! Theupload_session_id
field is properly integrated.The new optional
String?
field is correctly implemented across all necessary locations in the Freezed-generated code, including:
- Field declarations
- Constructor parameters
- copyWith method
- toString, equality, and hashCode implementations
Also applies to: 450-450, 509-512, 682-682, 705-706, 731-731, 746-747, 771-771, 804-804, 827-828
417-417
: Verify session ID persistence across app restarts.Since
upload_session_id
is used for resumable uploads, ensure that it's properly persisted and can survive app restarts to maintain upload progress.Also applies to: 705-706
✅ Verification successful
Upload session ID is properly persisted for resumable uploads
The implementation uses SQLite database storage which ensures upload_session_id persists across app restarts. The session ID is stored in a dedicated column in the upload_queue_table and is properly managed through the updateUploadSessionId method. This allows for reliable resumption of uploads from their last position.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if upload_session_id is being persisted in any storage implementations rg -A 5 "upload_session_id.*persist|persist.*upload_session_id" # Check if there are any database schema or migration files fd -e sql -e dart ".*schema.*|.*migration.*"Length of output: 112
Script:
#!/bin/bash # Find the original MediaProcess class file fd -e dart "media_process" --exclude "*.freezed.dart" --exclude "*.g.dart" # Search for all occurrences of upload_session_id rg -l "upload_session_id" # Look for storage/persistence related files fd -e dart "storage|repository|dao|database"Length of output: 509
Script:
#!/bin/bash # Check the repository implementation cat data/lib/repositories/media_process_repository.dart # Look for any persistence-related code in the model cat data/lib/models/media_process/media_process.dartLength of output: 35603
data/lib/apis/dropbox/dropbox_content_endpoints.dart (3)
164-199
: Well-structured implementation ofDropboxStartUploadEndpoint
The
DropboxStartUploadEndpoint
class is well-implemented, properly setting up the initial upload session with correct headers and data handling.
200-245
: Effective implementation ofDropboxAppendUploadEndpoint
The
DropboxAppendUploadEndpoint
class efficiently handles appending data to an ongoing upload session, with appropriate management of headers and content.
246-312
: Comprehensive implementation ofDropboxFinishUploadEndpoint
Apart from the minor issue with the endpoint path, the
DropboxFinishUploadEndpoint
class is thoroughly implemented, correctly finalizing the upload session with proper handling of commit details and cursor information.data/lib/services/google_drive_service.dart (1)
220-220
: Correct property name for content typeEnsure consistency in property naming. The
AppMediaContent
class usestype
as the property, but if any part of the code still referencescontentType
, it should be updated totype
to prevent errors.Run the following script to verify all usages of
contentType
:✅ Verification successful
Property naming is correct and consistent ✅
The codebase maintains proper separation between HTTP-standard
contentType
in the network layer andtype
in application models. The mapping between these conventions is handled correctly inmedia_content.dart
. No changes needed.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Find all references to 'contentType' in the codebase. # Search for 'contentType' in Dart files rg --glob '*.dart' 'contentType'Length of output: 702
data/lib/models/media_content/media_content.dart (2)
11-12
: LGTM! The changes support resumable uploads.The addition of the
range
property and renaming ofcontentType
totype
align well with implementing resumable uploads. Therange
property will be useful for specifying byte ranges during resumable uploads.
18-18
: Verify type compatibility with Google Drive API.The factory method correctly maps Google Drive's
contentType
to the newtype
property.Let's verify the type compatibility:
✅ Verification successful
Type compatibility verified successfully
The
contentType
from Google Drive API is correctly mapped to thetype
property, as both represent MIME types and are used consistently in Content-Type headers across the API implementations.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for any other usages of media.contentType to ensure consistency rg "media\.contentType" --type dartLength of output: 119
Script:
#!/bin/bash # Find MediaContent class definition and type property ast-grep --pattern 'class MediaContent { $$$ }' # Search for type-related definitions rg "(?i)content.*type" --type dart -A 3 # Look for Google Drive API related files fd "drive.*\.dart$"Length of output: 4871
data/lib/models/media_process/media_process.dart (3)
20-20
: LGTM! The paused status supports resumable uploads.The addition of the
paused
status is essential for implementing resumable uploads.
37-37
: LGTM! The isPaused getter is well-implemented.The getter provides a clean way to check for paused status.
125-125
: Verify session ID persistence.The
upload_session_id
is crucial for resumable uploads. Ensure it's properly persisted and recovered after app restarts.Let's check the persistence implementation:
✅ Verification successful
Upload session ID persistence implementation verified.
The
upload_session_id
is properly persisted in the SQLite database and recovered through JSON serialization, ensuring it survives app restarts for resumable uploads.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for upload_session_id usage in database operations rg "upload_session_id" --type dart -A 5 # Search for session-related operations ast-grep --pattern 'class $_ { $$$ upload_session_id $$$ }'Length of output: 12168
data/lib/models/media_process/media_process.g.dart (2)
56-56
: LGTM: Status enum updated for pause functionalityThe addition of
MediaQueueProcessStatus.paused
status is correctly implemented and aligns with the resumable upload feature.
67-67
: LGTM: Upload session tracking implementedThe
upload_session_id
field is properly integrated into both JSON serialization and deserialization methods, enabling proper tracking of resumable upload sessions.Also applies to: 91-91
data/lib/models/media_content/media_content.freezed.dart (1)
21-22
: LGTM: Media content model enhanced for resumable uploadsThe model has been correctly updated to support resumable uploads:
- Replaced
contentType
with a more specifictype
field- Added optional
range
field for tracking upload progress- All related methods are properly updated
Also applies to: 147-149
data/lib/apis/google_drive/google_drive_endpoint.dart (2)
87-115
: LGTM: Start upload endpoint implementationThe
GoogleDriveStartUploadEndpoint
correctly implements the resumable upload initiation as per Google Drive API specifications.
117-160
: Verify Content-Range header formatThe
GoogleDriveAppendUploadEndpoint
implementation looks good, but ensure that theContent-Range
header follows the format specified by Google Drive API:
- For first chunk:
bytes 0-524287/2000000
- For final chunk:
bytes 524288-2000000/2000000
✅ Verification successful
Content-Range header format is correctly implemented
The Content-Range header follows the required Google Drive API format. The implementation in
google_drive_service.dart
correctly generates the range asbytes start-end/total
with proper zero-based byte ranges.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if Content-Range header format matches Google Drive API specs ast-grep --pattern 'content.range = "bytes $_-$_/$_"'Length of output: 55
Script:
#!/bin/bash # Find AppMediaContent class definition and implementation ast-grep --pattern 'class AppMediaContent { $$$ }' # Also search for any mentions of range property or method rg -A 5 'range' --type dartLength of output: 9948
app/lib/ui/flow/media_transfer/components/transfer_item.dart (3)
14-15
: LGTM! Well-structured implementation of pause/resume callbacks.The addition of
onPausedTap
andonResumeTap
callbacks provides the necessary control for implementing resumable uploads.Also applies to: 22-23
93-101
: LGTM! Clean implementation of the pause button.The pause button is appropriately shown only when the upload is running, using platform-specific icons.
84-92
: LGTM! Well-designed state-specific resume buttons.The implementation thoughtfully uses different icons based on the upload state:
- Play icon for paused uploads
- Refresh icon for terminated/failed uploads
This provides clear visual feedback to users about the available actions.Also applies to: 113-121
data/lib/services/dropbox_services.dart (2)
295-295
: LGTM! Consistent renaming of content type property.The change from
contentType
totype
is applied consistently across allAppMediaContent
instantiations.Also applies to: 348-349, 389-390, 409-410, 539-539, 564-564, 598-598
328-361
: LGTM! Robust implementation of upload session initialization.The implementation includes:
- File existence validation
- Proper error handling
- Clear session ID return for subsequent operations
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: 1
♻️ Duplicate comments (2)
data/lib/services/google_drive_service.dart (1)
256-258
:⚠️ Potential issueAdd null safety checks when parsing headers
The code is vulnerable to null pointer exceptions when accessing the 'location' header and parsing the URI.
Apply this diff to enhance null safety:
- return Uri.parse(res.headers.value('location')!) - .queryParameters['upload_id']!; + final locationHeader = res.headers.value('location'); + if (locationHeader == null) { + throw SomethingWentWrongError( + message: 'Missing Location header in response', + statusCode: res.statusCode, + ); + } + final uploadId = Uri.parse(locationHeader).queryParameters['upload_id']; + if (uploadId == null) { + throw SomethingWentWrongError( + message: 'Missing upload_id in Location header', + statusCode: res.statusCode, + ); + } + return uploadId;data/lib/apis/dropbox/dropbox_content_endpoints.dart (1)
281-281
:⚠️ Potential issueFix missing leading slash in the endpoint path
The path property is missing a leading slash. This could lead to incorrect API endpoint resolution.
Apply this diff to correct the path:
- String get path => '/files/upload_session/finish'; + String get path => '/files/upload_session/finish';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
data/lib/apis/dropbox/dropbox_content_endpoints.dart
(2 hunks)data/lib/repositories/media_process_repository.dart
(21 hunks)data/lib/services/dropbox_services.dart
(5 hunks)data/lib/services/google_drive_service.dart
(7 hunks)
🧰 Additional context used
🪛 GitHub Actions: Analyze
data/lib/repositories/media_process_repository.dart
[info] 705-705: Missing a required trailing comma. Try adding a trailing comma.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (13)
data/lib/services/google_drive_service.dart (3)
95-95
: LGTM! Good separation of concernsThe query modification correctly excludes album files from media listing, maintaining a clean separation between media and album management.
220-220
: LGTM! Consistent parameter namingThe parameter rename from
contentType
totype
is applied consistently across all methods, improving code consistency.Also applies to: 459-459, 485-485, 562-562, 636-636
267-315
: LGTM! Robust implementation of appendUploadSessionThe implementation includes:
- Proper file existence validation
- Correct byte range handling
- Comprehensive error handling including resume status (308)
data/lib/apis/dropbox/dropbox_content_endpoints.dart (3)
150-150
: LGTM! Property name change fromcontentType
totype
The change aligns with the updated
AppMediaContent
model.
164-197
: LGTM! Well-structured upload session initialization endpointThe implementation follows the endpoint pattern consistently and includes all necessary configurations for starting an upload session.
199-242
: LGTM! Well-implemented append operation endpointThe implementation correctly handles session continuation with proper offset tracking and content streaming.
data/lib/services/dropbox_services.dart (4)
295-295
: LGTM! Consistent property name updatesThe changes from
contentType
totype
are applied consistently across all occurrences.Also applies to: 561-561, 586-586, 620-620
328-364
: LGTM! Well-implemented upload session initializationThe method includes proper error handling and file existence validation.
366-403
: LGTM! Well-structured append operationThe method follows consistent error handling patterns and properly manages upload session continuation.
405-450
: LGTM! Well-implemented session finalizationThe method properly handles session completion with appropriate error handling.
data/lib/repositories/media_process_repository.dart (3)
15-15
: LGTM! Consistent integration of UniqueIdGeneratorThe UniqueIdGenerator is properly injected and used consistently across the repository.
Also applies to: 31-31, 81-81, 246-246, 298-298, 388-388
141-141
: LGTM! Database schema update for session managementThe upload_session_id column is properly added to support resumable uploads.
463-490
: LGTM! Well-implemented pause/resume functionalityThe implementation properly handles upload process state transitions between paused and resumed states.
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: 0
♻️ Duplicate comments (3)
data/lib/repositories/media_process_repository.dart (3)
463-473
: 🛠️ Refactor suggestionImplement request cancellation in pauseUploadProcess.
The
pauseUploadProcess
method only updates the status but doesn't cancel ongoing upload requests. This could lead to unnecessary network usage.
626-628
:⚠️ Potential issueFix incorrect method call in error handling.
The error handling block incorrectly calls
updateDownloadProcessStatus
instead ofupdateUploadProcessStatus
.
705-705
:⚠️ Potential issueAdd missing trailing comma.
A trailing comma is required for consistent formatting.
🧰 Tools
🪛 GitHub Actions: Analyze
[info] 705-705: Missing a required trailing comma. Try adding a trailing comma.
🧹 Nitpick comments (3)
data/lib/repositories/media_process_repository.dart (3)
15-15
: LGTM! Consider adding documentation for the new dependency.The addition of
UniqueIdGenerator
as a dependency is a good practice for better testability and maintainability.Consider adding a documentation comment explaining the purpose of
_uniqueIdGenerator
:/// Generator for creating unique identifiers used in media processes final UniqueIdGenerator _uniqueIdGenerator;Also applies to: 81-81, 109-109
141-141
: Consider adding an index for the upload_session_id column.Since
upload_session_id
will be frequently queried during upload operations, adding an index could improve query performance.CREATE INDEX idx_upload_session_id ON ${LocalDatabaseConstants.uploadQueueTable} (upload_session_id);
571-624
: Extract common upload functionality into a shared method.The upload implementations for Google Drive and Dropbox share similar logic for chunking, progress tracking, and notifications. Consider extracting these into a shared method to reduce code duplication.
Future<void> _handleChunkedUpload({ required UploadMediaProcess process, required Future<void> Function(int startByte, int endByte) uploadChunk, required Future<void> Function(String message) showNotification, required AppMedia? response, }) async { int chunk = process.chunk; final total = await File(process.path).length(); while (chunk < total) { if (process.status.isTerminated || process.status.isPaused) { showNotification(process.status.isTerminated ? 'Upload Cancelled' : 'Upload Paused'); break; } final end = (chunk + ApiConfigs.uploadRequestByteSize).clamp(0, total); await uploadChunk(chunk, end); chunk = end; await updateUploadProcessProgress( id: process.id, chunk: chunk, total: total, ); await showNotification( '${chunk.formatBytes} / ${total.formatBytes} - ${(chunk / total * 100).round()}%', ); } if (chunk >= total) { await updateUploadProcessStatus( status: MediaQueueProcessStatus.completed, response: response, id: process.id, ); await showNotification('Upload completed successfully'); await clearUploadProcessResponse(id: process.id); } }Also applies to: 664-749
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
data/lib/repositories/media_process_repository.dart
(21 hunks)
🧰 Additional context used
🪛 GitHub Actions: Analyze
data/lib/repositories/media_process_repository.dart
[info] 705-705: Missing a required trailing comma. Try adding a trailing comma.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
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: 2
🧹 Nitpick comments (2)
data/lib/repositories/media_process_repository.dart (2)
111-111
: Consider adding error handling for database initialization.The database initialization is called directly in the constructor without any error handling. If the initialization fails, it could leave the repository in an invalid state.
Consider wrapping the initialization in a try-catch block and handling potential database errors:
- _initializeLocalDatabase(); + try { + await _initializeLocalDatabase(); + } catch (e) { + debugPrint('Failed to initialize database: $e'); + // Consider throwing a custom exception or handling the error appropriately + }
557-624
: Add cleanup for abandoned upload sessions.When an upload is terminated or fails, the upload session is not cleaned up on Google Drive's side. This could lead to orphaned upload sessions.
Consider adding cleanup logic:
if (process.status.isTerminated) { + try { + await _googleDriveService.deleteUploadSession(uploadSessionId); + } catch (e) { + debugPrint('Failed to cleanup upload session: $e'); + } showNotification('Upload to Google Drive Cancelled'); break; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
data/lib/repositories/media_process_repository.dart
(21 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (2)
data/lib/repositories/media_process_repository.dart (2)
591-596
: Implement cancellation support during upload.The
appendUploadSession
call should support cancellation to prevent unnecessary data transfer when the upload is paused or terminated.
Line range hint
124-164
: Consider implementing database migration strategy.The database schema has been updated with a new column
upload_session_id
, but there's no migration strategy for existing installations. This could cause issues for users upgrading from a previous version.Consider implementing a version-based migration strategy. Also, verify if there are any existing installations that need migration:
# Conflicts: # data/lib/repositories/media_process_repository.dart
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
♻️ Duplicate comments (4)
data/lib/repositories/media_process_repository.dart (4)
477-491
: 🛠️ Refactor suggestionImprove resumeUploadProcess implementation.
The method has several potential issues:
- No status update before resuming the upload
- No validation of current status
- Potential race condition when accessing the process
Consider this improved implementation:
Future<void> resumeUploadProcess(String id) async { + bool updated = false; + await database.transaction((txn) async { + final result = await txn.rawUpdate( + "UPDATE ${LocalDatabaseConstants.uploadQueueTable} SET status = ? WHERE id = ? AND status = ?", + [MediaQueueProcessStatus.waiting.value, id, MediaQueueProcessStatus.paused.value], + ); + updated = result > 0; + }); + + if (!updated) return; + final process = _uploadQueue.firstWhere( (element) => element.id == id, ); if (process.provider == MediaProvider.googleDrive) { _uploadInGoogleDrive(process); } else if (process.provider == MediaProvider.dropbox) { _uploadInDropbox(process); } else { updateUploadProcessStatus( status: MediaQueueProcessStatus.failed, id: process.id, ); } }
592-597
: 🛠️ Refactor suggestionImplement cancellation support during upload.
The
appendUploadSession
call should support cancellation to properly handle paused or terminated uploads.Add a CancelToken to cancel ongoing requests when the upload is paused or terminated:
+ final cancelToken = CancelToken(); final res = await _googleDriveService.appendUploadSession( startByte: chunk, endByte: end, uploadId: uploadSessionId, path: process.path, + cancelToken: cancelToken, );And cancel it when needed:
if (process.status.isTerminated) { + cancelToken.cancel(); showNotification('Upload to Google Drive Cancelled'); break; } else if (process.status.isPaused) { + cancelToken.cancel(); showNotification('Upload to Google Drive Paused'); break; }
734-743
: 🛠️ Refactor suggestionAdd retry limit to prevent infinite loop.
The upload session restart logic could potentially loop indefinitely if there are persistent failures.
Add a retry limit and exponential backoff:
if (chunk != 0) { + static const maxRetries = 3; + int retryCount = 0; + while (retryCount < maxRetries) { await updateUploadProcessProgress( id: process.id, chunk: 0, total: total, ); process = _uploadQueue.firstWhere((e) => e.id == process.id); - _uploadInDropbox(process); - return; + try { + await _uploadInDropbox(process); + return; + } catch (e) { + retryCount++; + if (retryCount >= maxRetries) throw e; + await Future.delayed(Duration(seconds: math.pow(2, retryCount).toInt())); + } + } }
637-639
:⚠️ Potential issueFix incorrect method call for updating upload process status.
The catch block incorrectly calls
updateDownloadProcessStatus
instead ofupdateUploadProcessStatus
.Apply this fix:
- await updateDownloadProcessStatus( + await updateUploadProcessStatus( status: MediaQueueProcessStatus.failed, id: process.id, );
🧹 Nitpick comments (4)
app/lib/ui/flow/media_transfer/components/transfer_item.dart (3)
14-23
: Maintain consistent naming convention for callback functionsThe callback naming is inconsistent:
onPausedTap
vsonResumeTap
. One uses past tense while the other doesn't.Consider using consistent naming:
- final void Function() onPausedTap; + final void Function() onPauseTap; final void Function() onResumeTap;
84-92
: Maintain consistent icon usage for resume actionThe resume action uses different icons depending on the state:
- Play icon (
CupertinoIcons.play
) for paused state- Refresh icon (
CupertinoIcons.refresh
) for failed stateThis might confuse users as both actions resume the upload.
Consider using the same icon for consistency:
// For failed state - CupertinoIcons.refresh, + CupertinoIcons.play,Also applies to: 113-121
88-90
: Maintain consistent color scheme for action buttonsThe resume action uses different colors depending on the state:
textPrimary
for paused statetextSecondary
for failed stateConsider using the same color for consistency:
// For failed state - color: context.colorScheme.textSecondary, + color: context.colorScheme.textPrimary,Also applies to: 117-119
data/lib/repositories/media_process_repository.dart (1)
417-426
: Add validation for uploadSessionId parameter.The
updateUploadSessionId
method doesn't validate theuploadSessionId
parameter. A null or empty session ID could cause issues.Consider adding parameter validation:
Future<void> updateUploadSessionId({ required String id, required String uploadSessionId, }) async { + if (uploadSessionId.isEmpty) { + throw ArgumentError.value(uploadSessionId, 'uploadSessionId', 'Upload session ID cannot be empty'); + } + await database.rawUpdate( "UPDATE ${LocalDatabaseConstants.uploadQueueTable} SET upload_session_id = ? WHERE id = ?", [uploadSessionId, id], ); await updateQueue(database); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/ios_deploy.yml
(1 hunks)app/lib/ui/flow/media_transfer/components/transfer_item.dart
(4 hunks)data/lib/domain/config.dart
(1 hunks)data/lib/repositories/media_process_repository.dart
(21 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- data/lib/domain/config.dart
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: ios_deploy
- GitHub Check: build
🔇 Additional comments (1)
app/lib/ui/flow/media_transfer/components/transfer_item.dart (1)
Line range hint
84-121
: Well-implemented pause/resume functionalityThe implementation provides clear visual feedback with appropriate controls for different upload states. The conditional rendering ensures users see relevant actions based on the current state of the upload.
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: 0
🧹 Nitpick comments (1)
data/lib/errors/app_error.dart (1)
25-28
: Avoid hardcoding API domains.The Google Drive API domain is hardcoded. Consider moving it to a constant or configuration file for better maintainability.
+ // At the top of the file + const googleApisHost = 'www.googleapis.com'; - if (error.response?.statusCode == 403 && - error.requestOptions.uri.host == 'www.googleapis.com') { + if (error.response?.statusCode == 403 && + error.requestOptions.uri.host == googleApisHost) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
app/assets/locales/app_en.arb
(1 hunks)app/lib/domain/extensions/app_error_extensions.dart
(1 hunks)data/lib/errors/app_error.dart
(3 hunks)data/lib/errors/l10n_error_codes.dart
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- data/lib/errors/l10n_error_codes.dart
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ios_deploy
- GitHub Check: build
- GitHub Check: analyze
🔇 Additional comments (4)
app/lib/domain/extensions/app_error_extensions.dart (1)
24-25
: LGTM! Error handling properly integrated.The new error case for
NoGoogleDriveAccessError
is well-integrated with the existing error handling pattern and localization system.data/lib/errors/app_error.dart (2)
80-87
: LGTM! Well-structured error class.The
NoGoogleDriveAccessError
class is well-implemented with a clear error message and proper integration with the localization system.
21-22
: Consider more specific PlatformException handling.While consolidating network error handling is good, the current implementation might be too broad. Consider checking additional properties of PlatformException to ensure it's specifically a network error.
Run this script to analyze other potential PlatformException codes in the codebase:
app/assets/locales/app_en.arb (1)
43-43
: LGTM! Clear and actionable error message.The localization message is well-written, clearly explains the issue, and provides actionable steps for users to resolve it.
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: 0
🧹 Nitpick comments (4)
app/lib/ui/flow/accounts/accounts_screen.dart (2)
144-158
: Add error handling and loading state for sign-out operation.While the async implementation and mounted check are good, consider these improvements:
- Add error handling in case sign-out fails
- Show loading state during the sign-out process
Consider this implementation:
onPressed: () async { + try { + notifier.setLoading(true); await notifier.signOutWithGoogle(); if (context.mounted) { showSnackBar( context: context, text: context.l10n.successfully_sign_out_from_google_drive, icon: SvgPicture.asset( Assets.images.icGoogleDrive, height: 22, width: 22, ), ); } + } catch (e) { + if (context.mounted) { + showErrorSnackBar(context: context, error: e.toString()); + } + } finally { + if (context.mounted) { + notifier.setLoading(false); + } + } },
233-246
: Extract common sign-out logic to reduce duplication.The sign-out implementation is duplicated between Google Drive and Dropbox. Consider extracting this into a reusable method.
Here's a suggested implementation:
+ Future<void> _handleSignOut({ + required BuildContext context, + required Future<void> Function() signOutAction, + required String successMessage, + required String iconAsset, + }) async { + try { + notifier.setLoading(true); + await signOutAction(); + if (context.mounted) { + showSnackBar( + context: context, + text: successMessage, + icon: SvgPicture.asset( + iconAsset, + height: 22, + width: 22, + ), + ); + } + } catch (e) { + if (context.mounted) { + showErrorSnackBar(context: context, error: e.toString()); + } + } finally { + if (context.mounted) { + notifier.setLoading(false); + } + } + } // Usage in Google Drive sign-out onPressed: () => _handleSignOut( context: context, signOutAction: notifier.signOutWithGoogle, successMessage: context.l10n.successfully_sign_out_from_google_drive, iconAsset: Assets.images.icGoogleDrive, ), // Usage in Dropbox sign-out onPressed: () => _handleSignOut( context: context, signOutAction: notifier.signOutWithDropbox, successMessage: context.l10n.successfully_sign_out_from_dropbox, iconAsset: Assets.images.icDropbox, ),app/lib/ui/flow/media_transfer/components/transfer_item.dart (2)
14-15
: Maintain consistent naming convention for callback parameters.The callback parameter names have inconsistent tense usage:
onPausedTap
uses past tenseonResumeTap
uses base formApply this diff to maintain consistency:
- final void Function() onPausedTap; + final void Function() onPauseTap; final void Function() onResumeTap; const UploadProcessItem({ super.key, required this.process, required this.onCancelTap, required this.onRemoveTap, - required this.onPausedTap, + required this.onPauseTap, required this.onResumeTap, });Also applies to: 22-23
Line range hint
153-251
: Consider adding pause/resume functionality to downloads for consistency.While uploads now support pause/resume functionality, downloads don't have similar controls. This might create an inconsistent user experience, as users would expect similar controls for both operations.
Consider extending the pause/resume functionality to the
DownloadProcessItem
class to maintain feature parity between upload and download operations.Would you like me to help implement the pause/resume functionality for downloads?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
app/assets/locales/app_en.arb
(2 hunks)app/lib/ui/flow/accounts/accounts_screen.dart
(2 hunks)app/lib/ui/flow/media_transfer/components/transfer_item.dart
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/assets/locales/app_en.arb
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (2)
app/lib/ui/flow/media_transfer/components/transfer_item.dart (2)
84-92
: Well-implemented UI controls for pause/resume functionality!The implementation shows good attention to detail:
- Appropriate button states for running, paused, and failed states
- Consistent styling with existing UI elements
- Logical button ordering and visibility conditions
Also applies to: 93-101, 102-104, 113-121
141-142
: Good job implementing the paused state message with localization!The implementation properly follows the existing pattern and addresses the previous review comment about using localization for consistency.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores