Skip to content

Commit

Permalink
Merge pull request #494 from ardriveapp/dev
Browse files Browse the repository at this point in the history
Release v1.16.0
  • Loading branch information
thiagocarvalhodev authored May 12, 2022
2 parents 79bf783 + 7fd464f commit 7732099
Show file tree
Hide file tree
Showing 13 changed files with 476 additions and 100 deletions.
2 changes: 1 addition & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:name="${applicationName}"
android:label="drive"
android:icon="@mipmap/ic_launcher">
<activity
Expand Down
189 changes: 189 additions & 0 deletions lib/blocs/fs_entry_preview/fs_entry_preview_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import 'dart:async';

import 'package:ardrive/blocs/drive_detail/selected_item.dart';
import 'package:ardrive/blocs/profile/profile_cubit.dart';
import 'package:ardrive/entities/entities.dart';
import 'package:ardrive/models/models.dart';
import 'package:ardrive/services/services.dart';
import 'package:ardrive/utils/constants.dart';
import 'package:bloc/bloc.dart';
import 'package:cryptography/cryptography.dart';
import 'package:equatable/equatable.dart';
import 'package:http/http.dart' as http;
import 'package:mime/mime.dart';
import 'package:moor/moor.dart';

part 'fs_entry_preview_state.dart';

class FsEntryPreviewCubit extends Cubit<FsEntryPreviewState> {
final String driveId;
final SelectedItem? maybeSelectedItem;

final DriveDao _driveDao;
final AppConfig _config;
final ArweaveService _arweave;
final ProfileCubit _profileCubit;

StreamSubscription? _entrySubscription;

final previewMaxFileSize = 1024 * 1024 * 100;
final allowedPreviewContentTypes = [];

FsEntryPreviewCubit({
required this.driveId,
this.maybeSelectedItem,
required DriveDao driveDao,
required AppConfig config,
required ArweaveService arweave,
required ProfileCubit profileCubit,
}) : _driveDao = driveDao,
_config = config,
_arweave = arweave,
_profileCubit = profileCubit,
super(FsEntryPreviewInitial()) {
preview();
}

Future<void> preview() async {
final selectedItem = maybeSelectedItem;
if (selectedItem != null) {
if (selectedItem.runtimeType == SelectedFile) {
_entrySubscription = _driveDao
.fileById(driveId: driveId, fileId: selectedItem.id)
.watchSingle()
.listen((file) {
if (file.size <= previewMaxFileSize) {
final contentType =
file.dataContentType ?? lookupMimeType(file.name);
final fileExtension = contentType?.split('/').last;
final previewType = contentType?.split('/').first;
final previewUrl =
'${_config.defaultArweaveGatewayUrl}/${file.dataTxId}';
if (!_supportedExtension(previewType, fileExtension)) {
emit(FsEntryPreviewUnavailable());
return;
}

switch (previewType) {
case 'image':
emitImagePreview(file, previewUrl);
break;

/// Enable more previews in the future after dealing
/// with state and widget disposal
// case 'audio':
// emit(FsEntryPreviewAudio(previewUrl: previewUrl));
// break;
// case 'video':
// emit(FsEntryPreviewVideo(previewUrl: previewUrl));
// break;
// case 'text':
// emit(FsEntryPreviewText(previewUrl: previewUrl));
// break;

default:
emit(FsEntryPreviewUnavailable());
}
}
});
}
} else {
emit(FsEntryPreviewUnavailable());
}
}

Future<void> emitImagePreview(FileEntry file, String dataUrl) async {
try {
emit(FsEntryPreviewLoading());

final dataTx = await _arweave.getTransactionDetails(file.dataTxId);
if (dataTx == null) {
emit(FsEntryPreviewFailure());
return;
}

late Uint8List dataBytes;
final cachedBytes = await _driveDao.getPreviewDataFromMemory(dataTx.id);
if (cachedBytes == null) {
final dataRes = await http.get(Uri.parse(dataUrl));
dataBytes = dataRes.bodyBytes;
await _driveDao.putPreviewDataInMemory(
dataTxId: dataTx.id,
bytes: dataBytes,
);
} else {
dataBytes = cachedBytes;
}

final drive = await _driveDao.driveById(driveId: driveId).getSingle();
switch (drive.privacy) {
case DrivePrivacy.public:
emit(
FsEntryPreviewImage(imageBytes: dataBytes, previewUrl: dataUrl),
);
break;
case DrivePrivacy.private:
final profile = _profileCubit.state;
SecretKey? driveKey;

if (profile is ProfileLoggedIn) {
driveKey = await _driveDao.getDriveKey(
drive.id,
profile.cipherKey,
);
} else {
driveKey = await _driveDao.getDriveKeyFromMemory(driveId);
}

if (driveKey == null) {
throw StateError('Drive Key not found');
}

final fileKey = await _driveDao.getFileKey(file.id, driveKey);
final decodedBytes = await decryptTransactionData(
dataTx,
dataBytes,
fileKey,
);
emit(
FsEntryPreviewImage(imageBytes: decodedBytes, previewUrl: dataUrl),
);
break;

default:
emit(FsEntryPreviewFailure());
}
} catch (err) {
addError(err);
}
}

@override
void onError(Object error, StackTrace stackTrace) {
emit(FsEntryPreviewFailure());
super.onError(error, stackTrace);

print('Failed to load entity activity: $error $stackTrace');
}

bool _supportedExtension(String? previewType, String? fileExtension) {
if (previewType == null || fileExtension == null) {
return false;
}

switch (previewType) {
case 'image':
return supportedImageTypesInFilePreview
.any((element) => element.contains(fileExtension));
default:
return false;
}
}

@override
Future<void> close() {
_entrySubscription?.cancel();
return super.close();
}
}
66 changes: 66 additions & 0 deletions lib/blocs/fs_entry_preview/fs_entry_preview_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
part of 'fs_entry_preview_cubit.dart';

