From 424b5c83b6506c3b124628fd2744053b0b01aa19 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:44:04 -0300 Subject: [PATCH 01/11] start work --- lib/blocs/upload/upload_cubit.dart | 128 ++++- lib/blocs/upload/upload_state.dart | 29 +- lib/components/upload_form.dart | 459 +++++++++++++----- .../ardrive_ui/lib/src/components/button.dart | 5 +- 4 files changed, 497 insertions(+), 124 deletions(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index f99565324..c13880053 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -105,6 +105,9 @@ class UploadCubit extends Cubit { List _manifestFiles = []; final List _selectedManifestFiles = []; + Map _arnsUndernamesLinkedToManifest = {}; + Map _arnsAntRecordsLinkedToManifest = {}; + UploadMethod? _manifestUploadMethod; bool _isManifestsUploadCancelled = false; @@ -112,19 +115,68 @@ class UploadCubit extends Cubit { void selectManifestFile(FileEntry file) { final readyState = state as UploadReady; + final manifestModel = UploadManifestModel( + name: file.name, + isCompleted: false, + freeThanksToTurbo: + file.size <= configService.config.allowedDataItemSizeForTurbo, + isUploading: false, + existingManifestFileId: file.id, + undername: _arnsUndernamesLinkedToManifest[file.id], + antRecord: _arnsAntRecordsLinkedToManifest[file.id], + ); + final newReadyState = readyState.copyWith( - selectedManifests: List.of(_selectedManifestFiles)..add(file)); + selectedManifests: List.of( + _selectedManifestFiles + .map( + (f) => UploadManifestModel( + name: f.name, + isCompleted: false, + freeThanksToTurbo: + f.size <= configService.config.allowedDataItemSizeForTurbo, + isUploading: false, + existingManifestFileId: f.id, + undername: _arnsUndernamesLinkedToManifest[f.id], + antRecord: _arnsAntRecordsLinkedToManifest[f.id], + ), + ) + .toList(), + )..add(manifestModel)); _selectedManifestFiles.add(file); emit(newReadyState); } + void linkManifestToUndername( + FileEntry file, ANTRecord antRecord, ARNSUndername undername) { + _arnsUndernamesLinkedToManifest[file.id] = undername; + _arnsAntRecordsLinkedToManifest[file.id] = antRecord; + } + + void unlinkManifestToUndername(FileEntry file) { + _arnsUndernamesLinkedToManifest.remove(file.id); + } + void unselectManifestFile(FileEntry file) { _selectedManifestFiles.remove(file); - emit((state as UploadReady) - .copyWith(selectedManifests: _selectedManifestFiles)); + emit((state as UploadReady).copyWith( + selectedManifests: _selectedManifestFiles + .map( + (f) => UploadManifestModel( + name: f.name, + isCompleted: false, + freeThanksToTurbo: + f.size <= configService.config.allowedDataItemSizeForTurbo, + isUploading: false, + existingManifestFileId: f.id, + undername: _arnsUndernamesLinkedToManifest[f.id], + antRecord: _arnsAntRecordsLinkedToManifest[f.id], + ), + ) + .toList())); } void setManifestUploadMethod( @@ -142,6 +194,8 @@ class UploadCubit extends Cubit { f.size <= configService.config.allowedDataItemSizeForTurbo, isUploading: false, existingManifestFileId: f.id, + undername: _arnsUndernamesLinkedToManifest[f.id], + antRecord: _arnsAntRecordsLinkedToManifest[f.id], ), ) .toList(); @@ -222,12 +276,28 @@ class UploadCubit extends Cubit { method: _manifestUploadMethod, ); - manifestModels[i] = - manifestModels[i].copyWith(isCompleted: true, isUploading: false); + if (manifestModels[i].undername != null) { + manifestModels[i] = manifestModels[i].copyWith( + isCompleted: false, isUploading: false, isAssigningUndername: true); + emit(UploadingManifests( + manifestFiles: manifestModels, + completedCount: ++completedCount, + )); + + await _arnsRepository.setUndernamesToFile( + undername: manifestModels[i].undername!, + driveId: _driveId, + fileId: manifestModels[i].existingManifestFileId!, + processId: manifestModels[i].antRecord!.processId, + ); + } + + manifestModels[i] = manifestModels[i].copyWith( + isCompleted: true, isUploading: false, isAssigningUndername: false); emit(UploadingManifests( manifestFiles: manifestModels, - completedCount: ++completedCount, + completedCount: completedCount, )); } @@ -272,6 +342,7 @@ class UploadCubit extends Cubit { /// ArNS ANTRecord? _selectedAntRecord; + List _ants = []; ARNSUndername? _selectedUndername; /// Thumbnail upload @@ -403,10 +474,24 @@ class UploadCubit extends Cubit { loadingArNSNames: true, arnsCheckboxChecked: _showArnsNameSelectionCheckBoxValue, totalSize: await _getTotalSize(), - selectedManifests: _selectedManifestFiles, + selectedManifests: _selectedManifestFiles + .map( + (f) => UploadManifestModel( + name: f.name, + isCompleted: false, + freeThanksToTurbo: f.size <= + configService.config.allowedDataItemSizeForTurbo, + isUploading: false, + existingManifestFileId: f.id, + undername: _arnsUndernamesLinkedToManifest[f.id], + antRecord: _arnsAntRecordsLinkedToManifest[f.id], + ), + ) + .toList(), showSettings: showSettings, canShowSettings: showSettings, manifestFiles: _manifestFiles, + arnsRecords: _ants, ), ); @@ -445,9 +530,23 @@ class UploadCubit extends Cubit { showArnsNameSelection: false, arnsCheckboxChecked: _showArnsNameSelectionCheckBoxValue, totalSize: await _getTotalSize(), - selectedManifests: _selectedManifestFiles, + selectedManifests: _selectedManifestFiles + .map( + (f) => UploadManifestModel( + name: f.name, + isCompleted: false, + freeThanksToTurbo: f.size <= + configService.config.allowedDataItemSizeForTurbo, + isUploading: false, + existingManifestFileId: f.id, + undername: _arnsUndernamesLinkedToManifest[f.id], + antRecord: _arnsAntRecordsLinkedToManifest[f.id], + ), + ) + .toList(), showSettings: showSettings, manifestFiles: _manifestFiles, + arnsRecords: _ants, canShowSettings: showSettings, ), ); @@ -815,11 +914,22 @@ class UploadCubit extends Cubit { emit(UploadLoadingFilesSuccess()); } + Future> getARNSUndernames( + ANTRecord antRecord, + ) async { + return _arnsRepository.getARNSUndernames(antRecord); + } + Future startUploadPreparation({ bool isRetryingToPayWithTurbo = false, }) async { final walletAddress = await _auth.getWalletAddress(); - _arnsRepository.getAntRecordsForWallet(walletAddress!); + _arnsRepository.getAntRecordsForWallet(walletAddress!).then((value) { + _ants = value; + if (state is UploadReady) { + emit((state as UploadReady).copyWith(arnsRecords: value)); + } + }); _files .removeWhere((file) => filesNamesToExclude.contains(file.ioFile.name)); diff --git a/lib/blocs/upload/upload_state.dart b/lib/blocs/upload/upload_state.dart index 340c9c7a9..cc8e096bd 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -107,10 +107,11 @@ class UploadReady extends UploadState { final bool loadingArNSNamesError; final bool arnsCheckboxChecked; final int totalSize; - final List selectedManifests; + final List selectedManifests; final List manifestFiles; final bool showSettings; final bool canShowSettings; + final List arnsRecords; final bool isArConnect; @@ -132,6 +133,7 @@ class UploadReady extends UploadState { required this.showSettings, required this.canShowSettings, required this.manifestFiles, + required this.arnsRecords, }); // copyWith @@ -151,9 +153,10 @@ class UploadReady extends UploadState { bool? loadingArNSNamesError, bool? arnsCheckboxChecked, int? totalSize, - List? selectedManifests, + List? selectedManifests, List? manifestFiles, bool? canShowSettings, + List? arnsRecords, }) { return UploadReady( loadingArNSNames: loadingArNSNames ?? this.loadingArNSNames, @@ -175,6 +178,7 @@ class UploadReady extends UploadState { showSettings: showSettings ?? this.showSettings, manifestFiles: manifestFiles ?? this.manifestFiles, canShowSettings: canShowSettings ?? this.canShowSettings, + arnsRecords: arnsRecords ?? this.arnsRecords, ); } @@ -304,7 +308,7 @@ class UploadingManifests extends UploadState { }); @override - List get props => [manifestFiles, completedCount]; + List get props => [UniqueKey()]; } class UploadWalletMismatch extends UploadState {} @@ -347,11 +351,13 @@ class UploadManifestSelectPaymentMethod extends UploadState { class UploadManifestModel extends Equatable { final String name; final bool isCompleted; + final bool isAssigningUndername; final bool freeThanksToTurbo; final bool isUploading; final String? existingManifestFileId; final IOFile? file; - + final ARNSUndername? undername; + final ANTRecord? antRecord; const UploadManifestModel({ required this.name, this.isCompleted = false, @@ -359,6 +365,9 @@ class UploadManifestModel extends Equatable { this.isUploading = false, this.existingManifestFileId, this.file, + this.undername, + this.antRecord, + this.isAssigningUndername = false, }); UploadManifestModel copyWith({ @@ -367,6 +376,9 @@ class UploadManifestModel extends Equatable { String? existingManifestFileId, bool? freeThanksToTurbo, IOFile? file, + ARNSUndername? undername, + ANTRecord? antRecord, + bool? isAssigningUndername, }) { return UploadManifestModel( name: name, @@ -376,6 +388,9 @@ class UploadManifestModel extends Equatable { existingManifestFileId ?? this.existingManifestFileId, freeThanksToTurbo: freeThanksToTurbo ?? this.freeThanksToTurbo, file: file ?? this.file, + undername: undername ?? this.undername, + antRecord: antRecord ?? this.antRecord, + isAssigningUndername: isAssigningUndername ?? this.isAssigningUndername, ); } @@ -385,7 +400,11 @@ class UploadManifestModel extends Equatable { isCompleted, isUploading, existingManifestFileId, - freeThanksToTurbo + freeThanksToTurbo, + isAssigningUndername, + antRecord, + undername, + file, ]; } diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index d5783dc19..15e8f4802 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -330,31 +330,43 @@ class _UploadingManifestsWidget extends StatelessWidget { itemCount: state.manifestFiles.length, shrinkWrap: true, itemBuilder: (context, index) { - return Row( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - ArDriveIcons.manifest(size: 16), - const SizedBox(width: 8), - Text( - '${state.manifestFiles[index].name}...', - style: typography.paragraphNormal( - fontWeight: ArFontWeight.semiBold, - ), - ), - const Spacer(), - if (state.manifestFiles[index].isUploading) ...[ - const SizedBox(width: 8), - const SizedBox( - height: 16, - width: 16, - child: CircularProgressIndicator( - strokeWidth: 2, + Row( + children: [ + ArDriveIcons.manifest(size: 16), + const SizedBox(width: 8), + Text( + '${state.manifestFiles[index].name}...', + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), ), + const Spacer(), + if (state.manifestFiles[index].isUploading) ...[ + const SizedBox(width: 8), + const SizedBox( + height: 16, + width: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + ], + if (state.manifestFiles[index].isCompleted) ...[ + const SizedBox(width: 8), + ArDriveIcons.checkCirle(size: 16), + ], + ], + ), + if (state.manifestFiles[index].isAssigningUndername) ...[ + const SizedBox(height: 2), + Text( + 'Assigning ArNS Name...', + style: typography.paragraphNormal(), ), ], - if (state.manifestFiles[index].isCompleted) ...[ - const SizedBox(width: 8), - ArDriveIcons.checkCirle(size: 16), - ], ], ); }, @@ -1012,7 +1024,8 @@ class _UploadReadyModalBaseState extends State { {bool scrollable = true}) { return Padding( padding: const EdgeInsets.only(bottom: 42.0), - child: ListView.builder( + child: ListView.separated( + separatorBuilder: (context, index) => const SizedBox(height: 4), physics: scrollable ? const ScrollPhysics() : const NeverScrollableScrollPhysics(), @@ -1020,61 +1033,138 @@ class _UploadReadyModalBaseState extends State { itemCount: state.manifestFiles.length, itemBuilder: (context, index) { final file = state.manifestFiles[index]; - final hiddenColor = - ArDriveTheme.of(context).themeData.colors.themeFgDisabled; - return Row( - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - flex: 2, - child: Row( - children: [ - ArDriveIcons.manifest( - size: 16, color: file.isHidden ? hiddenColor : null), - const SizedBox(width: 8), - Text( - file.name, - style: typography.paragraphNormal( - color: file.isHidden ? hiddenColor : null, + return _ManifestOptionTile( + file: file, + state: state, + ); + }, + ), + ); + } +} + +class _ManifestOptionTile extends StatefulWidget { + final UploadReady state; + final FileEntry file; + + const _ManifestOptionTile({ + required this.state, + required this.file, + }); + + @override + State<_ManifestOptionTile> createState() => __ManifestOptionTileState(); +} + +class __ManifestOptionTileState extends State<_ManifestOptionTile> { + bool _isExpanded = false; + + @override + Widget build(BuildContext context) { + final hiddenColor = + ArDriveTheme.of(context).themeData.colors.themeFgDisabled; + final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + decoration: BoxDecoration( + color: colorTokens.containerL2, + borderRadius: BorderRadius.circular(5), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + height: _isExpanded ? 130 : 50, + child: GestureDetector( + onTap: () { + setState(() { + _isExpanded = !_isExpanded; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Row( + children: [ + Flexible( + flex: 2, + child: Row( + children: [ + ArDriveIcons.manifest( + size: 16, + color: + widget.file.isHidden ? hiddenColor : null), + const SizedBox(width: 8), + Text( + widget.file.name, + style: typography.paragraphNormal( + color: + widget.file.isHidden ? hiddenColor : null, + ), + ), + if (widget.file.isHidden) ...[ + const SizedBox(width: 8), + Text('(hidden)', + style: typography.paragraphNormal( + color: hiddenColor, + )) + ] + ], + ), ), - ), - if (file.isHidden) ...[ - const SizedBox(width: 8), - Text('(hidden)', - style: typography.paragraphNormal( - color: hiddenColor, - )) - ] - ], + Flexible( + flex: 1, + child: ArDriveCheckBox( + checked: widget.state.selectedManifests + .contains(widget.file), + onChange: (value) { + if (value) { + context + .read() + .selectManifestFile(widget.file); + } else { + context + .read() + .unselectManifestFile(widget.file); + } + }, + ), + ), + ], + ), ), - ), - Flexible( - flex: 1, - child: ArDriveCheckBox( - checked: state.selectedManifests - .contains(state.manifestFiles[index]), - onChange: (value) { - if (value) { - context - .read() - .selectManifestFile(state.manifestFiles[index]); - } else { - context - .read() - .unselectManifestFile(state.manifestFiles[index]); - } + ArDriveButtonNew( + text: 'Add ArNS', + typography: typography, + isDisabled: + !widget.state.selectedManifests.contains(widget.file), + fontStyle: typography.paragraphSmall(), + variant: ButtonVariant.primary, + maxWidth: 80, + maxHeight: 30, + onPressed: () { + setState(() { + _isExpanded = !_isExpanded; + }); }, - ), + ) + // TODO: Add back when we have the right UI for it + ], + ), + if (_isExpanded) ...[ + const SizedBox(height: 8), + Expanded( + flex: 1, + child: AntSelector(fileEntry: widget.file), ), - // TODO: Add back when we have the right UI for it - // const Expanded( - // flex: 1, - // child: AntSelector(), - // ), ], - ); - }, + ], + ), ), ); } @@ -2111,16 +2201,29 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { shrinkWrap: true, children: [ ...state.readyState.selectedManifests.map( - (e) => Row( + (e) => Column( children: [ - ArDriveIcons.manifest(size: 16), - const SizedBox(width: 8), - Text( - e.name, - style: typography.paragraphNormal( - fontWeight: ArFontWeight.semiBold, - ), + Row( + children: [ + ArDriveIcons.manifest(size: 16), + const SizedBox(width: 8), + Text( + e.name, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + ), + ], ), + if (e.undername != null) ...[ + const SizedBox(height: 4), + Text( + 'ArNS Name: ${getLiteralARNSRecordName(e.undername!)}', + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + ), + ], ], ), ), @@ -2913,35 +3016,119 @@ List getModalActions( } class AntSelector extends StatefulWidget { - const AntSelector({super.key}); + const AntSelector({super.key, required this.fileEntry}); + + final FileEntry fileEntry; @override State createState() => _AntSelectorState(); } class _AntSelectorState extends State { - final List ants = ['Ant 1', 'Ant 2', 'Ant 3']; - String _selectedAnt = 'Ant 1'; + ANTRecord? _selectedAnt; + ARNSUndername? _selectedUndername; + + List _arnsUndernames = []; + bool _loadingUndernames = false; + + loadARNSUndernames( + ANTRecord ant, + ) async { + setState(() { + _loadingUndernames = true; + }); + + _arnsUndernames = await context.read().getARNSUndernames(ant); + + setState(() { + _loadingUndernames = false; + }); + } @override Widget build(BuildContext context) { - return ArDriveDropdown( - height: 45, - items: ants.map((ant) => _buildDropdownItem(context, ant)).toList(), - child: ArDriveClickArea( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - children: [ - _buildSelectedItem(context), - ], - ), - ArDriveIcons.chevronDown(), - ], - ), - ), + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return BlocBuilder( + builder: (context, state) { + if (state is UploadReady) { + return Column( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: colorTokens.inputDisabled, + border: Border.all( + color: colorTokens.textXLow, + width: 1, + ), + ), + child: ArDriveDropdown( + height: 45, + items: state.arnsRecords + .map((ant) => _buildDropdownItem(context, ant)) + .toList(), + child: ArDriveClickArea( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + children: [ + _buildSelectedItem(context), + ], + ), + ArDriveIcons.chevronDown(), + ], + ), + ), + ), + ), + if (_loadingUndernames) const CircularProgressIndicator(), + if (!_loadingUndernames && _arnsUndernames.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 8), + child: Container( + alignment: Alignment.centerLeft, + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: colorTokens.inputDisabled, + border: Border.all( + color: colorTokens.textXLow, + width: 1, + ), + ), + child: ArDriveDropdown( + height: 45, + items: _arnsUndernames + .map((undername) => + _buildDropdownItemUndername(context, undername)) + .toList(), + child: ArDriveClickArea( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + children: [ + _buildSelectedItemUndername(context), + ], + ), + ArDriveIcons.chevronDown(), + ], + ), + ), + ), + ), + ), + ], + ); + } + return const SizedBox(); + }, ); } @@ -2949,32 +3136,45 @@ class _AntSelectorState extends State { final typography = ArDriveTypographyNew.of(context); return Text( - _selectedAnt, - style: typography.paragraphLarge( + _selectedAnt?.domain ?? 'Choose ArNS name', + style: typography.paragraphSmall( fontWeight: ArFontWeight.semiBold, ), ); } - ArDriveDropdownItem _buildDropdownItem(BuildContext context, String ant) { + Widget _buildSelectedItemUndername(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + + return Text( + _selectedUndername?.name ?? 'under_name (optional)', + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ); + } + + ArDriveDropdownItem _buildDropdownItem(BuildContext context, ANTRecord ant) { final typography = ArDriveTypographyNew.of(context); return ArDriveDropdownItem( content: SizedBox( - width: 164, + width: 235, height: 45, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - ant, - style: typography.paragraphLarge( - fontWeight: ArFontWeight.semiBold, + Flexible( + child: Text( + ant.domain, + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), ), ), - if (ant == 'Ant 1') + if (ant.domain == _selectedAnt?.domain) ArDriveIcons.checkmark( size: 16, ) @@ -2985,6 +3185,49 @@ class _AntSelectorState extends State { onClick: () { setState(() { _selectedAnt = ant; + + _arnsUndernames = []; + loadARNSUndernames(ant); + }); + }, + ); + } + + ArDriveDropdownItem _buildDropdownItemUndername( + BuildContext context, ARNSUndername undername) { + final typography = ArDriveTypographyNew.of(context); + + return ArDriveDropdownItem( + content: SizedBox( + width: 235, + height: 45, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + undername.name, + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ), + ), + if (undername.name == _selectedUndername?.name) + ArDriveIcons.checkmark( + size: 16, + ) + ], + ), + ), + ), + onClick: () { + setState(() { + _selectedUndername = undername; + + context.read().linkManifestToUndername( + widget.fileEntry, _selectedAnt!, _selectedUndername!); }); }, ); diff --git a/packages/ardrive_ui/lib/src/components/button.dart b/packages/ardrive_ui/lib/src/components/button.dart index 089fb77cb..4cd2e75dc 100644 --- a/packages/ardrive_ui/lib/src/components/button.dart +++ b/packages/ardrive_ui/lib/src/components/button.dart @@ -366,8 +366,9 @@ class _ArDriveButtonNewState extends State { final text = Text(widget.text, textAlign: TextAlign.center, - style: typography.paragraphLarge( - color: foregroundColor, fontWeight: ArFontWeight.semiBold)); + style: widget.fontStyle ?? + typography.paragraphLarge( + color: foregroundColor, fontWeight: ArFontWeight.semiBold)); final buttonH = widget.maxHeight ?? buttonDefaultHeight; From 55eb76948bbecd15fdcc5a4fc114d5dce97ba681 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:29:45 -0300 Subject: [PATCH 02/11] fix bugs --- lib/blocs/upload/upload_cubit.dart | 258 +++++++++++++++-------------- lib/blocs/upload/upload_state.dart | 21 +-- lib/components/upload_form.dart | 75 +++++++-- 3 files changed, 206 insertions(+), 148 deletions(-) diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index c13880053..0ae64fe7a 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -102,81 +102,103 @@ class UploadCubit extends Cubit { late final bool _isUploadFolders; /// Manifest - List _manifestFiles = []; - final List _selectedManifestFiles = []; + List _manifestFiles = []; + final List _selectedManifestFileIds = []; - Map _arnsUndernamesLinkedToManifest = {}; - Map _arnsAntRecordsLinkedToManifest = {}; + /// key is domain + /// value is undername name + final Map> _arnsUndernamesLinkedToManifest = {}; UploadMethod? _manifestUploadMethod; bool _isManifestsUploadCancelled = false; - void selectManifestFile(FileEntry file) { + void selectManifestFile(UploadManifestModel file) { final readyState = state as UploadReady; - final manifestModel = UploadManifestModel( - name: file.name, - isCompleted: false, - freeThanksToTurbo: - file.size <= configService.config.allowedDataItemSizeForTurbo, - isUploading: false, - existingManifestFileId: file.id, - undername: _arnsUndernamesLinkedToManifest[file.id], - antRecord: _arnsAntRecordsLinkedToManifest[file.id], - ); + // final manifestModel = UploadManifestModel( + // name: file.name, + // isCompleted: false, + // freeThanksToTurbo: + // file.size <= configService.config.allowedDataItemSizeForTurbo, + // isUploading: false, + // existingManifestFileId: file.id, + // undername: _arnsUndernamesLinkedToManifest[file.id], + // antRecord: _arnsAntRecordsLinkedToManifest[file.id], + // ); + + _selectedManifestFileIds.add(file.entry.id); final newReadyState = readyState.copyWith( - selectedManifests: List.of( - _selectedManifestFiles - .map( - (f) => UploadManifestModel( - name: f.name, - isCompleted: false, - freeThanksToTurbo: - f.size <= configService.config.allowedDataItemSizeForTurbo, - isUploading: false, - existingManifestFileId: f.id, - undername: _arnsUndernamesLinkedToManifest[f.id], - antRecord: _arnsAntRecordsLinkedToManifest[f.id], - ), - ) + selectedManifests: _selectedManifestFileIds + .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) .toList(), - )..add(manifestModel)); - - _selectedManifestFiles.add(file); + ); emit(newReadyState); } + Future isArNSNameAlreadyLinked( + {required ANTRecord record, ARNSUndername? undername}) async { + if (_arnsUndernamesLinkedToManifest[record.domain] == null) { + return false; + } + + final undernames = await getARNSUndernames(record); + final linkedUnderNames = _arnsUndernamesLinkedToManifest[record.domain]; + + if (linkedUnderNames == null) { + return undernames.length == 1; + } + + return linkedUnderNames.contains(undername?.name); + } + void linkManifestToUndername( - FileEntry file, ANTRecord antRecord, ARNSUndername undername) { - _arnsUndernamesLinkedToManifest[file.id] = undername; - _arnsAntRecordsLinkedToManifest[file.id] = antRecord; + UploadManifestModel file, ANTRecord antRecord, ARNSUndername? undername) { + final index = _manifestFiles + .indexWhere((element) => element.entry.id == file.entry.id); + _manifestFiles[index] = + file.copyWith(undername: undername, antRecord: antRecord); + if (_arnsUndernamesLinkedToManifest[antRecord.domain] == null) { + _arnsUndernamesLinkedToManifest[antRecord.domain] = []; + } + + if (undername != null) { + _arnsUndernamesLinkedToManifest[antRecord.domain]!.add(undername.name); + } } - void unlinkManifestToUndername(FileEntry file) { - _arnsUndernamesLinkedToManifest.remove(file.id); + void unlinkManifestToUndername(UploadManifestModel file) { + if (file.undername == null) { + return; + } + + final undername = file.undername!; + final antRecord = file.antRecord!; + + final index = _manifestFiles + .indexWhere((element) => element.entry.id == file.entry.id); + _manifestFiles[index] = UploadManifestModel( + entry: file.entry, + freeThanksToTurbo: file.freeThanksToTurbo, + existingManifestFileId: file.existingManifestFileId, + file: file.file, + undername: null, + antRecord: null, + ); + + _arnsUndernamesLinkedToManifest[antRecord.domain]?.remove(undername.name); } - void unselectManifestFile(FileEntry file) { - _selectedManifestFiles.remove(file); + void unselectManifestFile(UploadManifestModel file) { + _selectedManifestFileIds.remove(file.entry.id); emit((state as UploadReady).copyWith( - selectedManifests: _selectedManifestFiles - .map( - (f) => UploadManifestModel( - name: f.name, - isCompleted: false, - freeThanksToTurbo: - f.size <= configService.config.allowedDataItemSizeForTurbo, - isUploading: false, - existingManifestFileId: f.id, - undername: _arnsUndernamesLinkedToManifest[f.id], - antRecord: _arnsAntRecordsLinkedToManifest[f.id], - ), - ) - .toList())); + selectedManifests: _selectedManifestFileIds + .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) + .toList(), + )); } void setManifestUploadMethod( @@ -185,20 +207,10 @@ class UploadCubit extends Cubit { } Future prepareManifestUpload() async { - final manifestModels = _selectedManifestFiles - .map( - (f) => UploadManifestModel( - name: f.name, - isCompleted: false, - freeThanksToTurbo: - f.size <= configService.config.allowedDataItemSizeForTurbo, - isUploading: false, - existingManifestFileId: f.id, - undername: _arnsUndernamesLinkedToManifest[f.id], - antRecord: _arnsAntRecordsLinkedToManifest[f.id], - ), - ) + final manifestModels = _selectedManifestFileIds + .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) .toList(); + for (int i = 0; i < manifestModels.length; i++) { if (_isManifestsUploadCancelled) { break; @@ -207,7 +219,7 @@ class UploadCubit extends Cubit { manifestModels[i] = manifestModels[i].copyWith(isUploading: true); await _createManifestCubit.prepareManifestTx( - manifestName: manifestModels[i].name, + manifestName: manifestModels[i].entry.name, folderId: _targetFolder.id, existingManifestFileId: manifestModels[i].existingManifestFileId, ); @@ -262,7 +274,7 @@ class UploadCubit extends Cubit { )); await _createManifestCubit.prepareManifestTx( - manifestName: manifestModels[i].name, + manifestName: manifestModels[i].entry.name, folderId: _targetFolder.id, existingManifestFileId: manifestModels[i].existingManifestFileId, ); @@ -276,22 +288,46 @@ class UploadCubit extends Cubit { method: _manifestUploadMethod, ); - if (manifestModels[i].undername != null) { - manifestModels[i] = manifestModels[i].copyWith( - isCompleted: false, isUploading: false, isAssigningUndername: true); - emit(UploadingManifests( - manifestFiles: manifestModels, - completedCount: ++completedCount, - )); + final manifestFile = await _driveDao + .fileById( + driveId: _driveId, + fileId: manifestModels[i].existingManifestFileId, + ) + .getSingleOrNull(); - await _arnsRepository.setUndernamesToFile( - undername: manifestModels[i].undername!, - driveId: _driveId, - fileId: manifestModels[i].existingManifestFileId!, - processId: manifestModels[i].antRecord!.processId, + if (manifestFile == null) { + throw StateError('Manifest file not found'); + } + + ARNSUndername undername; + + if (manifestModels[i].undername == null) { + undername = ARNSUndername( + name: '@', + domain: manifestModels[i].antRecord!.domain, + record: ARNSRecord( + transactionId: manifestFile.dataTxId, + ttlSeconds: 3600, + ), ); + } else { + undername = manifestModels[i].undername!; } + manifestModels[i] = manifestModels[i].copyWith( + isCompleted: false, isUploading: false, isAssigningUndername: true); + emit(UploadingManifests( + manifestFiles: manifestModels, + completedCount: ++completedCount, + )); + + await _arnsRepository.setUndernamesToFile( + undername: undername, + driveId: _driveId, + fileId: manifestModels[i].existingManifestFileId, + processId: manifestModels[i].antRecord!.processId, + ); + manifestModels[i] = manifestModels[i].copyWith( isCompleted: true, isUploading: false, isAssigningUndername: false); @@ -301,16 +337,12 @@ class UploadCubit extends Cubit { )); } - emit(UploadComplete( - manifestFiles: _selectedManifestFiles, - )); + emit(UploadComplete()); } void cancelManifestsUpload() { _isManifestsUploadCancelled = true; - emit(UploadComplete( - manifestFiles: _selectedManifestFiles, - )); + emit(UploadComplete()); } /// License forms @@ -474,19 +506,8 @@ class UploadCubit extends Cubit { loadingArNSNames: true, arnsCheckboxChecked: _showArnsNameSelectionCheckBoxValue, totalSize: await _getTotalSize(), - selectedManifests: _selectedManifestFiles - .map( - (f) => UploadManifestModel( - name: f.name, - isCompleted: false, - freeThanksToTurbo: f.size <= - configService.config.allowedDataItemSizeForTurbo, - isUploading: false, - existingManifestFileId: f.id, - undername: _arnsUndernamesLinkedToManifest[f.id], - antRecord: _arnsAntRecordsLinkedToManifest[f.id], - ), - ) + selectedManifests: _selectedManifestFileIds + .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) .toList(), showSettings: showSettings, canShowSettings: showSettings, @@ -530,19 +551,8 @@ class UploadCubit extends Cubit { showArnsNameSelection: false, arnsCheckboxChecked: _showArnsNameSelectionCheckBoxValue, totalSize: await _getTotalSize(), - selectedManifests: _selectedManifestFiles - .map( - (f) => UploadManifestModel( - name: f.name, - isCompleted: false, - freeThanksToTurbo: f.size <= - configService.config.allowedDataItemSizeForTurbo, - isUploading: false, - existingManifestFileId: f.id, - undername: _arnsUndernamesLinkedToManifest[f.id], - antRecord: _arnsAntRecordsLinkedToManifest[f.id], - ), - ) + selectedManifests: _selectedManifestFileIds + .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) .toList(), showSettings: showSettings, manifestFiles: _manifestFiles, @@ -558,7 +568,7 @@ class UploadCubit extends Cubit { if (state is UploadReady) { if (_showArnsNameSelectionCheckBoxValue) { showArnsNameSelection(state as UploadReady); - } else if (_selectedManifestFiles.isNotEmpty) { + } else if (_selectedManifestFileIds.isNotEmpty) { emit(UploadReview(readyState: state as UploadReady)); } else { final readyState = state as UploadReady; @@ -1052,11 +1062,21 @@ class UploadCubit extends Cubit { ), ); - _manifestFiles = await _manifestRepository.getManifestFilesInFolder( + final manifestFileEntries = + await _manifestRepository.getManifestFilesInFolder( folderId: _targetFolder.id, driveId: _targetDrive.id, ); + _manifestFiles = manifestFileEntries + .map((e) => UploadManifestModel( + entry: e, + existingManifestFileId: e.id, + freeThanksToTurbo: + e.size <= configService.config.allowedDataItemSizeForTurbo, + )) + .toList(); + // if there are no files that can be used to generate a thumbnail, we disable the option if (!containsSupportedImageTypeForThumbnailGeneration) { _uploadThumbnail = false; @@ -1281,13 +1301,11 @@ class UploadCubit extends Cubit { ); } - if (_selectedManifestFiles.isNotEmpty) { + if (_selectedManifestFileIds.isNotEmpty) { await prepareManifestUpload(); } - emit(UploadComplete( - manifestFiles: _selectedManifestFiles, - )); + emit(UploadComplete()); unawaited(_profileCubit.refreshBalance()); }, @@ -1361,11 +1379,11 @@ class UploadCubit extends Cubit { 'Upload finished with success. Number of tasks: ${tasks.length}', ); - if (_selectedManifestFiles.isNotEmpty) { + if (_selectedManifestFileIds.isNotEmpty) { await prepareManifestUpload(); } - emit(UploadComplete(manifestFiles: _selectedManifestFiles)); + emit(UploadComplete()); PlausibleEventTracker.trackUploadSuccess(); }, diff --git a/lib/blocs/upload/upload_state.dart b/lib/blocs/upload/upload_state.dart index cc8e096bd..8cea3bb68 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -108,7 +108,7 @@ class UploadReady extends UploadState { final bool arnsCheckboxChecked; final int totalSize; final List selectedManifests; - final List manifestFiles; + final List manifestFiles; final bool showSettings; final bool canShowSettings; final List arnsRecords; @@ -154,7 +154,7 @@ class UploadReady extends UploadState { bool? arnsCheckboxChecked, int? totalSize, List? selectedManifests, - List? manifestFiles, + List? manifestFiles, bool? canShowSettings, List? arnsRecords, }) { @@ -292,10 +292,9 @@ class UploadFailure extends UploadState { } class UploadComplete extends UploadState { - final List manifestFiles; final ARNSRecord? arnsRecord; - UploadComplete({required this.manifestFiles, this.arnsRecord}); + UploadComplete({this.arnsRecord}); } class UploadingManifests extends UploadState { @@ -349,21 +348,22 @@ class UploadManifestSelectPaymentMethod extends UploadState { } class UploadManifestModel extends Equatable { - final String name; + final FileEntry entry; final bool isCompleted; final bool isAssigningUndername; final bool freeThanksToTurbo; final bool isUploading; - final String? existingManifestFileId; + final String existingManifestFileId; final IOFile? file; final ARNSUndername? undername; final ANTRecord? antRecord; + const UploadManifestModel({ - required this.name, + required this.entry, this.isCompleted = false, required this.freeThanksToTurbo, this.isUploading = false, - this.existingManifestFileId, + required this.existingManifestFileId, this.file, this.undername, this.antRecord, @@ -379,9 +379,10 @@ class UploadManifestModel extends Equatable { ARNSUndername? undername, ANTRecord? antRecord, bool? isAssigningUndername, + FileEntry? entry, }) { return UploadManifestModel( - name: name, + entry: entry ?? this.entry, isCompleted: isCompleted ?? this.isCompleted, isUploading: isUploading ?? this.isUploading, existingManifestFileId: @@ -396,7 +397,7 @@ class UploadManifestModel extends Equatable { @override List get props => [ - name, + entry, isCompleted, isUploading, existingManifestFileId, diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 15e8f4802..ed170a4e2 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -338,7 +338,7 @@ class _UploadingManifestsWidget extends StatelessWidget { ArDriveIcons.manifest(size: 16), const SizedBox(width: 8), Text( - '${state.manifestFiles[index].name}...', + '${state.manifestFiles[index].entry.name}...', style: typography.paragraphNormal( fontWeight: ArFontWeight.semiBold, ), @@ -1046,7 +1046,7 @@ class _UploadReadyModalBaseState extends State { class _ManifestOptionTile extends StatefulWidget { final UploadReady state; - final FileEntry file; + final UploadManifestModel file; const _ManifestOptionTile({ required this.state, @@ -1077,9 +1077,9 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { height: _isExpanded ? 130 : 50, child: GestureDetector( onTap: () { - setState(() { - _isExpanded = !_isExpanded; - }); + // setState(() { + // _isExpanded = !_isExpanded; + // }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1097,17 +1097,19 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { children: [ ArDriveIcons.manifest( size: 16, - color: - widget.file.isHidden ? hiddenColor : null), + color: widget.file.entry.isHidden + ? hiddenColor + : null), const SizedBox(width: 8), Text( - widget.file.name, + widget.file.entry.name, style: typography.paragraphNormal( - color: - widget.file.isHidden ? hiddenColor : null, + color: widget.file.entry.isHidden + ? hiddenColor + : null, ), ), - if (widget.file.isHidden) ...[ + if (widget.file.entry.isHidden) ...[ const SizedBox(width: 8), Text('(hidden)', style: typography.paragraphNormal( @@ -1149,7 +1151,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { maxHeight: 30, onPressed: () { setState(() { - _isExpanded = !_isExpanded; + _isExpanded = true; }); }, ) @@ -1160,7 +1162,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { const SizedBox(height: 8), Expanded( flex: 1, - child: AntSelector(fileEntry: widget.file), + child: AntSelector(file: widget.file), ), ], ], @@ -2208,7 +2210,7 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { ArDriveIcons.manifest(size: 16), const SizedBox(width: 8), Text( - e.name, + e.entry.name, style: typography.paragraphNormal( fontWeight: ArFontWeight.semiBold, ), @@ -2331,7 +2333,7 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { ArDriveIcons.manifest(size: 16), const SizedBox(width: 8), Text( - e.name, + e.entry.name, style: typography.paragraphNormal( fontWeight: ArFontWeight.semiBold, ), @@ -3016,9 +3018,9 @@ List getModalActions( } class AntSelector extends StatefulWidget { - const AntSelector({super.key, required this.fileEntry}); + const AntSelector({super.key, required this.file}); - final FileEntry fileEntry; + final UploadManifestModel file; @override State createState() => _AntSelectorState(); @@ -3030,6 +3032,7 @@ class _AntSelectorState extends State { List _arnsUndernames = []; bool _loadingUndernames = false; + bool _loadingIsAlreadyLinked = false; loadARNSUndernames( ANTRecord ant, @@ -3045,6 +3048,18 @@ class _AntSelectorState extends State { }); } + loadIsAlreadyLinked() async { + + setState(() { + _loadingIsAlreadyLinked = true; + }); + + _isAlreadyLinked = await context.read().isArNSNameAlreadyLinked( + record: widget.file.antRecord!, + undername: _selectedUndername, + ); + } + @override Widget build(BuildContext context) { final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; @@ -3157,6 +3172,8 @@ class _AntSelectorState extends State { ArDriveDropdownItem _buildDropdownItem(BuildContext context, ANTRecord ant) { final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return ArDriveDropdownItem( content: SizedBox( width: 235, @@ -3171,6 +3188,7 @@ class _AntSelectorState extends State { ant.domain, style: typography.paragraphSmall( fontWeight: ArFontWeight.semiBold, + color: isAlreadyLinked ? colorTokens.textLow : null, ), ), ), @@ -3183,11 +3201,21 @@ class _AntSelectorState extends State { ), ), onClick: () { + if (isAlreadyLinked) { + return; + } + setState(() { _selectedAnt = ant; _arnsUndernames = []; loadARNSUndernames(ant); + + context.read().linkManifestToUndername( + widget.file, + _selectedAnt!, + null, + ); }); }, ); @@ -3197,6 +3225,12 @@ class _AntSelectorState extends State { BuildContext context, ARNSUndername undername) { final typography = ArDriveTypographyNew.of(context); + final isAlreadyLinked = context.read().isArNSNameAlreadyLinked( + record: _selectedAnt!, + undername: undername, + ); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + return ArDriveDropdownItem( content: SizedBox( width: 235, @@ -3211,6 +3245,7 @@ class _AntSelectorState extends State { undername.name, style: typography.paragraphSmall( fontWeight: ArFontWeight.semiBold, + color: isAlreadyLinked ? colorTokens.textLow : null, ), ), ), @@ -3223,11 +3258,15 @@ class _AntSelectorState extends State { ), ), onClick: () { + if (isAlreadyLinked) { + return; + } + setState(() { _selectedUndername = undername; context.read().linkManifestToUndername( - widget.fileEntry, _selectedAnt!, _selectedUndername!); + widget.file, _selectedAnt!, _selectedUndername!); }); }, ); From 26361f65d3615e71ab870c43699f9a3e9d50a17c Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:02:50 -0300 Subject: [PATCH 03/11] Update drive_detail_page.dart fix hide state on mobile --- lib/pages/drive_detail/drive_detail_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/drive_detail/drive_detail_page.dart b/lib/pages/drive_detail/drive_detail_page.dart index 543da301e..691b06a7a 100644 --- a/lib/pages/drive_detail/drive_detail_page.dart +++ b/lib/pages/drive_detail/drive_detail_page.dart @@ -842,7 +842,7 @@ class _DriveDetailPageState extends State { List items, GlobalHideState globalHideState, ) { - final isShowingHiddenFiles = globalHideState is HiddingItems; + final isShowingHiddenFiles = globalHideState is ShowingHiddenItems; final List filteredItems; From ed8d519a34e11031a263a70addb46e78688fb446 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 04:26:55 -0300 Subject: [PATCH 04/11] feat(assign arns name) - handle case where the same name could be assigned - minor improvements on the ui - add icons, load states, etc --- lib/blocs/upload/upload_cubit.dart | 219 +++---- lib/blocs/upload/upload_state.dart | 17 +- lib/components/upload_form.dart | 575 +++++------------- .../blocs/upload_manifest_options_bloc.dart | 164 +++++ .../blocs/upload_manifest_options_event.dart | 49 ++ .../blocs/upload_manifest_options_state.dart | 37 ++ .../manifest_options/manifest_options.dart | 516 ++++++++++++++++ .../ario_sdk/lib/src/models/ant_record.dart | 9 +- .../ario_sdk/lib/src/models/arns_record.dart | 8 +- .../utils/get_literal_arns_record_name.dart | 1 + 10 files changed, 1013 insertions(+), 582 deletions(-) create mode 100644 lib/core/upload/view/blocs/upload_manifest_options_bloc.dart create mode 100644 lib/core/upload/view/blocs/upload_manifest_options_event.dart create mode 100644 lib/core/upload/view/blocs/upload_manifest_options_state.dart create mode 100644 lib/core/upload/view/manifest_options/manifest_options.dart diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index 0ae64fe7a..f85c218b3 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -10,6 +10,7 @@ import 'package:ardrive/blocs/upload/upload_file_checker.dart'; import 'package:ardrive/core/activity_tracker.dart'; import 'package:ardrive/core/upload/domain/repository/upload_repository.dart'; import 'package:ardrive/core/upload/uploader.dart'; +import 'package:ardrive/core/upload/view/blocs/upload_manifest_options_bloc.dart'; import 'package:ardrive/main.dart'; import 'package:ardrive/manifest/domain/manifest_repository.dart'; import 'package:ardrive/models/forms/cc.dart'; @@ -102,102 +103,21 @@ class UploadCubit extends Cubit { late final bool _isUploadFolders; /// Manifest - List _manifestFiles = []; - final List _selectedManifestFileIds = []; - /// key is domain - /// value is undername name - final Map> _arnsUndernamesLinkedToManifest = {}; + Map _manifestFiles = {}; + final List _selectedManifestModels = []; UploadMethod? _manifestUploadMethod; bool _isManifestsUploadCancelled = false; - void selectManifestFile(UploadManifestModel file) { - final readyState = state as UploadReady; - - // final manifestModel = UploadManifestModel( - // name: file.name, - // isCompleted: false, - // freeThanksToTurbo: - // file.size <= configService.config.allowedDataItemSizeForTurbo, - // isUploading: false, - // existingManifestFileId: file.id, - // undername: _arnsUndernamesLinkedToManifest[file.id], - // antRecord: _arnsAntRecordsLinkedToManifest[file.id], - // ); - - _selectedManifestFileIds.add(file.entry.id); - - final newReadyState = readyState.copyWith( - selectedManifests: _selectedManifestFileIds - .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) - .toList(), - ); - - emit(newReadyState); - } - - Future isArNSNameAlreadyLinked( - {required ANTRecord record, ARNSUndername? undername}) async { - if (_arnsUndernamesLinkedToManifest[record.domain] == null) { - return false; - } - - final undernames = await getARNSUndernames(record); - final linkedUnderNames = _arnsUndernamesLinkedToManifest[record.domain]; - - if (linkedUnderNames == null) { - return undernames.length == 1; - } - - return linkedUnderNames.contains(undername?.name); - } - - void linkManifestToUndername( - UploadManifestModel file, ANTRecord antRecord, ARNSUndername? undername) { - final index = _manifestFiles - .indexWhere((element) => element.entry.id == file.entry.id); - _manifestFiles[index] = - file.copyWith(undername: undername, antRecord: antRecord); - if (_arnsUndernamesLinkedToManifest[antRecord.domain] == null) { - _arnsUndernamesLinkedToManifest[antRecord.domain] = []; - } - - if (undername != null) { - _arnsUndernamesLinkedToManifest[antRecord.domain]!.add(undername.name); - } - } + void updateManifestSelection(List selections) { + _selectedManifestModels.clear(); - void unlinkManifestToUndername(UploadManifestModel file) { - if (file.undername == null) { - return; - } - - final undername = file.undername!; - final antRecord = file.antRecord!; - - final index = _manifestFiles - .indexWhere((element) => element.entry.id == file.entry.id); - _manifestFiles[index] = UploadManifestModel( - entry: file.entry, - freeThanksToTurbo: file.freeThanksToTurbo, - existingManifestFileId: file.existingManifestFileId, - file: file.file, - undername: null, - antRecord: null, - ); - - _arnsUndernamesLinkedToManifest[antRecord.domain]?.remove(undername.name); - } - - void unselectManifestFile(UploadManifestModel file) { - _selectedManifestFileIds.remove(file.entry.id); + _selectedManifestModels.addAll(selections); emit((state as UploadReady).copyWith( - selectedManifests: _selectedManifestFileIds - .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) - .toList(), + selectedManifestSelections: selections, )); } @@ -207,8 +127,14 @@ class UploadCubit extends Cubit { } Future prepareManifestUpload() async { - final manifestModels = _selectedManifestFileIds - .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) + final manifestModels = _selectedManifestModels + .map((e) => UploadManifestModel( + entry: e.manifest, + freeThanksToTurbo: false, + existingManifestFileId: e.manifest.id, + antRecord: e.antRecord, + undername: e.undername, + )) .toList(); for (int i = 0; i < manifestModels.length; i++) { @@ -299,42 +225,51 @@ class UploadCubit extends Cubit { throw StateError('Manifest file not found'); } - ARNSUndername undername; + if (manifestModels[i].antRecord != null) { + ARNSUndername undername; - if (manifestModels[i].undername == null) { - undername = ARNSUndername( - name: '@', - domain: manifestModels[i].antRecord!.domain, - record: ARNSRecord( - transactionId: manifestFile.dataTxId, - ttlSeconds: 3600, - ), - ); - } else { - undername = manifestModels[i].undername!; - } + if (manifestModels[i].undername == null) { + undername = ARNSUndername( + name: '@', + domain: manifestModels[i].antRecord!.domain, + record: ARNSRecord( + transactionId: manifestFile.dataTxId, + ttlSeconds: 3600, + ), + ); + } else { + undername = ARNSUndername( + name: manifestModels[i].undername!.name, + domain: manifestModels[i].antRecord!.domain, + record: ARNSRecord( + transactionId: manifestFile.dataTxId, + ttlSeconds: 3600, + ), + ); + } - manifestModels[i] = manifestModels[i].copyWith( - isCompleted: false, isUploading: false, isAssigningUndername: true); - emit(UploadingManifests( - manifestFiles: manifestModels, - completedCount: ++completedCount, - )); + manifestModels[i] = manifestModels[i].copyWith( + isCompleted: false, isUploading: false, isAssigningUndername: true); + emit(UploadingManifests( + manifestFiles: manifestModels, + completedCount: ++completedCount, + )); - await _arnsRepository.setUndernamesToFile( - undername: undername, - driveId: _driveId, - fileId: manifestModels[i].existingManifestFileId, - processId: manifestModels[i].antRecord!.processId, - ); + await _arnsRepository.setUndernamesToFile( + undername: undername, + driveId: _driveId, + fileId: manifestModels[i].existingManifestFileId, + processId: manifestModels[i].antRecord!.processId, + ); - manifestModels[i] = manifestModels[i].copyWith( - isCompleted: true, isUploading: false, isAssigningUndername: false); + manifestModels[i] = manifestModels[i].copyWith( + isCompleted: true, isUploading: false, isAssigningUndername: false); - emit(UploadingManifests( - manifestFiles: manifestModels, - completedCount: completedCount, - )); + emit(UploadingManifests( + manifestFiles: manifestModels, + completedCount: completedCount, + )); + } } emit(UploadComplete()); @@ -506,13 +441,12 @@ class UploadCubit extends Cubit { loadingArNSNames: true, arnsCheckboxChecked: _showArnsNameSelectionCheckBoxValue, totalSize: await _getTotalSize(), - selectedManifests: _selectedManifestFileIds - .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) - .toList(), showSettings: showSettings, canShowSettings: showSettings, - manifestFiles: _manifestFiles, + manifestFiles: _manifestFiles.values.toList(), arnsRecords: _ants, + showReviewButtonText: false, + selectedManifestSelections: _selectedManifestModels, ), ); @@ -551,13 +485,12 @@ class UploadCubit extends Cubit { showArnsNameSelection: false, arnsCheckboxChecked: _showArnsNameSelectionCheckBoxValue, totalSize: await _getTotalSize(), - selectedManifests: _selectedManifestFileIds - .map((id) => _manifestFiles.firstWhere((e) => e.entry.id == id)) - .toList(), showSettings: showSettings, - manifestFiles: _manifestFiles, + manifestFiles: _manifestFiles.values.toList(), arnsRecords: _ants, canShowSettings: showSettings, + showReviewButtonText: false, + selectedManifestSelections: _selectedManifestModels, ), ); } @@ -568,7 +501,7 @@ class UploadCubit extends Cubit { if (state is UploadReady) { if (_showArnsNameSelectionCheckBoxValue) { showArnsNameSelection(state as UploadReady); - } else if (_selectedManifestFileIds.isNotEmpty) { + } else if (_selectedManifestModels.isNotEmpty) { emit(UploadReview(readyState: state as UploadReady)); } else { final readyState = state as UploadReady; @@ -1068,20 +1001,28 @@ class UploadCubit extends Cubit { driveId: _targetDrive.id, ); - _manifestFiles = manifestFileEntries - .map((e) => UploadManifestModel( - entry: e, - existingManifestFileId: e.id, - freeThanksToTurbo: - e.size <= configService.config.allowedDataItemSizeForTurbo, - )) - .toList(); + _manifestFiles = {}; + + for (var entry in manifestFileEntries) { + _manifestFiles[entry.id] = UploadManifestModel( + entry: entry, + existingManifestFileId: entry.id, + freeThanksToTurbo: + entry.size <= configService.config.allowedDataItemSizeForTurbo, + ); + } // if there are no files that can be used to generate a thumbnail, we disable the option if (!containsSupportedImageTypeForThumbnailGeneration) { _uploadThumbnail = false; } + if (manifestFileEntries.isNotEmpty) { + // load arns names + await _arnsRepository + .getAntRecordsForWallet(_auth.currentUser.walletAddress); + } + emit( UploadReadyToPrepare( params: UploadParams( @@ -1301,7 +1242,7 @@ class UploadCubit extends Cubit { ); } - if (_selectedManifestFileIds.isNotEmpty) { + if (_selectedManifestModels.isNotEmpty) { await prepareManifestUpload(); } @@ -1379,7 +1320,7 @@ class UploadCubit extends Cubit { 'Upload finished with success. Number of tasks: ${tasks.length}', ); - if (_selectedManifestFileIds.isNotEmpty) { + if (_selectedManifestModels.isNotEmpty) { await prepareManifestUpload(); } diff --git a/lib/blocs/upload/upload_state.dart b/lib/blocs/upload/upload_state.dart index 8cea3bb68..c71c3f7db 100644 --- a/lib/blocs/upload/upload_state.dart +++ b/lib/blocs/upload/upload_state.dart @@ -107,13 +107,14 @@ class UploadReady extends UploadState { final bool loadingArNSNamesError; final bool arnsCheckboxChecked; final int totalSize; - final List selectedManifests; final List manifestFiles; + final List selectedManifestSelections; final bool showSettings; final bool canShowSettings; final List arnsRecords; final bool isArConnect; + final bool showReviewButtonText; UploadReady({ required this.paymentInfo, @@ -129,11 +130,12 @@ class UploadReady extends UploadState { this.loadingArNSNamesError = false, required this.arnsCheckboxChecked, required this.totalSize, - required this.selectedManifests, required this.showSettings, required this.canShowSettings, required this.manifestFiles, required this.arnsRecords, + required this.showReviewButtonText, + required this.selectedManifestSelections, }); // copyWith @@ -153,10 +155,11 @@ class UploadReady extends UploadState { bool? loadingArNSNamesError, bool? arnsCheckboxChecked, int? totalSize, - List? selectedManifests, List? manifestFiles, bool? canShowSettings, List? arnsRecords, + bool? showReviewButtonText, + List? selectedManifestSelections, }) { return UploadReady( loadingArNSNames: loadingArNSNames ?? this.loadingArNSNames, @@ -174,11 +177,13 @@ class UploadReady extends UploadState { loadingArNSNamesError ?? this.loadingArNSNamesError, arnsCheckboxChecked: arnsCheckboxChecked ?? this.arnsCheckboxChecked, totalSize: totalSize ?? this.totalSize, - selectedManifests: selectedManifests ?? this.selectedManifests, showSettings: showSettings ?? this.showSettings, manifestFiles: manifestFiles ?? this.manifestFiles, canShowSettings: canShowSettings ?? this.canShowSettings, arnsRecords: arnsRecords ?? this.arnsRecords, + showReviewButtonText: showReviewButtonText ?? this.showReviewButtonText, + selectedManifestSelections: + selectedManifestSelections ?? this.selectedManifestSelections, ); } @@ -357,6 +362,7 @@ class UploadManifestModel extends Equatable { final IOFile? file; final ARNSUndername? undername; final ANTRecord? antRecord; + final bool selectionExpanded; const UploadManifestModel({ required this.entry, @@ -368,6 +374,7 @@ class UploadManifestModel extends Equatable { this.undername, this.antRecord, this.isAssigningUndername = false, + this.selectionExpanded = false, }); UploadManifestModel copyWith({ @@ -380,6 +387,7 @@ class UploadManifestModel extends Equatable { ANTRecord? antRecord, bool? isAssigningUndername, FileEntry? entry, + bool? selectionExpanded, }) { return UploadManifestModel( entry: entry ?? this.entry, @@ -392,6 +400,7 @@ class UploadManifestModel extends Equatable { undername: undername ?? this.undername, antRecord: antRecord ?? this.antRecord, isAssigningUndername: isAssigningUndername ?? this.isAssigningUndername, + selectionExpanded: selectionExpanded ?? this.selectionExpanded, ); } diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 0657bcef8..2978dacc0 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -27,6 +27,8 @@ import 'package:ardrive/core/arfs/repository/folder_repository.dart'; import 'package:ardrive/core/arfs/utils/arfs_revision_status_utils.dart'; import 'package:ardrive/core/upload/domain/repository/upload_repository.dart'; import 'package:ardrive/core/upload/uploader.dart'; +import 'package:ardrive/core/upload/view/blocs/upload_manifest_options_bloc.dart'; +import 'package:ardrive/core/upload/view/manifest_options/manifest_options.dart'; import 'package:ardrive/entities/manifest_data.dart'; import 'package:ardrive/l11n/validation_messages.dart'; import 'package:ardrive/main.dart'; @@ -50,6 +52,7 @@ import 'package:ardrive_ui/ardrive_ui.dart'; import 'package:ardrive_uploader/ardrive_uploader.dart'; import 'package:ardrive_utils/ardrive_utils.dart'; import 'package:ario_sdk/ario_sdk.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -251,8 +254,57 @@ class _UploadFormState extends State { state is UploadPreparationInitialized) { return _PreparingUploadWidget(state: state); } else if (state is UploadReady) { - return _UploadReadyWidget( - state: state, driveDetailCubit: widget.driveDetailCubit); + return BlocProvider( + create: (context) { + List manifestSelections = []; + List selectedManifestIds = []; + + manifestSelections = state.manifestFiles.map((e) { + final selectedManifest = state.selectedManifestSelections + .firstWhereOrNull((selectedManifest) => + selectedManifest.manifest.id == e.entry.id); + + ANTRecord? antRecord; + ARNSUndername? undername; + + if (selectedManifest != null) { + antRecord = selectedManifest.antRecord; + undername = selectedManifest.undername; + selectedManifestIds.add(e.entry.id); + } + + return ManifestSelection( + manifest: e.entry, + antRecord: antRecord, + undername: undername, + ); + }).toList(); + + return UploadManifestOptionsBloc( + manifestFiles: manifestSelections, + arnsRepository: context.read(), + arDriveAuth: context.read(), + selectedManifestIds: selectedManifestIds, + )..add(LoadAnts()); + }, + child: BlocListener( + listener: (context, state) { + if (state is UploadManifestOptionsReady) { + context.read().updateManifestSelection( + state.manifestFiles + .where((e) => state.selectedManifestIds + .contains(e.manifest.id)) + .toList(), + ); + } + }, + child: _UploadReadyWidget( + state: state, + driveDetailCubit: widget.driveDetailCubit, + ), + ), + ); } else if (state is UploadConfiguringLicense) { return _UploadConfiguringLicenseWidget(state: state); } else if (state is UploadReview) { @@ -1020,152 +1072,8 @@ class _UploadReadyModalBaseState extends State { Widget manifestOptionsView( UploadReady state, BuildContext context, ArdriveTypographyNew typography, {bool scrollable = true}) { - return Padding( - padding: const EdgeInsets.only(bottom: 42.0), - child: ListView.separated( - separatorBuilder: (context, index) => const SizedBox(height: 4), - physics: scrollable - ? const ScrollPhysics() - : const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: state.manifestFiles.length, - itemBuilder: (context, index) { - final file = state.manifestFiles[index]; - - return _ManifestOptionTile( - file: file, - state: state, - ); - }, - ), - ); - } -} - -class _ManifestOptionTile extends StatefulWidget { - final UploadReady state; - final UploadManifestModel file; - - const _ManifestOptionTile({ - required this.state, - required this.file, - }); - - @override - State<_ManifestOptionTile> createState() => __ManifestOptionTileState(); -} - -class __ManifestOptionTileState extends State<_ManifestOptionTile> { - bool _isExpanded = false; - - @override - Widget build(BuildContext context) { - final hiddenColor = - ArDriveTheme.of(context).themeData.colors.themeFgDisabled; - final typography = ArDriveTypographyNew.of(context); - final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - - return AnimatedContainer( - duration: const Duration(milliseconds: 300), - decoration: BoxDecoration( - color: colorTokens.containerL2, - borderRadius: BorderRadius.circular(5), - ), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - height: _isExpanded ? 130 : 50, - child: GestureDetector( - onTap: () { - // setState(() { - // _isExpanded = !_isExpanded; - // }); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Row( - children: [ - Flexible( - flex: 2, - child: Row( - children: [ - ArDriveIcons.manifest( - size: 16, - color: widget.file.entry.isHidden - ? hiddenColor - : null), - const SizedBox(width: 8), - Text( - widget.file.entry.name, - style: typography.paragraphNormal( - color: widget.file.entry.isHidden - ? hiddenColor - : null, - ), - ), - if (widget.file.entry.isHidden) ...[ - const SizedBox(width: 8), - Text('(hidden)', - style: typography.paragraphNormal( - color: hiddenColor, - )) - ] - ], - ), - ), - Flexible( - flex: 1, - child: ArDriveCheckBox( - checked: widget.state.selectedManifests - .contains(widget.file), - onChange: (value) { - if (value) { - context - .read() - .selectManifestFile(widget.file); - } else { - context - .read() - .unselectManifestFile(widget.file); - } - }, - ), - ), - ], - ), - ), - ArDriveButtonNew( - text: 'Add ArNS', - typography: typography, - isDisabled: - !widget.state.selectedManifests.contains(widget.file), - fontStyle: typography.paragraphSmall(), - variant: ButtonVariant.primary, - maxWidth: 80, - maxHeight: 30, - onPressed: () { - setState(() { - _isExpanded = true; - }); - }, - ) - // TODO: Add back when we have the right UI for it - ], - ), - if (_isExpanded) ...[ - const SizedBox(height: 8), - Expanded( - flex: 1, - child: AntSelector(file: widget.file), - ), - ], - ], - ), - ), + return ManifestOptions( + scrollable: scrollable, ); } } @@ -2173,7 +2081,7 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { ), ), ], - if (state.readyState.selectedManifests.isNotEmpty) ...[ + if (state.readyState.selectedManifestSelections.isNotEmpty) ...[ Padding( padding: const EdgeInsets.only(bottom: 16.0), child: ConstrainedBox( @@ -2200,28 +2108,44 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { child: ListView( shrinkWrap: true, children: [ - ...state.readyState.selectedManifests.map( + ...state.readyState.selectedManifestSelections.map( (e) => Column( children: [ Row( children: [ ArDriveIcons.manifest(size: 16), const SizedBox(width: 8), - Text( - e.entry.name, - style: typography.paragraphNormal( - fontWeight: ArFontWeight.semiBold, + Flexible( + child: Text( + e.manifest.name, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), ], ), - if (e.undername != null) ...[ - const SizedBox(height: 4), - Text( - 'ArNS Name: ${getLiteralARNSRecordName(e.undername!)}', - style: typography.paragraphNormal( - fontWeight: ArFontWeight.semiBold, - ), + if (e.antRecord != null || + e.undername != null) ...[ + const SizedBox(height: 2), + Row( + children: [ + ArDriveIcons.arnsName(size: 16), + const SizedBox(width: 8), + Flexible( + child: Text( + getLiteralArNSName( + e.antRecord!, e.undername), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], ), ], ], @@ -2298,12 +2222,12 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { ), ), ], - if (state.readyState.selectedManifests.isNotEmpty) ...[ + if (state.readyState.selectedManifestSelections.isNotEmpty) ...[ Padding( padding: const EdgeInsets.only(bottom: 16.0), child: ConstrainedBox( constraints: const BoxConstraints( - // maxHeight: 125, + maxHeight: 125, minWidth: kLargeDialogWidth, ), child: Column( @@ -2321,27 +2245,57 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { ), const SizedBox(height: 4), Flexible( - child: ListView( - shrinkWrap: true, - children: [ - ...state.readyState.selectedManifests.map( - (e) => Row( - mainAxisSize: MainAxisSize.min, - children: [ - ArDriveIcons.manifest(size: 16), - const SizedBox(width: 8), - Text( - e.entry.name, - style: typography.paragraphNormal( - fontWeight: ArFontWeight.semiBold, + child: Expanded( + child: ListView( + shrinkWrap: true, + children: [ + ...state.readyState.selectedManifestSelections.map( + (e) => Column( + children: [ + Row( + children: [ + ArDriveIcons.manifest(size: 16), + const SizedBox(width: 8), + Flexible( + child: Text( + e.manifest.name, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], ), - ), - ], + if (e.antRecord != null || + e.undername != null) ...[ + const SizedBox(height: 2), + Row( + children: [ + ArDriveIcons.arnsName(size: 16), + const SizedBox(width: 8), + Flexible( + child: Text( + getLiteralArNSName( + e.antRecord!, e.undername), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ], + ], + ), ), - ), - ], + ], + ), ), - ) + ), ], ), ), @@ -2984,7 +2938,7 @@ List getModalActions( variant: ButtonVariant.primary, ), ]; - } else if (state.selectedManifests.isNotEmpty) { + } else if (state.selectedManifestSelections.isNotEmpty) { return [ ArDriveButtonNew( isDisabled: !state.isNextButtonEnabled, @@ -3015,260 +2969,11 @@ List getModalActions( ]; } -class AntSelector extends StatefulWidget { - const AntSelector({super.key, required this.file}); - - final UploadManifestModel file; - - @override - State createState() => _AntSelectorState(); -} - -class _AntSelectorState extends State { - ANTRecord? _selectedAnt; - ARNSUndername? _selectedUndername; - - List _arnsUndernames = []; - bool _loadingUndernames = false; - bool _loadingIsAlreadyLinked = false; - - loadARNSUndernames( - ANTRecord ant, - ) async { - setState(() { - _loadingUndernames = true; - }); - - _arnsUndernames = await context.read().getARNSUndernames(ant); - - setState(() { - _loadingUndernames = false; - }); - } - - loadIsAlreadyLinked() async { - - setState(() { - _loadingIsAlreadyLinked = true; - }); - - _isAlreadyLinked = await context.read().isArNSNameAlreadyLinked( - record: widget.file.antRecord!, - undername: _selectedUndername, - ); - } - - @override - Widget build(BuildContext context) { - final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - return BlocBuilder( - builder: (context, state) { - if (state is UploadReady) { - return Column( - children: [ - Container( - alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: colorTokens.inputDisabled, - border: Border.all( - color: colorTokens.textXLow, - width: 1, - ), - ), - child: ArDriveDropdown( - height: 45, - items: state.arnsRecords - .map((ant) => _buildDropdownItem(context, ant)) - .toList(), - child: ArDriveClickArea( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - children: [ - _buildSelectedItem(context), - ], - ), - ArDriveIcons.chevronDown(), - ], - ), - ), - ), - ), - if (_loadingUndernames) const CircularProgressIndicator(), - if (!_loadingUndernames && _arnsUndernames.isNotEmpty) - Padding( - padding: const EdgeInsets.only(top: 8), - child: Container( - alignment: Alignment.centerLeft, - padding: - const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: colorTokens.inputDisabled, - border: Border.all( - color: colorTokens.textXLow, - width: 1, - ), - ), - child: ArDriveDropdown( - height: 45, - items: _arnsUndernames - .map((undername) => - _buildDropdownItemUndername(context, undername)) - .toList(), - child: ArDriveClickArea( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - children: [ - _buildSelectedItemUndername(context), - ], - ), - ArDriveIcons.chevronDown(), - ], - ), - ), - ), - ), - ), - ], - ); - } - return const SizedBox(); - }, - ); - } - - Widget _buildSelectedItem(BuildContext context) { - final typography = ArDriveTypographyNew.of(context); - - return Text( - _selectedAnt?.domain ?? 'Choose ArNS name', - style: typography.paragraphSmall( - fontWeight: ArFontWeight.semiBold, - ), - ); - } - - Widget _buildSelectedItemUndername(BuildContext context) { - final typography = ArDriveTypographyNew.of(context); - - return Text( - _selectedUndername?.name ?? 'under_name (optional)', - style: typography.paragraphSmall( - fontWeight: ArFontWeight.semiBold, - ), - ); +String getLiteralArNSName(ANTRecord record, ARNSUndername? undername) { + if (undername != null) { + return getLiteralARNSRecordName(undername); } - ArDriveDropdownItem _buildDropdownItem(BuildContext context, ANTRecord ant) { - final typography = ArDriveTypographyNew.of(context); - - final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - - return ArDriveDropdownItem( - content: SizedBox( - width: 235, - height: 45, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( - ant.domain, - style: typography.paragraphSmall( - fontWeight: ArFontWeight.semiBold, - color: isAlreadyLinked ? colorTokens.textLow : null, - ), - ), - ), - if (ant.domain == _selectedAnt?.domain) - ArDriveIcons.checkmark( - size: 16, - ) - ], - ), - ), - ), - onClick: () { - if (isAlreadyLinked) { - return; - } - - setState(() { - _selectedAnt = ant; - - _arnsUndernames = []; - loadARNSUndernames(ant); - - context.read().linkManifestToUndername( - widget.file, - _selectedAnt!, - null, - ); - }); - }, - ); - } - - ArDriveDropdownItem _buildDropdownItemUndername( - BuildContext context, ARNSUndername undername) { - final typography = ArDriveTypographyNew.of(context); - - final isAlreadyLinked = context.read().isArNSNameAlreadyLinked( - record: _selectedAnt!, - undername: undername, - ); - final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - - return ArDriveDropdownItem( - content: SizedBox( - width: 235, - height: 45, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( - undername.name, - style: typography.paragraphSmall( - fontWeight: ArFontWeight.semiBold, - color: isAlreadyLinked ? colorTokens.textLow : null, - ), - ), - ), - if (undername.name == _selectedUndername?.name) - ArDriveIcons.checkmark( - size: 16, - ) - ], - ), - ), - ), - onClick: () { - if (isAlreadyLinked) { - return; - } - - setState(() { - _selectedUndername = undername; - - context.read().linkManifestToUndername( - widget.file, _selectedAnt!, _selectedUndername!); - }); - }, - ); - } + return record.domain; } - // diff --git a/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart b/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart new file mode 100644 index 000000000..35747df3c --- /dev/null +++ b/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart @@ -0,0 +1,164 @@ +import 'package:ardrive/arns/domain/arns_repository.dart'; +import 'package:ardrive/authentication/ardrive_auth.dart'; +import 'package:ardrive/models/models.dart'; +import 'package:ario_sdk/ario_sdk.dart'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'upload_manifest_options_event.dart'; +part 'upload_manifest_options_state.dart'; + +/// Outputs the selected manifest +/// with the corresponding names if any +class UploadManifestOptionsBloc + extends Bloc { + final ARNSRepository arnsRepository; + final ArDriveAuth arDriveAuth; + + final List manifestFiles; + final Set _selectedManifestIds = {}; + final Set _showingArNSSelection = {}; + Map> reservedNames = {}; + + List? _ants; + + UploadManifestOptionsBloc({ + required this.manifestFiles, + required this.arnsRepository, + required this.arDriveAuth, + List? selectedManifestIds, + }) : super(UploadManifestOptionsReady( + manifestFiles: Set.from(manifestFiles), + selectedManifestIds: Set.from(selectedManifestIds ?? []), + showingArNSSelection: const {}, + ants: null, + reservedNames: const {}, + arnsNamesLoaded: false, + )) { + if (selectedManifestIds != null) { + _selectedManifestIds.addAll(selectedManifestIds); + + final selectedManifests = selectedManifestIds + .map((id) => manifestFiles.firstWhere((e) => e.manifest.id == id)) + .toList(); + + for (var manifest in selectedManifests) { + if (manifest.antRecord != null) { + if (reservedNames[manifest.antRecord!.domain] == null) { + reservedNames[manifest.antRecord!.domain] = []; + } + + reservedNames[manifest.antRecord!.domain]! + .add(manifest.undername?.name ?? '@'); + } + } + } + + on((event, emit) async { + final walletAddress = await arDriveAuth.getWalletAddress(); + _ants = await arnsRepository.getAntRecordsForWallet(walletAddress!); + emit(_createReadyState()); + }); + + on((event, emit) async { + _selectedManifestIds.add(event.manifest.id); + emit(_createReadyState()); + }); + + on((event, emit) { + _selectedManifestIds.remove(event.manifest.id); + _showingArNSSelection.remove(event.manifest.id); + + final indexOf = + manifestFiles.indexWhere((e) => e.manifest.id == event.manifest.id); + + if (manifestFiles[indexOf].antRecord != null) { + reservedNames[manifestFiles[indexOf].antRecord!.domain]! + .remove(manifestFiles[indexOf].undername?.name ?? '@'); + + manifestFiles[indexOf] = ManifestSelection( + manifest: manifestFiles[indexOf].manifest, + ); + } + + emit(_createReadyState()); + }); + + on((event, emit) async { + _showingArNSSelection.add(event.manifest.id); + + emit(_createReadyState()); + }); + + on((event, emit) { + _showingArNSSelection.remove(event.manifest.id); + emit(_createReadyState()); + }); + + on((event, emit) { + if (reservedNames[event.antRecord.domain] == null) { + reservedNames[event.antRecord.domain] = []; + } + + final indexOf = + manifestFiles.indexWhere((e) => e.manifest.id == event.manifest.id); + + final manifest = manifestFiles[indexOf]; + + if (manifest.antRecord != null) { + reservedNames[manifest.antRecord!.domain]! + .remove(manifest.undername?.name ?? '@'); + } + + manifestFiles[indexOf] = manifestFiles[indexOf].copyWith( + antRecord: event.antRecord, + undername: event.undername, + ); + + reservedNames[event.antRecord.domain]!.add(event.undername?.name ?? '@'); + _showingArNSSelection.remove(event.manifest.id); + emit(_createReadyState()); + }); + } + + Future> getARNSUndernames( + ANTRecord antRecord, + ) async { + return arnsRepository.getARNSUndernames(antRecord); + } + + UploadManifestOptionsReady _createReadyState() { + return UploadManifestOptionsReady( + manifestFiles: Set.from(manifestFiles), + selectedManifestIds: Set.from(_selectedManifestIds), + showingArNSSelection: Set.from(_showingArNSSelection), + ants: _ants, + reservedNames: reservedNames, + arnsNamesLoaded: _ants != null, + ); + } +} + +class ManifestSelection extends Equatable { + final FileEntry manifest; + final ANTRecord? antRecord; + final ARNSUndername? undername; + + const ManifestSelection({ + required this.manifest, + this.antRecord, + this.undername, + }); + + @override + List get props => [manifest, antRecord?.domain, undername?.name]; + + ManifestSelection copyWith({ + ANTRecord? antRecord, + ARNSUndername? undername, + }) { + return ManifestSelection( + manifest: manifest, antRecord: antRecord, undername: undername); + } +} +// diff --git a/lib/core/upload/view/blocs/upload_manifest_options_event.dart b/lib/core/upload/view/blocs/upload_manifest_options_event.dart new file mode 100644 index 000000000..d28392cd8 --- /dev/null +++ b/lib/core/upload/view/blocs/upload_manifest_options_event.dart @@ -0,0 +1,49 @@ +part of 'upload_manifest_options_bloc.dart'; + +sealed class UploadManifestOptionsEvent extends Equatable { + const UploadManifestOptionsEvent(); + + @override + List get props => []; +} + +final class SelectManifest extends UploadManifestOptionsEvent { + final FileEntry manifest; + + const SelectManifest({required this.manifest}); + + @override + List get props => [manifest]; +} + +final class DeselectManifest extends UploadManifestOptionsEvent { + final FileEntry manifest; + + const DeselectManifest({required this.manifest}); +} + +final class ShowArNSSelection extends UploadManifestOptionsEvent { + final FileEntry manifest; + + const ShowArNSSelection({required this.manifest}); +} + +final class HideArNSSelection extends UploadManifestOptionsEvent { + final FileEntry manifest; + + const HideArNSSelection({required this.manifest}); +} + +final class LinkManifestToUndername extends UploadManifestOptionsEvent { + final FileEntry manifest; + final ANTRecord antRecord; + final ARNSUndername? undername; + + const LinkManifestToUndername({ + required this.manifest, + required this.antRecord, + required this.undername, + }); +} + +final class LoadAnts extends UploadManifestOptionsEvent {} diff --git a/lib/core/upload/view/blocs/upload_manifest_options_state.dart b/lib/core/upload/view/blocs/upload_manifest_options_state.dart new file mode 100644 index 000000000..9b8a72164 --- /dev/null +++ b/lib/core/upload/view/blocs/upload_manifest_options_state.dart @@ -0,0 +1,37 @@ +part of 'upload_manifest_options_bloc.dart'; + +sealed class UploadManifestOptionsState extends Equatable { + const UploadManifestOptionsState(); + + @override + List get props => []; +} + +final class UploadManifestOptionsInitial extends UploadManifestOptionsState {} + +final class UploadManifestOptionsReady extends UploadManifestOptionsState { + final Set manifestFiles; + final Set selectedManifestIds; + final Set showingArNSSelection; + final List? ants; + final Map> reservedNames; + final bool arnsNamesLoaded; + + const UploadManifestOptionsReady({ + required this.manifestFiles, + required this.selectedManifestIds, + required this.showingArNSSelection, + required this.ants, + required this.reservedNames, + required this.arnsNamesLoaded, + }); + + @override + List get props => [ + manifestFiles, + selectedManifestIds, + showingArNSSelection, + ants, + reservedNames, + ]; +} diff --git a/lib/core/upload/view/manifest_options/manifest_options.dart b/lib/core/upload/view/manifest_options/manifest_options.dart new file mode 100644 index 000000000..b74064a7b --- /dev/null +++ b/lib/core/upload/view/manifest_options/manifest_options.dart @@ -0,0 +1,516 @@ +import 'package:ardrive/components/components.dart'; +import 'package:ardrive/core/upload/view/blocs/upload_manifest_options_bloc.dart'; +import 'package:ardrive_ui/ardrive_ui.dart'; +import 'package:ario_sdk/ario_sdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ManifestOptions extends StatelessWidget { + const ManifestOptions({super.key, this.scrollable = true}); + + final bool scrollable; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UploadManifestOptionsReady) { + final selectedManifestIds = state.selectedManifestIds; + final manifestFiles = state.manifestFiles; + + return Padding( + padding: const EdgeInsets.only(bottom: 42.0), + child: ListView.separated( + separatorBuilder: (context, index) => const SizedBox(height: 4), + physics: scrollable + ? const ScrollPhysics() + : const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: manifestFiles.length, + itemBuilder: (context, index) { + final file = manifestFiles.elementAt(index).manifest; + final isSelected = selectedManifestIds.contains(file.id); + + return _ManifestOptionTile( + manifestSelection: manifestFiles.elementAt(index), + isSelected: isSelected, + onSelect: () { + if (isSelected) { + context + .read() + .add(DeselectManifest(manifest: file)); + } else { + context + .read() + .add(SelectManifest(manifest: file)); + } + }, + ); + }, + ), + ); + } + + return const SizedBox(); + }, + ); + } +} + +class _ManifestOptionTile extends StatefulWidget { + final ManifestSelection manifestSelection; + final bool isSelected; + final VoidCallback onSelect; + + const _ManifestOptionTile({ + required this.manifestSelection, + required this.isSelected, + required this.onSelect, + }); + + @override + State<_ManifestOptionTile> createState() => __ManifestOptionTileState(); +} + +class __ManifestOptionTileState extends State<_ManifestOptionTile> { + @override + Widget build(BuildContext context) { + final state = context.read().state; + + if (state is UploadManifestOptionsReady) { + final isExpanded = state.showingArNSSelection + .contains(widget.manifestSelection.manifest.id); + final file = widget.manifestSelection.manifest; + + final hiddenColor = + ArDriveTheme.of(context).themeData.colors.themeFgDisabled; + final typography = ArDriveTypographyNew.of(context); + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + final showingName = !isExpanded && + (widget.manifestSelection.antRecord != null || + widget.manifestSelection.undername != null); + final hasSelectedAnt = widget.manifestSelection.antRecord != null; + + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + decoration: BoxDecoration( + color: colorTokens.containerL2, + borderRadius: BorderRadius.circular(5), + ), + curve: Curves.easeInOut, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + height: isExpanded + ? 204 + : showingName + ? 70 + : 50, + child: GestureDetector( + onTap: () {}, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Row( + children: [ + Flexible( + flex: 2, + child: Row( + children: [ + ArDriveIcons.manifest( + size: 16, + color: file.isHidden ? hiddenColor : null), + const SizedBox(width: 8), + Text( + file.name, + style: typography.paragraphNormal( + color: file.isHidden ? hiddenColor : null, + ), + ), + if (file.isHidden) ...[ + const SizedBox(width: 8), + Text('(hidden)', + style: typography.paragraphNormal( + color: hiddenColor, + )) + ] + ], + ), + ), + Flexible( + flex: 1, + child: ArDriveCheckBox( + checked: widget.isSelected, + onChange: (value) { + if (value) { + context + .read() + .add(SelectManifest(manifest: file)); + } else { + context + .read() + .add(DeselectManifest(manifest: file)); + } + }, + ), + ), + ], + ), + ), + + ArDriveTooltip( + message: (state.arnsNamesLoaded && state.ants!.isEmpty) + ? 'No ARNS names found for your wallet' + : '', + child: ArDriveButtonNew( + text: !state.arnsNamesLoaded + ? 'Loading Names...' + : hasSelectedAnt + ? 'Change ArNS' + : 'Add ArNS', + typography: typography, + isDisabled: !widget.isSelected || + (state.arnsNamesLoaded && state.ants!.isEmpty), + fontStyle: typography.paragraphSmall(), + variant: ButtonVariant.primary, + maxWidth: 100, + maxHeight: 30, + onPressed: () { + context + .read() + .add(ShowArNSSelection(manifest: file)); + }, + ), + ) + // TODO: Add back when we have the right UI for it + ], + ), + if (showingName) ...[ + const SizedBox(height: 8), + Row( + children: [ + ArDriveIcons.arnsName( + size: 16, + color: colorTokens.textHigh, + ), + const SizedBox(width: 8), + Flexible( + child: Text( + getLiteralArNSName(widget.manifestSelection.antRecord!, + widget.manifestSelection.undername), + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ], + if (isExpanded) ...[ + const SizedBox(height: 8), + Expanded( + flex: 1, + child: AntSelector( + manifestSelection: widget.manifestSelection, + ), + ), + ], + ], + ), + ), + ); + } + return const SizedBox(); + } +} + +class AntSelector extends StatefulWidget { + final ManifestSelection manifestSelection; + + const AntSelector({super.key, required this.manifestSelection}); + + @override + State createState() => _AntSelectorState(); +} + +class _AntSelectorState extends State { + ANTRecord? _selectedAnt; + ARNSUndername? _selectedUndername; + + List _arnsUndernames = []; + bool _loadingUndernames = false; + + Future loadARNSUndernames( + ANTRecord ant, + ) async { + setState(() { + _loadingUndernames = true; + }); + + _arnsUndernames = + await context.read().getARNSUndernames(ant); + _arnsUndernames.removeWhere((e) => e.name == '@'); + + setState(() { + _loadingUndernames = false; + }); + } + + @override + void initState() { + super.initState(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + _selectedAnt = widget.manifestSelection.antRecord; + _selectedUndername = widget.manifestSelection.undername; + } + + @override + Widget build(BuildContext context) { + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + final typography = ArDriveTypographyNew.of(context); + + return BlocBuilder( + builder: (context, state) { + if (state is UploadManifestOptionsReady) { + final reservedNames = + context.read().reservedNames; + + bool isNameAlreadyInUse = reservedNames[_selectedAnt?.domain] + ?.contains(_selectedUndername?.name ?? '@') ?? + false; + + return Column( + children: [ + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: colorTokens.inputDisabled, + border: Border.all( + color: colorTokens.textXLow, + width: 1, + ), + ), + child: ArDriveDropdown( + height: 45, + maxHeight: (state.ants!.length > 6) ? 45 * 6 : null, + items: state.ants! + .map((ant) => _buildDropdownItem(context, ant)) + .toList(), + child: ArDriveClickArea( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + children: [ + _buildSelectedItem(context), + ], + ), + ArDriveIcons.chevronDown(), + ], + ), + ), + ), + ), + if (_loadingUndernames) const CircularProgressIndicator(), + if (_selectedUndername != null || + (!_loadingUndernames && _arnsUndernames.isNotEmpty)) + Padding( + padding: const EdgeInsets.only(top: 8), + child: Container( + alignment: Alignment.centerLeft, + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: colorTokens.inputDisabled, + border: Border.all( + color: colorTokens.textXLow, + width: 1, + ), + ), + child: ArDriveDropdown( + maxHeight: (_arnsUndernames.length > 6) ? 45 * 6 : null, + height: 45, + items: _arnsUndernames + .map((undername) => + _buildDropdownItemUndername(context, undername)) + .toList(), + child: ArDriveClickArea( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + children: [ + _buildSelectedItemUndername(context), + ], + ), + ArDriveIcons.chevronDown(), + ], + ), + ), + ), + ), + ), + if (isNameAlreadyInUse && + widget.manifestSelection.antRecord?.domain != + _selectedAnt?.domain) + Padding( + padding: const EdgeInsets.only(top: 8, left: 8), + child: Text( + 'Name already in use, please choose another name or select a undername', + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8, left: 8), + child: Align( + alignment: Alignment.centerRight, + child: ArDriveButtonNew( + text: 'Add', + typography: typography, + fontStyle: typography.paragraphSmall(), + variant: ButtonVariant.primary, + maxWidth: 80, + maxHeight: 30, + isDisabled: isNameAlreadyInUse || _selectedAnt == null, + onPressed: () { + context + .read() + .add(LinkManifestToUndername( + manifest: widget.manifestSelection.manifest, + antRecord: _selectedAnt!, + undername: _selectedUndername, + )); + }, + ), + ), + ), + ], + ); + } + return const SizedBox(); + }, + ); + } + + Widget _buildSelectedItem(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + + return Column( + children: [ + Text( + _selectedAnt?.domain ?? 'Choose ArNS name', + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ), + ], + ); + } + + Widget _buildSelectedItemUndername(BuildContext context) { + final typography = ArDriveTypographyNew.of(context); + + return Text( + _selectedUndername?.name ?? 'under_name (optional)', + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ); + } + + ArDriveDropdownItem _buildDropdownItem(BuildContext context, ANTRecord ant) { + final typography = ArDriveTypographyNew.of(context); + + final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + + return ArDriveDropdownItem( + content: SizedBox( + width: 235, + height: 45, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + ant.domain, + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ), + ), + if (ant.domain == _selectedAnt?.domain) + ArDriveIcons.checkmark( + size: 16, + ) + ], + ), + ), + ), + onClick: () { + setState(() { + _selectedAnt = ant; + + _arnsUndernames = []; + _selectedUndername = null; + loadARNSUndernames(ant); + }); + }, + ); + } + + ArDriveDropdownItem _buildDropdownItemUndername( + BuildContext context, ARNSUndername undername) { + final typography = ArDriveTypographyNew.of(context); + + return ArDriveDropdownItem( + content: SizedBox( + width: 235, + height: 45, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + undername.name, + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ), + ), + if (undername.name == _selectedUndername?.name) + ArDriveIcons.checkmark( + size: 16, + ) + ], + ), + ), + ), + onClick: () { + setState(() { + _selectedUndername = undername; + }); + }, + ); + } +} diff --git a/packages/ario_sdk/lib/src/models/ant_record.dart b/packages/ario_sdk/lib/src/models/ant_record.dart index 6165e85f0..f154fb3bc 100644 --- a/packages/ario_sdk/lib/src/models/ant_record.dart +++ b/packages/ario_sdk/lib/src/models/ant_record.dart @@ -1,9 +1,14 @@ -class ANTRecord { +import 'package:equatable/equatable.dart'; + +class ANTRecord extends Equatable { final String domain; final String processId; - ANTRecord({ + const ANTRecord({ required this.domain, required this.processId, }); + + @override + List get props => [domain, processId]; } diff --git a/packages/ario_sdk/lib/src/models/arns_record.dart b/packages/ario_sdk/lib/src/models/arns_record.dart index dc86687c5..fd1394756 100644 --- a/packages/ario_sdk/lib/src/models/arns_record.dart +++ b/packages/ario_sdk/lib/src/models/arns_record.dart @@ -1,16 +1,20 @@ +import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; part 'arns_record.g.dart'; @JsonSerializable() -class ARNSRecord { +class ARNSRecord extends Equatable { final String transactionId; final int ttlSeconds; - ARNSRecord({required this.transactionId, required this.ttlSeconds}); + const ARNSRecord({required this.transactionId, required this.ttlSeconds}); factory ARNSRecord.fromJson(Map json) => _$ARNSRecordFromJson(json); Map toJson() => _$ARNSRecordToJson(this); + + @override + List get props => [transactionId, ttlSeconds]; } diff --git a/packages/ario_sdk/lib/src/utils/get_literal_arns_record_name.dart b/packages/ario_sdk/lib/src/utils/get_literal_arns_record_name.dart index dcd7018f2..bcd4c8dd6 100644 --- a/packages/ario_sdk/lib/src/utils/get_literal_arns_record_name.dart +++ b/packages/ario_sdk/lib/src/utils/get_literal_arns_record_name.dart @@ -6,3 +6,4 @@ String getLiteralARNSRecordName(ARNSUndername undername) { } return '${undername.name}_${undername.domain}'; } + From 55c101e8cedfe5d4b1bc5fd96949303410a12961 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 04:28:11 -0300 Subject: [PATCH 05/11] Update manifest_options.dart --- lib/core/upload/view/manifest_options/manifest_options.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/core/upload/view/manifest_options/manifest_options.dart b/lib/core/upload/view/manifest_options/manifest_options.dart index b74064a7b..065019a7c 100644 --- a/lib/core/upload/view/manifest_options/manifest_options.dart +++ b/lib/core/upload/view/manifest_options/manifest_options.dart @@ -438,8 +438,6 @@ class _AntSelectorState extends State { ArDriveDropdownItem _buildDropdownItem(BuildContext context, ANTRecord ant) { final typography = ArDriveTypographyNew.of(context); - final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - return ArDriveDropdownItem( content: SizedBox( width: 235, From a0d48a99d5bf3ba3d6ef77f4f0a89d05b9f61202 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 04:30:17 -0300 Subject: [PATCH 06/11] chore: fix lint issues --- .../create_manifest_cubit.dart | 2 +- lib/blocs/upload/upload_cubit.dart | 2 +- .../blocs/upload_manifest_options_bloc.dart | 4 +- .../get_literal_arns_record_name_test.dart | 4 +- .../assign_name_bloc_test.dart | 54 +++++++++---------- .../domain/manifest_repository_test.dart | 6 +-- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/blocs/create_manifest/create_manifest_cubit.dart b/lib/blocs/create_manifest/create_manifest_cubit.dart index f4e277516..02532bb9b 100644 --- a/lib/blocs/create_manifest/create_manifest_cubit.dart +++ b/lib/blocs/create_manifest/create_manifest_cubit.dart @@ -347,7 +347,7 @@ class CreateManifestCubit extends Cubit { return ARNSUndername( name: '@', domain: _selectedAntRecord!.domain, - record: ARNSRecord( + record: const ARNSRecord( transactionId: 'to_assign', ttlSeconds: 3600, ), diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index f85c218b3..1ee724320 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -1403,7 +1403,7 @@ class UploadCubit extends Cubit { return ARNSUndername( name: '@', domain: _selectedAntRecord!.domain, - record: ARNSRecord( + record: const ARNSRecord( transactionId: 'to_assign', ttlSeconds: 3600, ), diff --git a/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart b/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart index 35747df3c..784a3eca9 100644 --- a/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart +++ b/lib/core/upload/view/blocs/upload_manifest_options_bloc.dart @@ -2,8 +2,8 @@ import 'package:ardrive/arns/domain/arns_repository.dart'; import 'package:ardrive/authentication/ardrive_auth.dart'; import 'package:ardrive/models/models.dart'; import 'package:ario_sdk/ario_sdk.dart'; -import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; part 'upload_manifest_options_event.dart'; part 'upload_manifest_options_state.dart'; @@ -95,7 +95,7 @@ class UploadManifestOptionsBloc emit(_createReadyState()); }); - on((event, emit) { + on((event, emit) { if (reservedNames[event.antRecord.domain] == null) { reservedNames[event.antRecord.domain] = []; } diff --git a/packages/ario_sdk/test/src/utils/get_literal_arns_record_name_test.dart b/packages/ario_sdk/test/src/utils/get_literal_arns_record_name_test.dart index c69a721ea..2bd02abc4 100644 --- a/packages/ario_sdk/test/src/utils/get_literal_arns_record_name_test.dart +++ b/packages/ario_sdk/test/src/utils/get_literal_arns_record_name_test.dart @@ -5,7 +5,7 @@ void main() { group('getLiteralARNSRecordName', () { test('returns the correct record name for a given ARNSUndername', () { // Arrange - final undername = ARNSUndername( + const undername = ARNSUndername( name: 'test', domain: 'example.com', record: ARNSRecord( @@ -24,7 +24,7 @@ void main() { test('returns the correct record name for a given ARNSUndername with @', () { // Arrange - final undername = ARNSUndername( + const undername = ARNSUndername( name: '@', // @ is the default name for the root domain domain: 'example.com', record: ARNSRecord( diff --git a/test/arns/presentation/assign_name_bloc/assign_name_bloc_test.dart b/test/arns/presentation/assign_name_bloc/assign_name_bloc_test.dart index 874ec963e..e3375298a 100644 --- a/test/arns/presentation/assign_name_bloc/assign_name_bloc_test.dart +++ b/test/arns/presentation/assign_name_bloc/assign_name_bloc_test.dart @@ -14,13 +14,13 @@ class MockFileDataTableItem extends Mock implements FileDataTableItem {} void main() { setUpAll(() { - registerFallbackValue(ARNSUndername( + registerFallbackValue(const ARNSUndername( name: 'test_undername', domain: 'test.ar', record: ARNSRecord(transactionId: 'test_tx_id', ttlSeconds: 3600), )); registerFallbackValue( - ANTRecord(domain: 'test.ar', processId: 'test_process_id')); + const ANTRecord(domain: 'test.ar', processId: 'test_process_id')); }); group('AssignNameBloc', () { @@ -51,8 +51,8 @@ void main() { // Arrange const walletAddress = 'test_wallet_address'; final antRecords = [ - ANTRecord(domain: 'test1.ar', processId: 'process1'), - ANTRecord(domain: 'test2.ar', processId: 'process2'), + const ANTRecord(domain: 'test1.ar', processId: 'process1'), + const ANTRecord(domain: 'test2.ar', processId: 'process2'), ]; when(() => mockAuth.getWalletAddress()) @@ -138,8 +138,8 @@ void main() { () async { // Arrange final antRecords = [ - ANTRecord(domain: 'domain1.ar', processId: 'process1'), - ANTRecord(domain: 'domain2.ar', processId: 'process2'), + const ANTRecord(domain: 'domain1.ar', processId: 'process1'), + const ANTRecord(domain: 'domain2.ar', processId: 'process2'), ]; final selectedName = antRecords[0]; @@ -165,12 +165,12 @@ void main() { () async { // Arrange final antRecords = [ - ANTRecord(domain: 'domain1.ar', processId: 'process1'), - ANTRecord(domain: 'domain2.ar', processId: 'process2'), + const ANTRecord(domain: 'domain1.ar', processId: 'process1'), + const ANTRecord(domain: 'domain2.ar', processId: 'process2'), ]; final selectedName = antRecords[1]; final undernames = [ - ARNSUndername( + const ARNSUndername( name: 'undername1', domain: 'domain1.ar', record: ARNSRecord(transactionId: 'tx1', ttlSeconds: 3600)), @@ -205,17 +205,17 @@ void main() { () async { // Arrange final antRecords = [ - ANTRecord(domain: 'domain1.ar', processId: 'process1'), - ANTRecord(domain: 'domain2.ar', processId: 'process2'), + const ANTRecord(domain: 'domain1.ar', processId: 'process1'), + const ANTRecord(domain: 'domain2.ar', processId: 'process2'), ]; final selectedName = antRecords[0]; final undernames = [ - ARNSUndername( + const ARNSUndername( name: 'undername1', domain: 'domain1.ar', record: ARNSRecord(transactionId: 'tx1', ttlSeconds: 3600), ), - ARNSUndername( + const ARNSUndername( name: 'undername2', domain: 'domain1.ar', record: ARNSRecord(transactionId: 'tx2', ttlSeconds: 3600), @@ -258,17 +258,17 @@ void main() { () async { // Arrange final antRecords = [ - ANTRecord(domain: 'domain1.ar', processId: 'process1'), - ANTRecord(domain: 'domain2.ar', processId: 'process2'), + const ANTRecord(domain: 'domain1.ar', processId: 'process1'), + const ANTRecord(domain: 'domain2.ar', processId: 'process2'), ]; final selectedName = antRecords[0]; final undernames = [ - ARNSUndername( + const ARNSUndername( name: 'undername1', domain: 'domain1.ar', record: ARNSRecord(transactionId: 'tx1', ttlSeconds: 3600), ), - ARNSUndername( + const ARNSUndername( name: 'undername2', domain: 'domain1.ar', record: ARNSRecord(transactionId: 'tx2', ttlSeconds: 3600), @@ -311,8 +311,8 @@ void main() { when(() => mockFileDataTableItem.fileId).thenReturn('test_file_id'); when(() => mockFileDataTableItem.driveId).thenReturn('test_drive_id'); final antRecords = [ - ANTRecord(domain: 'test1.ar', processId: 'process1'), - ANTRecord(domain: 'test2.ar', processId: 'process2'), + const ANTRecord(domain: 'test1.ar', processId: 'process1'), + const ANTRecord(domain: 'test2.ar', processId: 'process2'), ]; const walletAddress = 'test_wallet_address'; @@ -329,7 +329,7 @@ void main() { )).thenAnswer((_) async {}); when(() => mockArnsRepository.getARNSUndernames(any())).thenAnswer( (_) async => [ - ARNSUndername( + const ARNSUndername( name: 'undername', domain: 'domain', record: @@ -342,9 +342,9 @@ void main() { act: (bloc) { bloc.add(const LoadNames()); bloc.add( - SelectName(ANTRecord(domain: 'domain', processId: 'process_id'))); + const SelectName(ANTRecord(domain: 'domain', processId: 'process_id'))); bloc.add(const LoadUndernames()); - bloc.add(SelectUndername( + bloc.add(const SelectUndername( undername: ARNSUndername( name: 'undername', domain: 'domain', @@ -385,8 +385,8 @@ void main() { when(() => mockFileDataTableItem.fileId).thenReturn('test_file_id'); when(() => mockFileDataTableItem.driveId).thenReturn('test_drive_id'); final antRecords = [ - ANTRecord(domain: 'test1.ar', processId: 'process1'), - ANTRecord(domain: 'test2.ar', processId: 'process2'), + const ANTRecord(domain: 'test1.ar', processId: 'process1'), + const ANTRecord(domain: 'test2.ar', processId: 'process2'), ]; const walletAddress = 'test_wallet_address'; @@ -406,7 +406,7 @@ void main() { )).thenThrow(StateError('Test error')); when(() => mockArnsRepository.getARNSUndernames(any())).thenAnswer( (_) async => [ - ARNSUndername( + const ARNSUndername( name: 'undername', domain: 'domain', record: ARNSRecord(transactionId: 'test_tx_id', ttlSeconds: 3600), @@ -418,9 +418,9 @@ void main() { act: (bloc) { bloc.add(const LoadNames()); bloc.add( - SelectName(ANTRecord(domain: 'domain', processId: 'process_id'))); + const SelectName(ANTRecord(domain: 'domain', processId: 'process_id'))); bloc.add(const LoadUndernames()); - bloc.add(SelectUndername( + bloc.add(const SelectUndername( undername: ARNSUndername( name: 'undername', domain: 'domain', diff --git a/test/manifest/domain/manifest_repository_test.dart b/test/manifest/domain/manifest_repository_test.dart index 83d6fb82a..ee6306307 100644 --- a/test/manifest/domain/manifest_repository_test.dart +++ b/test/manifest/domain/manifest_repository_test.dart @@ -65,7 +65,7 @@ void main() async { registerFallbackValue(FileEntity()); registerFallbackValue(const FileRevisionsCompanion()); registerFallbackValue( - ARNSUndername( + const ARNSUndername( name: 'undername', domain: 'domain', record: ARNSRecord( @@ -284,7 +284,7 @@ void main() async { await repository.uploadManifest( params: mockUploadParams, processId: 'process_id', - undername: ARNSUndername( + undername: const ARNSUndername( name: 'undername', domain: 'domain', record: ARNSRecord( @@ -347,7 +347,7 @@ void main() async { await repository.uploadManifest( params: mockUploadParams, processId: 'process_id', - undername: ARNSUndername( + undername: const ARNSUndername( name: 'undername', domain: 'domain', record: ARNSRecord( From bede9f2fcc436f8180bfd758b8cb7ecca26c80c6 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 07:08:19 -0300 Subject: [PATCH 07/11] feat(assign name arns) - improve ui --- lib/components/upload_form.dart | 20 ++++- .../manifest_options/manifest_options.dart | 81 ++++++++++--------- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 2978dacc0..15b422ed5 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -2031,6 +2031,12 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + double heightForManifestSelections = 125; + + if (readyState.params.arnsUnderName == null) { + heightForManifestSelections += 45; + } + return StatsScreen( readyState: readyState, modalActions: [ @@ -2085,8 +2091,8 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { Padding( padding: const EdgeInsets.only(bottom: 16.0), child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 125, + constraints: BoxConstraints( + maxHeight: heightForManifestSelections, minWidth: kLargeDialogWidth, ), child: Column( @@ -2177,6 +2183,12 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; + double heightForManifestSelections = 125; + + if (state.readyState.params.arnsUnderName == null) { + heightForManifestSelections += 45; + } + return StatsScreen( readyState: state.readyState, modalActions: [ @@ -2226,8 +2238,8 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { Padding( padding: const EdgeInsets.only(bottom: 16.0), child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 125, + constraints: BoxConstraints( + maxHeight: heightForManifestSelections, minWidth: kLargeDialogWidth, ), child: Column( diff --git a/lib/core/upload/view/manifest_options/manifest_options.dart b/lib/core/upload/view/manifest_options/manifest_options.dart index 065019a7c..be48f3e01 100644 --- a/lib/core/upload/view/manifest_options/manifest_options.dart +++ b/lib/core/upload/view/manifest_options/manifest_options.dart @@ -100,7 +100,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { curve: Curves.easeInOut, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), height: isExpanded - ? 204 + ? 168 : showingName ? 70 : 50, @@ -108,7 +108,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { onTap: () {}, child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Row( mainAxisSize: MainAxisSize.max, @@ -160,7 +160,6 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { ], ), ), - ArDriveTooltip( message: (state.arnsNamesLoaded && state.ants!.isEmpty) ? 'No ARNS names found for your wallet' @@ -172,7 +171,8 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { ? 'Change ArNS' : 'Add ArNS', typography: typography, - isDisabled: !widget.isSelected || + isDisabled: isExpanded || + !widget.isSelected || (state.arnsNamesLoaded && state.ants!.isEmpty), fontStyle: typography.paragraphSmall(), variant: ButtonVariant.primary, @@ -365,41 +365,48 @@ class _AntSelectorState extends State { ), ), ), - if (isNameAlreadyInUse && - widget.manifestSelection.antRecord?.domain != - _selectedAnt?.domain) - Padding( - padding: const EdgeInsets.only(top: 8, left: 8), - child: Text( - 'Name already in use, please choose another name or select a undername', - style: typography.paragraphSmall( - fontWeight: ArFontWeight.semiBold, + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (isNameAlreadyInUse && + widget.manifestSelection.antRecord?.domain != + _selectedAnt?.domain) + Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 8, left: 8), + child: Text( + 'Name already in use, please choose another name or select a undername', + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8, left: 8), + child: Align( + alignment: Alignment.centerRight, + child: ArDriveButtonNew( + text: 'Add', + typography: typography, + fontStyle: typography.paragraphSmall(), + variant: ButtonVariant.primary, + maxWidth: 80, + maxHeight: 30, + isDisabled: isNameAlreadyInUse || _selectedAnt == null, + onPressed: () { + context + .read() + .add(LinkManifestToUndername( + manifest: widget.manifestSelection.manifest, + antRecord: _selectedAnt!, + undername: _selectedUndername, + )); + }, + ), ), ), - ), - Padding( - padding: const EdgeInsets.only(top: 8, left: 8), - child: Align( - alignment: Alignment.centerRight, - child: ArDriveButtonNew( - text: 'Add', - typography: typography, - fontStyle: typography.paragraphSmall(), - variant: ButtonVariant.primary, - maxWidth: 80, - maxHeight: 30, - isDisabled: isNameAlreadyInUse || _selectedAnt == null, - onPressed: () { - context - .read() - .add(LinkManifestToUndername( - manifest: widget.manifestSelection.manifest, - antRecord: _selectedAnt!, - undername: _selectedUndername, - )); - }, - ), - ), + ], ), ], ); From d4d4628d7b65cbd48dc06bb78c512962a802d5ea Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:46:05 -0300 Subject: [PATCH 08/11] feat(assign arns name): fix spacing issues --- lib/components/upload_form.dart | 90 ++++++++++--------- .../manifest_options/manifest_options.dart | 56 ++++++------ 2 files changed, 74 insertions(+), 72 deletions(-) diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 15b422ed5..d51241c33 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -2031,10 +2031,15 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - double heightForManifestSelections = 125; + double heightForManifestSelections = + (readyState.selectedManifestSelections.length * 30) + 16; + + if (heightForManifestSelections > 200) { + heightForManifestSelections = 175; + } if (readyState.params.arnsUnderName == null) { - heightForManifestSelections += 45; + heightForManifestSelections += 50; } return StatsScreen( @@ -2087,9 +2092,10 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { ), ), ], + LicenseReviewInfo(licenseState: state.licenseState), if (state.readyState.selectedManifestSelections.isNotEmpty) ...[ Padding( - padding: const EdgeInsets.only(bottom: 16.0), + padding: const EdgeInsets.only(bottom: 8.0), child: ConstrainedBox( constraints: BoxConstraints( maxHeight: heightForManifestSelections, @@ -2100,7 +2106,6 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ - const SizedBox(height: 8), Text( 'Updated manifest(s):', style: typography.paragraphNormal( @@ -2109,21 +2114,40 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { ), ), const SizedBox(height: 4), - Flexible( - child: Expanded( - child: ListView( - shrinkWrap: true, - children: [ - ...state.readyState.selectedManifestSelections.map( - (e) => Column( - children: [ + Expanded( + child: ListView( + shrinkWrap: true, + children: [ + ...state.readyState.selectedManifestSelections.map( + (e) => Column( + children: [ + Row( + children: [ + ArDriveIcons.manifest(size: 16), + const SizedBox(width: 8), + Flexible( + child: Text( + e.manifest.name, + style: typography.paragraphNormal( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + if (e.antRecord != null || + e.undername != null) ...[ + const SizedBox(height: 2), Row( children: [ - ArDriveIcons.manifest(size: 16), + ArDriveIcons.arnsName(size: 16), const SizedBox(width: 8), Flexible( child: Text( - e.manifest.name, + getLiteralArNSName( + e.antRecord!, e.undername), style: typography.paragraphNormal( fontWeight: ArFontWeight.semiBold, ), @@ -2133,41 +2157,18 @@ class _UploadReviewWithLicenseWidget extends StatelessWidget { ), ], ), - if (e.antRecord != null || - e.undername != null) ...[ - const SizedBox(height: 2), - Row( - children: [ - ArDriveIcons.arnsName(size: 16), - const SizedBox(width: 8), - Flexible( - child: Text( - getLiteralArNSName( - e.antRecord!, e.undername), - style: typography.paragraphNormal( - fontWeight: ArFontWeight.semiBold, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], ], - ), + ], ), - ], - ), + ), + ], ), ), - const SizedBox(height: 8), ], ), ), ), ], - LicenseReviewInfo(licenseState: state.licenseState), ], ); } @@ -2183,10 +2184,15 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { final typography = ArDriveTypographyNew.of(context); final colorTokens = ArDriveTheme.of(context).themeData.colorTokens; - double heightForManifestSelections = 125; + double heightForManifestSelections = + (state.readyState.selectedManifestSelections.length * 30) + 16; + + if (heightForManifestSelections > 200) { + heightForManifestSelections = 200; + } if (state.readyState.params.arnsUnderName == null) { - heightForManifestSelections += 45; + heightForManifestSelections += 50; } return StatsScreen( diff --git a/lib/core/upload/view/manifest_options/manifest_options.dart b/lib/core/upload/view/manifest_options/manifest_options.dart index be48f3e01..ffe37f30c 100644 --- a/lib/core/upload/view/manifest_options/manifest_options.dart +++ b/lib/core/upload/view/manifest_options/manifest_options.dart @@ -162,7 +162,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { ), ArDriveTooltip( message: (state.arnsNamesLoaded && state.ants!.isEmpty) - ? 'No ARNS names found for your wallet' + ? 'No ArNS names found for your wallet' : '', child: ArDriveButtonNew( text: !state.arnsNamesLoaded @@ -313,11 +313,7 @@ class _AntSelectorState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Row( - children: [ - _buildSelectedItem(context), - ], - ), + Flexible(child: _buildSelectedItem(context)), ArDriveIcons.chevronDown(), ], ), @@ -365,15 +361,20 @@ class _AntSelectorState extends State { ), ), ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (isNameAlreadyInUse && - widget.manifestSelection.antRecord?.domain != - _selectedAnt?.domain) - Expanded( - child: Padding( - padding: const EdgeInsets.only(top: 8, left: 8), + const Spacer(), + Padding( + padding: const EdgeInsets.only( + top: 8, + left: 8, + bottom: 4, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (isNameAlreadyInUse && + widget.manifestSelection.antRecord?.domain != + _selectedAnt?.domain) + Expanded( child: Text( 'Name already in use, please choose another name or select a undername', style: typography.paragraphSmall( @@ -381,10 +382,7 @@ class _AntSelectorState extends State { ), ), ), - ), - Padding( - padding: const EdgeInsets.only(top: 8, left: 8), - child: Align( + Align( alignment: Alignment.centerRight, child: ArDriveButtonNew( text: 'Add', @@ -405,8 +403,8 @@ class _AntSelectorState extends State { }, ), ), - ), - ], + ], + ), ), ], ); @@ -419,15 +417,13 @@ class _AntSelectorState extends State { Widget _buildSelectedItem(BuildContext context) { final typography = ArDriveTypographyNew.of(context); - return Column( - children: [ - Text( - _selectedAnt?.domain ?? 'Choose ArNS name', - style: typography.paragraphSmall( - fontWeight: ArFontWeight.semiBold, - ), - ), - ], + return Text( + _selectedAnt?.domain ?? 'Choose ArNS name', + style: typography.paragraphSmall( + fontWeight: ArFontWeight.semiBold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ); } From 393c452ffc6d7a6c935394917cf8075dfa7881cb Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:12:37 -0300 Subject: [PATCH 09/11] Update manifest_options.dart --- lib/core/upload/view/manifest_options/manifest_options.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/upload/view/manifest_options/manifest_options.dart b/lib/core/upload/view/manifest_options/manifest_options.dart index ffe37f30c..827c3927a 100644 --- a/lib/core/upload/view/manifest_options/manifest_options.dart +++ b/lib/core/upload/view/manifest_options/manifest_options.dart @@ -173,6 +173,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { typography: typography, isDisabled: isExpanded || !widget.isSelected || + !state.arnsNamesLoaded || (state.arnsNamesLoaded && state.ants!.isEmpty), fontStyle: typography.paragraphSmall(), variant: ButtonVariant.primary, From f02cacebfb96c8713dd0ca49f2856eede4a89f9b Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:17:29 -0300 Subject: [PATCH 10/11] Update manifest_options.dart --- lib/core/upload/view/manifest_options/manifest_options.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/upload/view/manifest_options/manifest_options.dart b/lib/core/upload/view/manifest_options/manifest_options.dart index 827c3927a..755957b05 100644 --- a/lib/core/upload/view/manifest_options/manifest_options.dart +++ b/lib/core/upload/view/manifest_options/manifest_options.dart @@ -177,7 +177,7 @@ class __ManifestOptionTileState extends State<_ManifestOptionTile> { (state.arnsNamesLoaded && state.ants!.isEmpty), fontStyle: typography.paragraphSmall(), variant: ButtonVariant.primary, - maxWidth: 100, + maxWidth: state.arnsNamesLoaded ? 100 : 120, maxHeight: 30, onPressed: () { context From 11524a2e540b89796d3256c993e637b7e3c65589 Mon Sep 17 00:00:00 2001 From: Thiago Carvalho <32248947+thiagocarvalhodev@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:32:56 -0300 Subject: [PATCH 11/11] Update upload_form.dart --- lib/components/upload_form.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index d51241c33..da7a9de38 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -2253,7 +2253,6 @@ class _UploadReviewWithArnsNameWidget extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ - const SizedBox(height: 8), Text( 'Updated manifest(s):', style: typography.paragraphNormal(