Skip to content

Commit

Permalink
feat(custom manifest)
Browse files Browse the repository at this point in the history
- rename variables
- add validations on isCustomManifest method
- update tests
  • Loading branch information
thiagocarvalhodev committed Nov 18, 2024
1 parent 8ff7d80 commit 09e816d
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 31 deletions.
22 changes: 12 additions & 10 deletions lib/blocs/upload/upload_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ class UploadCubit extends Cubit<UploadState> {
UploadMethod? _manifestUploadMethod;

bool _isManifestsUploadCancelled = false;
bool _isUploadingCustomManifest = false;

/// if true, the file will change its content type to `application/x.arweave-manifest+json`
bool _uploadFileAsCustomManifest = false;

void updateManifestSelection(List<ManifestSelection> selections) {
_selectedManifestModels.clear();
Expand All @@ -130,7 +132,7 @@ class UploadCubit extends Cubit<UploadState> {
}

void setIsUploadingCustomManifest(bool value) {
_isUploadingCustomManifest = value;
_uploadFileAsCustomManifest = value;
emit((state as UploadReady).copyWith(isUploadingCustomManifest: value));
}

Expand Down Expand Up @@ -449,9 +451,8 @@ class UploadCubit extends Cubit<UploadState> {
bool showArnsCheckbox = false;

if (_targetDrive.isPublic && _files.length == 1) {
final isACustomManifest = await isCustomManifest(_files.first.ioFile);

logger.d('Is a custom manifest: $isACustomManifest');
final fileIsACustomManifest =
await isCustomManifest(_files.first.ioFile);

emit(
UploadReady(
Expand All @@ -473,8 +474,8 @@ class UploadCubit extends Cubit<UploadState> {
arnsRecords: _ants,
showReviewButtonText: false,
selectedManifestSelections: _selectedManifestModels,
isCustomManifest: isACustomManifest,
isUploadingCustomManifest: false,
shouldShowCustomManifestCheckbox: fileIsACustomManifest,
uploadFileAsCustomManifest: false,
),
);

Expand Down Expand Up @@ -519,8 +520,9 @@ class UploadCubit extends Cubit<UploadState> {
canShowSettings: showSettings,
showReviewButtonText: false,
selectedManifestSelections: _selectedManifestModels,
isCustomManifest: false, // only applies for single file uploads
isUploadingCustomManifest: false,
uploadFileAsCustomManifest: false,
// only applies for single file uploads
shouldShowCustomManifestCheckbox: false,
),
);
}
Expand Down Expand Up @@ -1099,7 +1101,7 @@ class UploadCubit extends Cubit<UploadState> {
return;
}

if (_isUploadingCustomManifest) {
if (_uploadFileAsCustomManifest) {
final fileWithCustomContentType = await IOFile.fromData(
await _files.first.ioFile.readAsBytes(),
name: _files.first.ioFile.name,
Expand Down
16 changes: 9 additions & 7 deletions lib/blocs/upload/upload_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ class UploadReady extends UploadState {

final bool isArConnect;
final bool showReviewButtonText;
final bool isCustomManifest;
final bool isUploadingCustomManifest;
final bool shouldShowCustomManifestCheckbox;
final bool uploadFileAsCustomManifest;

UploadReady({
required this.paymentInfo,
required this.uploadIsPublic,
Expand All @@ -137,8 +138,8 @@ class UploadReady extends UploadState {
required this.arnsRecords,
required this.showReviewButtonText,
required this.selectedManifestSelections,
required this.isCustomManifest,
required this.isUploadingCustomManifest,
required this.shouldShowCustomManifestCheckbox,
required this.uploadFileAsCustomManifest,
});

// copyWith
Expand Down Expand Up @@ -189,9 +190,10 @@ class UploadReady extends UploadState {
showReviewButtonText: showReviewButtonText ?? this.showReviewButtonText,
selectedManifestSelections:
selectedManifestSelections ?? this.selectedManifestSelections,
isCustomManifest: isCustomManifest ?? this.isCustomManifest,
isUploadingCustomManifest:
isUploadingCustomManifest ?? this.isUploadingCustomManifest,
shouldShowCustomManifestCheckbox:
isCustomManifest ?? this.shouldShowCustomManifestCheckbox,
uploadFileAsCustomManifest:
isUploadingCustomManifest ?? this.uploadFileAsCustomManifest,
);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/components/upload_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1975,11 +1975,11 @@ class _UploadReadyWidget extends StatelessWidget {
),
),
],
if (state.isCustomManifest) ...[
if (state.shouldShowCustomManifestCheckbox) ...[
const SizedBox(height: 8),
ArDriveCheckBox(
title: 'Convert this file to an Arweave manifest.',
checked: state.isUploadingCustomManifest,
checked: state.uploadFileAsCustomManifest,
useNewIcons: true,
titleStyle: typography.paragraphNormal(
fontWeight: ArFontWeight.semiBold,
Expand Down
32 changes: 25 additions & 7 deletions lib/utils/is_custom_manifest.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
import 'dart:convert';

import 'package:ardrive/utils/logger.dart';
import 'package:ardrive_io/ardrive_io.dart';

/// Checks if a file is an Arweave manifest file by examining its content type and contents.
///
/// Returns true if the file has JSON content type and contains the string "arweave/paths",
/// which indicates it follows the Arweave path manifest specification.
Future<bool> isCustomManifest(IOFile file) async {
if (file.contentType == 'application/json') {
/// Read the first 100 bytes of the file
final first100Bytes = file.openReadStream(0, 100);
try {
if (file.contentType == 'application/json') {
final fileLength = await file.length;

await for (var bytes in first100Bytes) {
// verify if file contains "arweave/paths"f
if (utf8.decode(bytes).contains('arweave/paths')) {
int bytesToRead = 100;

if (fileLength < bytesToRead) {
bytesToRead = fileLength;
}

/// Read the first 100 bytes of the file
final first100Bytes = file.openReadStream(0, bytesToRead);

String content = '';

await for (var bytes in first100Bytes) {
content += utf8.decode(bytes);
}

/// verify if file contains "arweave/paths"
if (content.contains('arweave/paths')) {
return true;
}
}
return false;
} catch (e) {
logger.e('Error checking if file is a custom manifest', e);
return false;
}
return false;
}
11 changes: 6 additions & 5 deletions test/utils/is_custom_manifest_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void main() {
when(() => mockFile.contentType).thenReturn('application/json');
when(() => mockFile.openReadStream(any(), any()))
.thenAnswer((_) => Stream.value(Uint8List.fromList(bytes)));

when(() => mockFile.length).thenReturn(bytes.length);
// Act
final result = await isCustomManifest(mockFile);

Expand All @@ -45,19 +45,20 @@ void main() {
when(() => mockFile.contentType).thenReturn('application/json');
when(() => mockFile.openReadStream(any(), any()))
.thenAnswer((_) => Stream.value(Uint8List.fromList(bytes)));
when(() => mockFile.length).thenReturn(bytes.length);

// Act
final result = await isCustomManifest(mockFile);

// Assert
expect(result, false);
verify(() => mockFile.openReadStream(0, 100)).called(1);
verify(() => mockFile.openReadStream(0, 33)).called(1);
});

test('returns false when file is not JSON', () async {
// Arrange
when(() => mockFile.contentType).thenReturn('text/plain');

when(() => mockFile.length).thenReturn(0);
// Act
final result = await isCustomManifest(mockFile);

Expand All @@ -71,13 +72,13 @@ void main() {
when(() => mockFile.contentType).thenReturn('application/json');
when(() => mockFile.openReadStream(any(), any()))
.thenAnswer((_) => Stream.value(Uint8List(0)));

when(() => mockFile.length).thenReturn(0);
// Act
final result = await isCustomManifest(mockFile);

// Assert
expect(result, false);
verify(() => mockFile.openReadStream(0, 100)).called(1);
verify(() => mockFile.openReadStream(0, 0)).called(1);
});
});
}

0 comments on commit 09e816d

Please sign in to comment.