From f10553be47d0444a864d88d004fa947c739b3a74 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Mon, 6 May 2024 10:34:14 -0300 Subject: [PATCH 1/9] implement android search --- lib/app_shell.dart | 29 ++-- lib/components/app_top_bar.dart | 14 +- lib/pages/drive_detail/drive_detail_page.dart | 38 ++++++ lib/search/search_modal.dart | 126 +++++++++++++++--- 4 files changed, 166 insertions(+), 41 deletions(-) diff --git a/lib/app_shell.dart b/lib/app_shell.dart index 2a51c6fb2c..3550d09c06 100644 --- a/lib/app_shell.dart +++ b/lib/app_shell.dart @@ -222,20 +222,6 @@ class MobileAppBar extends StatelessWidget implements PreferredSizeWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.only( - left: 24.0, - ), - child: ArDriveImage( - image: AssetImage( - isLightMode - ? Resources.images.brand.blackLogo1 - : Resources.images.brand.whiteLogo1, - ), - width: 128, - height: 28, - ), - ), Padding( padding: const EdgeInsets.only(left: 7.0), child: leading ?? @@ -252,6 +238,21 @@ class MobileAppBar extends StatelessWidget implements PreferredSizeWidget { ) : Container()), ), + if (!showDrawerButton) + Padding( + padding: const EdgeInsets.only( + left: 24.0, + ), + child: ArDriveImage( + image: AssetImage( + isLightMode + ? Resources.images.brand.blackLogo1 + : Resources.images.brand.whiteLogo1, + ), + width: 128, + height: 28, + ), + ), const Spacer(), const SyncButton(), const SizedBox( diff --git a/lib/components/app_top_bar.dart b/lib/components/app_top_bar.dart index b0cf597db2..35feb1cb55 100644 --- a/lib/components/app_top_bar.dart +++ b/lib/components/app_top_bar.dart @@ -10,7 +10,6 @@ import 'package:ardrive/sync/domain/cubit/sync_cubit.dart'; import 'package:ardrive/utils/app_localizations_wrapper.dart'; import 'package:ardrive/utils/plausible_event_tracker/plausible_custom_event_properties.dart'; import 'package:ardrive/utils/plausible_event_tracker/plausible_event_tracker.dart'; -import 'package:ardrive/utils/show_general_dialog.dart'; import 'package:ardrive_ui/ardrive_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -21,7 +20,6 @@ class AppTopBar extends StatelessWidget { @override Widget build(BuildContext context) { final enableSearch = context.read().config.enableSearch; - final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; final controller = TextEditingController(); return SizedBox( @@ -40,14 +38,10 @@ class AppTopBar extends StatelessWidget { child: SearchTextField( controller: controller, onFieldSubmitted: (query) { - showArDriveDialog( - context, - content: FileSearchModal( - initialQuery: query, - driveDetailCubit: context.read(), - controller: controller, - ), - barrierColor: colorTokens.containerL1.withOpacity(0.8), + showSearchModalDesktop( + context: context, + driveDetailCubit: context.read(), + controller: controller, ); }, ), diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index 0dfb220ecf..2eefc6e641 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -32,6 +32,8 @@ import 'package:ardrive/pages/drive_detail/components/dropdown_item.dart'; import 'package:ardrive/pages/drive_detail/components/file_icon.dart'; import 'package:ardrive/pages/drive_detail/components/hover_widget.dart'; import 'package:ardrive/pages/drive_detail/components/unpreviewable_content.dart'; +import 'package:ardrive/search/search_modal.dart'; +import 'package:ardrive/search/search_text_field.dart'; import 'package:ardrive/services/services.dart'; import 'package:ardrive/sharing/sharing_file_listener.dart'; import 'package:ardrive/sync/domain/cubit/sync_cubit.dart'; @@ -83,6 +85,7 @@ class DriveDetailPage extends StatefulWidget { class _DriveDetailPageState extends State { bool checkboxEnabled = false; final _scrollController = ScrollController(); + final controller = TextEditingController(); @override void initState() { @@ -742,8 +745,43 @@ class _DriveDetailPageState extends State { filteredItems = items.where((item) => item.isHidden == false).toList(); } + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; return Column( children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: SearchTextField( + controller: controller, + onFieldSubmitted: (query) { + showModalBottomSheet( + isScrollControlled: true, + context: context, + backgroundColor: Colors.transparent, + builder: (_) => Container( + height: MediaQuery.of(context).size.height * 0.85, + decoration: BoxDecoration( + color: colorTokens.containerL2, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(6.0), + topRight: Radius.circular(6.0), + ), + ), + child: Padding( + padding: MediaQuery.of(context).viewInsets, + child: BlocProvider.value( + value: context.read(), + child: FileSearchModal( + initialQuery: query, + driveDetailCubit: context.read(), + controller: controller, + ), + ), + ), + ), + ); + }, + ), + ), Padding( padding: const EdgeInsets.only(top: 8), child: Row( diff --git a/lib/search/search_modal.dart b/lib/search/search_modal.dart index 0c07a256c7..5b310cef3d 100644 --- a/lib/search/search_modal.dart +++ b/lib/search/search_modal.dart @@ -11,13 +11,68 @@ import 'package:ardrive/search/domain/bloc/search_bloc.dart'; import 'package:ardrive/search/domain/repository/search_repository.dart'; import 'package:ardrive/search/search_result.dart'; import 'package:ardrive/search/search_text_field.dart'; +import 'package:ardrive/utils/show_general_dialog.dart'; import 'package:ardrive_ui/ardrive_ui.dart'; import 'package:ardrive_utils/ardrive_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:responsive_builder/responsive_builder.dart'; import '../models/models.dart'; +Future showSearchModalBottomSheet({ + required BuildContext context, + required DriveDetailCubit driveDetailCubit, + required TextEditingController controller, + String? query, +}) { + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return showModalBottomSheet( + isScrollControlled: true, + context: context, + backgroundColor: Colors.transparent, + builder: (_) => Container( + height: MediaQuery.of(context).size.height * 0.85, + decoration: BoxDecoration( + color: colorTokens.containerL2, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(6.0), + topRight: Radius.circular(6.0), + ), + ), + child: Padding( + padding: MediaQuery.of(context).viewInsets, + child: BlocProvider.value( + value: context.read(), + child: FileSearchModal( + initialQuery: query, + driveDetailCubit: context.read(), + controller: controller, + ), + ), + ), + ), + ); +} + +Future showSearchModalDesktop({ + required BuildContext context, + required DriveDetailCubit driveDetailCubit, + required TextEditingController controller, + String? query, +}) { + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return showArDriveDialog( + context, + content: FileSearchModal( + initialQuery: query, + driveDetailCubit: context.read(), + controller: controller, + ), + barrierColor: colorTokens.containerL1.withOpacity(0.8), + ); +} + class FileSearchModal extends StatelessWidget { const FileSearchModal({ super.key, @@ -92,25 +147,62 @@ class _FileSearchModalState extends State<_FileSearchModal> { Widget build(BuildContext context) { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - return ArDriveLoginModal( - width: MediaQuery.of(context).size.width * 0.6, - content: SizedBox( - height: MediaQuery.of(context).size.height * 0.7, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildSearchHeader(typography, colorTokens), - const SizedBox(height: 16), - SearchTextField( - controller: widget.controller, - onFieldSubmitted: (_) => searchFiles(widget.controller.text), + return ScreenTypeLayout.builder(mobile: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: colorTokens.containerRed, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(9), + topRight: Radius.circular(9), + ), ), - const SizedBox(height: 16), - _buildSearchResults(context, typography, colorTokens), - ], + height: 6, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + _buildSearchHeader(typography, colorTokens), + const SizedBox(height: 16), + SearchTextField( + controller: widget.controller, + onFieldSubmitted: (_) => + searchFiles(widget.controller.text), + ), + const SizedBox(height: 16), + _buildSearchResults(context, typography, colorTokens), + ], + ), + ), + ), + ], + ); + }, desktop: (context) { + return ArDriveLoginModal( + width: MediaQuery.of(context).size.width * 0.6, + content: SizedBox( + height: MediaQuery.of(context).size.height * 0.7, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _buildSearchHeader(typography, colorTokens), + const SizedBox(height: 16), + SearchTextField( + controller: widget.controller, + onFieldSubmitted: (_) => searchFiles(widget.controller.text), + ), + const SizedBox(height: 16), + _buildSearchResults(context, typography, colorTokens), + ], + ), ), - ), - ); + ); + }); } Widget _buildSearchHeader( From 778df838a201a19d744618b415598d8f611a3263 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Mon, 6 May 2024 15:18:53 -0300 Subject: [PATCH 2/9] Update search_modal.dart Null check operator used on a null value --- lib/search/search_modal.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/search/search_modal.dart b/lib/search/search_modal.dart index 5b310cef3d..5395eaa78f 100644 --- a/lib/search/search_modal.dart +++ b/lib/search/search_modal.dart @@ -140,7 +140,9 @@ class _FileSearchModalState extends State<_FileSearchModal> { } Future searchFiles(String query) async { - context.read().add(SearchQueryChanged(query)); + if(mounted) { + context.read().add(SearchQueryChanged(query)); + } } @override @@ -428,7 +430,7 @@ class _FileSearchModalState extends State<_FileSearchModal> { } } - Future _navigateToFile( +Future _navigateToFile( BuildContext context, FileEntry result, ) async { From af2e60d909a05c6ba9c70eab7964feb23bbe8c2b Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Mon, 6 May 2024 15:19:47 -0300 Subject: [PATCH 3/9] Update search_modal.dart --- lib/search/search_modal.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/search/search_modal.dart b/lib/search/search_modal.dart index 5395eaa78f..83d59bb803 100644 --- a/lib/search/search_modal.dart +++ b/lib/search/search_modal.dart @@ -140,7 +140,7 @@ class _FileSearchModalState extends State<_FileSearchModal> { } Future searchFiles(String query) async { - if(mounted) { + if (mounted) { context.read().add(SearchQueryChanged(query)); } } @@ -430,7 +430,7 @@ class _FileSearchModalState extends State<_FileSearchModal> { } } -Future _navigateToFile( + Future _navigateToFile( BuildContext context, FileEntry result, ) async { From 1c1caf3df03b44b3c5f709fb631c2643c8699ede Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 7 May 2024 14:50:12 -0300 Subject: [PATCH 4/9] Update drive_detail_page.dart fix navigation on mobile --- lib/pages/drive_detail/drive_detail_page.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index 2eefc6e641..6ccb0d463a 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -964,6 +964,12 @@ class MobileFolderNavigation extends StatelessWidget { Expanded( child: InkWell( onTap: () { + if (path.length == 1) { + // If we are at the root folder, open the drive + context.read().openFolder(); + return; + } + String? targetId; if (path.isNotEmpty) { From 46f7da9b3c946271d9a13690b5112da5def82c67 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 7 May 2024 15:06:50 -0300 Subject: [PATCH 5/9] Update drive_detail_page.dart --- lib/pages/drive_detail/drive_detail_page.dart | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index 6ccb0d463a..14fd5277a5 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -962,48 +962,52 @@ class MobileFolderNavigation extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( - child: InkWell( - onTap: () { - if (path.length == 1) { - // If we are at the root folder, open the drive - context.read().openFolder(); - return; - } - - String? targetId; + child: SizedBox( + height: 45, + child: InkWell( + onTap: () { + if (path.length == 1) { + // If we are at the root folder, open the drive + context.read().openFolder(); + return; + } + String? targetId; - if (path.isNotEmpty) { - targetId = path.first.targetId; - } + if (path.isNotEmpty) { + targetId = path.first.targetId; + } - context.read().openFolder(folderId: targetId); - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (path.isNotEmpty) - Padding( - padding: const EdgeInsets.only( - left: 16, - right: 8, - top: 4, + context + .read() + .openFolder(folderId: targetId); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (path.isNotEmpty) + Padding( + padding: const EdgeInsets.only( + left: 16, + right: 8, + top: 4, + ), + child: ArDriveIcons.arrowLeft(), ), - child: ArDriveIcons.arrowLeft(), - ), - Expanded( - child: Padding( - padding: path.isEmpty - ? const EdgeInsets.only(left: 16, top: 6, bottom: 6) - : EdgeInsets.zero, - child: Text( - _pathToName( - path.isEmpty ? driveName : path.last.text, + Expanded( + child: Padding( + padding: path.isEmpty + ? const EdgeInsets.only(left: 16, top: 6, bottom: 6) + : EdgeInsets.zero, + child: Text( + _pathToName( + path.isEmpty ? driveName : path.last.text, + ), + style: ArDriveTypography.body.buttonNormalBold(), ), - style: ArDriveTypography.body.buttonNormalBold(), ), ), - ), - ], + ], + ), ), ), ), From d14dd89707e5b7273390b0052eebb7adf1cdabdc Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 09:55:51 -0300 Subject: [PATCH 6/9] fix(search): - if search is empty dont show the modal for search - click outisde the modal now closes the keyboard - fix long file names alignment --- lib/pages/drive_detail/drive_detail_page.dart | 4 ++++ lib/search/search_modal.dart | 3 +-- lib/search/search_text_field.dart | 3 +++ packages/ardrive_ui/lib/src/components/text_field_new.dart | 3 +++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index 14fd5277a5..9af301be25 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -753,6 +753,10 @@ class _DriveDetailPageState extends State { child: SearchTextField( controller: controller, onFieldSubmitted: (query) { + if (query.isEmpty) { + return; + } + showModalBottomSheet( isScrollControlled: true, context: context, diff --git a/lib/search/search_modal.dart b/lib/search/search_modal.dart index 83d59bb803..f83a9d8b00 100644 --- a/lib/search/search_modal.dart +++ b/lib/search/search_modal.dart @@ -286,8 +286,7 @@ class _FileSearchModalState extends State<_FileSearchModal> { child: ListTile( onTap: () => _handleNavigation(context, searchResult), leading: leadingIcon, - title: Row( - mainAxisSize: MainAxisSize.min, + title: Wrap( children: [ Text( name, diff --git a/lib/search/search_text_field.dart b/lib/search/search_text_field.dart index b567c9ff23..f7155a9bb7 100644 --- a/lib/search/search_text_field.dart +++ b/lib/search/search_text_field.dart @@ -27,6 +27,9 @@ class SearchTextField extends StatelessWidget { color: colorTokens.textMid, ), ), + onTapOutside: (_) { + FocusScope.of(context).unfocus(); + }, suffixIcon: Transform( // move 4 pixels bottom transform: Matrix4.translationValues(0.0, 4.0, 0.0), diff --git a/packages/ardrive_ui/lib/src/components/text_field_new.dart b/packages/ardrive_ui/lib/src/components/text_field_new.dart index 03e98a8a2f..31ca3dc3f4 100644 --- a/packages/ardrive_ui/lib/src/components/text_field_new.dart +++ b/packages/ardrive_ui/lib/src/components/text_field_new.dart @@ -218,6 +218,7 @@ class ArDriveTextFieldNew extends StatefulWidget { this.maxLines = 1, this.errorMessage, this.prefixIcon, + this.onTapOutside, }); final bool isEnabled; @@ -253,6 +254,7 @@ class ArDriveTextFieldNew extends StatefulWidget { final int? maxLines; final String? errorMessage; final Widget? prefixIcon; + final Function(PointerDownEvent)? onTapOutside; @override State createState() => ArDriveTextFieldStateNew(); @@ -307,6 +309,7 @@ class ArDriveTextFieldStateNew extends State { ), ), TextFormField( + onTapOutside: widget.onTapOutside, controller: controller, autocorrect: widget.autocorrect, autofocus: widget.autofocus, From 4b98d5951de33adb8957f029dbcccbd51830184f Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 11:16:28 -0300 Subject: [PATCH 7/9] (WIP): skip success uploads --- .../enums/conflicting_files_actions.dart | 2 +- lib/blocs/upload/upload_cubit.dart | 54 ++++++- lib/blocs/upload/upload_state.dart | 12 ++ lib/components/upload_form.dart | 139 ++++++++++++------ 4 files changed, 160 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..85dcdef903 100644 --- a/lib/blocs/upload/enums/conflicting_files_actions.dart +++ b/lib/blocs/upload/enums/conflicting_files_actions.dart @@ -3,4 +3,4 @@ /// `Skip` Will ignore the files and don't upload them. /// /// `Replace` will upload the conflicting file and replace the existent. -enum UploadActions { skip, replace } +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 0a804a6ab084e74787a7b24f3378ac1213a935af Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 11:17:29 -0300 Subject: [PATCH 8/9] Revert "(WIP): skip success uploads" This reverts commit 4b98d5951de33adb8957f029dbcccbd51830184f. --- .../enums/conflicting_files_actions.dart | 2 +- lib/blocs/upload/upload_cubit.dart | 54 +------ lib/blocs/upload/upload_state.dart | 12 -- lib/components/upload_form.dart | 139 ++++++------------ 4 files changed, 47 insertions(+), 160 deletions(-) diff --git a/lib/blocs/upload/enums/conflicting_files_actions.dart b/lib/blocs/upload/enums/conflicting_files_actions.dart index 85dcdef903..422d1c06ad 100644 --- a/lib/blocs/upload/enums/conflicting_files_actions.dart +++ b/lib/blocs/upload/enums/conflicting_files_actions.dart @@ -3,4 +3,4 @@ /// `Skip` Will ignore the files and don't upload them. /// /// `Replace` will upload the conflicting file and replace the existent. -enum UploadActions { skip, skipSuccessfullyUploads, replace } +enum UploadActions { skip, replace } diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index c9dcadee08..ba56b66764 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -186,7 +186,6 @@ 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, @@ -302,9 +301,7 @@ class UploadCubit extends Cubit { checkConflicts(); } - Future checkConflictingFiles({ - bool checkFailedFiles = true, - }) async { + Future checkConflictingFiles() async { emit(UploadPreparationInProgress()); _removeFilesWithFolderNameConflicts(); @@ -325,52 +322,11 @@ 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 { @@ -452,8 +408,6 @@ class UploadCubit extends Cubit { if (uploadAction == UploadActions.skip) { _removeFilesWithFileNameConflicts(); - } else if (uploadAction == UploadActions.skipSuccessfullyUploads) { - _removeSuccessfullyUploadedFiles(); } logger.d( @@ -965,12 +919,6 @@ 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 7580bc654d..c588b84299 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -27,33 +27,21 @@ 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 17d56a7451..ce69a7d729 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -287,112 +287,63 @@ class _UploadFormState extends State { ), ], ); - } else if (state is UploadConflictWithFailedFiles) { + } else if (state is UploadFileConflict) { return ArDriveStandardModal( - 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(), + 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, ), + 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: 16), + Text( + appLocalizationsOf(context).conflictingFiles, + 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(), + 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) + 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, + ), ModalAction( action: () => context .read() .prepareUploadPlanAndCostEstimates( - uploadAction: UploadActions.skip), - title: appLocalizationsOf(context).skipEmphasized, + uploadAction: UploadActions.replace), + title: appLocalizationsOf(context).replaceEmphasized, ), - 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 bb49c7f7ef667c6d55a7878bcc44690f725a1762 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Wed, 8 May 2024 11:39:47 -0300 Subject: [PATCH 9/9] bump version and release notes --- android/fastlane/metadata/android/en-US/changelogs/126.txt | 1 + pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 android/fastlane/metadata/android/en-US/changelogs/126.txt diff --git a/android/fastlane/metadata/android/en-US/changelogs/126.txt b/android/fastlane/metadata/android/en-US/changelogs/126.txt new file mode 100644 index 0000000000..6a41b2fd27 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/126.txt @@ -0,0 +1 @@ +- New Feature: Search (for quickly locating files and navigating through folders and drives) \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 9dc4906a2a..c3aae8d1c3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Secure, permanent storage publish_to: 'none' -version: 2.43.0 +version: 2.44.0 environment: sdk: '>=3.2.0 <4.0.0'