diff --git a/lib/blip_ds.dart b/lib/blip_ds.dart index 7f2b52e5..759dc59e 100644 --- a/lib/blip_ds.dart +++ b/lib/blip_ds.dart @@ -24,6 +24,7 @@ export 'src/services/ds_auth.service.dart' show DSAuthService; export 'src/services/ds_bottom_sheet.service.dart' show DSBottomSheetService; export 'src/services/ds_dialog.service.dart' show DSDialogService; export 'src/services/ds_file.service.dart' show DSFileService; +export 'src/services/ds_media_format.service.dart' show DSMediaFormatService; export 'src/services/ds_toast.service.dart' show DSToastService; export 'src/themes/colors/ds_colors.theme.dart' show DSColors; export 'src/themes/colors/ds_linear_gradient.theme.dart' show DSLinearGradient; diff --git a/lib/src/controllers/chat/ds_file_message_bubble.controller.dart b/lib/src/controllers/chat/ds_file_message_bubble.controller.dart index 188ce30e..a4cb43ef 100644 --- a/lib/src/controllers/chat/ds_file_message_bubble.controller.dart +++ b/lib/src/controllers/chat/ds_file_message_bubble.controller.dart @@ -1,4 +1,4 @@ -import 'package:filesize/filesize.dart'; +import 'package:file_sizes/file_sizes.dart'; import 'package:get/get.dart'; import '../../services/ds_file.service.dart'; @@ -7,17 +7,20 @@ class DSFileMessageBubbleController extends GetxController { final isDownloading = RxBool(false); String getFileSize(final int size) { - return size > 0 ? filesize(size, 1) : ''; + return size > 0 + ? FileSize.getSize( + size, + precision: PrecisionValue.One, + ) + : ''; } Future openFile({ - required final String filename, required final String url, final Map? httpHeaders, }) => DSFileService.open( - filename, - url, + url: url, onDownloadStateChange: (loading) => isDownloading.value = loading, httpHeaders: httpHeaders, ); diff --git a/lib/src/controllers/chat/ds_image_message_bubble.controller.dart b/lib/src/controllers/chat/ds_image_message_bubble.controller.dart index 7cef0da9..5a0fb230 100644 --- a/lib/src/controllers/chat/ds_image_message_bubble.controller.dart +++ b/lib/src/controllers/chat/ds_image_message_bubble.controller.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:get/get.dart'; -import 'package:path/path.dart' as path_utils; import '../../services/ds_auth.service.dart'; import '../../services/ds_file.service.dart'; @@ -40,21 +39,20 @@ class DSImageMessageBubbleController extends GetxController { final uri = Uri.parse(url); - final fullPath = await DSDirectoryFormatter.getPath( + final cachePath = await DSDirectoryFormatter.getCachePath( type: mediaType!, - fileName: md5.convert(utf8.encode(uri.path)).toString(), + filename: md5.convert(utf8.encode(uri.path)).toString(), ); - if (await File(fullPath).exists()) { - localPath.value = fullPath; + if (File(cachePath).existsSync()) { + localPath.value = cachePath; return; } try { final savedFilePath = await DSFileService.download( - url, - path_utils.basename(fullPath), - path: path_utils.dirname(fullPath), + url: url, + path: cachePath, onReceiveProgress: _onReceiveProgress, httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null, ); diff --git a/lib/src/controllers/chat/ds_video_message_bubble.controller.dart b/lib/src/controllers/chat/ds_video_message_bubble.controller.dart index a8660f57..bade8679 100644 --- a/lib/src/controllers/chat/ds_video_message_bubble.controller.dart +++ b/lib/src/controllers/chat/ds_video_message_bubble.controller.dart @@ -2,13 +2,12 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; -import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart'; import 'package:file_sizes/file_sizes.dart'; import 'package:get/get.dart'; -import 'package:path/path.dart' as path_utils; import '../../models/ds_toast_props.model.dart'; import '../../services/ds_file.service.dart'; +import '../../services/ds_media_format.service.dart'; import '../../services/ds_toast.service.dart'; import '../../utils/ds_directory_formatter.util.dart'; @@ -48,19 +47,23 @@ class DSVideoMessageBubbleController { try { isLoadingThumbnail.value = true; final fileName = md5.convert(utf8.encode(Uri.parse(url).path)).toString(); - final fullPath = await DSDirectoryFormatter.getPath( + + final fullPath = await DSDirectoryFormatter.getCachePath( type: type, - fileName: fileName, + filename: fileName, ); - final fullThumbnailPath = await DSDirectoryFormatter.getPath( + + final fullThumbnailPath = await DSDirectoryFormatter.getCachePath( type: 'image/png', - fileName: '$fileName-thumbnail', + filename: '$fileName-thumbnail', ); + final file = File(fullPath); final thumbnailfile = File(fullThumbnailPath); - if (await thumbnailfile.exists()) { + + if (thumbnailfile.existsSync()) { thumbnail.value = thumbnailfile.path; - } else if (await file.exists() && thumbnail.value.isEmpty) { + } else if (file.existsSync() && thumbnail.value.isEmpty) { await _generateThumbnail(file.path); } } finally { @@ -70,29 +73,27 @@ class DSVideoMessageBubbleController { Future getFullThumbnailPath() async { final fileName = md5.convert(utf8.encode(Uri.parse(url).path)).toString(); - final mediaPath = await DSDirectoryFormatter.getPath( + + return DSDirectoryFormatter.getCachePath( type: 'image/png', - fileName: '$fileName-thumbnail', + filename: '$fileName-thumbnail', ); - return mediaPath; } Future downloadVideo() async { - final fileName = md5.convert(utf8.encode(Uri.parse(url).path)).toString(); isDownloading.value = true; try { - final fullPath = await DSDirectoryFormatter.getPath( + final cachePath = await DSDirectoryFormatter.getCachePath( type: 'video/mp4', - fileName: fileName, + filename: md5.convert(utf8.encode(Uri.parse(url).path)).toString(), ); - final outputFile = File(fullPath); - if (!await outputFile.exists()) { + final outputFile = File(cachePath); + + if (!outputFile.existsSync()) { final inputFilePath = await DSFileService.download( - url, - path_utils.basename(fullPath), - path: path_utils.dirname(fullPath), + url: url, onReceiveProgress: (current, max) { downloadProgress.value = current; maximumProgress.value = max; @@ -100,8 +101,15 @@ class DSVideoMessageBubbleController { httpHeaders: httpHeaders, ); - _generateThumbnail(inputFilePath!); + final isSuccess = await DSMediaFormatService.formatVideo( + input: inputFilePath!, + output: cachePath, + ); + + hasError.value = !isSuccess; } + + _generateThumbnail(outputFile.path); } catch (_) { hasError.value = true; @@ -120,8 +128,9 @@ class DSVideoMessageBubbleController { Future _generateThumbnail(String path) async { final thumbnailPath = await getFullThumbnailPath(); - await FFmpegKit.execute( - '-hide_banner -y -i "$path" -vframes 1 "$thumbnailPath"', + await DSMediaFormatService.getVideoThumbnail( + input: path, + output: thumbnailPath, ); thumbnail.value = thumbnailPath; diff --git a/lib/src/controllers/ds_video_player.controller.dart b/lib/src/controllers/ds_video_player.controller.dart index 00f50711..578ddea5 100644 --- a/lib/src/controllers/ds_video_player.controller.dart +++ b/lib/src/controllers/ds_video_player.controller.dart @@ -69,10 +69,7 @@ class DSVideoPlayerController extends GetxController { Get.back(); Get.delete(); - final filename = url.substring(url.lastIndexOf('/')).substring(1); - await DSVideoErrorDialog.show( - filename: filename, url: url, httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null, ); diff --git a/lib/src/extensions/ds_border_radius.extension.dart b/lib/src/extensions/ds_border_radius.extension.dart index d510566e..4d95f5bd 100644 --- a/lib/src/extensions/ds_border_radius.extension.dart +++ b/lib/src/extensions/ds_border_radius.extension.dart @@ -1,6 +1,7 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../enums/ds_border_radius.enum.dart'; + /// A Design System's extension that adds functionalities to [DSBorderRadius] enum. extension DSBorderRadiusExtension on DSBorderRadius { BorderRadius getCircularBorderRadius({ diff --git a/lib/src/extensions/ds_delivery_report_status.extension.dart b/lib/src/extensions/ds_delivery_report_status.extension.dart index fc022d00..67b6aefa 100644 --- a/lib/src/extensions/ds_delivery_report_status.extension.dart +++ b/lib/src/extensions/ds_delivery_report_status.extension.dart @@ -1,4 +1,4 @@ -import 'package:blip_ds/blip_ds.dart'; +import '../enums/ds_delivery_report_status.enum.dart'; extension DSDeliveryReportStatusExtension on DSDeliveryReportStatus { DSDeliveryReportStatus getValue(String value) => diff --git a/lib/src/models/ds_message_item.model.dart b/lib/src/models/ds_message_item.model.dart index 856119b8..e4d01fcf 100644 --- a/lib/src/models/ds_message_item.model.dart +++ b/lib/src/models/ds_message_item.model.dart @@ -1,4 +1,5 @@ -import 'package:blip_ds/blip_ds.dart'; +import '../enums/ds_align.enum.dart'; +import '../enums/ds_delivery_report_status.enum.dart'; /// A Design System message model used with [DSGroupCard] to display grouped bubble class DSMessageItemModel { diff --git a/lib/src/models/ds_select_option.model.dart b/lib/src/models/ds_select_option.model.dart index 053b2a25..e5ebdc07 100644 --- a/lib/src/models/ds_select_option.model.dart +++ b/lib/src/models/ds_select_option.model.dart @@ -1,6 +1,3 @@ -import 'package:blip_ds/src/widgets/chat/ds_quick_reply.widget.dart'; -import 'package:blip_ds/src/widgets/chat/ds_select_menu.widget.dart'; - /// A Design System select options model used with [DSSelectMenu], [DSQuickReply] to display a options menu class DSSelectOptionModel { String text; diff --git a/lib/src/services/ds_file.service.dart b/lib/src/services/ds_file.service.dart index 1952ca00..c4eeb2ef 100644 --- a/lib/src/services/ds_file.service.dart +++ b/lib/src/services/ds_file.service.dart @@ -7,25 +7,27 @@ import 'package:path/path.dart' as path_utils; import 'package:path_provider/path_provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; +import '../utils/ds_utils.util.dart'; + abstract class DSFileService { - static Future open( - final String filename, - final String url, { + static Future open({ + required final String url, + final String? path, final void Function(bool)? onDownloadStateChange, final Map? httpHeaders, }) async { - final path = await download( - url, - filename, + final filePath = await download( + url: url, + path: path, onDownloadStateChange: onDownloadStateChange, httpHeaders: httpHeaders, ); - if (path?.isEmpty ?? true) { + if (filePath?.isEmpty ?? true) { return; } - final result = await OpenFilex.open(path); + final result = await OpenFilex.open(filePath); switch (result.type) { case ResultType.done: @@ -41,9 +43,8 @@ abstract class DSFileService { } } - static Future download( - final String url, - final String filename, { + static Future download({ + required final String url, final String? path, final void Function(bool)? onDownloadStateChange, final Map? httpHeaders, @@ -51,10 +52,14 @@ abstract class DSFileService { }) async { try { onDownloadStateChange?.call(true); - final savedFilePath = path_utils.join( - path ?? (await getTemporaryDirectory()).path, filename); - if (await File(savedFilePath).exists()) { + var savedFilePath = path ?? + path_utils.join( + (await getTemporaryDirectory()).path, + DSUtils.generateUniqueID(), + ); + + if (File(savedFilePath).existsSync()) { return savedFilePath; } @@ -70,7 +75,26 @@ abstract class DSFileService { : null, ); - if (response.statusCode == 200) return savedFilePath; + if (response.statusCode == 200) { + final hasExtension = path_utils.extension(savedFilePath).isNotEmpty; + + if (!hasExtension) { + final newExtension = getFileExtensionFromMime( + response.headers.map['content-type']?.first, + ); + + if (newExtension.isNotEmpty) { + final filename = savedFilePath.substring(0); + + final newFilePath = '$filename.$newExtension'; + + File(savedFilePath).renameSync(newFilePath); + savedFilePath = newFilePath; + } + } + + return savedFilePath; + } } finally { onDownloadStateChange?.call(false); } diff --git a/lib/src/services/ds_media_format.service.dart b/lib/src/services/ds_media_format.service.dart new file mode 100644 index 00000000..9dcaf6e3 --- /dev/null +++ b/lib/src/services/ds_media_format.service.dart @@ -0,0 +1,122 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart'; +import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart'; +import 'package:path/path.dart' as path_utils; +import 'package:video_compress/video_compress.dart'; + +abstract class DSMediaFormatService { + static Future formatVideo({ + required final String input, + required final String output, + }) async { + var outputVideo = File(output); + + if (input != output) { + final inputHasExtension = path_utils.extension(input).isNotEmpty; + final outputHasExtension = path_utils.extension(output).isNotEmpty; + + final lastInputDotIndex = inputHasExtension ? input.lastIndexOf('.') : -1; + + final lastOutputDotIndex = + outputHasExtension ? output.lastIndexOf('.') : -1; + + final inputExtension = input.substring( + lastInputDotIndex + 1, + ); + + final outputExtension = output.substring( + lastOutputDotIndex + 1, + ); + + var inputVideo = File(input); + + if (lastInputDotIndex >= 0 && + lastOutputDotIndex >= 0 && + inputExtension.isNotEmpty && + inputExtension != outputExtension) { + final temp = await VideoCompress.compressVideo( + input, + quality: VideoQuality.HighestQuality, + frameRate: 60, + ); + + if (temp?.file != null) { + temp!.file!.copySync(output); + temp.file!.deleteSync(); + } + } else { + inputVideo.copySync(output); + } + + inputVideo.deleteSync(); + } + + return outputVideo.exists(); + } + + static Future getVideoThumbnail({ + required final String input, + required final String output, + }) async { + final temp = await VideoCompress.getFileThumbnail(input); + + final thumbnail = temp.copySync(output); + temp.deleteSync(); + + return thumbnail.exists(); + } + + static Future transcodeAudio({ + required final String input, + required final String output, + }) => + _executeCommand( + command: '-i "$input" $_transcodeAudioArgs "$output"', + ); + + static Future mergeAudio({ + required final String firstInput, + required final String secondInput, + required final String output, + }) => + _executeCommand( + command: + '-i "$firstInput" -i "$secondInput" $_mergeAudioArgs "$output"', + ); + + static Future _executeCommand({ + required final String command, + }) async { + final session = await FFmpegKit.execute( + '$_defaultArgs $command', + ); + + return ReturnCode.isSuccess( + await session.getReturnCode(), + ); + } + + static String get _defaultArgs { + const hideInfoBanner = '-hide_banner'; + const answerYes = '-y'; + + return '$hideInfoBanner $answerYes'; + } + + static String get _transcodeAudioArgs { + const codec = '-c:a aac'; + + return codec; + } + + static String get _mergeAudioArgs { + const filter = '-filter_complex'; + const firstInput = '[0:0]'; + const secondInput = '[1:0]'; + const audioConcat = 'concat=n=2:v=0:a=1'; + + return '$filter "$firstInput$secondInput$audioConcat"'; + } +} diff --git a/lib/src/services/ds_toast.service.dart b/lib/src/services/ds_toast.service.dart index 4638b015..1f5eecc1 100644 --- a/lib/src/services/ds_toast.service.dart +++ b/lib/src/services/ds_toast.service.dart @@ -20,11 +20,14 @@ abstract class DSToastService { ); _visibleToasts.insert(0, toast); - _controller?.animateTo( - 0, - duration: DSUtils.defaultAnimationDuration, - curve: Curves.easeInOut, - ); + + if (_controller?.hasClients ?? false) { + _controller?.animateTo( + 0, + duration: DSUtils.defaultAnimationDuration, + curve: Curves.easeInOut, + ); + } if (_overlayEntry == null) { _controller = ScrollController(); diff --git a/lib/src/themes/colors/ds_colors.theme.dart b/lib/src/themes/colors/ds_colors.theme.dart index a8bcf7c2..a0dfda1a 100644 --- a/lib/src/themes/colors/ds_colors.theme.dart +++ b/lib/src/themes/colors/ds_colors.theme.dart @@ -1,6 +1,7 @@ -import 'package:blip_ds/src/themes/colors/ds_linear_gradient.theme.dart'; import 'package:flutter/material.dart'; +import 'ds_linear_gradient.theme.dart'; + /// All [Color] constants that are used by this Design System. abstract class DSColors { static const Color primary = Color(0xFF1E6BF1); diff --git a/lib/src/utils/ds_directory_formatter.util.dart b/lib/src/utils/ds_directory_formatter.util.dart index 49fb68c0..a03a5260 100644 --- a/lib/src/utils/ds_directory_formatter.util.dart +++ b/lib/src/utils/ds_directory_formatter.util.dart @@ -4,19 +4,22 @@ import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; abstract class DSDirectoryFormatter { - static Future getPath({ + static Future getCachePath({ required final String type, - required final String fileName, + required final String filename, + String? extension, }) async { final cachePath = (await getApplicationCacheDirectory()).path; - final typeFolder = '${type.split('/').first.capitalizeFirst}'; - final extension = type.split('/').last; + final typeComponents = type.split('/'); + + final typeFolder = '${typeComponents.first.capitalizeFirst}'; + extension ??= typeComponents.last; final typePrefix = '${typeFolder.substring(0, 3).toUpperCase()}-'; final newFileName = - '${!fileName.startsWith(typePrefix) ? typePrefix : ''}$fileName'; + '${!filename.startsWith(typePrefix) ? typePrefix : ''}$filename'; final path = await _formatDirectory( type: typeFolder, diff --git a/lib/src/utils/ds_utils.util.dart b/lib/src/utils/ds_utils.util.dart index 58d908f1..626f51e8 100644 --- a/lib/src/utils/ds_utils.util.dart +++ b/lib/src/utils/ds_utils.util.dart @@ -1,3 +1,7 @@ +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; + import '../models/ds_country.model.dart'; /// All utility constants that are used by this Design System. @@ -7,6 +11,9 @@ abstract class DSUtils { static const bubbleMaxSize = 480.0; static const defaultAnimationDuration = Duration(milliseconds: 300); + static String generateUniqueID() => + md5.convert(utf8.encode(DateTime.now().toIso8601String())).toString(); + static const countriesList = [ DSCountry( code: '+55', diff --git a/lib/src/widgets/animations/ds_animated_size.widget.dart b/lib/src/widgets/animations/ds_animated_size.widget.dart index 062b3967..f115ab93 100644 --- a/lib/src/widgets/animations/ds_animated_size.widget.dart +++ b/lib/src/widgets/animations/ds_animated_size.widget.dart @@ -1,6 +1,7 @@ -import 'package:blip_ds/src/utils/ds_utils.util.dart'; import 'package:flutter/material.dart'; +import '../../utils/ds_utils.util.dart'; + /// A Design System's [AnimatedSize] that animates widgets whenever the given child's size changes. class DSAnimatedSize extends AnimatedSize { /// Creates a Design System's [AnimatedSize]. diff --git a/lib/src/widgets/animations/ds_spinner_loading.widget.dart b/lib/src/widgets/animations/ds_spinner_loading.widget.dart index eb88cc72..a96fc78d 100644 --- a/lib/src/widgets/animations/ds_spinner_loading.widget.dart +++ b/lib/src/widgets/animations/ds_spinner_loading.widget.dart @@ -1,8 +1,9 @@ import 'dart:math'; -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../themes/colors/ds_colors.theme.dart'; + class DSSpinnerLoading extends StatefulWidget { const DSSpinnerLoading({ Key? key, diff --git a/lib/src/widgets/buttons/ds_attachment_button.widget.dart b/lib/src/widgets/buttons/ds_attachment_button.widget.dart index 67957791..c402f3e6 100644 --- a/lib/src/widgets/buttons/ds_attachment_button.widget.dart +++ b/lib/src/widgets/buttons/ds_attachment_button.widget.dart @@ -1,6 +1,9 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; +import 'ds_icon_button.widget.dart'; + class DSAttachmentButton extends DSIconButton { DSAttachmentButton({ super.key, diff --git a/lib/src/widgets/buttons/ds_custom_replies_icon_button.widget.dart b/lib/src/widgets/buttons/ds_custom_replies_icon_button.widget.dart index 1f51a3ef..138526ae 100644 --- a/lib/src/widgets/buttons/ds_custom_replies_icon_button.widget.dart +++ b/lib/src/widgets/buttons/ds_custom_replies_icon_button.widget.dart @@ -7,17 +7,20 @@ import 'ds_icon_button.widget.dart'; class DSCustomRepliesIconButton extends StatelessWidget { final void Function() onPressed; final bool isVisible; + final bool isLoading; const DSCustomRepliesIconButton({ super.key, required this.onPressed, this.isVisible = true, + this.isLoading = false, }); @override Widget build(BuildContext context) => Visibility( visible: isVisible, child: DSIconButton( + isLoading: isLoading, onPressed: onPressed, icon: const Icon( DSIcons.message_talk_outline, diff --git a/lib/src/widgets/buttons/ds_primary_button.widget.dart b/lib/src/widgets/buttons/ds_primary_button.widget.dart index adcff396..d349632e 100644 --- a/lib/src/widgets/buttons/ds_primary_button.widget.dart +++ b/lib/src/widgets/buttons/ds_primary_button.widget.dart @@ -1,6 +1,5 @@ -import 'package:blip_ds/src/widgets/buttons/ds_button.widget.dart'; - import '../../themes/colors/ds_colors.theme.dart'; +import 'ds_button.widget.dart'; /// A Design System's [ButtonStyleButton] primarily used by main actions. /// diff --git a/lib/src/widgets/buttons/ds_request_location_button.widget.dart b/lib/src/widgets/buttons/ds_request_location_button.widget.dart index 8e7e34cf..5ba124ec 100644 --- a/lib/src/widgets/buttons/ds_request_location_button.widget.dart +++ b/lib/src/widgets/buttons/ds_request_location_button.widget.dart @@ -1,6 +1,9 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; +import 'ds_button.widget.dart'; + class DSRequestLocationButton extends DSButton { final void Function(String, Map)? onSelected; DSRequestLocationButton({ diff --git a/lib/src/widgets/buttons/ds_secondary_button.widget.dart b/lib/src/widgets/buttons/ds_secondary_button.widget.dart index 24d7cbd5..0ee61c58 100644 --- a/lib/src/widgets/buttons/ds_secondary_button.widget.dart +++ b/lib/src/widgets/buttons/ds_secondary_button.widget.dart @@ -1,6 +1,5 @@ -import 'package:blip_ds/src/widgets/buttons/ds_button.widget.dart'; - import '../../themes/colors/ds_colors.theme.dart'; +import 'ds_button.widget.dart'; /// A Design System's [ButtonStyleButton] primarily used by secondary actions. /// diff --git a/lib/src/widgets/buttons/ds_tertiary_button.widget.dart b/lib/src/widgets/buttons/ds_tertiary_button.widget.dart index af5051fd..800f2651 100644 --- a/lib/src/widgets/buttons/ds_tertiary_button.widget.dart +++ b/lib/src/widgets/buttons/ds_tertiary_button.widget.dart @@ -1,7 +1,7 @@ -import 'package:blip_ds/src/widgets/buttons/ds_button.widget.dart'; import 'package:flutter/material.dart'; import '../../themes/colors/ds_colors.theme.dart'; +import 'ds_button.widget.dart'; /// A Design System's [ButtonStyleButton] primarily used by tertiary actions. /// diff --git a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart index e1c955d4..ae9f308b 100644 --- a/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_message_bubble.widget.dart @@ -13,14 +13,12 @@ class DSAudioMessageBubble extends StatelessWidget { final List borderRadius; final DSMessageBubbleStyle style; final String? uniqueId; - final String audioType; final bool shouldAuthenticate; DSAudioMessageBubble({ super.key, required this.uri, required this.align, - required this.audioType, this.uniqueId, this.borderRadius = const [DSBorderRadius.all], this.shouldAuthenticate = false, @@ -44,8 +42,6 @@ class DSAudioMessageBubble extends StatelessWidget { ), child: DSAudioPlayer( uri: uri, - uniqueId: uniqueId, - audioType: audioType, shouldAuthenticate: shouldAuthenticate, controlForegroundColor: isLightBubbleBackground ? DSColors.neutralDarkRooftop diff --git a/lib/src/widgets/chat/audio/ds_audio_player.widget.dart b/lib/src/widgets/chat/audio/ds_audio_player.widget.dart index ef01775a..4718cdb3 100644 --- a/lib/src/widgets/chat/audio/ds_audio_player.widget.dart +++ b/lib/src/widgets/chat/audio/ds_audio_player.widget.dart @@ -1,16 +1,17 @@ +import 'dart:convert'; import 'dart:io'; -import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart'; -import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart'; +import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:just_audio/just_audio.dart'; -import 'package:path_provider/path_provider.dart'; import '../../../controllers/chat/ds_audio_player.controller.dart'; import '../../../services/ds_auth.service.dart'; import '../../../services/ds_file.service.dart'; +import '../../../services/ds_media_format.service.dart'; import '../../../themes/colors/ds_colors.theme.dart'; +import '../../../utils/ds_directory_formatter.util.dart'; import '../../buttons/ds_pause_button.widget.dart'; import '../../buttons/ds_play_button.widget.dart'; import 'ds_audio_seek_bar.widget.dart'; @@ -18,8 +19,6 @@ import 'ds_audio_speed_button.widget.dart'; class DSAudioPlayer extends StatefulWidget { final Uri uri; - final String uniqueId; - final String audioType; final Color labelColor; final Color bufferActiveTrackColor; final Color bufferInactiveTrackColor; @@ -30,10 +29,9 @@ class DSAudioPlayer extends StatefulWidget { final Color speedBorderColor; final bool shouldAuthenticate; - DSAudioPlayer({ + const DSAudioPlayer({ super.key, required this.uri, - required this.audioType, required this.labelColor, required this.bufferActiveTrackColor, required this.bufferInactiveTrackColor, @@ -43,8 +41,7 @@ class DSAudioPlayer extends StatefulWidget { required this.speedForegroundColor, required this.speedBorderColor, this.shouldAuthenticate = false, - final String? uniqueId, - }) : uniqueId = uniqueId ?? DateTime.now().toIso8601String(); + }); @override State createState() => _DSAudioPlayerState(); @@ -115,16 +112,7 @@ class _DSAudioPlayerState extends State ); try { - Platform.isIOS && widget.audioType.contains('ogg') - ? await _transcoder() - : await _controller.player.setAudioSource( - AudioSource.uri( - widget.uri, - headers: widget.shouldAuthenticate - ? DSAuthService.httpHeaders - : null, - ), - ); + await _loadAudio(); _controller.isInitialized.value = true; } catch (_) { @@ -132,29 +120,73 @@ class _DSAudioPlayerState extends State } } - Future _transcoder() async { - final inputFileName = 'AUDIO-${widget.uniqueId}.ogg'; + Future _loadAudio() async { + if (!widget.uri.scheme.startsWith('http')) { + await _controller.player.setAudioSource( + AudioSource.uri( + widget.uri, + ), + ); - final inputFilePath = await DSFileService.download( - widget.uri.toString(), - inputFileName, - httpHeaders: widget.shouldAuthenticate ? DSAuthService.httpHeaders : null, + return; + } + + final outputPath = await DSDirectoryFormatter.getCachePath( + type: 'audio/mp4', + filename: md5.convert(utf8.encode(widget.uri.path)).toString(), + extension: 'm4a', + ); + + final outputFile = File(outputPath); + var hasCachedFile = outputFile.existsSync(); + + if (!hasCachedFile) { + await _downloadAudio( + outputPath: outputPath, + ); + + hasCachedFile = outputFile.existsSync(); + } + + await _controller.player.setAudioSource( + hasCachedFile + ? AudioSource.file( + outputPath, + ) + : AudioSource.uri( + widget.uri, + headers: + widget.shouldAuthenticate ? DSAuthService.httpHeaders : null, + ), ); + } - final temporaryPath = (await getTemporaryDirectory()).path; - final outputFile = File("$temporaryPath/AUDIO-${widget.uniqueId}.mp3"); + Future _downloadAudio({ + required final String outputPath, + }) async { + final tempPath = await DSFileService.download( + url: widget.uri.toString(), + httpHeaders: widget.shouldAuthenticate ? DSAuthService.httpHeaders : null, + ); - if (await outputFile.exists()) { - await _controller.player.setFilePath(outputFile.path); - } else { - final session = await FFmpegKit.execute( - '-hide_banner -y -i "$inputFilePath" -c:a libmp3lame -qscale:a 2 "${outputFile.path}"', + if (tempPath?.isNotEmpty ?? false) { + final isSuccess = await DSMediaFormatService.transcodeAudio( + input: tempPath!, + output: outputPath, ); - final returnCode = await session.getReturnCode(); + final tempFile = File(tempPath); + + if (tempFile.existsSync()) { + tempFile.deleteSync(); + } - if (ReturnCode.isSuccess(returnCode)) { - await _controller.player.setFilePath(outputFile.path); + if (!isSuccess) { + final outputFile = File(outputPath); + + if (outputFile.existsSync()) { + outputFile.deleteSync(); + } } } } diff --git a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart index 5cdbb064..9d9f73fb 100644 --- a/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_contact_message_bubble.widget.dart @@ -1,6 +1,14 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../enums/ds_align.enum.dart'; +import '../../enums/ds_border_radius.enum.dart'; +import '../../models/ds_message_bubble_style.model.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/texts/utils/ds_font_weights.theme.dart'; +import '../texts/ds_body_text.widget.dart'; +import '../texts/ds_caption_small_text.widget.dart'; +import 'ds_message_bubble.widget.dart'; + class DSContactMessageBubble extends StatelessWidget { final String? name; final String? phone; @@ -50,6 +58,7 @@ class DSContactMessageBubble extends StatelessWidget { ), ), const SizedBox(height: 16.0), + /// TODO(format): Format phone number if (phone != null) Padding( diff --git a/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart b/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart index 2eda434a..26484d61 100644 --- a/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart +++ b/lib/src/widgets/chat/ds_delivery_report_icon.widget.dart @@ -1,7 +1,12 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import '../../enums/ds_delivery_report_status.enum.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; +import '../../utils/ds_utils.util.dart'; +import '../texts/ds_caption_small_text.widget.dart'; + /// A Design System widget used to display a delivery report status icon. class DSDeliveryReportIcon extends StatelessWidget { final DSDeliveryReportStatus deliveryStatus; diff --git a/lib/src/widgets/chat/ds_document_select.widget.dart b/lib/src/widgets/chat/ds_document_select.widget.dart index 9cb0cc87..d49e2e3c 100644 --- a/lib/src/widgets/chat/ds_document_select.widget.dart +++ b/lib/src/widgets/chat/ds_document_select.widget.dart @@ -1,8 +1,10 @@ -import 'package:blip_ds/blip_ds.dart'; -import 'package:blip_ds/src/widgets/buttons/ds_menu_item.widget.dart'; import 'package:flutter/material.dart'; + +import '../../enums/ds_align.enum.dart'; import '../../models/ds_document_select.model.dart'; +import '../../models/ds_message_bubble_style.model.dart'; import '../../utils/ds_message_content_type.util.dart'; +import '../buttons/ds_menu_item.widget.dart'; /// A Design System widget used to display a select menu with image. class DSDocumentSelect extends StatelessWidget { diff --git a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart index 5f605fab..625ed975 100644 --- a/lib/src/widgets/chat/ds_file_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_file_message_bubble.widget.dart @@ -40,7 +40,6 @@ class DSFileMessageBubble extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onTap: () => controller.openFile( - filename: filename, url: url, httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null, ), diff --git a/lib/src/widgets/chat/ds_message_bubble_detail.widget.dart b/lib/src/widgets/chat/ds_message_bubble_detail.widget.dart index c754c3a2..16c235f4 100644 --- a/lib/src/widgets/chat/ds_message_bubble_detail.widget.dart +++ b/lib/src/widgets/chat/ds_message_bubble_detail.widget.dart @@ -1,6 +1,12 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../enums/ds_align.enum.dart'; +import '../../enums/ds_delivery_report_status.enum.dart'; +import '../../models/ds_message_bubble_style.model.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../texts/ds_caption_small_text.widget.dart'; +import 'ds_delivery_report_icon.widget.dart'; + /// A Design System widget used to display a date and a [DSDeliveryReportIcon] at a message. class DSMessageBubbleDetail extends StatelessWidget { final String? date; diff --git a/lib/src/widgets/chat/ds_survey_message_bubble.widget.dart b/lib/src/widgets/chat/ds_survey_message_bubble.widget.dart index 814e610d..56b4f4e8 100644 --- a/lib/src/widgets/chat/ds_survey_message_bubble.widget.dart +++ b/lib/src/widgets/chat/ds_survey_message_bubble.widget.dart @@ -1,6 +1,17 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../enums/ds_align.enum.dart'; +import '../../enums/ds_border_radius.enum.dart'; +import '../../enums/ds_survey_scale.enum.dart'; +import '../../enums/ds_survey_type.enum.dart'; +import '../../models/ds_message_bubble_style.model.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; +import '../texts/ds_button_text.widget.dart'; +import '../texts/ds_caption_text.widget.dart'; +import '../texts/ds_headline_small_text.widget.dart'; +import 'ds_message_bubble.widget.dart'; + class DSSurveyMessageBubble extends StatelessWidget { final DSAlign align; final Widget? leftWidget; diff --git a/lib/src/widgets/chat/ds_url_preview.widget.dart b/lib/src/widgets/chat/ds_url_preview.widget.dart index 4571c255..fb2ccc08 100644 --- a/lib/src/widgets/chat/ds_url_preview.widget.dart +++ b/lib/src/widgets/chat/ds_url_preview.widget.dart @@ -1,9 +1,17 @@ -import 'package:blip_ds/blip_ds.dart'; -import 'package:blip_ds/src/controllers/chat/ds_url_preview.controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../controllers/chat/ds_url_preview.controller.dart'; +import '../../enums/ds_align.enum.dart'; +import '../../enums/ds_border_radius.enum.dart'; +import '../../models/ds_message_bubble_style.model.dart'; +import '../animations/ds_animated_size.widget.dart'; +import '../animations/ds_ring_loading.widget.dart'; +import '../texts/ds_body_text.widget.dart'; +import '../texts/ds_caption_small_text.widget.dart'; +import '../utils/ds_cached_network_image_view.widget.dart'; + /// A Design System's clickable URL previewer that shows some metadata infos about the given [url]. class DSUrlPreview extends StatefulWidget { final Uri url; diff --git a/lib/src/widgets/chat/typing/ds_typing_dot_animation.widget.dart b/lib/src/widgets/chat/typing/ds_typing_dot_animation.widget.dart index ec632a49..d62c53c5 100644 --- a/lib/src/widgets/chat/typing/ds_typing_dot_animation.widget.dart +++ b/lib/src/widgets/chat/typing/ds_typing_dot_animation.widget.dart @@ -1,6 +1,6 @@ -import 'package:blip_ds/src/utils/ds_utils.util.dart'; import 'package:flutter/material.dart'; +import '../../../utils/ds_utils.util.dart'; import 'ds_typing_dot.widget.dart'; class DSTypingDotAnimation extends StatefulWidget { diff --git a/lib/src/widgets/chat/video/ds_video_error.dialog.dart b/lib/src/widgets/chat/video/ds_video_error.dialog.dart index 0b2c0da9..cb27bd9e 100644 --- a/lib/src/widgets/chat/video/ds_video_error.dialog.dart +++ b/lib/src/widgets/chat/video/ds_video_error.dialog.dart @@ -1,13 +1,16 @@ +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; import 'package:get/get.dart'; import '../../../services/ds_dialog.service.dart'; import '../../../services/ds_file.service.dart'; +import '../../../utils/ds_directory_formatter.util.dart'; import '../../buttons/ds_primary_button.widget.dart'; import '../../buttons/ds_secondary_button.widget.dart'; abstract class DSVideoErrorDialog { static Future show({ - required final String filename, required final String url, final Map? httpHeaders, }) async { @@ -19,9 +22,15 @@ abstract class DSVideoErrorDialog { primaryButton: DSPrimaryButton( onPressed: () async { Get.back(); + + final cachePath = await DSDirectoryFormatter.getCachePath( + type: 'video/mp4', + filename: md5.convert(utf8.encode(Uri.parse(url).path)).toString(), + ); + await DSFileService.open( - filename, - url, + url: url, + path: cachePath, httpHeaders: httpHeaders, ); }, diff --git a/lib/src/widgets/radio/ds_radio.widget.dart b/lib/src/widgets/radio/ds_radio.widget.dart index 4dc66ee5..6e6c0f63 100644 --- a/lib/src/widgets/radio/ds_radio.widget.dart +++ b/lib/src/widgets/radio/ds_radio.widget.dart @@ -1,38 +1,8 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; -/// A Design System radio button custom. -/// -/// Used to select between a number of mutually exclusive values. When one radio -/// button in a group is selected, the other radio buttons in the group cease to -/// be selected. The values are of type `T`, the type parameter of the [Radio] -/// class. Enums are commonly used for this purpose. -/// -/// The radio button itself does not maintain any state. Instead, selecting the -/// radio invokes the [onChanged] callback, passing [value] as a parameter. If -/// [groupValue] and [value] match, this radio will be selected. Most widgets -/// will respond to [onChanged] by calling [State.setState] to update the -/// radio button's [groupValue]. -/// +import '../../themes/colors/ds_colors.theme.dart'; class DSRadio extends Radio { - /// Creates a Design System radio button. - /// - /// The radio button itself does not maintain any state. Instead, when the - /// radio button is selected, the widget calls the [onChanged] callback. Most - /// widgets that use a radio button will listen for the [onChanged] callback - /// and rebuild the radio button with a new [groupValue] to update the visual - /// appearance of the radio button. - /// - /// The following arguments are required: - /// - /// * [value] and [groupValue] together determine whether the radio button is - /// selected. - /// * [onChanged] is called when the user selects this radio button. const DSRadio({ Key? key, required super.value, @@ -44,7 +14,6 @@ class DSRadio extends Radio { key: key, ); - /// The [isEnabled] parameter allows disabling the radio button according to its true/false value. final bool isEnabled; bool get _selected => value == groupValue; diff --git a/lib/src/widgets/radio/ds_radio_tile.widget.dart b/lib/src/widgets/radio/ds_radio_tile.widget.dart index fdb27191..280d8ddf 100644 --- a/lib/src/widgets/radio/ds_radio_tile.widget.dart +++ b/lib/src/widgets/radio/ds_radio_tile.widget.dart @@ -1,6 +1,7 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../../../blip_ds.dart'; + class DSRadioTile extends StatelessWidget { /// Create a Design System's tile widget with a radio button /// diff --git a/lib/src/widgets/switch/ds_switch.widget.dart b/lib/src/widgets/switch/ds_switch.widget.dart index e599763f..78afddd8 100644 --- a/lib/src/widgets/switch/ds_switch.widget.dart +++ b/lib/src/widgets/switch/ds_switch.widget.dart @@ -1,7 +1,8 @@ -import 'package:blip_ds/blip_ds.dart'; -import 'package:blip_ds/src/widgets/switch/ds_switch_base.widget.dart'; import 'package:flutter/material.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import 'ds_switch_base.widget.dart'; + class DSSwitch extends DSSwitchBase { /// Creates a customizable switch for use alone or in conjunction with ListTile /// diff --git a/lib/src/widgets/switch/ds_switch_tile.widget.dart b/lib/src/widgets/switch/ds_switch_tile.widget.dart index f83ca429..0d8ce093 100644 --- a/lib/src/widgets/switch/ds_switch_tile.widget.dart +++ b/lib/src/widgets/switch/ds_switch_tile.widget.dart @@ -1,7 +1,8 @@ -import 'package:blip_ds/blip_ds.dart'; -import 'package:blip_ds/src/widgets/switch/ds_switch_base.widget.dart'; import 'package:flutter/material.dart'; +import 'ds_switch.widget.dart'; +import 'ds_switch_base.widget.dart'; + class DSSwitchTile extends DSSwitchBase { /// Create a tile widget with a switch button /// diff --git a/lib/src/widgets/ticket_message/ds_ticket_message.widget.dart b/lib/src/widgets/ticket_message/ds_ticket_message.widget.dart index 4ae19f96..3bfb84a0 100644 --- a/lib/src/widgets/ticket_message/ds_ticket_message.widget.dart +++ b/lib/src/widgets/ticket_message/ds_ticket_message.widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:blip_ds/blip_ds.dart'; +import '../../enums/ds_ticket_message_type.enum.dart'; +import '../texts/ds_body_text.widget.dart'; /// Messages and warnings to be shown in agent and customer actions /// diff --git a/lib/src/widgets/toast/ds_toast.widget.dart b/lib/src/widgets/toast/ds_toast.widget.dart index 93d8ccfd..e2e8ddbf 100644 --- a/lib/src/widgets/toast/ds_toast.widget.dart +++ b/lib/src/widgets/toast/ds_toast.widget.dart @@ -163,7 +163,7 @@ class _DSToastState extends State with AutomaticKeepAliveClientMixin { props.title, overflow: TextOverflow.visible, ), - if (props.message != null) + if (props.message?.isNotEmpty ?? false) DSBodyText( props.message, overflow: TextOverflow.visible, diff --git a/lib/src/widgets/utils/ds_card.widget.dart b/lib/src/widgets/utils/ds_card.widget.dart index 9744fd57..1159404b 100644 --- a/lib/src/widgets/utils/ds_card.widget.dart +++ b/lib/src/widgets/utils/ds_card.widget.dart @@ -255,7 +255,6 @@ class DSCard extends StatelessWidget { borderRadius: borderRadius, style: style, uniqueId: messageId, - audioType: media.type, shouldAuthenticate: shouldAuthenticate, ); } else if (media.type.contains('image')) { diff --git a/lib/src/widgets/utils/ds_chip.widget.dart b/lib/src/widgets/utils/ds_chip.widget.dart index 2be0e30e..22c7bdcc 100644 --- a/lib/src/widgets/utils/ds_chip.widget.dart +++ b/lib/src/widgets/utils/ds_chip.widget.dart @@ -1,6 +1,7 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:flutter/material.dart'; +import '../texts/ds_text.widget.dart'; + class DSChip extends StatelessWidget { final DSText? text; final Widget? leadingIcon; diff --git a/lib/src/widgets/utils/ds_group_card.widget.dart b/lib/src/widgets/utils/ds_group_card.widget.dart index 2e6dee95..5224557c 100644 --- a/lib/src/widgets/utils/ds_group_card.widget.dart +++ b/lib/src/widgets/utils/ds_group_card.widget.dart @@ -333,7 +333,7 @@ class _DSGroupCardState extends State { 0, Padding( key: ValueKey( - message.id ?? DateTime.now().toIso8601String(), + message.id ?? DSUtils.generateUniqueID(), ), padding: const EdgeInsets.symmetric(horizontal: 16), child: Table( diff --git a/lib/src/widgets/utils/ds_user_avatar.widget.dart b/lib/src/widgets/utils/ds_user_avatar.widget.dart index 8671bde2..5eee73dc 100644 --- a/lib/src/widgets/utils/ds_user_avatar.widget.dart +++ b/lib/src/widgets/utils/ds_user_avatar.widget.dart @@ -1,7 +1,11 @@ -import 'package:blip_ds/blip_ds.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import '../../themes/colors/ds_colors.theme.dart'; +import '../../themes/icons/ds_icons.dart'; +import '../../utils/ds_utils.util.dart'; +import '../texts/ds_body_text.widget.dart'; + class DSUserAvatar extends StatelessWidget { final String? text; final Uri? uri; @@ -46,15 +50,16 @@ class DSUserAvatar extends StatelessWidget { String get _initials { String initials = ''; - if ((text?.isNotEmpty ?? false) && (int.tryParse(text!) == null)) { - initials = - RegExp(text!.split(' ').length >= 2 ? r'\b[A-Za-z]' : r'[A-Za-z]') - .allMatches(text!) - .map((m) => m.group(0)) - .join() - .toUpperCase(); + if (text?.isNotEmpty ?? false) { + initials = RegExp('${text!.split(' ').length >= 2 ? '\\b' : ''}[A-Za-z]') + .allMatches(text!) + .map((m) => m.group(0)) + .join() + .toUpperCase(); - initials = initials.substring(0, initials.length >= 2 ? 2 : 1); + if (initials.isNotEmpty) { + initials = initials.substring(0, initials.length >= 2 ? 2 : 1); + } } return initials; @@ -66,7 +71,7 @@ class DSUserAvatar extends StatelessWidget { backgroundColor: backgroundColor, child: Padding( padding: const EdgeInsets.all(2.0), - child: int.tryParse(text!) == null + child: _initials.isNotEmpty ? DSBodyText( _initials, color: textColor, diff --git a/pubspec.yaml b/pubspec.yaml index f66c5c4f..9ef1c58f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,6 @@ dependencies: rxdart: ^0.27.4 flutter_spinkit: ^5.1.0 get: ^4.6.5 - filesize: ^2.0.1 open_filex: ^4.3.2 path_provider: ^2.1.1 dio: ^5.2.1+1 @@ -40,7 +39,8 @@ dependencies: map_launcher: ^2.5.0+1 mime: ^1.0.4 crypto: ^3.0.3 - + video_compress: ^3.1.2 + dev_dependencies: flutter_test: sdk: flutter diff --git a/sample/android/build.gradle b/sample/android/build.gradle index 3cdaac95..59437ce1 100644 --- a/sample/android/build.gradle +++ b/sample/android/build.gradle @@ -15,6 +15,7 @@ allprojects { repositories { google() mavenCentral() + jcenter() } } diff --git a/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart b/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart index a0f33e2a..4e226076 100644 --- a/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart +++ b/sample/lib/widgets/showcase/sample_message_bubble.showcase.dart @@ -237,13 +237,11 @@ class SampleMessageBubbleShowcase extends StatelessWidget { align: DSAlign.left, uri: Uri.parse(_sampleAudio), uniqueId: 'audio1', - audioType: 'mp3', ), DSAudioMessageBubble( align: DSAlign.right, uri: Uri.parse(_sampleAudio), uniqueId: 'audio2', - audioType: 'mp3', ), DSVideoMessageBubble( align: DSAlign.right, diff --git a/sample/macos/Flutter/GeneratedPluginRegistrant.swift b/sample/macos/Flutter/GeneratedPluginRegistrant.swift index 803cf2c1..3744928a 100644 --- a/sample/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/sample/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,6 +11,7 @@ import just_audio import path_provider_foundation import sqflite import url_launcher_macos +import video_compress import wakelock_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -20,5 +21,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) } diff --git a/sample/pubspec.lock b/sample/pubspec.lock index 67d875ba..d5a67aec 100644 --- a/sample/pubspec.lock +++ b/sample/pubspec.lock @@ -21,17 +21,17 @@ packages: dependency: transitive description: name: audio_session - sha256: e4acc4e9eaa32436dfc5d7aed7f0a370f2d7bb27ee27de30d6c4f220c2a05c73 + sha256: "8a2bc5e30520e18f3fb0e366793d78057fb64cd5287862c76af0c8771f2a52ad" url: "https://pub.dev" source: hosted - version: "0.1.13" + version: "0.1.16" blip_ds: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.0.81" + version: "0.0.85" boolean_selector: dependency: transitive description: @@ -208,14 +208,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" - filesize: - dependency: transitive - description: - name: filesize - sha256: f53df1f27ff60e466eefcd9df239e02d4722d5e2debee92a87dfd99ac66de2af - url: "https://pub.dev" - source: hosted - version: "2.0.1" flutter: dependency: "direct main" description: flutter @@ -331,26 +323,26 @@ packages: dependency: transitive description: name: just_audio - sha256: "7a5057a4d05c8f88ee968cec6fdfe1015577d5184e591d5ac15ab16d8f5ecb17" + sha256: "5ed0cd723e17dfd8cd4b0253726221e67f6546841ea4553635cf895061fc335b" url: "https://pub.dev" source: hosted - version: "0.9.31" + version: "0.9.35" just_audio_platform_interface: dependency: transitive description: name: just_audio_platform_interface - sha256: eff112d5138bea3ba544b6338b1e0537a32b5e1425e4d0dc38f732771cda7c84 + sha256: d8409da198bbc59426cd45d4c92fca522a2ec269b576ce29459d6d6fcaeb44df url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.2.1" just_audio_web: dependency: transitive description: name: just_audio_web - sha256: "89d8db6f19f3821bb6bf908c4bfb846079afb2ab575b783d781a6bf119e3abaf" + sha256: ff62f733f437b25a0ff590f0e295fa5441dcb465f1edbdb33b3dea264705bc13 url: "https://pub.dev" source: hosted - version: "0.4.7" + version: "0.4.8" libphonenumber_platform_interface: dependency: transitive description: @@ -860,6 +852,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + video_compress: + dependency: transitive + description: + name: video_compress + sha256: "407693726e674a1e1958801deb2d9daf5a5297707ba6d03375007012dae7389a" + url: "https://pub.dev" + source: hosted + version: "3.1.2" video_player: dependency: transitive description: