Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion app/lib/ui/flow/media_transfer/components/transfer_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ class UploadProcessItem extends StatelessWidget {
final UploadMediaProcess process;
final void Function() onCancelTap;
final void Function() onRemoveTap;
final void Function() onPausedTap;
final void Function() onResumeTap;

const UploadProcessItem({
super.key,
required this.process,
required this.onCancelTap,
required this.onRemoveTap,
required this.onPausedTap,
required this.onResumeTap,
});

@override
Expand Down Expand Up @@ -77,7 +81,27 @@ class UploadProcessItem extends StatelessWidget {
],
),
),
if (process.status.isRunning || process.status.isWaiting)
if (process.status.isPaused)
ActionButton(
onPressed: onResumeTap,
icon: Icon(
CupertinoIcons.play,
color: context.colorScheme.textPrimary,
size: 20,
),
),
if (process.status.isRunning)
ActionButton(
onPressed: onPausedTap,
icon: Icon(
CupertinoIcons.pause,
color: context.colorScheme.textPrimary,
size: 20,
),
),
if (process.status.isRunning ||
process.status.isWaiting ||
process.status.isPaused)
ActionButton(
onPressed: onCancelTap,
icon: Icon(
Expand All @@ -86,6 +110,15 @@ class UploadProcessItem extends StatelessWidget {
size: 20,
),
),
if (process.status.isTerminated || process.status.isFailed)
ActionButton(
onPressed: onResumeTap,
icon: Icon(
CupertinoIcons.refresh,
color: context.colorScheme.textSecondary,
size: 20,
),
),
if (process.status.isTerminated ||
process.status.isFailed ||
process.status.isCompleted)
Expand All @@ -105,6 +138,8 @@ class UploadProcessItem extends StatelessWidget {
String _getUploadMessage(BuildContext context) {
if (process.status.isWaiting) {
return context.l10n.upload_status_waiting;
} else if (process.status.isPaused) {
return "Upload paused";
} else if (process.status.isFailed) {
return context.l10n.upload_status_failed;
} else if (process.status.isCompleted) {
Expand Down
6 changes: 6 additions & 0 deletions app/lib/ui/flow/media_transfer/media_transfer_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ class _MediaTransferScreenState extends ConsumerState<MediaTransferScreen> {
itemBuilder: (context, index) => UploadProcessItem(
key: ValueKey(uploadProcesses[index].id),
process: uploadProcesses[index],
onResumeTap: () {
notifier.onResumeUploadProcess(uploadProcesses[index].id);
},
onPausedTap: () {
notifier.onPauseUploadProcess(uploadProcesses[index].id);
},
onRemoveTap: () {
notifier.onRemoveUploadProcess(uploadProcesses[index].id);
},
Expand Down
8 changes: 8 additions & 0 deletions app/lib/ui/flow/media_transfer/media_transfer_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class MediaTransferStateNotifier extends StateNotifier<MediaTransferState> {
_mediaProcessRepo.removeItemFromUploadQueue(id);
}

void onPauseUploadProcess(String id) {
_mediaProcessRepo.pauseUploadProcess(id);
}

void onResumeUploadProcess(String id) {
_mediaProcessRepo.resumeUploadProcess(id);
}

void onRemoveDownloadProcess(String id) {
_mediaProcessRepo.removeItemFromDownloadQueue(id);
}
Expand Down
2 changes: 1 addition & 1 deletion data/.flutter-plugins-dependencies

Large diffs are not rendered by default.

161 changes: 160 additions & 1 deletion data/lib/apis/dropbox/dropbox_content_endpoints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class DropboxUploadEndpoint extends Endpoint {
}
],
}),
'Content-Type': content.contentType,
'Content-Type': content.type,
'Content-Length': content.length,
};

Expand All @@ -161,6 +161,165 @@ class DropboxUploadEndpoint extends Endpoint {
void Function(int p1, int p2)? get onSendProgress => onProgress;
}

class DropboxStartUploadEndpoint extends Endpoint {
final AppMediaContent content;
final void Function(int chunk, int length)? onProgress;
final CancelToken? cancellationToken;

const DropboxStartUploadEndpoint({
this.cancellationToken,
this.onProgress,
required this.content,
});

@override
String get baseUrl => BaseURL.dropboxContentV2;

@override
HttpMethod get method => HttpMethod.post;

@override
String get path => '/files/upload_session/start';

@override
Map<String, dynamic> get headers => {
'Content-Type': content.type,
};

@override
Object? get data => content.stream;

@override
CancelToken? get cancelToken => cancellationToken;

@override
void Function(int p1, int p2)? get onSendProgress => onProgress;
}

class DropboxAppendUploadEndpoint extends Endpoint {
final String sessionId;
final int offset;
final AppMediaContent content;
final void Function(int chunk, int length)? onProgress;
final CancelToken? cancellationToken;

const DropboxAppendUploadEndpoint({
required this.sessionId,
required this.offset,
this.cancellationToken,
this.onProgress,
required this.content,
});

@override
String get baseUrl => BaseURL.dropboxContentV2;

@override
HttpMethod get method => HttpMethod.post;

@override
String get path => '/files/upload_session/append_v2';

@override
Map<String, dynamic> get headers => {
'Dropbox-API-Arg': jsonEncode({
'cursor': {
'session_id': sessionId,
'offset': offset,
},
}),
'Content-Type': content.type,
};

@override
Object? get data => content.stream;

@override
CancelToken? get cancelToken => cancellationToken;

@override
void Function(int p1, int p2)? get onSendProgress => onProgress;
}

class DropboxFinishUploadEndpoint extends Endpoint {
final String? appPropertyTemplateId;
final String filePath;
final String? localRefId;
final String mode;
final bool autoRename;
final bool mute;
final bool strictConflict;

final String sessionId;
final int offset;
final AppMediaContent content;
final void Function(int chunk, int length)? onProgress;
final CancelToken? cancellationToken;

const DropboxFinishUploadEndpoint({
this.appPropertyTemplateId,
required this.filePath,
this.mode = 'add',
this.autoRename = true,
this.mute = false,
this.localRefId,
this.strictConflict = false,
this.cancellationToken,
this.onProgress,
required this.content,
required this.sessionId,
required this.offset,
});

@override
String get baseUrl => BaseURL.dropboxContentV2;

@override
HttpMethod get method => HttpMethod.post;

@override
String get path => '/files/upload_session/finish';

@override
Map<String, dynamic> get headers => {
'Dropbox-API-Arg': jsonEncode({
"commit": {
"autorename": autoRename,
"mode": mode,
"mute": mute,
"path": filePath,
"strict_conflict": strictConflict,
if (appPropertyTemplateId != null && localRefId != null)
'property_groups': [
{
"fields": [
{
"name": ProviderConstants.localRefIdKey,
"value": localRefId ?? '',
},
],
"template_id": appPropertyTemplateId,
}
],
},
"cursor": {
"offset": offset,
"session_id": sessionId,
},
}),
'Content-Type': content.type,
};

@override
Object? get data => content.stream;

@override
CancelToken? get cancelToken => cancellationToken;

@override
void Function(int p1, int p2)? get onSendProgress => onProgress;
}

class DropboxDownloadEndpoint extends DownloadEndpoint {
final String filePath;
final String? storagePath;
Expand Down
79 changes: 77 additions & 2 deletions data/lib/apis/google_drive/google_drive_endpoint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class GoogleDriveUploadEndpoint extends Endpoint {

@override
Map<String, dynamic> get headers => {
'Content-Type': content.contentType,
'Content-Type': content.type,
'Content-Length': content.length.toString(),
};

Expand Down Expand Up @@ -84,6 +84,81 @@ class GoogleDriveUploadEndpoint extends Endpoint {
void Function(int p1, int p2)? get onSendProgress => onProgress;
}

class GoogleDriveStartUploadEndpoint extends Endpoint {
final drive.File request;
final CancelToken? cancellationToken;

const GoogleDriveStartUploadEndpoint({
required this.request,
this.cancellationToken,
});

@override
String get baseUrl => BaseURL.googleDriveUploadV3;

@override
CancelToken? get cancelToken => cancellationToken;

@override
HttpMethod get method => HttpMethod.post;

@override
Object? get data => request.toJson();

@override
String get path => '/files';

@override
Map<String, dynamic>? get queryParameters => {
'uploadType': 'resumable',
};
}

class GoogleDriveAppendUploadEndpoint extends Endpoint {
final String uploadId;
final AppMediaContent content;
final CancelToken? cancellationToken;
final void Function(int chunk, int length)? onProgress;

const GoogleDriveAppendUploadEndpoint({
required this.uploadId,
required this.content,
this.cancellationToken,
this.onProgress,
});

@override
String get baseUrl => BaseURL.googleDriveUploadV3;

@override
CancelToken? get cancelToken => cancellationToken;

@override
HttpMethod get method => HttpMethod.put;

@override
Map<String, dynamic> get headers => {
'Content-Type': content.type,
'Content-Length': content.length.toString(),
'Content-Range': content.range,
};

@override
Object? get data => content.stream;

@override
String get path => '/files';

@override
Map<String, dynamic>? get queryParameters => {
'upload_id': uploadId,
'uploadType': 'resumable',
};

@override
void Function(int p1, int p2)? get onSendProgress => onProgress;
}

class GoogleDriveContentUpdateEndpoint extends Endpoint {
final AppMediaContent content;
final String id;
Expand All @@ -108,7 +183,7 @@ class GoogleDriveContentUpdateEndpoint extends Endpoint {

@override
Map<String, dynamic> get headers => {
'Content-Type': content.contentType,
'Content-Type': content.type,
'Content-Length': content.length.toString(),
};

Expand Down
8 changes: 8 additions & 0 deletions data/lib/domain/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ class LocalDatabaseConstants {
class FeatureFlag {
static final googleDriveSupport = true;
}

class ApiConfigs {
/// The size of the byte to be uploaded from the server in one request.
static final uploadRequestByteSize = 262144;

/// The duration to wait before updating the progress of the process.
static final processProgressUpdateDuration = Duration(milliseconds: 300);
}
Loading
Loading