Implemented StreamAttachmentPackage and fixed operations in widgets linked to full screen implementation, also fixed tests

This commit is contained in:
Ayush Shekhar
2022-04-13 18:02:49 +05:30
parent 65a5af7b6c
commit 180c24ae72
18 changed files with 373 additions and 253 deletions
@@ -9,14 +9,18 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
typedef AttachmentDownloader = Future<String> Function(
Attachment attachment, {
ProgressCallback? progressCallback,
DownloadedPathCallback? downloadedPathCallback,
});
/// Callback to receive the path once the attachment asset is downloaded
typedef DownloadedPathCallback = void Function(String? path);
/// Widget that shows the options in the gallery view
class AttachmentActionsModal extends StatelessWidget {
/// Returns a new [AttachmentActionsModal]
const AttachmentActionsModal({
Key? key,
required this.currentIndex,
required this.attachment,
required this.message,
this.onShowMessage,
this.imageDownloader,
@@ -28,12 +32,12 @@ class AttachmentActionsModal extends StatelessWidget {
this.customActions = const [],
}) : super(key: key);
/// The attachment object for which the actions are to be performed
final Attachment attachment;
/// The message containing the attachments
final Message message;
/// Current page index
final int currentIndex;
/// Callback to show the message
final VoidCallback? onShowMessage;
@@ -62,7 +66,7 @@ class AttachmentActionsModal extends StatelessWidget {
/// specified attributes overridden.
AttachmentActionsModal copyWith({
Key? key,
int? currentIndex,
Attachment? attachment,
Message? message,
VoidCallback? onShowMessage,
AttachmentDownloader? imageDownloader,
@@ -75,7 +79,7 @@ class AttachmentActionsModal extends StatelessWidget {
}) =>
AttachmentActionsModal(
key: key ?? this.key,
currentIndex: currentIndex ?? this.currentIndex,
attachment: attachment ?? this.attachment,
message: message ?? this.message,
onShowMessage: onShowMessage ?? this.onShowMessage,
imageDownloader: imageDownloader ?? this.imageDownloader,
@@ -138,7 +142,7 @@ class AttachmentActionsModal extends StatelessWidget {
if (showSave)
_buildButton(
context,
message.attachments[currentIndex].type == 'video'
attachment.type == 'video'
? context.translations.saveVideoLabel
: context.translations.saveImageLabel,
StreamSvgIcon.iconSave(
@@ -146,15 +150,16 @@ class AttachmentActionsModal extends StatelessWidget {
color: theme.colorTheme.textLowEmphasis,
),
() {
final attachment = message.attachments[currentIndex];
final isImage = attachment.type == 'image';
final Future<String?> Function(
Attachment, {
void Function(int, int) progressCallback,
DownloadedPathCallback downloadedPathCallback,
}) saveFile = fileDownloader ?? _downloadAttachment;
final Future<String?> Function(
Attachment, {
void Function(int, int) progressCallback,
DownloadedPathCallback downloadedPathCallback,
}) saveImage = imageDownloader ?? _downloadAttachment;
final downloader = isImage ? saveImage : saveFile;
@@ -162,15 +167,25 @@ class AttachmentActionsModal extends StatelessWidget {
ValueNotifier<_DownloadProgress?>(
_DownloadProgress.initial(),
);
final downloadedPathNotifier = ValueNotifier<String?>(
null,
);
downloader(
attachment,
progressCallback: (received, total) {
print('>>>>>>>>>>>>>>>');
print('>>r : $received');
print('>>t : $total');
print('>>>>>>>>>>>>>>>');
progressNotifier.value = _DownloadProgress(
total,
received,
);
},
downloadedPathCallback: (String? path) {
downloadedPathNotifier.value = path;
},
).catchError((e, stk) {
progressNotifier.value = null;
});
@@ -186,6 +201,7 @@ class AttachmentActionsModal extends StatelessWidget {
builder: (context) => _buildDownloadProgressDialog(
context,
progressNotifier,
downloadedPathNotifier,
),
);
},
@@ -204,8 +220,12 @@ class AttachmentActionsModal extends StatelessWidget {
final channel = StreamChannel.of(context).channel;
if (message.attachments.length > 1 ||
message.text?.isNotEmpty == true) {
final currentAttachmentIndex =
message.attachments.indexWhere(
(element) => element.id == attachment.id,
);
final remainingAttachments = [...message.attachments]
..removeAt(currentIndex);
..removeAt(currentAttachmentIndex);
channel.updateMessage(message.copyWith(
attachments: remainingAttachments,
));
@@ -285,73 +305,86 @@ class AttachmentActionsModal extends StatelessWidget {
Widget _buildDownloadProgressDialog(
BuildContext context,
ValueNotifier<_DownloadProgress?> progressNotifier,
ValueNotifier<String?> downloadedFilePathNotifier,
) {
final theme = StreamChatTheme.of(context);
return ValueListenableBuilder(
valueListenable: progressNotifier,
builder: (_, _DownloadProgress? progress, __) {
// Pop the dialog in case the progress is null or it's completed.
if (progress == null || progress.toProgressIndicatorValue == 1.0) {
valueListenable: downloadedFilePathNotifier,
builder: (_, String? path, __) {
final _downloadComplete = path != null && path.isNotEmpty;
// Pop the dialog in case the download has completed
if (_downloadComplete) {
Future.delayed(
const Duration(milliseconds: 500),
() => Navigator.of(context).maybePop(),
);
}
return Material(
type: MaterialType.transparency,
child: Center(
child: Container(
height: 182,
width: 182,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: theme.colorTheme.barsBg,
),
return ValueListenableBuilder(
valueListenable: progressNotifier,
builder: (_, _DownloadProgress? progress, __) {
// Pop the dialog in case the progress is null.
if (progress == null) {
Future.delayed(
const Duration(milliseconds: 500),
() => Navigator.of(context).maybePop(),
);
}
return Material(
type: MaterialType.transparency,
child: Center(
child: progress == null
? SizedBox(
height: 100,
width: 100,
child: StreamSvgIcon.error(
color: theme.colorTheme.disabled,
),
)
: progress.toProgressIndicatorValue == 1.0
child: Container(
height: 182,
width: 182,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: theme.colorTheme.barsBg,
),
child: Center(
child: progress == null
? SizedBox(
key: const Key('completedIcon'),
height: 160,
width: 160,
child: StreamSvgIcon.check(
height: 100,
width: 100,
child: StreamSvgIcon.error(
color: theme.colorTheme.disabled,
),
)
: SizedBox(
height: 100,
width: 100,
child: Stack(
fit: StackFit.expand,
children: [
CircularProgressIndicator(
value: progress.toProgressIndicatorValue,
strokeWidth: 8,
valueColor: AlwaysStoppedAnimation(
theme.colorTheme.accentPrimary,
),
: _downloadComplete
? SizedBox(
key: const Key('completedIcon'),
height: 160,
width: 160,
child: StreamSvgIcon.check(
color: theme.colorTheme.disabled,
),
Center(
child: Text(
'${progress.toPercentage}%',
style: theme.textTheme.headline.copyWith(
color: theme.colorTheme.textLowEmphasis,
)
: SizedBox(
height: 100,
width: 100,
child: Stack(
fit: StackFit.expand,
children: [
CircularProgressIndicator(
strokeWidth: 8,
color: theme.colorTheme.accentPrimary,
),
),
Center(
child: Text(
'${progress.receivedValueInMB} MB',
style:
theme.textTheme.headline.copyWith(
color:
theme.colorTheme.textLowEmphasis,
),
),
),
],
),
],
),
),
),
),
),
),
),
),
);
},
);
},
);
@@ -360,6 +393,7 @@ class AttachmentActionsModal extends StatelessWidget {
Future<String?> _downloadAttachment(
Attachment attachment, {
ProgressCallback? progressCallback,
DownloadedPathCallback? downloadedPathCallback,
}) async {
String? filePath;
final appDocDir = await getTemporaryDirectory();
@@ -375,6 +409,7 @@ class AttachmentActionsModal extends StatelessWidget {
onReceiveProgress: progressCallback,
);
final result = await ImageGallerySaver.saveFile(filePath!);
downloadedPathCallback?.call((result as Map)['filePath']);
return (result as Map)['filePath'];
}
}
@@ -388,6 +423,8 @@ class _DownloadProgress {
final int total;
final int received;
String get receivedValueInMB => ((received / 1024) / 1024).toStringAsFixed(2);
double get toProgressIndicatorValue => received / total;
int get toPercentage => (received * 100) ~/ total;