Skip to content

Commit

Permalink
feat(manifest 0.2.0)
Browse files Browse the repository at this point in the history
- update manifest version
- adds the UI and validations for inserting a fallback
- updates the manifest builder introducing the fallback tx id
  • Loading branch information
thiagocarvalhodev committed Jan 6, 2025
1 parent f3b0d60 commit f6c9218
Show file tree
Hide file tree
Showing 11 changed files with 948 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Fixed profile card alignment
- Fixed an issue where sync gets stuck after failing to fetch a metadata t
31 changes: 31 additions & 0 deletions lib/blocs/create_manifest/create_manifest_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class CreateManifestCubit extends Cubit<CreateManifestState> {
canUpload: canUpload,
freeUpload: info.isFreeThanksToTurbo,
assignedName: (state as CreateManifestUploadReview).assignedName,
fallbackTxId: (state as CreateManifestUploadReview).fallbackTxId,
),
);
}
Expand Down Expand Up @@ -139,6 +140,7 @@ class CreateManifestCubit extends Cubit<CreateManifestState> {
CreateManifestFolderLoadSuccess(
viewingRootFolder: f.folder.parentFolderId == null,
viewingFolder: f,
enableManifestCreationButton: _getEnableManifestCreationButton(),
),
),
);
Expand Down Expand Up @@ -249,6 +251,7 @@ class CreateManifestCubit extends Cubit<CreateManifestState> {
manifestName: manifestName,
rootFolderNode: rootFolderNode,
driveId: _drive.id,
fallbackTxId: getFallbackTxId(),
);

