Skip to content

Commit

Permalink
Merge branch 'dev' into PE-7123
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagocarvalhodev committed Nov 22, 2024
2 parents 524c883 + 0c1d671 commit 44c0572
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 55 deletions.
1 change: 1 addition & 0 deletions android/fastlane/metadata/android/en-US/changelogs/164.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added support for uploading custom Arweave manifest files
13 changes: 11 additions & 2 deletions lib/blocs/drive_detail/drive_detail_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,21 @@ class DriveDetailCubit extends Cubit<DriveDetailState> {
_folderSubscription =
Rx.combineLatest3<Drive?, FolderWithContents, ProfileState, void>(
_driveRepository.watchDrive(driveId: driveId),
_driveDao.watchFolderContents(
_driveDao
.watchFolderContents(
driveId,
orderBy: contentOrderBy,
orderingMode: contentOrderingMode,
folderId: folderId,
),
)
.handleError((error, stack) {
logger.e('Error watching folder contents', error, stack);
if (error is DriveNotFoundException) {
emit(DriveDetailLoadNotFound());
}

return null;
}),
_profileCubit.stream.startWith(ProfileCheckingAvailability()),
(drive, folderContents, _) async {
if (isClosed) {
Expand Down
47 changes: 30 additions & 17 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,8 +132,8 @@ class UploadCubit extends Cubit<UploadState> {
}

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

Future<void> prepareManifestUpload() async {
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 All @@ -495,9 +496,11 @@ class UploadCubit extends Cubit<UploadState> {
emit(readyState.copyWith(
loadingArNSNames: false, showArnsCheckbox: showArnsCheckbox));
} catch (e) {
final readyState = state as UploadReady;
emit(readyState.copyWith(
loadingArNSNamesError: true, loadingArNSNames: false));
if (state is UploadReady) {
final readyState = state as UploadReady;
emit(readyState.copyWith(
loadingArNSNamesError: true, loadingArNSNames: false));
}
}
} else {
emit(
Expand All @@ -519,8 +522,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 @@ -902,6 +906,10 @@ class UploadCubit extends Cubit<UploadState> {
if (state is UploadReady) {
emit((state as UploadReady).copyWith(arnsRecords: value));
}
}).catchError((e) {
logger.e(
'Error getting ant records for wallet. Proceeding with the upload...',
e);
});

_files
Expand Down Expand Up @@ -1048,9 +1056,14 @@ class UploadCubit extends Cubit<UploadState> {
}

if (manifestFileEntries.isNotEmpty) {
// load arns names
await _arnsRepository
.getAntRecordsForWallet(_auth.currentUser.walletAddress);
try {
await _arnsRepository
.getAntRecordsForWallet(_auth.currentUser.walletAddress);
} catch (e) {
logger.e(
'Error getting ant records for wallet. Proceeding with the upload...',
e);
}
}

emit(
Expand Down Expand Up @@ -1099,7 +1112,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
20 changes: 11 additions & 9 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 All @@ -163,8 +164,8 @@ class UploadReady extends UploadState {
List<ANTRecord>? arnsRecords,
bool? showReviewButtonText,
List<ManifestSelection>? selectedManifestSelections,
bool? isCustomManifest,
bool? isUploadingCustomManifest,
bool? shouldShowCustomManifestCheckbox,
bool? uploadFileAsCustomManifest,
}) {
return UploadReady(
loadingArNSNames: loadingArNSNames ?? this.loadingArNSNames,
Expand All @@ -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: shouldShowCustomManifestCheckbox ??
this.shouldShowCustomManifestCheckbox,
uploadFileAsCustomManifest:
uploadFileAsCustomManifest ?? 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
6 changes: 5 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ late ConfigService configService;
late ArweaveService arweave;
late TurboUploadService _turboUpload;
late PaymentService _turboPayment;
late Database db;

void main() async {
await runZonedGuarded(() async {
Expand Down Expand Up @@ -129,11 +130,14 @@ Future<void> _initializeServices() async {

final config = configService.config;

db = Database();

arweave = ArweaveService(
Arweave(
gatewayUrl: Uri.parse(config.defaultArweaveGatewayForDataRequest.url),
),
ArDriveCrypto(),
db.driveDao,
configService,
);
_turboUpload = config.useTurboUpload
Expand Down Expand Up @@ -395,7 +399,7 @@ class AppState extends State<App> {
),
),
),
RepositoryProvider<Database>(create: (_) => Database()),
RepositoryProvider<Database>(create: (_) => db),
RepositoryProvider<ProfileDao>(
create: (context) => context.read<Database>().profileDao),
RepositoryProvider<DriveDao>(
Expand Down
3 changes: 2 additions & 1 deletion lib/pages/drive_detail/drive_detail_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ class _DriveDetailPageState extends State<DriveDetailPage> {
child: BlocListener<DrivesCubit, DrivesState>(
listener: (context, state) {
if (state is DrivesLoadSuccess) {
if (state.userDrives.isNotEmpty) {
if (state.userDrives.isNotEmpty ||
state.sharedDrives.isNotEmpty) {
final driveDetailState = context.read<DriveDetailCubit>().state;

if (driveDetailState is DriveDetailLoadSuccess &&
Expand Down
32 changes: 23 additions & 9 deletions lib/services/arweave/arweave_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:convert';

import 'package:ardrive/core/crypto/crypto.dart';
import 'package:ardrive/entities/entities.dart';
import 'package:ardrive/models/daos/drive_dao/drive_dao.dart';
import 'package:ardrive/services/arweave/arweave_service_exception.dart';
import 'package:ardrive/services/arweave/error/gateway_error.dart';
import 'package:ardrive/services/arweave/get_segmented_transaction_from_drive_strategy.dart';
Expand Down Expand Up @@ -42,12 +43,13 @@ const kMaxNumberOfTransactionsPerPage = 100;
class ArweaveService {
Arweave client;
final ArDriveCrypto _crypto;

final DriveDao _driveDao;
final ArtemisClient _gql;

ArweaveService(
this.client,
this._crypto,
this._driveDao,
ConfigService configService, {
ArtemisClient? artemisClient,
}) : _gql = artemisClient ??
Expand Down Expand Up @@ -262,6 +264,7 @@ class ArweaveService {

yield licenseComposedQuery.data!.transactions.edges
.map((e) => e.node)
.where((e) => e.tags.any((t) => t.name == 'License'))
.toList();
}
}
Expand Down Expand Up @@ -588,15 +591,26 @@ class ArweaveService {
continue;
}

final driveKey =
driveTx.getTag(EntityTag.drivePrivacy) == DrivePrivacyTag.private
? await _crypto.deriveDriveKey(
wallet,
driveTx.getTag(EntityTag.driveId)!,
password,
)
: null;
SecretKey? driveKey;

if (driveTx.getTag(EntityTag.drivePrivacy) == DrivePrivacyTag.private) {
driveKey = await _driveDao.getDriveKeyFromMemory(
driveTx.getTag(EntityTag.driveId)!,
);

if (driveKey == null) {
driveKey = await _crypto.deriveDriveKey(
wallet,
driveTx.getTag(EntityTag.driveId)!,
password,
);

_driveDao.putDriveKeyInMemory(
driveID: driveTx.getTag(EntityTag.driveId)!,
driveKey: driveKey,
);
}
}
try {
final drive = await DriveEntity.fromTransaction(
driveTx,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
query LicenseComposed($transactionIds: [ID!]) {
transactions(
ids: $transactionIds
tags: [{ name: "License", values: [""], op: NEQ }]
) {
edges {
node {
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;
}
8 changes: 8 additions & 0 deletions packages/ardrive_uploader/lib/src/upload_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ class UploadDispatcher {
}) async {
try {
if (task is FileUploadTask) {
logger.d('Preparing data items for file ${task.file.name}...');

controller.updateProgress(
task: task.copyWith(
status: UploadStatus.creatingMetadata,
),
);

final uploadPreparation = await prepareDataItems(
file: task.file,
metadata: task.metadata,
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Secure, permanent storage

publish_to: 'none'

version: 2.57.1
version: 2.58.0

environment:
sdk: '>=3.2.0 <4.0.0'
Expand Down
Loading

0 comments on commit 44c0572

Please sign in to comment.