Merge pull request #197 from GetStream/feat/mention-screen

Add mention screen
This commit is contained in:
Salvatore Giordano
2021-01-08 09:58:03 +01:00
committed by GitHub
3 changed files with 204 additions and 45 deletions
+101 -2
View File
@@ -286,8 +286,75 @@ class _HomePageState extends State<HomePage> {
class UserMentionPage extends StatelessWidget { class UserMentionPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( final user = StreamChat.of(context).user;
child: Text('On Pause Right Now!'), return MessageSearchBloc(
child: MessageSearchListView(
filters: {
'members': {
r'$in': [user.id],
},
},
messageFilters: {
'mentioned_users.id': {
r'$contains': user.id,
},
},
sortOptions: [
SortOption(
'created_at',
direction: SortOption.ASC,
),
],
paginationParams: PaginationParams(limit: 20),
showResultCount: false,
emptyBuilder: (_, __) {
return LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(24),
child: StreamSvgIcon.mentions(
size: 96,
color: Colors.grey,
),
),
Text('No mentions exist yet...'),
],
),
),
),
);
},
);
},
onItemTap: (messageResponse) async {
final client = StreamChat.of(context).client;
final message = messageResponse.message;
final channel = client.channel(
messageResponse.channel.type,
id: messageResponse.channel.id,
);
if (channel.state == null) {
await channel.watch();
}
Navigator.pushNamed(
context,
Routes.CHANNEL_PAGE,
arguments: ChannelPageArgs(
channel: channel,
initialMessage: message,
),
);
},
),
); );
} }
} }
@@ -371,7 +438,39 @@ class _ChannelListPageState extends State<ChannelListPage> {
direction: SortOption.ASC, direction: SortOption.ASC,
), ),
], ],
pullToRefresh: false,
paginationParams: PaginationParams(limit: 20), paginationParams: PaginationParams(limit: 20),
emptyBuilder: (_, query) {
return LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight:
viewportConstraints.maxHeight,
),
child: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(24),
child: StreamSvgIcon.search(
size: 96,
color: Colors.grey,
),
),
Text(
'No results for \"$query\"...',
),
],
),
),
),
);
},
);
},
onItemTap: (messageResponse) async { onItemTap: (messageResponse) async {
final client = StreamChat.of(context).client; final client = StreamChat.of(context).client;
final message = messageResponse.message; final message = messageResponse.message;
+49 -12
View File
@@ -52,7 +52,7 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
Stream<bool> get queryMessagesLoading => Stream<bool> get queryMessagesLoading =>
_queryMessagesLoadingController.stream; _queryMessagesLoadingController.stream;
/// Calls [Client.search] updating [queryMessagesLoading] stream /// Calls [Client.search] updating [messageResponses] stream
Future<void> search({ Future<void> search({
Map<String, dynamic> filter, Map<String, dynamic> filter,
Map<String, dynamic> messageFilter, Map<String, dynamic> messageFilter,
@@ -60,10 +60,30 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
String query, String query,
PaginationParams pagination, PaginationParams pagination,
}) async { }) 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 || /// Calls [Client.search] updating [queryMessagesLoading] stream
_queryMessagesLoadingController.value == true) { Future<void> loadMore({
Map<String, dynamic> filter,
Map<String, dynamic> messageFilter,
List<SortOption> sort,
String query,
PaginationParams pagination,
}) async {
if (_queryMessagesLoadingController.value == true) {
return; return;
} }
_queryMessagesLoadingController.add(true); _queryMessagesLoadingController.add(true);
@@ -74,18 +94,18 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []); final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
final messageResponse = await client.search( final messages = await _search(
filter, filter: filter,
sort, messageFilter: messageFilter,
query, sort: sort,
pagination, query: query,
messageFilters: messageFilter, pagination: pagination,
); );
if (clear) { if (clear) {
_messageResponses.add(messageResponse.results); _messageResponses.add(messages.results);
} else { } else {
final temp = oldMessages + messageResponse.results; final temp = oldMessages + messages.results;
_messageResponses.add(temp); _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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
+54 -31
View File
@@ -51,16 +51,18 @@ class MessageSearchListView extends StatefulWidget {
/// Instantiate a new MessageSearchListView /// Instantiate a new MessageSearchListView
const MessageSearchListView({ const MessageSearchListView({
Key key, Key key,
@required this.messageQuery, this.messageQuery,
@required this.filters, this.filters,
this.sortOptions, this.sortOptions,
this.paginationParams, this.paginationParams,
this.messageFilters,
this.emptyBuilder, this.emptyBuilder,
this.errorBuilder, this.errorBuilder,
this.separatorBuilder, this.separatorBuilder,
this.itemBuilder, this.itemBuilder,
this.onItemTap, this.onItemTap,
this.showResultCount = true, this.showResultCount = true,
this.pullToRefresh = true,
}) : super(key: key); }) : super(key: key);
/// Message String to search on /// Message String to search on
@@ -83,6 +85,11 @@ class MessageSearchListView extends StatefulWidget {
/// message_limit: how many messages should be included to each channel /// message_limit: how many messages should be included to each channel
final PaginationParams paginationParams; 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 /// Builder used to create a custom item preview
final MessageSearchItemBuilder itemBuilder; final MessageSearchItemBuilder itemBuilder;
@@ -101,6 +108,9 @@ class MessageSearchListView extends StatefulWidget {
/// Set it to false to hide total results text /// Set it to false to hide total results text
final bool showResultCount; final bool showResultCount;
/// Set it to false to disable the pull-to-refresh widget
final bool pullToRefresh;
@override @override
_MessageSearchListViewState createState() => _MessageSearchListViewState(); _MessageSearchListViewState createState() => _MessageSearchListViewState();
} }
@@ -115,6 +125,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
sort: widget.sortOptions, sort: widget.sortOptions,
query: widget.messageQuery, query: widget.messageQuery,
pagination: widget.paginationParams, pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
); );
} }
@@ -127,9 +138,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
Widget _separatorBuilder(BuildContext context, int index) { Widget _separatorBuilder(BuildContext context, int index) {
return Container( return Container(
height: 1, height: 1,
color: Theme.of(context).brightness == Brightness.dark color: StreamChatTheme.of(context).colorTheme.greyWhisper,
? StreamChatTheme.of(context).colorTheme.white.withOpacity(0.1)
: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.1),
); );
} }
@@ -205,9 +214,7 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
children: [ children: [
WidgetSpan( WidgetSpan(
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(right: 2.0),
right: 2.0,
),
child: Icon(Icons.error_outline), child: Icon(Icons.error_outline),
), ),
), ),
@@ -217,18 +224,17 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
style: Theme.of(context).textTheme.headline6, style: Theme.of(context).textTheme.headline6,
), ),
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(top: 16.0),
top: 16.0,
),
child: Text(message), child: Text(message),
), ),
FlatButton( RaisedButton(
onPressed: () { onPressed: () {
messageSearchBloc.search( messageSearchBloc.search(
filter: widget.filters, filter: widget.filters,
sort: widget.sortOptions, sort: widget.sortOptions,
query: widget.messageQuery, query: widget.messageQuery,
pagination: widget.paginationParams, pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
); );
}, },
child: Text('Retry'), 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( child = LazyLoadScrollView(
onEndOfPage: () => messageSearchBloc.search( onEndOfPage: () => messageSearchBloc.loadMore(
filter: widget.filters, filter: widget.filters,
sort: widget.sortOptions, sort: widget.sortOptions,
pagination: widget.paginationParams.copyWith( pagination: widget.paginationParams.copyWith(
offset: messageSearchBloc.messageResponses?.length ?? 0, offset: messageSearchBloc.messageResponses?.length ?? 0,
), ),
query: widget.messageQuery, query: widget.messageQuery,
messageFilter: widget.messageFilters,
), ),
child: ListView.separated( child: child,
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.showResultCount) { if (widget.showResultCount) {
@@ -344,13 +364,16 @@ class _MessageSearchListViewState extends State<MessageSearchListView> {
jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) ||
widget.paginationParams?.toJson()?.toString() != widget.paginationParams?.toJson()?.toString() !=
oldWidget.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); final messageSearchBloc = MessageSearchBloc.of(context);
messageSearchBloc.search( messageSearchBloc.search(
filter: widget.filters, filter: widget.filters,
sort: widget.sortOptions, sort: widget.sortOptions,
query: widget.messageQuery, query: widget.messageQuery,
pagination: widget.paginationParams, pagination: widget.paginationParams,
messageFilter: widget.messageFilters,
); );
} }
} }