Skip to content

Commit

Permalink
Merge branch 'develop' into feature/532627-text-reply
Browse files Browse the repository at this point in the history
  • Loading branch information
RaulRodrigo06 committed Nov 16, 2023
2 parents 71d7247 + 41cfa56 commit aef600d
Show file tree
Hide file tree
Showing 55 changed files with 554 additions and 311 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 0.0.86

- [DSMediaFormatService] Added service to format all kinds of media, like audios and videos.
- [DSAudioPlayer] Play audio from local storage when available.
- [DSFileService] Improved download method to get extension based on mime type.

## 0.0.85

- [DSVideoMessageBubble] Added a progressive loading

## 0.0.84

- [DSImageMessageBubble] Improved the performance of the image bubble
Expand Down
4 changes: 4 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
include: package:flutter_lints/flutter.yaml

linter:
rules:
- prefer_relative_imports

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
3 changes: 3 additions & 0 deletions lib/blip_ds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -150,6 +151,8 @@ export 'src/widgets/utils/ds_cached_network_image_view.widget.dart'
show DSCachedNetworkImageView;
export 'src/widgets/utils/ds_card.widget.dart' show DSCard;
export 'src/widgets/utils/ds_chip.widget.dart' show DSChip;
export 'src/widgets/utils/ds_circular_progress.widget.dart'
show DSCircularProgress;
export 'src/widgets/utils/ds_divider.widget.dart' show DSDivider;
export 'src/widgets/utils/ds_expanded_image.widget.dart' show DSExpandedImage;
export 'src/widgets/utils/ds_file_extension_icon.util.dart'
Expand Down
13 changes: 8 additions & 5 deletions lib/src/controllers/chat/ds_file_message_bubble.controller.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<void> openFile({
required final String filename,
required final String url,
final Map<String, String?>? httpHeaders,
}) =>
DSFileService.open(
filename,
url,
url: url,
onDownloadStateChange: (loading) => isDownloading.value = loading,
httpHeaders: httpHeaders,
);
Expand Down
18 changes: 7 additions & 11 deletions lib/src/controllers/chat/ds_image_message_bubble.controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:blip_ds/src/utils/ds_directory_formatter.util.dart';
import 'package:crypto/crypto.dart';
import 'package:get/get.dart';

import '../../services/ds_auth.service.dart';
import '../../services/ds_file.service.dart';
import '../../utils/ds_directory_formatter.util.dart';

class DSImageMessageBubbleController extends GetxController {
final maximumProgress = RxInt(0);
Expand Down Expand Up @@ -39,24 +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;
}

final fileName = fullPath.split('/').last;
final path = fullPath.substring(0, fullPath.lastIndexOf('/'));

try {
final savedFilePath = await DSFileService.download(
url,
fileName,
path: path,
url: url,
path: cachePath,
onReceiveProgress: _onReceiveProgress,
httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null,
);
Expand Down
78 changes: 43 additions & 35 deletions lib/src/controllers/chat/ds_video_message_bubble.controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import 'dart:convert';
import 'dart:io';

import 'package:crypto/crypto.dart';
import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart';
import 'package:file_sizes/file_sizes.dart';
import 'package:get/get.dart';

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';
import '../../widgets/chat/video/ds_video_error.dialog.dart';

class DSVideoMessageBubbleController {
final String url;
final int mediaSize;
final Map<String, String?>? httpHeaders;
final String type;
final maximumProgress = RxInt(0);
final downloadProgress = RxInt(0);

DSVideoMessageBubbleController({
required this.url,
Expand Down Expand Up @@ -47,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 {
Expand All @@ -69,46 +73,40 @@ class DSVideoMessageBubbleController {

Future<String> 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<void> 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,
fileName,
url: url,
onReceiveProgress: (current, max) {
downloadProgress.value = current;
maximumProgress.value = max;
},
httpHeaders: httpHeaders,
);

final session = await FFmpegKit.execute(
'-hide_banner -y -i "$inputFilePath" "${outputFile.path}"');

File(inputFilePath!).delete();

final returnCode = await session.getReturnCode();
final isSuccess = await DSMediaFormatService.formatVideo(
input: inputFilePath!,
output: cachePath,
);

if (!ReturnCode.isSuccess(returnCode)) {
hasError.value = true;
await DSVideoErrorDialog.show(
filename: fileName,
url: url,
httpHeaders: httpHeaders,
);
}
hasError.value = !isSuccess;
}

_generateThumbnail(outputFile.path);
Expand All @@ -130,10 +128,20 @@ class DSVideoMessageBubbleController {
Future<void> _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;
}

String getDownloadProgress() {
String getSize(int value) => FileSize.getSize(
value,
precision: PrecisionValue.One,
);

return '${getSize(downloadProgress.value)} / ${getSize(maximumProgress.value)}';
}
}
3 changes: 0 additions & 3 deletions lib/src/controllers/ds_video_player.controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ class DSVideoPlayerController extends GetxController {
Get.back();
Get.delete<DSVideoPlayerController>();

final filename = url.substring(url.lastIndexOf('/')).substring(1);

await DSVideoErrorDialog.show(
filename: filename,
url: url,
httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null,
);
Expand Down
3 changes: 2 additions & 1 deletion lib/src/extensions/ds_border_radius.extension.dart
Original file line number Diff line number Diff line change
@@ -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({
Expand Down
Original file line number Diff line number Diff line change
@@ -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) =>
Expand Down
3 changes: 2 additions & 1 deletion lib/src/models/ds_message_item.model.dart
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
3 changes: 0 additions & 3 deletions lib/src/models/ds_select_option.model.dart
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
54 changes: 39 additions & 15 deletions lib/src/services/ds_file.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> open(
final String filename,
final String url, {
static Future<void> open({
required final String url,
final String? path,
final void Function(bool)? onDownloadStateChange,
final Map<String, String?>? 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:
Expand All @@ -41,20 +43,23 @@ abstract class DSFileService {
}
}

static Future<String?> download(
final String url,
final String filename, {
static Future<String?> download({
required final String url,
final String? path,
final void Function(bool)? onDownloadStateChange,
final Map<String, String?>? httpHeaders,
final Function(int, int)? onReceiveProgress,
}) 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;
}

Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit aef600d

Please sign in to comment.