From abd3446c9708ba0e9fa09c770d06e6ce39d8b5f0 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 11:36:32 -0300 Subject: [PATCH 1/9] (WIP): add a modal to filter success uploads --- .../enums/conflicting_files_actions.dart | 4 +- lib/blocs/upload/upload_cubit.dart | 54 ++++++- lib/blocs/upload/upload_state.dart | 12 ++ lib/components/upload_form.dart | 139 ++++++++++++------ 4 files changed, 162 insertions(+), 47 deletions(-) diff --git a/lib/blocs/upload/enums/conflicting_files_actions.dart b/lib/blocs/upload/enums/conflicting_files_actions.dart index 422d1c06ad..a8883791bc 100644 --- a/lib/blocs/upload/enums/conflicting_files_actions.dart +++ b/lib/blocs/upload/enums/conflicting_files_actions.dart @@ -3,4 +3,6 @@ /// `Skip` Will ignore the files and don't upload them. /// /// `Replace` will upload the conflicting file and replace the existent. -enum UploadActions { skip, replace } +/// +/// `SkipSuccessfullyUploads` will skip the files that were successfully uploaded. +enum UploadActions { skip, skipSuccessfullyUploads, replace } diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index ba56b66764..c9dcadee08 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -186,6 +186,7 @@ class UploadCubit extends Cubit { /// Map of conflicting file ids keyed by their file names. final Map conflictingFiles = {}; final List conflictingFolders = []; + List failedFiles = []; UploadCubit({ required this.driveId, @@ -301,7 +302,9 @@ class UploadCubit extends Cubit { checkConflicts(); } - Future checkConflictingFiles() async { + Future checkConflictingFiles({ + bool checkFailedFiles = true, + }) async { emit(UploadPreparationInProgress()); _removeFilesWithFolderNameConflicts(); @@ -322,11 +325,52 @@ class UploadCubit extends Cubit { conflictingFiles[file.getIdentifier()] = existingFileId; } } + if (conflictingFiles.isNotEmpty) { + if (checkFailedFiles) { + failedFiles.clear(); + + conflictingFiles.forEach((key, value) { + logger.d('Checking if file $key has failed'); + }); + + for (final fileNameKey in conflictingFiles.keys) { + final fileId = conflictingFiles[fileNameKey]; + + final fileRevision = await _driveDao + .latestFileRevisionByFileId( + driveId: driveId, + fileId: fileId!, + ) + .getSingleOrNull(); + + final status = _driveDao.select(_driveDao.networkTransactions) + ..where((tbl) => tbl.id.equals(fileRevision!.metadataTxId)); + + final transaction = await status.getSingleOrNull(); + + if (transaction?.status == 'pending') { + failedFiles.add(fileNameKey); + } + } + + if (failedFiles.isNotEmpty) { + emit( + UploadConflictWithFailedFiles( + areAllFilesConflicting: conflictingFiles.length == files.length, + conflictingFileNames: conflictingFiles.keys.toList(), + conflictingFileNamesForFailedFiles: failedFiles, + ), + ); + return; + } + } + emit( UploadFileConflict( areAllFilesConflicting: conflictingFiles.length == files.length, conflictingFileNames: conflictingFiles.keys.toList(), + conflictingFileNamesForFailedFiles: const [], ), ); } else { @@ -408,6 +452,8 @@ class UploadCubit extends Cubit { if (uploadAction == UploadActions.skip) { _removeFilesWithFileNameConflicts(); + } else if (uploadAction == UploadActions.skipSuccessfullyUploads) { + _removeSuccessfullyUploadedFiles(); } logger.d( @@ -919,6 +965,12 @@ class UploadCubit extends Cubit { ); } + void _removeSuccessfullyUploadedFiles() { + files.removeWhere( + (file) => !failedFiles.contains(file.getIdentifier()), + ); + } + void _removeFilesWithFolderNameConflicts() { files.removeWhere((file) => conflictingFolders.contains(file.ioFile.name)); } diff --git a/lib/blocs/upload/upload_state.dart b/lib/blocs/upload/upload_state.dart index c588b84299..7580bc654d 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -27,21 +27,33 @@ class UploadSigningInProgress extends UploadState { class UploadFileConflict extends UploadState { final List conflictingFileNames; + final List conflictingFileNamesForFailedFiles; + final bool areAllFilesConflicting; UploadFileConflict({ required this.conflictingFileNames, required this.areAllFilesConflicting, + required this.conflictingFileNamesForFailedFiles, }); @override List get props => [conflictingFileNames]; } +class UploadConflictWithFailedFiles extends UploadFileConflict { + UploadConflictWithFailedFiles({ + required super.conflictingFileNames, + required super.areAllFilesConflicting, + super.conflictingFileNamesForFailedFiles = const [], + }); +} + class UploadFolderNameConflict extends UploadFileConflict { UploadFolderNameConflict({ required super.conflictingFileNames, required super.areAllFilesConflicting, + super.conflictingFileNamesForFailedFiles = const [], }); } diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index ce69a7d729..17d56a7451 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -287,63 +287,112 @@ class _UploadFormState extends State { ), ], ); - } else if (state is UploadFileConflict) { + } else if (state is UploadConflictWithFailedFiles) { return ArDriveStandardModal( - title: appLocalizationsOf(context) - .duplicateFiles(state.conflictingFileNames.length), - content: SizedBox( - width: kMediumDialogWidth, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - appLocalizationsOf(context) - .filesWithTheSameNameAlreadyExists( - state.conflictingFileNames.length, + title: 'Retry Failed Uploads?', + content: SizedBox( + width: kMediumDialogWidth, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'There are ${state.conflictingFileNamesForFailedFiles.length} file(s) marked with a red dot, indicating they failed to upload. Would you like to retry uploading these files by replacing the failed versions? This action will only affect the failed uploads and will not alter any successfully uploaded files. Alternatively, you can choose to skip these files and proceed with the others.', + style: ArDriveTypography.body.buttonNormalRegular(), + ), + const SizedBox(height: 16), + Text( + 'Conflicting files', + style: ArDriveTypography.body.buttonNormalRegular(), + ), + const SizedBox(height: 8), + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 320), + child: SingleChildScrollView( + child: Text( + state.conflictingFileNamesForFailedFiles + .join(', \n'), + style: ArDriveTypography.body.buttonNormalRegular(), ), - style: ArDriveTypography.body.buttonNormalRegular(), ), - const SizedBox(height: 16), - Text( - appLocalizationsOf(context).conflictingFiles, - style: ArDriveTypography.body.buttonNormalRegular(), + ), + ], + ), + ), + actions: [ + ModalAction( + action: () => context + .read() + .checkConflictingFiles(checkFailedFiles: false), + title: appLocalizationsOf(context).skipEmphasized, + ), + ModalAction( + action: () => context + .read() + .prepareUploadPlanAndCostEstimates( + uploadAction: + UploadActions.skipSuccessfullyUploads), + title: 'Replace failed uploads', + ), + ], + ); + } else if (state is UploadFileConflict) { + return ArDriveStandardModal( + title: appLocalizationsOf(context) + .duplicateFiles(state.conflictingFileNames.length), + content: SizedBox( + width: kMediumDialogWidth, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + appLocalizationsOf(context) + .filesWithTheSameNameAlreadyExists( + state.conflictingFileNames.length, ), - const SizedBox(height: 8), - ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 320), - child: SingleChildScrollView( - child: Text( - state.conflictingFileNames.join(', \n'), - style: - ArDriveTypography.body.buttonNormalRegular(), - ), + style: ArDriveTypography.body.buttonNormalRegular(), + ), + const SizedBox(height: 16), + Text( + appLocalizationsOf(context).conflictingFiles, + style: ArDriveTypography.body.buttonNormalRegular(), + ), + const SizedBox(height: 8), + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 320), + child: SingleChildScrollView( + child: Text( + state.conflictingFileNames.join(', \n'), + style: ArDriveTypography.body.buttonNormalRegular(), ), ), - ], - ), - ), - actions: [ - if (!state.areAllFilesConflicting) - ModalAction( - action: () => context - .read() - .prepareUploadPlanAndCostEstimates( - uploadAction: UploadActions.skip), - title: appLocalizationsOf(context).skipEmphasized, ), - ModalAction( - action: () => Navigator.of(context).pop(false), - title: appLocalizationsOf(context).cancelEmphasized, - ), + ], + ), + ), + actions: [ + if (!state.areAllFilesConflicting) ModalAction( action: () => context .read() .prepareUploadPlanAndCostEstimates( - uploadAction: UploadActions.replace), - title: appLocalizationsOf(context).replaceEmphasized, + uploadAction: UploadActions.skip), + title: appLocalizationsOf(context).skipEmphasized, ), - ]); + ModalAction( + action: () => Navigator.of(context).pop(false), + title: appLocalizationsOf(context).cancelEmphasized, + ), + ModalAction( + action: () => context + .read() + .prepareUploadPlanAndCostEstimates( + uploadAction: UploadActions.replace), + title: appLocalizationsOf(context).replaceEmphasized, + ), + ], + ); } else if (state is UploadFileTooLarge) { return ArDriveStandardModal( title: appLocalizationsOf(context) From ffd375c8a84164c552a082a7f0dfbe9d94339f64 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 11:57:39 -0300 Subject: [PATCH 2/9] Update upload_cubit.dart --- lib/blocs/upload/upload_cubit.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index c9dcadee08..fd1b28d0fa 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -349,7 +349,7 @@ class UploadCubit extends Cubit { final transaction = await status.getSingleOrNull(); - if (transaction?.status == 'pending') { + if (transaction?.status == TransactionStatus.failed) { failedFiles.add(fileNameKey); } } From d7371df4adeeb19f0a21083501bf6bb1ae22dd7d Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 11:58:46 -0300 Subject: [PATCH 3/9] Update upload_cubit_test.dart --- test/blocs/upload_cubit_test.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/blocs/upload_cubit_test.dart b/test/blocs/upload_cubit_test.dart index c0212d1792..ed9e86add2 100644 --- a/test/blocs/upload_cubit_test.dart +++ b/test/blocs/upload_cubit_test.dart @@ -333,7 +333,12 @@ void main() { const TypeMatcher(), UploadFileConflict( areAllFilesConflicting: true, - conflictingFileNames: const ['${tRootFolderId}1']), + conflictingFileNames: const [ + '${tRootFolderId}1' + ], + conflictingFileNamesForFailedFiles: const [ + '${tRootFolderId}1' + ]), ]); blocTest( @@ -351,8 +356,10 @@ void main() { const TypeMatcher(), const TypeMatcher(), UploadFileConflict( - areAllFilesConflicting: false, - conflictingFileNames: const ['${tRootFolderId}1']) + areAllFilesConflicting: false, + conflictingFileNames: const ['${tRootFolderId}1'], + conflictingFileNamesForFailedFiles: const ['${tRootFolderId}1'], + ) ]); blocTest( From 04a2d50ebeab4e95208d6180ce018fa5fd7e1de6 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 12:04:06 -0300 Subject: [PATCH 4/9] Update conflicting_files_actions.dart --- lib/blocs/upload/enums/conflicting_files_actions.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/blocs/upload/enums/conflicting_files_actions.dart b/lib/blocs/upload/enums/conflicting_files_actions.dart index a8883791bc..f51e7cf91e 100644 --- a/lib/blocs/upload/enums/conflicting_files_actions.dart +++ b/lib/blocs/upload/enums/conflicting_files_actions.dart @@ -5,4 +5,4 @@ /// `Replace` will upload the conflicting file and replace the existent. /// /// `SkipSuccessfullyUploads` will skip the files that were successfully uploaded. -enum UploadActions { skip, skipSuccessfullyUploads, replace } +enum UploadActions { skip, skipSuccessfulUploads, replace } From 7810b50ef2d4777cfd9036f977a2dea5aa36d9ba Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 12:51:05 -0300 Subject: [PATCH 5/9] Update upload_cubit.dart --- lib/blocs/upload/upload_cubit.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index fd1b28d0fa..c61ddfe53e 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -452,7 +452,7 @@ class UploadCubit extends Cubit { if (uploadAction == UploadActions.skip) { _removeFilesWithFileNameConflicts(); - } else if (uploadAction == UploadActions.skipSuccessfullyUploads) { + } else if (uploadAction == UploadActions.skipSuccessfulUploads) { _removeSuccessfullyUploadedFiles(); } From a57c7713397f9ab8c499c625fe29b5eeae692592 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 14:23:45 -0300 Subject: [PATCH 6/9] Update upload_form.dart --- lib/components/upload_form.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 17d56a7451..40d98fc2b8 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -330,8 +330,7 @@ class _UploadFormState extends State { action: () => context .read() .prepareUploadPlanAndCostEstimates( - uploadAction: - UploadActions.skipSuccessfullyUploads), + uploadAction: UploadActions.skipSuccessfulUploads), title: 'Replace failed uploads', ), ], From b9e7b6de7145d79ed71a9af473271f4d5fdb74c1 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 23 May 2024 10:42:15 -0300 Subject: [PATCH 7/9] fix upload cubit get single or null --- lib/blocs/upload/upload_cubit.dart | 35 +++++++++++-------- .../FirstDriveEntityWithIdOwner.graphql | 1 + 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index c61ddfe53e..48ab476ce9 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -311,16 +311,18 @@ class UploadCubit extends Cubit { for (final file in files) { final fileName = file.ioFile.name; - final existingFileId = await _driveDao + final existingFileIds = await _driveDao .filesInFolderWithName( driveId: _targetDrive.id, parentFolderId: file.parentFolderId, name: fileName, ) .map((f) => f.id) - .getSingleOrNull(); + .get(); + + if (existingFileIds.isNotEmpty) { + final existingFileId = existingFileIds.first; - if (existingFileId != null) { logger.d('Found conflicting file. Existing file id: $existingFileId'); conflictingFiles[file.getIdentifier()] = existingFileId; } @@ -345,15 +347,19 @@ class UploadCubit extends Cubit { .getSingleOrNull(); final status = _driveDao.select(_driveDao.networkTransactions) - ..where((tbl) => tbl.id.equals(fileRevision!.metadataTxId)); + ..where((tbl) => tbl.id.equals(fileRevision!.dataTxId)); final transaction = await status.getSingleOrNull(); + logger.d('Transaction status: ${transaction?.status}'); + if (transaction?.status == TransactionStatus.failed) { failedFiles.add(fileNameKey); } } + logger.d('Failed files: $failedFiles'); + if (failedFiles.isNotEmpty) { emit( UploadConflictWithFailedFiles( @@ -402,19 +408,20 @@ class UploadCubit extends Cubit { ) .map((f) => f.id) .getSingleOrNull(); - final existingFileId = await _driveDao + final existingFileIds = await _driveDao .filesInFolderWithName( driveId: driveId, name: folder.name, parentFolderId: folder.parentFolderId, ) .map((f) => f.id) - .getSingleOrNull(); + .get(); + if (existingFolderId != null) { folder.id = existingFolderId; foldersToSkip.add(folder); } - if (existingFileId != null) { + if (existingFileIds.isNotEmpty) { conflictingFolders.add(folder.name); } } @@ -557,15 +564,15 @@ class UploadCubit extends Cubit { } if (uploadFolders) { - await _uploadFolderUsingArDriveUploader( - licenseStateConfigured: licenseStateConfigured, - ); - return; + // await _uploadFolderUsingArDriveUploader( + // licenseStateConfigured: licenseStateConfigured, + // ); + // return; } - await _uploadUsingArDriveUploader( - licenseStateConfigured: licenseStateConfigured, - ); + // await _uploadUsingArDriveUploader( + // licenseStateConfigured: licenseStateConfigured, + // ); return; } diff --git a/lib/services/arweave/graphql/queries/FirstDriveEntityWithIdOwner.graphql b/lib/services/arweave/graphql/queries/FirstDriveEntityWithIdOwner.graphql index 789accafaf..e7c194e9be 100644 --- a/lib/services/arweave/graphql/queries/FirstDriveEntityWithIdOwner.graphql +++ b/lib/services/arweave/graphql/queries/FirstDriveEntityWithIdOwner.graphql @@ -6,6 +6,7 @@ query FirstDriveEntityWithIdOwner($driveId: String!, $after: String) { tags: [ { name: "Drive-Id", values: [$driveId] } { name: "Entity-Type", values: ["drive"] } + { name: "Drive-Privacy", values: ["public", "private"]} ] ) { edges { From 3a74153bfac6217ca266ed2e7395d39bd6e7f698 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 23 May 2024 12:09:01 -0300 Subject: [PATCH 8/9] drive empty state --- lib/blocs/upload/upload_cubit.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index 48ab476ce9..0cd3af96ef 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -564,15 +564,15 @@ class UploadCubit extends Cubit { } if (uploadFolders) { - // await _uploadFolderUsingArDriveUploader( - // licenseStateConfigured: licenseStateConfigured, - // ); - // return; + await _uploadFolderUsingArDriveUploader( + licenseStateConfigured: licenseStateConfigured, + ); + return; } - // await _uploadUsingArDriveUploader( - // licenseStateConfigured: licenseStateConfigured, - // ); + await _uploadUsingArDriveUploader( + licenseStateConfigured: licenseStateConfigured, + ); return; } From 4c36450cdfa164c8ec66c51fc68f89a98243342d Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Fri, 9 Aug 2024 07:55:41 -0300 Subject: [PATCH 9/9] Update lib/blocs/upload/enums/conflicting_files_actions.dart Co-authored-by: Atticus --- lib/blocs/upload/enums/conflicting_files_actions.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/blocs/upload/enums/conflicting_files_actions.dart b/lib/blocs/upload/enums/conflicting_files_actions.dart index f51e7cf91e..0a5757bc92 100644 --- a/lib/blocs/upload/enums/conflicting_files_actions.dart +++ b/lib/blocs/upload/enums/conflicting_files_actions.dart @@ -4,5 +4,5 @@ /// /// `Replace` will upload the conflicting file and replace the existent. /// -/// `SkipSuccessfullyUploads` will skip the files that were successfully uploaded. +/// `SkipSuccessfulUploads` will skip the files that were successfully uploaded. enum UploadActions { skip, skipSuccessfulUploads, replace }