ARNSUndername? undername = getSelectedUndername();
Expand All @@ -264,6 +267,7 @@ class CreateManifestCubit extends Cubit<CreateManifestState> {
existingManifestFileId: existingManifestFileId,
assignedName:
undername != null ? getLiteralARNSRecordName(undername) : null,
fallbackTxId: getFallbackTxId(),
),
);
} catch (e) {
Expand Down Expand Up @@ -367,6 +371,33 @@ class CreateManifestCubit extends Cubit<CreateManifestState> {
prepareManifestTx(manifestName: manifestName);
}

TxID? _fallbackTxId;

void setFallbackTxId(TxID txId) {
_fallbackTxId = txId;

emit(
(state as CreateManifestFolderLoadSuccess).copyWith(
fallbackTxId: getFallbackTxId(),
enableManifestCreationButton: _getEnableManifestCreationButton(),
),
);
}

TxID? getFallbackTxId() {
if (_fallbackTxId == null || _fallbackTxId!.isEmpty) {
return null;
}

return _fallbackTxId;
}

bool _getEnableManifestCreationButton() {
return getFallbackTxId() == null ||
getFallbackTxId()!.isEmpty ||
isValidArweaveTxId(getFallbackTxId()!);
}

@override
Future<void> close() async {
await _selectedFolderSubscription?.cancel();
Expand Down
29 changes: 28 additions & 1 deletion lib/blocs/create_manifest/create_manifest_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,36 @@ class CreateManifestInitial extends CreateManifestState {}
class CreateManifestFolderLoadSuccess extends CreateManifestState {
final bool viewingRootFolder;
final FolderWithContents viewingFolder;
final bool enableManifestCreationButton;
final String? fallbackTxId;

CreateManifestFolderLoadSuccess({
required this.viewingRootFolder,
required this.viewingFolder,
required this.enableManifestCreationButton,
this.fallbackTxId,
});

CreateManifestFolderLoadSuccess copyWith({
bool? enableManifestCreationButton,
String? fallbackTxId,
}) {
return CreateManifestFolderLoadSuccess(
viewingRootFolder: viewingRootFolder,
viewingFolder: viewingFolder,
enableManifestCreationButton:
enableManifestCreationButton ?? this.enableManifestCreationButton,
fallbackTxId: fallbackTxId ?? this.fallbackTxId,
);
}

@override
List<Object> get props => [viewingRootFolder, viewingFolder];
List<Object?> get props => [
viewingRootFolder,
viewingFolder,
enableManifestCreationButton,
fallbackTxId,
];
}

/// User has selected a folder and we are checking for name conflicts
Expand Down Expand Up @@ -109,6 +131,7 @@ class CreateManifestUploadReview extends CreateManifestState {
final String? existingManifestFileId;
final bool canUpload;
final String? assignedName;
final String? fallbackTxId;

CreateManifestUploadReview({
required this.manifestSize,
Expand All @@ -122,6 +145,7 @@ class CreateManifestUploadReview extends CreateManifestState {
this.existingManifestFileId,
this.canUpload = false,
this.assignedName,
this.fallbackTxId,
});

@override
Expand All @@ -136,6 +160,7 @@ class CreateManifestUploadReview extends CreateManifestState {
parentFolder,
existingManifestFileId,
assignedName,
fallbackTxId,
];

CreateManifestUploadReview copyWith({
Expand All @@ -150,6 +175,7 @@ class CreateManifestUploadReview extends CreateManifestState {
String? existingManifestFileId,
bool? canUpload,
String? assignedName,
String? fallbackTxId,
}) {
return CreateManifestUploadReview(
manifestSize: manifestSize ?? this.manifestSize,
Expand All @@ -165,6 +191,7 @@ class CreateManifestUploadReview extends CreateManifestState {
existingManifestFileId ?? this.existingManifestFileId,
canUpload: canUpload ?? this.canUpload,
assignedName: assignedName ?? this.assignedName,
fallbackTxId: fallbackTxId ?? this.fallbackTxId,
);
}
}
Expand Down
69 changes: 69 additions & 0 deletions lib/components/create_manifest_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,30 @@ class _CreateManifestFormState extends State<CreateManifestForm> {
filesize(state.manifestSize),
style: textStyle,
),
const SizedBox(height: 8),
if (state.fallbackTxId != null) ...[
RichText(
text: TextSpan(
style: textStyle,
children: [
TextSpan(
text: 'Fallback TxId\n',
style: typography.paragraphLarge(
color: colorTokens.textHigh,
fontWeight: ArFontWeight.bold,
),
),
TextSpan(
text: state.fallbackTxId,
style: typography.paragraphSmall(
color: colorTokens.textMid,
fontWeight: ArFontWeight.semiBold,
),
),
],
),
),
],
],
),
),
Expand Down Expand Up @@ -874,6 +898,50 @@ class _CreateManifestFormState extends State<CreateManifestForm> {
),
),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ArDriveClickArea(
child: ArDriveTooltip(
message: 'Fallback TxId is the TxId of the fallback file.',
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Fallback TxId',
style: typography.paragraphNormal(
color: colorTokens.textLow,
fontWeight: ArFontWeight.semiBold,
),
),
const SizedBox(width: 8),
ArDriveIcons.info(
size: 16,
color: colorTokens.iconMid,
),
],
),
),
),
),
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ArDriveTextFieldNew(
hintText: 'TxId',
onChanged: (value) {
cubit.setFallbackTxId(value);
},
validator: (value) {
if (value == null || value.isEmpty) {
return null;
}

return isValidArweaveTxId(value) ? null : 'Invalid TxId';
},
),
),
const Divider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ArDriveCard(
Expand Down Expand Up @@ -901,6 +969,7 @@ class _CreateManifestFormState extends State<CreateManifestForm> {
),
),
action: ModalAction(
isEnable: state.enableManifestCreationButton,
action: () => cubit.checkForConflicts(_manifestNameController.text),
title: appLocalizationsOf(context).createHereEmphasized,
),
Expand Down
28 changes: 24 additions & 4 deletions lib/entities/manifest_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:ardrive/core/arfs/repository/file_repository.dart';
import 'package:ardrive/core/arfs/repository/folder_repository.dart';
import 'package:ardrive/entities/entities.dart';
import 'package:ardrive/models/models.dart';
import 'package:ardrive/utils/logger.dart';
import 'package:ardrive_utils/ardrive_utils.dart';
import 'package:arweave/arweave.dart';
import 'package:collection/collection.dart';
Expand Down Expand Up @@ -43,28 +44,45 @@ class ManifestPath {
Map<String, dynamic> toJson() => _$ManifestPathToJson(this);
}

