-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #494 from ardriveapp/dev
Release v1.16.0
- Loading branch information
Showing
13 changed files
with
476 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.