abstract class FsEntryPreviewState extends Equatable {
const FsEntryPreviewState();

@override
List<Object> get props => [];
}

class FsEntryPreviewUnavailable extends FsEntryPreviewState {}

class FsEntryPreviewInitial extends FsEntryPreviewState {}

class FsEntryPreviewSuccess extends FsEntryPreviewState {
final String previewUrl;

FsEntryPreviewSuccess({required this.previewUrl});

@override
List<Object> get props => [previewUrl];
}

class FsEntryPreviewLoading extends FsEntryPreviewSuccess {
FsEntryPreviewLoading() : super(previewUrl: '');
}

class FsEntryPreviewImage extends FsEntryPreviewSuccess {
final Uint8List imageBytes;

FsEntryPreviewImage({
required this.imageBytes,
required String previewUrl,
}) : super(previewUrl: previewUrl);

@override
List<Object> get props => [imageBytes, previewUrl];
}

class FsEntryPreviewAudio extends FsEntryPreviewSuccess {
FsEntryPreviewAudio({
required String previewUrl,
}) : super(previewUrl: previewUrl);

@override
List<Object> get props => [previewUrl];
}

class FsEntryPreviewVideo extends FsEntryPreviewSuccess {
FsEntryPreviewVideo({
required String previewUrl,
}) : super(previewUrl: previewUrl);

@override
List<Object> get props => [previewUrl];
}

class FsEntryPreviewText extends FsEntryPreviewSuccess {
FsEntryPreviewText({
required String previewUrl,
}) : super(previewUrl: previewUrl);

@override
List<Object> get props => [previewUrl];
}