@JsonSerializable()
class ManifestFallback {
final String id;

ManifestFallback(this.id);

factory ManifestFallback.fromJson(Map<String, dynamic> json) =>
ManifestFallback(json['fallback']['id'] as String);

Map<String, dynamic> toJson() => {'id': id};
}

@JsonSerializable(explicitToJson: true)
class ManifestData {
@JsonKey()
String manifest = 'arweave/paths';
@JsonKey()
String version = '0.1.0';
String version = '0.2.0';
@JsonKey(includeIfNull: false)
final ManifestFallback? fallback;
@JsonKey()
final ManifestIndex index;
@JsonKey()
final Map<String, ManifestPath> paths;

ManifestData(
this.index,
this.paths,
);
this.paths, {
this.fallback,
});

int get size => jsonData.lengthInBytes;
Uint8List get jsonData => utf8.encode(json.encode(this));

Future<DataItem> asPreparedDataItem({
required ArweaveAddressString owner,
}) async {
logger.d(json.encode(this));

final manifestDataItem = DataItem.withBlobData(data: jsonData)
..setOwner(owner)
..addApplicationTags(
Expand All @@ -84,7 +102,7 @@ class ManifestData {
/// replace spaces with underscores for arweave.net URL compatibility
String prepareManifestPath({
required String filePath,
required String rootFolderPath,
required String rootFolderPath,
}) {
return filePath.substring(rootFolderPath.length + 1).replaceAll(' ', '_');
}
Expand All @@ -100,6 +118,7 @@ class ManifestDataBuilder {

Future<ManifestData> build({
required FolderNode folderNode,
String? fallbackTxId,
}) async {
final fileList = folderNode
.getRecursiveFiles()
Expand Down Expand Up @@ -144,6 +163,7 @@ class ManifestDataBuilder {
return ManifestData(
index,
paths,
fallback: fallbackTxId != null ? ManifestFallback(fallbackTxId) : null,
);
}
}
3 changes: 3 additions & 0 deletions lib/manifest/domain/manifest_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ abstract class ManifestRepository {
required String manifestName,
required FolderNode rootFolderNode,
required String driveId,
String? fallbackTxId,
});

Future<bool> hasPendingFilesOnTargetFolder({required FolderNode folderNode});
Expand Down Expand Up @@ -192,13 +193,15 @@ class ManifestRepositoryImpl implements ManifestRepository {
required String manifestName,
required FolderNode rootFolderNode,
required String driveId,
String? fallbackTxId,
}) async {
try {
final folderNode = rootFolderNode.searchForFolder(parentFolder.id) ??
await _driveDao.getFolderTree(driveId, parentFolder.id);

final arweaveManifest = await _builder.build(
folderNode: folderNode,
fallbackTxId: fallbackTxId,
);

final manifestFile = await IOFileAdapter().fromData(
Expand Down
1 change: 1 addition & 0 deletions packages/ardrive_ui/lib/src/components/modal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class ArDriveModalNew extends StatelessWidget {
text: action!.title,
onPressed: action!.action,
typography: ArDriveTypographyNew.of(context),
isDisabled: !action!.isEnable,
),
),
],
Expand Down
18 changes: 18 additions & 0 deletions packages/ardrive_utils/lib/src/validations.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:ardrive_utils/ardrive_utils.dart';

bool isValidUuidV4(String uuid) {
final RegExp uuidV4Pattern = RegExp(
r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89aAbB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$');
Expand All @@ -11,3 +13,19 @@ bool isValidUuidFormat(String uuid) {

return uuidPattern.hasMatch(uuid.toLowerCase());
}

bool isValidTxId(String txId) {
final RegExp txIdPattern = RegExp(r'^[0-9a-f]{64}$');
return txIdPattern.hasMatch(txId.toLowerCase());
}

bool isValidArweaveTxId(TxID txId) {
// Check if the length of the string is 43
if (txId.length != 43) {
return false;
}

// Check if the string contains only base64url valid characters
final base64UrlRegex = RegExp(r'^[A-Za-z0-9_-]+$');
return base64UrlRegex.hasMatch(txId);
}
Loading

0 comments on commit f6c9218

Please sign in to comment.