Merge branch 'feature/new-ui' into feature/mono-repo

This commit is contained in:
Salvatore Giordano
2021-01-08 15:44:55 +01:00
12 changed files with 440 additions and 101 deletions
@@ -22,9 +22,9 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/user_avatar.dart';
import 'package:substring_highlight/substring_highlight.dart';
import 'package:video_compress/video_compress.dart';
import 'extension.dart';
import '../stream_chat_flutter.dart';
import 'extension.dart';
import 'quoted_message_widget.dart';
import 'stream_channel.dart';
@@ -477,9 +477,7 @@ class MessageInputState extends State<MessageInput> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
StreamSvgIcon.lightning(
color: StreamChatTheme.of(context)
.colorTheme
.white,
color: Colors.white,
size: 16.0,
),
Text(
@@ -488,9 +486,8 @@ class MessageInputState extends State<MessageInput> {
.textTheme
.footnote
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.white),
color: Colors.white,
),
),
],
),
@@ -673,7 +670,7 @@ class MessageInputState extends State<MessageInput> {
if (commands.isNotEmpty)
Padding(
padding:
const EdgeInsets.only(left: 8.0, top: 8.0),
const EdgeInsets.only(left: 0.0, top: 8.0),
child: Row(
children: [
Padding(
@@ -698,31 +695,51 @@ class MessageInputState extends State<MessageInput> {
],
),
),
SizedBox(
height: 10.0,
),
...commands
.map(
(c) => ListTile(
leading: c.name == 'giphy'
? _buildGiphyIcon()
: null,
title: Text.rich(
TextSpan(
text: '${c.name.capitalize()}',
style: TextStyle(
fontWeight: FontWeight.bold),
(c) => InkWell(
onTap: () {
_setCommand(c);
},
child: Container(
height: 40.0,
child: Row(
children: [
TextSpan(
text: ' /${c.name} ${c.args}',
style: TextStyle(
fontWeight: FontWeight.w300,
SizedBox(
width: 16.0,
),
_buildCommandIcon(c.name),
SizedBox(
width: 8.0,
),
Text.rich(
TextSpan(
text: '${c.name.capitalize()}',
style: TextStyle(
fontWeight: FontWeight.bold),
children: [
TextSpan(
text: ' /${c.name} ${c.args}',
style:
StreamChatTheme.of(context)
.textTheme
.body
.copyWith(
color: StreamChatTheme
.of(context)
.colorTheme
.grey,
),
),
],
),
),
],
),
),
//subtitle: Text(c.description),
onTap: () {
_setCommand(c);
},
),
)
.toList(),
@@ -957,9 +974,22 @@ class MessageInputState extends State<MessageInput> {
'svgs/icon_picture_empty_state.svg',
package: 'stream_chat_flutter',
height: 140,
color:
StreamChatTheme.of(context).colorTheme.accentBlue,
color: StreamChatTheme.of(context)
.colorTheme
.greyGainsboro,
),
Text(
'Please enable access to your photos \nand videos so you can share them with friends.',
style: StreamChatTheme.of(context)
.textTheme
.body
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
.grey),
textAlign: TextAlign.center,
),
SizedBox(height: 6.0),
Center(
child: Text(
'Allow access to your gallery',
@@ -1083,28 +1113,87 @@ class MessageInputState extends State<MessageInput> {
}
}
CircleAvatar _buildGiphyIcon() {
if (kIsWeb) {
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.black,
child: Image.asset(
'images/giphy_icon.png',
package: 'stream_chat_flutter',
width: 24.0,
height: 24.0,
),
radius: 12,
);
} else {
return CircleAvatar(
child: SvgPicture.asset(
'svgs/giphy_icon.svg',
package: 'stream_chat_flutter',
width: 24.0,
height: 24.0,
),
radius: 12,
);
Widget _buildCommandIcon(String iconType) {
switch (iconType) {
case 'giphy':
return CircleAvatar(
child: StreamSvgIcon.giphyIcon(
size: 24.0,
),
radius: 12,
);
break;
case 'ban':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.Icon_user_delete(
size: 16.0,
color: Colors.white,
),
radius: 12,
);
break;
case 'flag':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.flag(
size: 14.0,
color: Colors.white,
),
radius: 12,
);
break;
case 'imgur':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: ClipOval(
child: StreamSvgIcon.imgur(
size: 24.0,
),
),
radius: 12,
);
break;
case 'mute':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.mute(
size: 16.0,
color: Colors.white,
),
radius: 12,
);
break;
case 'unban':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.userAdd(
size: 16.0,
color: Colors.white,
),
radius: 12,
);
break;
case 'unmute':
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.volumeUp(
size: 16.0,
color: Colors.white,
),
radius: 12,
);
break;
default:
return CircleAvatar(
backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue,
child: StreamSvgIcon.lightning(
size: 16.0,
color: Colors.white,
),
radius: 12,
);
break;
}
}
@@ -52,7 +52,7 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
Stream<bool> get queryMessagesLoading =>
_queryMessagesLoadingController.stream;
/// Calls [Client.search] updating [queryMessagesLoading] stream
/// Calls [Client.search] updating [messageResponses] stream
Future<void> search({
Map<String, dynamic> filter,
Map<String, dynamic> messageFilter,
@@ -60,10 +60,30 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
String query,
PaginationParams pagination,
}) async {
final client = StreamChat.of(context).client;
_messageResponses.add(null);
try {
final messages = await _search(
filter: filter,
messageFilter: messageFilter,
sort: sort,
query: query,
pagination: pagination,
);
_messageResponses.add(messages.results);
} catch (err, stk) {
_messageResponses.addError(err, stk);
}
}
if (client.state?.user == null ||
_queryMessagesLoadingController.value == true) {
/// Calls [Client.search] updating [queryMessagesLoading] stream
Future<void> loadMore({
Map<String, dynamic> filter,
Map<String, dynamic> messageFilter,
List<SortOption> sort,
String query,
PaginationParams pagination,
}) async {
if (_queryMessagesLoadingController.value == true) {
return;
}
_queryMessagesLoadingController.add(true);
@@ -74,18 +94,18 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
final messageResponse = await client.search(
filter,
sort,
query,
pagination,
messageFilters: messageFilter,
final messages = await _search(
filter: filter,
messageFilter: messageFilter,
sort: sort,
query: query,
pagination: pagination,
);
if (clear) {
_messageResponses.add(messageResponse.results);
_messageResponses.add(messages.results);
} else {
final temp = oldMessages + messageResponse.results;
final temp = oldMessages + messages.results;
_messageResponses.add(temp);
}
@@ -95,6 +115,23 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
}
}
Future<SearchMessagesResponse> _search({
Map<String, dynamic> filter,
Map<String, dynamic> messageFilter,
List<SortOption> sort,
String query,
PaginationParams pagination,
}) {
final client = StreamChat.of(context).client;
return client.search(
filter,
sort,
query,
pagination,
messageFilters: messageFilter,
);
}
@override
Widget build(BuildContext context) {
super.build(context);
@@ -51,16 +51,18 @@ class MessageSearchListView extends StatefulWidget {
/// Instantiate a new MessageSearchListView
const MessageSearchListView({
Key key,
@required this.messageQuery,
@required this.filters,
this.messageQuery,
this.filters,
this.sortOptions,
this.paginationParams,
this.messageFilters,
this.emptyBuilder,
this.errorBuilder,
this.separatorBuilder,
this.itemBuilder,
this.onItemTap,
this.showResultCount = true,
this.pullToRefresh = true,
}) : super(key: key);
/// Message String to search on
@@ -83,6 +85,11 @@ class MessageSearchListView extends StatefulWidget {
/// message_limit: how many messages should be included to each channel
final PaginationParams paginationParams;
/// The message query filters to use.
/// You can query on any of the custom fields you've defined on the [Channel].
/// You can also filter other built-in channel fields.
final Map<String, dynamic> messageFilters;
/// Builder used to create a custom item preview
final MessageSearchItemBuilder itemBuilder;
@@ -101,6 +108,9 @@ class MessageSearchListView extends StatefulWidget {
/// Set it to false to hide total results text
final bool showResultCount;
/// Set it to false to disable the pull-to-refresh widget
final bool pullToRefresh;
@override
_MessageSearchListViewState createState() => _MessageSearchListViewState();
}
@@ -115,6 +125,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
sort: widget.sortOptions,
query: widget.messageQuery,
pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
);
}
@@ -127,9 +138,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
Widget _separatorBuilder(BuildContext context, int index) {
return Container(
height: 1,
color: Theme.of(context).brightness == Brightness.dark
? StreamChatTheme.of(context).colorTheme.white.withOpacity(0.1)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.1),
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
);
}
@@ -205,9 +214,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
children: [
WidgetSpan(
child: Padding(
padding: const EdgeInsets.only(
right: 2.0,
),
padding: const EdgeInsets.only(right: 2.0),
child: Icon(Icons.error_outline),
),
),
@@ -217,18 +224,17 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
style: Theme.of(context).textTheme.headline6,
),
Padding(
padding: const EdgeInsets.only(
top: 16.0,
),
padding: const EdgeInsets.only(top: 16.0),
child: Text(message),
),
FlatButton(
RaisedButton(
onPressed: () {
messageSearchBloc.search(
filter: widget.filters,
sort: widget.sortOptions,
query: widget.messageQuery,
pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
);
},
child: Text('Retry'),
@@ -279,32 +285,46 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
);
}
Widget child;
Widget child = ListView.separated(
physics: AlwaysScrollableScrollPhysics(),
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
separatorBuilder: (_, index) {
if (widget.separatorBuilder != null) {
return widget.separatorBuilder(context, index);
}
return _separatorBuilder(context, index);
},
itemBuilder: (context, index) {
if (index < items.length) {
return _listItemBuilder(context, items[index]);
}
return _buildQueryProgressIndicator(context, messageSearchBloc);
},
);
if (widget.pullToRefresh) {
child = RefreshIndicator(
onRefresh: () async => messageSearchBloc.search(
filter: widget.filters,
sort: widget.sortOptions,
query: widget.messageQuery,
pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
),
child: child,
);
}
child = LazyLoadScrollView(
onEndOfPage: () => messageSearchBloc.search(
onEndOfPage: () => messageSearchBloc.loadMore(
filter: widget.filters,
sort: widget.sortOptions,
pagination: widget.paginationParams.copyWith(
offset: messageSearchBloc.messageResponses?.length ?? 0,
),
query: widget.messageQuery,
messageFilter: widget.messageFilters,
),
child: ListView.separated(
physics: AlwaysScrollableScrollPhysics(),
itemCount: items.isNotEmpty ? items.length + 1 : items.length,
separatorBuilder: (_, index) {
if (widget.separatorBuilder != null) {
return widget.separatorBuilder(context, index);
}
return _separatorBuilder(context, index);
},
itemBuilder: (context, index) {
if (index < items.length) {
return _listItemBuilder(context, items[index]);
}
return _buildQueryProgressIndicator(context, messageSearchBloc);
},
),
child: child,
);
if (widget.showResultCount) {
@@ -344,13 +364,16 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) ||
widget.paginationParams?.toJson()?.toString() !=
oldWidget.paginationParams?.toJson()?.toString() ||
widget.messageQuery?.toString() != oldWidget.messageQuery?.toString()) {
widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() ||
widget.messageFilters?.toString() !=
oldWidget.messageFilters?.toString()) {
final messageSearchBloc = MessageSearchBloc.of(context);
messageSearchBloc.search(
filter: widget.filters,
sort: widget.sortOptions,
query: widget.messageQuery,
pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
);
}
}
@@ -829,4 +829,52 @@ class StreamSvgIcon extends StatelessWidget {
height: size,
);
}
factory StreamSvgIcon.giphyIcon({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'giphy_icon.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.imgur({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'imgur.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.volumeUp({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'volume-up.svg',
color: color,
width: size,
height: size,
);
}
factory StreamSvgIcon.flag({
double size,
Color color,
}) {
return StreamSvgIcon(
assetName: 'flag.svg',
color: color,
width: size,
height: size,
);
}
}
@@ -54,7 +54,7 @@ class TypingIndicator extends StatelessWidget {
height: 4,
),
Text(
' ${snapshot.data.map((u) => u.name).join(',')} ${snapshot.data.length == 1 ? 'is' : 'are'} typing',
' ${snapshot.data[0].name}${snapshot.data.length == 1 ? '' : ' and ${snapshot.data.length - 1} more'} ${snapshot.data.length == 1 ? 'is' : 'are'} typing',
maxLines: 1,
style: style,
),
@@ -34,10 +34,10 @@ class UrlAttachment extends StatelessWidget {
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Stack(
children: [
Center(
child: CachedNetworkImage(
imageUrl: urlAttachment.imageUrl,
),
CachedNetworkImage(
width: double.infinity,
imageUrl: urlAttachment.imageUrl,
fit: BoxFit.cover,
),
Positioned(
left: 0.0,