Merge pull request #153 from GetStream/hotfix/media-picker

Hotfix/media picker
This commit is contained in:
Salvatore Giordano
2020-11-26 12:24:56 +01:00
committed by GitHub
7 changed files with 266 additions and 165 deletions
+12 -6
View File
@@ -121,9 +121,9 @@ PODS:
- Flutter - Flutter
- PromisesObjC (1.2.11) - PromisesObjC (1.2.11)
- Protobuf (3.13.0) - Protobuf (3.13.0)
- SDWebImage (5.9.4): - SDWebImage (5.10.0):
- SDWebImage/Core (= 5.9.4) - SDWebImage/Core (= 5.10.0)
- SDWebImage/Core (5.9.4) - SDWebImage/Core (5.10.0)
- shared_preferences (0.0.1): - shared_preferences (0.0.1):
- Flutter - Flutter
- sqflite (0.0.2): - sqflite (0.0.2):
@@ -148,11 +148,13 @@ PODS:
- sqlite3/perf-threadsafe - sqlite3/perf-threadsafe
- sqlite3/rtree - sqlite3/rtree
- Starscream (4.0.4) - Starscream (4.0.4)
- StreamChatClient (2.4.1): - StreamChatClient (2.4.2):
- Starscream (~> 4.0) - Starscream (~> 4.0)
- SwiftyGif (5.3.0) - SwiftyGif (5.3.0)
- url_launcher (0.0.1): - url_launcher (0.0.1):
- Flutter - Flutter
- video_compress (0.3.0):
- Flutter
- video_player (0.0.1): - video_player (0.0.1):
- Flutter - Flutter
- wakelock (0.0.1): - wakelock (0.0.1):
@@ -177,6 +179,7 @@ DEPENDENCIES:
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
- StreamChatClient - StreamChatClient
- url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- video_compress (from `.symlinks/plugins/video_compress/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`) - video_player (from `.symlinks/plugins/video_player/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`)
@@ -237,6 +240,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios" :path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
url_launcher: url_launcher:
:path: ".symlinks/plugins/url_launcher/ios" :path: ".symlinks/plugins/url_launcher/ios"
video_compress:
:path: ".symlinks/plugins/video_compress/ios"
video_player: video_player:
:path: ".symlinks/plugins/video_player/ios" :path: ".symlinks/plugins/video_player/ios"
wakelock: wakelock:
@@ -270,15 +275,16 @@ SPEC CHECKSUMS:
permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6 permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6
PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f
Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748 Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748
SDWebImage: b69257f4ab14e9b6a2ef53e910fdf914d8f757c1 SDWebImage: 9169792e9eec3e45bba2a0c02f74bf8bd922d1ee
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
sqlite3: 8f7d2078ae27778699a622a94b853285793422a2 sqlite3: 8f7d2078ae27778699a622a94b853285793422a2
sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f sqlite3_flutter_libs: 5651f8ff48e3b44d910863c4ea5916085b1b245f
Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9 Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
StreamChatClient: d061cf52babcd9df930aee9cf864ab0379427eff StreamChatClient: fc6419fa7ceda9c048188bb7c8e9f07755ced05f
SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
wakelock: bfc7955c418d0db797614075aabbc58a39ab5107 wakelock: bfc7955c418d0db797614075aabbc58a39ab5107
@@ -335,6 +335,7 @@
"${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework", "${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework",
"${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework",
"${BUILT_PRODUCTS_DIR}/video_compress/video_compress.framework",
"${BUILT_PRODUCTS_DIR}/video_player/video_player.framework", "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework",
"${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework",
); );
@@ -366,6 +367,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_compress.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework",
); );
+1 -1
View File
@@ -1,6 +1,6 @@
name: example name: example
description: A new Flutter project. description: A new Flutter project.
version: 1.0.75+77 version: 1.0.76+78
environment: environment:
sdk: ">=2.2.2 <3.0.0" sdk: ">=2.2.2 <3.0.0"
+108 -107
View File
@@ -167,122 +167,123 @@ class _ChannelListViewState extends State<ChannelListView>
ChannelsBlocState channelsBlocState, ChannelsBlocState channelsBlocState,
) { ) {
return StreamBuilder<List<Channel>>( return StreamBuilder<List<Channel>>(
stream: channelsBlocState.channelsStream, stream: channelsBlocState.channelsStream,
builder: (context, snapshot) { builder: (context, snapshot) {
var child; var child;
if (snapshot.hasError) { if (snapshot.hasError) {
child = _buildErrorWidget( child = _buildErrorWidget(
snapshot, snapshot,
context, context,
channelsBlocState, channelsBlocState,
); );
} else if (!snapshot.hasData) { } else if (!snapshot.hasData) {
child = _buildLoadingWidget(); child = _buildLoadingWidget();
} else { } else {
final channels = snapshot.data; final channels = snapshot.data;
if (channels.isEmpty && widget.emptyBuilder != null) { if (channels.isEmpty && widget.emptyBuilder != null) {
child = widget.emptyBuilder(context); child = widget.emptyBuilder(context);
} }
if (channels.isEmpty && widget.emptyBuilder == null) { if (channels.isEmpty && widget.emptyBuilder == null) {
child = LayoutBuilder( child = LayoutBuilder(
builder: (context, viewportConstraints) { builder: (context, viewportConstraints) {
return SingleChildScrollView( return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(), physics: AlwaysScrollableScrollPhysics(),
child: Stack( child: Stack(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight, minHeight: viewportConstraints.maxHeight,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: StreamSvgIcon.message(
size: 136,
color: Color(0xffDBDBDB),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Lets start chatting!',
style: TextStyle(
fontSize: 16,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 52,
),
child: Text(
'How about sending your first message to a friend?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xff7A7A7A),
),
),
),
],
),
), ),
if (widget.onStartChatPressed != null) child: Column(
Positioned( mainAxisAlignment: MainAxisAlignment.center,
right: 0, children: [
left: 0, Padding(
bottom: 32, padding: const EdgeInsets.all(8.0),
child: Center( child: StreamSvgIcon.message(
child: FlatButton( size: 136,
onPressed: widget.onStartChatPressed, color: Color(0xffDBDBDB),
child: Text( ),
'Start a chat', ),
style: TextStyle( Padding(
color: padding: const EdgeInsets.all(8.0),
StreamChatTheme.of(context).accentColor, child: Text(
fontWeight: FontWeight.bold, 'Lets start chatting!',
), style: TextStyle(
fontSize: 16,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 52,
),
child: Text(
'How about sending your first message to a friend?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Color(0xff7A7A7A),
),
),
),
],
),
),
if (widget.onStartChatPressed != null)
Positioned(
right: 0,
left: 0,
bottom: 32,
child: Center(
child: FlatButton(
onPressed: widget.onStartChatPressed,
child: Text(
'Start a chat',
style: TextStyle(
color:
StreamChatTheme.of(context).accentColor,
fontWeight: FontWeight.bold,
), ),
), ),
), ),
), ),
], ),
), ],
); ),
}, );
); },
} );
if (channels.isNotEmpty) {
child = ListView.custom(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _itemBuilder(context, i, channels);
},
childCount: (channels.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = channels.indexWhere(
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
);
}
} }
return AnimatedSwitcher( if (channels.isNotEmpty) {
child: child, child = ListView.custom(
duration: Duration(milliseconds: 500), physics: AlwaysScrollableScrollPhysics(),
); controller: _scrollController,
}); childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
return _itemBuilder(context, i, channels);
},
childCount: (channels.length * 2) + 1,
findChildIndexCallback: (key) {
final ValueKey<String> valueKey = key;
final index = channels.indexWhere(
(channel) => 'CHANNEL-${channel.id}' == valueKey.value);
return index != -1 ? (index * 2) : null;
},
),
);
}
}
return AnimatedSwitcher(
child: child,
duration: Duration(milliseconds: 500),
);
},
);
} }
Widget _buildLoadingWidget() { Widget _buildLoadingWidget() {
+20
View File
@@ -0,0 +1,20 @@
import 'dart:async';
import 'package:synchronized/synchronized.dart';
import 'package:video_compress/video_compress.dart';
class ICompressVideoService {
static final ICompressVideoService instance = ICompressVideoService._();
final _lock = Lock();
ICompressVideoService._();
Future<MediaInfo> compressVideo(String path) async {
return _lock.synchronized(() {
return VideoCompress.compressVideo(
path,
);
});
}
}
ICompressVideoService get CompressVideoService =>
ICompressVideoService.instance;
+121 -51
View File
@@ -14,6 +14,7 @@ import 'package:media_gallery/media_gallery.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/compress_video_service.dart';
import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart';
import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
@@ -21,6 +22,7 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart'; import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:stream_chat_flutter/src/video_thumbnail.dart'; import 'package:stream_chat_flutter/src/video_thumbnail.dart';
import 'package:substring_highlight/substring_highlight.dart'; import 'package:substring_highlight/substring_highlight.dart';
import 'package:video_compress/video_compress.dart';
import '../stream_chat_flutter.dart'; import '../stream_chat_flutter.dart';
import 'stream_channel.dart'; import 'stream_channel.dart';
@@ -44,6 +46,8 @@ enum DefaultAttachmentTypes {
const _kMinMediaPickerSize = 360.0; const _kMinMediaPickerSize = 360.0;
const _kMaxAttachmentSize = 20480; //20MB
/// Inactive state /// Inactive state
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/message_input_paint.png)
@@ -797,7 +801,6 @@ class MessageInputState extends State<MessageInput> {
var status = await (Platform.isAndroid var status = await (Platform.isAndroid
? Permission.storage.status ? Permission.storage.status
: Permission.photos.status); : Permission.photos.status);
print('status: ${status}');
if (status.isPermanentlyDenied || status.isDenied) { if (status.isPermanentlyDenied || status.isDenied) {
if (await openAppSettings()) { if (await openAppSettings()) {
setState(() {}); setState(() {});
@@ -844,55 +847,102 @@ class MessageInputState extends State<MessageInput> {
} }
void _addAttachment(Media medium) async { void _addAttachment(Media medium) async {
final attachment = _SendingAttachment( try {
id: medium.id, final attachment = _SendingAttachment(
); id: medium.id,
setState(() {
_attachments.add(attachment);
});
final mediaFile = await medium.getFile();
final file = PlatformFile(
path: mediaFile.path,
bytes: mediaFile.readAsBytesSync(),
);
final channel = StreamChannel.of(context).channel;
setState(() {
attachment
..file = file
..attachment = Attachment(
localUri: file.path != null ? Uri.parse(file.path) : null,
type: medium.mediaType == MediaType.image ? 'image' : 'video',
);
});
final url = await _uploadAttachment(
file,
medium.mediaType == MediaType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video,
channel);
final fileType = medium.mediaType == MediaType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video;
if (fileType == DefaultAttachmentTypes.image) {
attachment.attachment = attachment.attachment.copyWith(
imageUrl: url,
); );
} else {
attachment.attachment = attachment.attachment.copyWith( setState(() {
assetUrl: url, _attachments.add(attachment);
});
final mediaFile = await medium.getFile();
var file = PlatformFile(
path: mediaFile.path,
size: (await mediaFile.length()) ~/ 1024,
bytes: mediaFile.readAsBytesSync(),
);
if (file.size > _kMaxAttachmentSize) {
if (medium.mediaType == MediaType.video) {
final mediaInfo = await CompressVideoService.compressVideo(file.path);
if (mediaInfo.filesize / (1024 * 1024) > _kMaxAttachmentSize) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.',
),
),
);
setState(() {
_attachments.remove(attachment);
});
return;
}
file = PlatformFile(
name: file.name,
size: mediaInfo.filesize,
bytes: await mediaInfo.file.readAsBytes(),
path: mediaInfo.path,
);
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
'The file is too large to upload. The file size limit is 20MB',
),
),
);
}
}
final channel = StreamChannel.of(context).channel;
setState(() {
attachment
..file = file
..attachment = Attachment(
localUri: file.path != null ? Uri.parse(file.path) : null,
type: medium.mediaType == MediaType.image ? 'image' : 'video',
);
});
final url = await _uploadAttachment(
file,
medium.mediaType == MediaType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video,
channel);
final fileType = medium.mediaType == MediaType.image
? DefaultAttachmentTypes.image
: DefaultAttachmentTypes.video;
if (fileType == DefaultAttachmentTypes.image) {
attachment.attachment = attachment.attachment.copyWith(
imageUrl: url,
);
} else {
attachment.attachment = attachment.attachment.copyWith(
assetUrl: url,
);
}
if (mounted) {
setState(() {
attachment.uploaded = true;
});
}
} catch (e, s) {
print(s);
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Error adding the attachment: $e'),
),
); );
} }
setState(() {
attachment.uploaded = true;
});
} }
CircleAvatar _buildGiphyIcon() { CircleAvatar _buildGiphyIcon() {
@@ -1267,9 +1317,10 @@ class MessageInputState extends State<MessageInput> {
Positioned.fill( Positioned.fill(
child: Container( child: Container(
child: VideoThumbnail( child: VideoThumbnail(
file: File( file: File(
attachment.file.path, attachment.file.path,
)), ),
),
), ),
), ),
Positioned( Positioned(
@@ -1496,7 +1547,6 @@ class MessageInputState extends State<MessageInput> {
); );
if (res?.files?.isNotEmpty == true) { if (res?.files?.isNotEmpty == true) {
file = res.files.single; file = res.files.single;
print('file.bytes?.length: ${file.bytes?.length}');
} }
} }
@@ -1508,6 +1558,26 @@ class MessageInputState extends State<MessageInput> {
return; return;
} }
if (file.size > _kMaxAttachmentSize) {
if (attachmentType == 'video') {
final mediaInfo = await CompressVideoService.compressVideo(file.path);
file = PlatformFile(
name: mediaInfo.title,
size: mediaInfo.filesize,
bytes: await mediaInfo.file.readAsBytes(),
path: mediaInfo.path,
);
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
'The file is too large to upload. The file size limit is 20MB',
),
),
);
}
}
final channel = StreamChannel.of(context).channel; final channel = StreamChannel.of(context).channel;
final attachment = _SendingAttachment( final attachment = _SendingAttachment(
file: file, file: file,
+2
View File
@@ -30,6 +30,7 @@ dependencies:
flutter_keyboard_visibility: ^3.3.0 flutter_keyboard_visibility: ^3.3.0
stream_chat: ^0.2.13+1 stream_chat: ^0.2.13+1
mime: ^0.9.6+3 mime: ^0.9.6+3
video_compress: ^2.1.1
visibility_detector: ^0.1.5 visibility_detector: ^0.1.5
http_parser: ^3.1.4 http_parser: ^3.1.4
lottie: ^0.7.0+1 lottie: ^0.7.0+1
@@ -42,6 +43,7 @@ dependencies:
permission_handler: ^5.0.1+1 permission_handler: ^5.0.1+1
transparent_image: ^1.0.0 transparent_image: ^1.0.0
ezanimation: ^0.4.1 ezanimation: ^0.4.1
synchronized: ^2.2.0+2
flutter: flutter:
assets: assets: