diff --git a/android/fastlane/metadata/android/en-US/changelogs/150.txt b/android/fastlane/metadata/android/en-US/changelogs/150.txt new file mode 100644 index 0000000000..98cbdf1a2f --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/150.txt @@ -0,0 +1 @@ + - Improved upload flow styling diff --git a/lib/arns/presentation/assign_name_modal.dart b/lib/arns/presentation/assign_name_modal.dart index 4a322e2428..3155d073f4 100644 --- a/lib/arns/presentation/assign_name_modal.dart +++ b/lib/arns/presentation/assign_name_modal.dart @@ -390,7 +390,7 @@ class __NameSelectorDropdownState extends State<_NameSelectorDropdown> { final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; double maxHeight; - + double maxWidth = 500; if (48 * widget.names.length.toDouble() > 240) { maxHeight = 240; } else if (widget.names.isEmpty) { @@ -399,6 +399,10 @@ class __NameSelectorDropdownState extends State<_NameSelectorDropdown> { maxHeight = 48 * widget.names.length.toDouble(); } + if (maxWidth >= MediaQuery.of(context).size.width) { + maxWidth = MediaQuery.of(context).size.width - 32; + } + return ArDriveDropdown( hasBorder: false, hasDivider: false, @@ -409,7 +413,7 @@ class __NameSelectorDropdownState extends State<_NameSelectorDropdown> { ), showScrollbars: true, maxHeight: maxHeight, - items: _buildList(widget.names), + items: _buildList(widget.names, maxWidth), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -439,7 +443,7 @@ class __NameSelectorDropdownState extends State<_NameSelectorDropdown> { ), ], ), - width: 500, + width: maxWidth, height: 56, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -480,7 +484,7 @@ class __NameSelectorDropdownState extends State<_NameSelectorDropdown> { return name; } - List _buildList(List items) { + List _buildList(List items, double maxWidth) { List list = []; for (var item in items) { @@ -494,13 +498,15 @@ class __NameSelectorDropdownState extends State<_NameSelectorDropdown> { }, content: Container( alignment: Alignment.centerLeft, - width: 500, + width: maxWidth, height: 48, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( _getName(item), style: ArDriveTypographyNew.of(context).paragraphLarge(), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), ), diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index e91b31a222..1706cb4771 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -80,12 +80,12 @@ class UploadCubit extends Cubit { showArnsNameSelectionCheckBoxValue = showArnsNameSelection; } - void showArnsNameSelection() { - emit((state as UploadReady).copyWith(showArnsNameSelection: true)); + void showArnsNameSelection(UploadReady readyState) { + emit(readyState.copyWith(showArnsNameSelection: true)); } - void hideArnsNameSelection() { - emit((state as UploadReady).copyWith(showArnsNameSelection: false)); + void hideArnsNameSelection(UploadReady readyState) { + emit(readyState.copyWith(showArnsNameSelection: false)); } void setUploadMethod( @@ -118,7 +118,8 @@ class UploadCubit extends Cubit { isDragNDrop: isDragNDrop, isNextButtonEnabled: canUpload, isArConnect: (state as UploadReadyToPrepare).isArConnect, - showArnsCheckbox: hasUndernames && files.length == 1, + showArnsCheckbox: + hasUndernames && files.length == 1 && _targetDrive.isPublic, showArnsNameSelection: false, ), ); @@ -128,7 +129,7 @@ class UploadCubit extends Cubit { void initialScreenUpload() { if (state is UploadReady) { if (showArnsNameSelectionCheckBoxValue) { - showArnsNameSelection(); + showArnsNameSelection(state as UploadReady); } else { final readyState = state as UploadReady; startUpload( @@ -201,6 +202,13 @@ class UploadCubit extends Cubit { licenseCategory: licenseCategory, ); emit(prevState); + } else if (state is UploadReviewWithArnsName) { + final reviewWithArnsName = state as UploadReviewWithArnsName; + final readyState = reviewWithArnsName.readyState.copyWith( + showArnsNameSelection: false, + ); + + emit(readyState); } } @@ -214,6 +222,8 @@ class UploadCubit extends Cubit { reviewWithLicense.readyState.paymentInfo.uploadPlanForTurbo, licenseStateConfigured: reviewWithLicense.licenseState, ); + } else if (state is UploadReviewWithArnsName) { + startUploadWithArnsName(); } } @@ -575,15 +585,23 @@ class UploadCubit extends Cubit { logger.d('Selected undername: $_selectedUndername'); - final readyState = state as UploadReady; + final readyState = (state as UploadReady).copyWith( + params: (state as UploadReady).params.copyWith( + arnsUnderName: getSelectedUndername(), + ), + ); - emit(readyState.copyWith( - showArnsNameSelection: false, - )); + emit(UploadReviewWithArnsName(readyState: readyState)); + } + + void startUploadWithArnsName() { + final reviewWithArnsName = state as UploadReviewWithArnsName; startUpload( - uploadPlanForAr: readyState.paymentInfo.uploadPlanForAR!, - uploadPlanForTurbo: readyState.paymentInfo.uploadPlanForTurbo, + uploadPlanForAr: + reviewWithArnsName.readyState.paymentInfo.uploadPlanForAR!, + uploadPlanForTurbo: + reviewWithArnsName.readyState.paymentInfo.uploadPlanForTurbo, ); } @@ -927,13 +945,14 @@ class UploadCubit extends Cubit { (tasks) async { if (tasks.length == 1) { final task = tasks.first; - if (task is FileUploadTask) { + if (task is FileUploadTask && task.status != UploadStatus.canceled) { final metadata = task.metadata; if (_selectedAntRecord != null || _selectedUndername != null) { final updatedTask = task.copyWith( status: UploadStatus.assigningUndername, ); + /// Emits emit( UploadInProgressUsingNewUploader( progress: UploadProgress( diff --git a/lib/blocs/upload/upload_state.dart b/lib/blocs/upload/upload_state.dart index 6153494fcd..9a003f04bf 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -179,6 +179,12 @@ class UploadConfiguringLicense extends UploadState { 'UploadConfiguringLicense { paymentInfo: ${readyState.paymentInfo} }'; } +class UploadReviewWithArnsName extends UploadState { + final UploadReady readyState; + + UploadReviewWithArnsName({required this.readyState}); +} + /// [UploadReviewWithLicense] means that the upload + license is being reviewed by the user and awaiting confirmation to begin upload. class UploadReviewWithLicense extends UploadState { final UploadReady readyState; diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 1d7a0884f1..ee74edb384 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -260,7 +260,7 @@ class _UploadFormState extends State { } if (state is UploadFolderNameConflict) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: appLocalizationsOf(context).duplicateFolders( state.conflictingFileNames.length, ), @@ -306,7 +306,7 @@ class _UploadFormState extends State { ], ); } else if (state is UploadConflictWithFailedFiles) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: 'Retry Failed Uploads?', content: SizedBox( width: kMediumDialogWidth, @@ -326,12 +326,27 @@ class _UploadFormState extends State { const SizedBox(height: 8), ConstrainedBox( constraints: const BoxConstraints(maxHeight: 320), - child: SingleChildScrollView( - child: Text( - state.conflictingFileNamesForFailedFiles - .join(', \n'), - style: ArDriveTypography.body.buttonNormalRegular(), - ), + child: ListView.builder( + itemCount: + state.conflictingFileNamesForFailedFiles.length, + shrinkWrap: true, + itemBuilder: (context, index) { + final file = + state.conflictingFileNamesForFailedFiles[index]; + final typography = ArDriveTypographyNew.of(context); + final colorTokens = + ArDriveTheme.of(context).themeData.colorTokens; + + return ListTile( + title: Text(file, + style: typography.paragraphNormal( + color: colorTokens.textMid, + )), + leading: getIconForContentType( + getFileExtensionFromFileName(fileName: file), + ), + ); + }, ), ), ], @@ -350,11 +365,16 @@ class _UploadFormState extends State { .prepareUploadPlanAndCostEstimates( uploadAction: UploadActions.skipSuccessfulUploads), title: 'Replace failed uploads', + customWidth: 160, + customHeight: 60, ), ], ); } else if (state is UploadFileConflict) { - return ArDriveStandardModal( + final typography = ArDriveTypographyNew.of(context); + final colorTokens = + ArDriveTheme.of(context).themeData.colorTokens; + return ArDriveStandardModalNew( title: appLocalizationsOf(context) .duplicateFiles(state.conflictingFileNames.length), content: SizedBox( @@ -368,21 +388,37 @@ class _UploadFormState extends State { .filesWithTheSameNameAlreadyExists( state.conflictingFileNames.length, ), - style: ArDriveTypography.body.buttonNormalRegular(), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold), ), const SizedBox(height: 16), Text( appLocalizationsOf(context).conflictingFiles, - style: ArDriveTypography.body.buttonNormalRegular(), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold), ), const SizedBox(height: 8), ConstrainedBox( constraints: const BoxConstraints(maxHeight: 320), - child: SingleChildScrollView( - child: Text( - state.conflictingFileNames.join(', \n'), - style: ArDriveTypography.body.buttonNormalRegular(), - ), + child: ListView.builder( + shrinkWrap: true, + itemCount: state.conflictingFileNames.length, + itemBuilder: (context, index) { + final file = state.conflictingFileNames[index]; + final typography = ArDriveTypographyNew.of(context); + + return ListTile( + title: Text( + file, + style: typography.paragraphNormal( + color: colorTokens.textMid, + ), + ), + leading: getIconForContentType( + getFileExtensionFromFileName(fileName: file), + ), + ); + }, ), ), ], @@ -411,7 +447,7 @@ class _UploadFormState extends State { ], ); } else if (state is UploadFileTooLarge) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: appLocalizationsOf(context) .filesTooLarge(state.tooLargeFileNames.length), content: SizedBox( @@ -460,7 +496,7 @@ class _UploadFormState extends State { ); } else if (state is UploadPreparationInProgress || state is UploadPreparationInitialized) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: appLocalizationsOf(context).preparingUpload, content: SizedBox( width: kMediumDialogWidth, @@ -492,12 +528,13 @@ class _UploadFormState extends State { onSelectionConfirmed: (name) { context.read().selectUndername( name.selectedName, name.selectedUndername); - context.read().hideArnsNameSelection(); }, ); } final typography = ArDriveTypographyNew.of(context); + final colorTokens = + ArDriveTheme.of(context).themeData.colorTokens; return ReactiveForm( formGroup: context.watch().licenseCategoryForm, child: ReactiveFormConsumer(builder: (_, form, __) { @@ -555,6 +592,7 @@ class _UploadFormState extends State { .read() .setUploadMethod(method, info, canUpload); }, + useNewArDriveUI: true, ), ), if (state.params @@ -581,7 +619,9 @@ class _UploadFormState extends State { Padding( padding: const EdgeInsets.only(left: 8.0), child: ArDriveIconButton( - icon: ArDriveIcons.info(), + icon: ArDriveIcons.info( + color: colorTokens.textMid, + ), tooltip: 'Uploading with thumbnails is free, but may make your upload take longer.\nYou can always attach a thumbnail later.', ), @@ -591,12 +631,12 @@ class _UploadFormState extends State { ), if (state.showArnsCheckbox) Padding( - padding: const EdgeInsets.symmetric(vertical: 16), + padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ ArDriveCheckBox( title: 'Assign an ARNS name', - checked: false, + checked: state.params.arnsUnderName != null, titleStyle: typography.paragraphLarge( fontWeight: ArFontWeight.semiBold, ), @@ -751,8 +791,51 @@ class _UploadFormState extends State { LicenseReviewInfo(licenseState: state.licenseState), ], ); + } else if (state is UploadReviewWithArnsName) { + final typography = ArDriveTypographyNew.of(context); + final colorTokens = + ArDriveTheme.of(context).themeData.colorTokens; + return StatsScreen( + readyState: state.readyState, + modalActions: [ + ModalAction( + action: () => { + context.read().reviewBack(), + }, + title: appLocalizationsOf(context).backEmphasized, + ), + ModalAction( + action: () { + context.read().reviewUpload(); + }, + title: appLocalizationsOf(context).uploadEmphasized, + ), + ], + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'ArNS Name: ', + style: typography.paragraphLarge( + fontWeight: ArFontWeight.semiBold, + color: colorTokens.textMid, + ), + ), + Text( + getLiteralARNSRecordName( + state.readyState.params.arnsUnderName!, + ), + style: typography.paragraphLarge( + fontWeight: ArFontWeight.semiBold, + ), + ), + ], + ), + ], + ); } else if (state is UploadSigningInProgress) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: state.uploadPlan.bundleUploadHandles.isNotEmpty ? appLocalizationsOf(context).bundlingAndSigningUpload : appLocalizationsOf(context).signingUpload, @@ -778,135 +861,8 @@ class _UploadFormState extends State { ); } else if (state is UploadInProgressUsingNewUploader) { return _uploadUsingNewUploader(state: state); - } else if (state is UploadInProgress) { - final numberOfFilesInBundles = - state.uploadPlan.bundleUploadHandles.isNotEmpty - ? state.uploadPlan.bundleUploadHandles - .map((e) => e.numberOfFiles) - .reduce((value, element) => value += element) - : 0; - final numberOfV2Files = - state.uploadPlan.fileV2UploadHandles.length; - - final v2Files = - state.uploadPlan.fileV2UploadHandles.values.toList(); - final bundles = state.uploadPlan.bundleUploadHandles.toList(); - final files = [...v2Files, ...bundles]; - - return ArDriveStandardModal( - title: - '${appLocalizationsOf(context).uploadingNFiles(numberOfFilesInBundles + numberOfV2Files)} ${(state.progress * 100).toStringAsFixed(2)}%', - content: SizedBox( - width: kMediumDialogWidth, - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 256), - child: Scrollbar( - child: ListView.builder( - shrinkWrap: true, - itemCount: files.length, - itemBuilder: (BuildContext context, int index) { - if (files[index] is FileV2UploadHandle) { - final file = files[index] as FileV2UploadHandle; - return Column( - children: [ - ListTile( - contentPadding: EdgeInsets.zero, - title: Row( - children: [ - Text( - file.entity.name!, - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ), - ), - Text( - filesize(file.entity.size), - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ), - ), - ], - ), - subtitle: Text( - '${filesize(file.uploadedSize)}/${filesize(file.size)}', - style: ArDriveTypography.body - .buttonNormalRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgOnDisabled, - ), - ), - ), - ], - ); - } else { - final file = files[index] as BundleUploadHandle; - return Column( - children: [ - ListTile( - contentPadding: EdgeInsets.zero, - title: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - for (var fileEntity in file.fileEntities) - Row( - children: [ - Text( - fileEntity.name!, - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ), - ), - Text( - filesize(fileEntity.size), - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ), - ), - ], - ), - ], - ), - subtitle: Text( - '${filesize(file.uploadedSize)}/${filesize(file.size)}', - style: ArDriveTypography.body - .buttonNormalRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgOnDisabled, - ), - ), - ), - ], - ); - } - }, - ), - ), - ), - ), - ); } else if (state is UploadCanceled) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: 'Upload canceled', description: 'Your upload was canceled', actions: [ @@ -918,7 +874,7 @@ class _UploadFormState extends State { ); } else if (state is UploadFailure) { if (state.error == UploadErrors.turboTimeout) { - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: appLocalizationsOf(context).uploadFailed, description: appLocalizationsOf(context).yourUploadFailedTurboTimeout, @@ -931,7 +887,7 @@ class _UploadFormState extends State { ); } - return ArDriveStandardModal( + return ArDriveStandardModalNew( hasCloseButton: true, width: state.failedTasks != null ? kLargeDialogWidth @@ -958,7 +914,7 @@ class _UploadFormState extends State { ); } else if (state is UploadShowingWarning) { // TODO: Fix use of startUpload - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: appLocalizationsOf(context).warningEmphasized, content: SizedBox( width: kMediumDialogWidth, @@ -1006,7 +962,9 @@ class _UploadFormState extends State { required UploadInProgressUsingNewUploader state, }) { final progress = state.progress; - return ArDriveStandardModal( + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + final typography = ArDriveTypographyNew.of(context); + return ArDriveStandardModalNew( actions: [ if (state.progress.hasUploadInProgress) ModalAction( @@ -1021,7 +979,7 @@ class _UploadFormState extends State { builder: (context, state) { if (state is UploadComplete) { // TODO: localize - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: 'Upload complete', description: 'Your upload is complete. You can not cancel it anymore.', @@ -1039,7 +997,7 @@ class _UploadFormState extends State { ); } // TODO: localize - return ArDriveStandardModal( + return ArDriveStandardModalNew( title: 'Warning', description: 'Cancelling this upload may still result in a charge to your wallet. Do you still wish to proceed?', @@ -1181,25 +1139,18 @@ class _UploadFormState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - flex: 1, + Expanded( + flex: 3, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( file.name, - style: ArDriveTypography.body - .buttonNormalBold( - color: - ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ) - .copyWith( - fontWeight: - FontWeight.bold), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.bold, + color: colorTokens.textMid, + ), ), AnimatedSwitcher( duration: @@ -1211,14 +1162,13 @@ class _UploadFormState extends State { Flexible( child: Text( status, - style: ArDriveTypography - .body - .buttonNormalBold( - color: ArDriveTheme - .of(context) - .themeData - .colors - .themeFgOnDisabled, + style: typography + .paragraphNormal( + fontWeight: + ArFontWeight + .semiBold, + color: colorTokens + .textMid, ), ), ), @@ -1230,18 +1180,16 @@ class _UploadFormState extends State { if (progressText != null) Text( progressText, - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgOnDisabled, + style: typography.paragraphNormal( + fontWeight: + ArFontWeight.semiBold, + color: colorTokens.textLow, ), ), ], ), ), - Flexible( + Expanded( flex: 1, child: Row( crossAxisAlignment: @@ -1252,30 +1200,8 @@ class _UploadFormState extends State { children: [ if (task.isProgressAvailable && statusAvailableForShowingProgress) ...[ - Flexible( - flex: 2, - child: ArDriveProgressBar( - height: 4, - indicatorColor: - _getUploadStatusColor( - context, - task, - ), - percentage: task.progress, - ), - ), - Flexible( - child: Text( - '${(task.progress * 100).toInt()}%', - style: ArDriveTypography.body - .buttonNormalBold( - color: - ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault, - ), - ), + CircularProgressWidget( + progress: task.progress, ), ], if (!task.isProgressAvailable || @@ -1317,34 +1243,26 @@ class _UploadFormState extends State { ), // TODO: localize Text( - 'Total uploaded: ${filesize(state.progress.totalUploaded)} of ${filesize(state.progress.totalSize)}', - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault) - .copyWith(fontWeight: FontWeight.bold), - ), + 'Total uploaded: ${filesize(state.progress.totalUploaded)} of ${filesize(state.progress.totalSize)}', + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + color: colorTokens.textMid, + )), // TODO: localize Text( - 'Files uploaded: ${state.progress.numberOfUploadedItems} of ${state.progress.numberOfItems}', - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault) - .copyWith(fontWeight: FontWeight.bold), - ), + 'Files uploaded: ${state.progress.numberOfUploadedItems} of ${state.progress.numberOfItems}', + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + color: colorTokens.textMid, + )), // TODO: localize if (state.progress.hasUploadInProgress) Text( - 'Upload speed: ${filesize(state.progress.calculateUploadSpeed().toInt())}/s', - style: ArDriveTypography.body.buttonNormalBold( - color: - ArDriveTheme.of(context).themeData.colors.themeFgDefault), - ), + 'Upload speed: ${filesize(state.progress.calculateUploadSpeed().toInt())}/s', + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + color: colorTokens.textMid, + )), ], ), ); @@ -1444,19 +1362,6 @@ class _UploadFormState extends State { ), ); } - - Color _getUploadStatusColor( - BuildContext context, UploadTask uploadStatusColor) { - final themeColors = ArDriveTheme.of(context).themeData.colors; - - if (uploadStatusColor.status == UploadStatus.failed) { - return themeColors.themeErrorDefault; - } else if (uploadStatusColor.progress == 1) { - return themeColors.themeSuccessDefault; - } else { - return themeColors.themeFgDefault; - } - } } class StatsScreen extends StatefulWidget { @@ -1522,6 +1427,10 @@ class _StatsScreenState extends State { @override Widget build(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return UploadReadyModalBase( readyState: widget.readyState, hasCloseButton: widget.hasCloseButton, @@ -1533,74 +1442,64 @@ class _StatsScreenState extends State { : Align( alignment: Alignment.topCenter, child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 120), + constraints: const BoxConstraints(maxHeight: 156), child: ArDriveScrollBar( controller: _scrollController, alwaysVisible: true, child: ListView.builder( - padding: const EdgeInsets.only(top: 0), + padding: const EdgeInsets.only(right: 8), controller: _scrollController, shrinkWrap: true, itemCount: files!.length, itemBuilder: (BuildContext context, int index) { final file = files![index]; if (file is FileV2UploadHandle) { - return Row( - children: [ - Flexible( - child: Text( - '${file.entity.name!} ', - style: ArDriveTypography.body.smallBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgSubtle, - ), - ), + return ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + file.entity.name!, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.bold, ), - Text( - filesize(file.size), - style: ArDriveTypography.body.smallRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgMuted, - ), + ), + leading: Text( + filesize(file.size), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, ), - ], + ), + trailing: getIconForContentType( + file.entity.dataContentType ?? '', + ), ); } else { final bundle = file as BundleUploadHandle; return ListView( - padding: EdgeInsets.zero, + padding: const EdgeInsets.only(right: 8), shrinkWrap: true, children: bundle.fileEntities.map((e) { - return Row( - children: [ - Flexible( - child: Text( - '${e.name!} ', - style: - ArDriveTypography.body.smallBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgSubtle, - ), - ), + final file = e; + return ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + file.name!, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.bold, + color: colorTokens.textMid, ), - Text( - filesize(e.size), - style: - ArDriveTypography.body.smallRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgMuted, - ), + ), + trailing: Text( + filesize(file.size), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + color: colorTokens.textMid, ), - ], + ), + leading: getIconForContentType( + file.dataContentType ?? '', + color: colorTokens.textMid, + ), ); }).toList()); } @@ -1614,43 +1513,37 @@ class _StatsScreenState extends State { children: [ TextSpan( text: 'Size: ', - style: ArDriveTypography.body.buttonNormalRegular( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgOnDisabled, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + color: colorTokens.textMid, ), ), TextSpan( text: filesize( widget.readyState.paymentInfo.totalSize, ), - style: ArDriveTypography.body - .buttonNormalBold( - color: ArDriveTheme.of(context) - .themeData - .colors - .themeFgDefault) - .copyWith(fontWeight: FontWeight.bold), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.bold, + color: colorTokens.textHigh, + ), ), ], ), ), - Text.rich( - TextSpan( - children: [ - if (widget.readyState.paymentInfo.isFreeThanksToTurbo) ...[ - TextSpan( - text: appLocalizationsOf(context).freeTurboTransaction, - style: ArDriveTypography.body.buttonNormalRegular(), - ), - ] - ], - style: ArDriveTypography.body.buttonNormalRegular(), - ), + const Divider( + height: 20, ), - const SizedBox(height: 10), - const Divider(height: 10), + if (widget.readyState.paymentInfo.isFreeThanksToTurbo) ...[ + const SizedBox(height: 8), + Text( + appLocalizationsOf(context).freeTurboTransaction, + style: typography.paragraphNormal( + color: colorTokens.textMid, + fontWeight: ArFontWeight.bold, + ), + ), + const SizedBox(height: 20), + ], ...widget.children, ], ); @@ -1709,7 +1602,7 @@ class ConfiguringLicenseScreen extends StatelessWidget { } } -class UploadReadyModalBase extends StatelessWidget { +class UploadReadyModalBase extends StatefulWidget { final UploadReady readyState; final List actions; final List children; @@ -1723,29 +1616,55 @@ class UploadReadyModalBase extends StatelessWidget { required this.actions, required this.children, this.hasCloseButton = true, - this.width = 408, + this.width = 440, }); + @override + State createState() => _UploadReadyModalBaseState(); +} + +class _UploadReadyModalBaseState extends State { @override Widget build(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); return ArDriveScrollBar( - child: SingleChildScrollView( - child: ArDriveStandardModal( - title: appLocalizationsOf(context) - .uploadNFiles(readyState.numberOfFiles), - width: width, - hasCloseButton: hasCloseButton, + child: SingleChildScrollView(child: + BlocBuilder(builder: (context, state) { + return ArDriveStandardModalNew( + width: widget.width, + hasCloseButton: widget.hasCloseButton, content: ConstrainedBox( - constraints: const BoxConstraints(minHeight: 185), + constraints: const BoxConstraints( + minHeight: 185, + ), child: Column( - mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: children, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + flex: 1, + child: Text( + appLocalizationsOf(context) + .uploadNFiles(widget.readyState.numberOfFiles), + style: typography.heading3( + fontWeight: ArFontWeight.bold, + ), + ), + ), + ], + ), + const SizedBox( + height: 20, + ), + ...widget.children, + ], ), ), - actions: actions, - ), - ), + actions: widget.actions, + ); + })), ); } } @@ -1882,3 +1801,124 @@ class _LicenseNameWithPopoverButtonState ); } } + +class UploadReadyModal extends StatefulWidget { + const UploadReadyModal({super.key}); + + @override + State createState() => _UploadReadyModalState(); +} + +class _UploadReadyModalState extends State { + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UploadReady) { + return ReactiveForm( + formGroup: context.watch().licenseCategoryForm, + child: ReactiveFormConsumer(builder: (_, form, __) { + final LicenseCategory? licenseCategory = + form.control('licenseCategory').value; + return Flexible( + flex: 1, + child: StatsScreen( + readyState: state, + // Don't show on first screen? + hasCloseButton: false, + modalActions: [ + ModalAction( + action: () => Navigator.of(context).pop(false), + title: appLocalizationsOf(context).cancelEmphasized, + ), + licenseCategory == null + ? ModalAction( + isEnable: state.isNextButtonEnabled, + action: () { + context.read().initialScreenUpload(); + }, + title: appLocalizationsOf(context).uploadEmphasized, + ) + : ModalAction( + isEnable: state.isNextButtonEnabled, + action: () { + context.read().initialScreenNext( + licenseCategory: licenseCategory, + ); + }, + title: + // TODO: Localize + // appLocalizationsOf(context).configureEmphasized, + 'CONFIGURE', + ), + ], + children: [ + RepositoryProvider.value( + value: context.read(), + child: UploadPaymentMethodView( + onError: () { + context + .read() + .emitErrorFromPreparation(); + }, + onTurboTopupSucess: () { + context.read().startUploadPreparation( + isRetryingToPayWithTurbo: true, + ); + }, + useNewArDriveUI: true, + onUploadMethodChanged: (method, info, canUpload) { + context + .read() + .setUploadMethod(method, info, canUpload); + }, + ), + ), + ], + ), + ); + }), + ); + } else { + return const SizedBox(); + } + }, + ); + } +} + +class CircularProgressWidget extends StatelessWidget { + final double progress; // progress value from 0 to 100 + + const CircularProgressWidget({super.key, required this.progress}); + + @override + Widget build(BuildContext context) { + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + final typography = ArDriveTypographyNew.of(context); + return Stack( + alignment: Alignment.center, + children: [ + SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator( + value: progress, + strokeWidth: 4, + valueColor: AlwaysStoppedAnimation(colorTokens.strokeRed), + backgroundColor: Colors.grey[850], // or any other background color + ), + ), + Center( + child: Text( + '${(progress * 100).toInt()}%', + style: typography.paragraphSmall( + color: colorTokens.textHigh, + fontWeight: ArFontWeight.semiBold, + ), + ), + ), + ], + ); + } +} diff --git a/lib/pages/congestion_warning_wrapper.dart b/lib/pages/congestion_warning_wrapper.dart index 691c3c73fd..5eb255d442 100644 --- a/lib/pages/congestion_warning_wrapper.dart +++ b/lib/pages/congestion_warning_wrapper.dart @@ -27,7 +27,7 @@ Future showCongestionDependentModalDialog( bool shouldShowDialog = false; await showArDriveDialog( context, - content: ArDriveStandardModal( + content: ArDriveStandardModalNew( title: appLocalizationsOf(context).warningEmphasized, content: SizedBox( width: kMediumDialogWidth, diff --git a/lib/pages/drive_detail/components/drive_explorer_item_tile.dart b/lib/pages/drive_detail/components/drive_explorer_item_tile.dart index 17abdc2582..b6f5003271 100644 --- a/lib/pages/drive_detail/components/drive_explorer_item_tile.dart +++ b/lib/pages/drive_detail/components/drive_explorer_item_tile.dart @@ -42,15 +42,17 @@ class DriveExplorerItemTile extends TableRowWidget { padding: const EdgeInsets.only(right: 8), child: Row( children: [ - Text( - name, - style: typography.paragraphNormal( - color: isHidden ? Colors.grey : null, - fontWeight: ArFontWeight.semiBold, + Flexible( + child: Text( + name, + style: typography.paragraphNormal( + color: isHidden ? Colors.grey : null, + fontWeight: ArFontWeight.semiBold, + ), + overflow: TextOverflow.fade, + maxLines: 1, + softWrap: false, ), - overflow: TextOverflow.fade, - maxLines: 1, - softWrap: false, ), if (dataTableItem is FileDataTableItem && dataTableItem.assignedNames != null && diff --git a/packages/ardrive_ui/lib/src/components/modal.dart b/packages/ardrive_ui/lib/src/components/modal.dart index 3647fba9db..3f8d7e05ac 100644 --- a/packages/ardrive_ui/lib/src/components/modal.dart +++ b/packages/ardrive_ui/lib/src/components/modal.dart @@ -466,6 +466,7 @@ class ArDriveStandardModal extends StatelessWidget { ArDriveButton( maxHeight: buttonActionHeight, style: ArDriveButtonStyle.secondary, + maxWidth: actions[0].customWidth, backgroundColor: ArDriveTheme.of(context).themeData.colors.themeFgDefault, fontStyle: ArDriveTypography.body @@ -487,6 +488,7 @@ class ArDriveStandardModal extends StatelessWidget { ? ArDriveButtonStyle.secondary : ArDriveButtonStyle.primary, maxHeight: buttonActionHeight, + maxWidth: actions[0].customWidth, backgroundColor: ArDriveTheme.of(context).themeData.colors.themeFgDefault, fontStyle: ArDriveTypography.body @@ -547,11 +549,15 @@ class ModalAction { required this.action, required this.title, this.isEnable = true, + this.customWidth, + this.customHeight, }); final String title; final dynamic Function() action; final bool isEnable; + final double? customWidth; + final double? customHeight; } Future showAnimatedDialog( @@ -781,8 +787,8 @@ class ArDriveStandardModalNew extends StatelessWidget { children: [ if (actions.isNotEmpty) ArDriveButtonNew( - maxHeight: 40, - maxWidth: 100, + maxHeight: actions[0].customHeight ?? 40, + maxWidth: actions[0].customWidth ?? 100, typography: typography, variant: actions.length > 1 ? ButtonVariant.secondary @@ -794,11 +800,11 @@ class ArDriveStandardModalNew extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 16), child: ArDriveButtonNew( - maxWidth: 100, + maxHeight: actions[1].customHeight ?? 40, + maxWidth: actions[1].customWidth ?? 110, variant: actions.length > 2 ? ButtonVariant.secondary : ButtonVariant.primary, - maxHeight: 40, isDisabled: !actions[1].isEnable, text: actions[1].title, onPressed: actions[1].action, @@ -810,10 +816,8 @@ class ArDriveStandardModalNew extends StatelessWidget { padding: const EdgeInsets.only(left: 16), child: ArDriveButtonNew( typography: typography, - variant: ButtonVariant.secondary, + variant: ButtonVariant.primary, maxHeight: 40, - backgroundColor: - ArDriveTheme.of(context).themeData.colors.themeFgDefault, fontStyle: ArDriveTypography.body .buttonNormalRegular( color: actions.length > 2 diff --git a/packages/ardrive_uploader/lib/src/upload_controller.dart b/packages/ardrive_uploader/lib/src/upload_controller.dart index d5d18d8142..97ecf5442a 100644 --- a/packages/ardrive_uploader/lib/src/upload_controller.dart +++ b/packages/ardrive_uploader/lib/src/upload_controller.dart @@ -371,6 +371,7 @@ class _UploadController implements UploadController { final cancelableTask = tasks.values .where((e) => + e.status != UploadStatus.assigningUndername && e.status != UploadStatus.complete && e.status != UploadStatus.failed) .toList(); diff --git a/pubspec.yaml b/pubspec.yaml index d23c5993d4..e847326136 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Secure, permanent storage publish_to: 'none' -version: 2.53.1 +version: 2.54.0 environment: sdk: '>=3.2.0 <4.0.0'