class FsEntryPreviewFailure extends FsEntryPreviewState {}
34 changes: 17 additions & 17 deletions lib/components/app_drawer/app_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ class AppDrawer extends StatelessWidget {
context: context,
isEnabled: state.hasWritePermissions && hasMinBalance,
itemTitle: appLocalizationsOf(context).newFolder,
message: hasMinBalance
? null
: appLocalizationsOf(context).insufficientFundsForCreateAFolder,
message: state.hasWritePermissions && !hasMinBalance
? appLocalizationsOf(context).insufficientFundsForCreateAFolder
: null,
value: (context) => promptToCreateFolder(
context,
driveId: state.currentDrive.id,
Expand All @@ -289,9 +289,9 @@ class AppDrawer extends StatelessWidget {
return _buildMenuItemTile(
context: context,
isEnabled: state.hasWritePermissions && hasMinBalance,
message: hasMinBalance
? null
: appLocalizationsOf(context).insufficientFundsForUploadFiles,
message: state.hasWritePermissions && !hasMinBalance
? appLocalizationsOf(context).insufficientFundsForUploadFiles
: null,
itemTitle: appLocalizationsOf(context).uploadFiles,
value: (context) => promptToUpload(
context,
Expand All @@ -308,9 +308,9 @@ class AppDrawer extends StatelessWidget {
context: context,
isEnabled: state.hasWritePermissions && hasMinBalance,
itemTitle: appLocalizationsOf(context).uploadFolder,
message: hasMinBalance
? null
: appLocalizationsOf(context).insufficientFundsForUploadFolders,
message: state.hasWritePermissions && !hasMinBalance
? appLocalizationsOf(context).insufficientFundsForUploadFolders
: null,
value: (context) => promptToUpload(
context,
driveId: state.currentDrive.id,
Expand All @@ -329,8 +329,8 @@ class AppDrawer extends StatelessWidget {
);
}

PopupMenuEntry<Function> _buildCreateDrive(BuildContext context,
DrivesLoadSuccess drivesState, bool hasMinBalance) {
PopupMenuEntry<Function> _buildCreateDrive(
BuildContext context, DrivesLoadSuccess drivesState, bool hasMinBalance) {
return _buildMenuItemTile(
context: context,
isEnabled: drivesState.canCreateNewDrive && hasMinBalance,
Expand Down Expand Up @@ -359,15 +359,15 @@ class AppDrawer extends StatelessWidget {
);
}

PopupMenuEntry<Function> _buildCreateManifestItem(BuildContext context,
DriveDetailLoadSuccess state, bool hasMinBalance) {
PopupMenuEntry<Function> _buildCreateManifestItem(
BuildContext context, DriveDetailLoadSuccess state, bool hasMinBalance) {
return _buildMenuItemTile(
context: context,
isEnabled: !state.driveIsEmpty,
isEnabled: !state.driveIsEmpty && hasMinBalance,
itemTitle: appLocalizationsOf(context).createManifest,
message: hasMinBalance
? null
: appLocalizationsOf(context).insufficientFundsForCreateAManifest,
message: !state.driveIsEmpty && !hasMinBalance
? appLocalizationsOf(context).insufficientFundsForCreateAManifest
: null,
value: (context) =>
promptToCreateManifest(context, drive: state.currentDrive),
);
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@
"@uploadDragAndDrop": {
"description": "The action of uploading a file"
},
"itemPreviewEmphasized": "PREVIEW",
"@itemPreviewEmphasized": {
"description": "Item preview"
},
"itemDetailsEmphasized": "DETAILS",
"@itemDetailsEmphasized": {
"description": "Item details such as dates, transaction IDs and entity IDs. Emphasized with upper case"
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@
"@uploadDragAndDrop": {
"description": "The action of uploading a file"
},
"itemPreviewEmphasized": "VISTA PREVIA",
"@itemPreviewEmphasized": {
"description": "Item preview"
},
"itemDetailsEmphasized": "DETALLES",
"@itemDetailsEmphasized": {
"description": "Item details such as dates, transaction IDs and entity IDs. Emphasized with upper case"
Expand Down
14 changes: 14 additions & 0 deletions lib/models/daos/drive_dao/drive_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {

late Vault<SecretKey> _driveKeyVault;

late Vault<Uint8List> _previewVault;

DriveDao(Database db) : super(db) {
// Creates a store
final store = newMemoryVaultStore();

// Creates a vault from the previously created store
_driveKeyVault = store.vault<SecretKey>(name: 'driveKeyVault');
_previewVault = store.vault<Uint8List>(name: 'previewVault');
}

Future<void> deleteSharedPrivateDrives(String? owner) async {
Expand Down Expand Up @@ -67,6 +70,17 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {
return await _driveKeyVault.put(driveID, driveKey);
}

Future<Uint8List?> getPreviewDataFromMemory(TxID dataTxId) async {
return await _previewVault.get(dataTxId);
}

Future<void> putPreviewDataInMemory({
required TxID dataTxId,
required Uint8List bytes,
}) async {
return await _previewVault.put(dataTxId, bytes);
}

/// Creates a drive with its accompanying root folder.
Future<CreateDriveResult> createDrive({
required String name,
Expand Down
Loading

0 comments on commit 7732099

Please sign in to comment.