diff --git a/lib/core/user/user_store.dart b/lib/core/user/user_store.dart index 17e4940c..1cc39d97 100644 --- a/lib/core/user/user_store.dart +++ b/lib/core/user/user_store.dart @@ -37,7 +37,7 @@ abstract class _UserStoreBase with Store { final success = await twitchApi.blockUser(userId: targetId, headers: headers); // Add a slight delay between requests, otherwise the blocked list won't properly update. // Weird behavior, might be something to do with PUT request and time to create and update resource? - await Future.delayed(const Duration(milliseconds: 200)); + await Future.delayed(const Duration(milliseconds: 300)); if (success) await refreshBlockedUsers(headers: headers); } diff --git a/lib/screens/channel/chat/details/chat_details.dart b/lib/screens/channel/chat/details/chat_details.dart index a198bf73..80c78b5e 100644 --- a/lib/screens/channel/chat/details/chat_details.dart +++ b/lib/screens/channel/chat/details/chat_details.dart @@ -31,7 +31,7 @@ class ChatDetails extends StatelessWidget { chatDetails: chatDetails, userLogin: userLogin, ), - ) + ), ], ), ), diff --git a/lib/screens/channel/chat/details/chat_users_list.dart b/lib/screens/channel/chat/details/chat_users_list.dart index b7a2f232..857bf298 100644 --- a/lib/screens/channel/chat/details/chat_users_list.dart +++ b/lib/screens/channel/chat/details/chat_users_list.dart @@ -126,8 +126,8 @@ class _ChattersListState extends State { ), ), ...chatDetailStore.filteredUsers.expandIndexed( - (index, type) => [ - if (type.isNotEmpty) ...[ + (index, users) => [ + if (users.isNotEmpty) ...[ SliverPadding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 20.0), sliver: SliverToBoxAdapter( @@ -141,8 +141,8 @@ class _ChattersListState extends State { padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0), sliver: SliverList( delegate: SliverChildBuilderDelegate( - (context, index) => Text(type[index]), - childCount: type.length, + (context, index) => Text(users[index]), + childCount: users.length, ), ), ), diff --git a/lib/screens/home/search/search_results_channels.dart b/lib/screens/home/search/search_results_channels.dart index cda1164f..0059fbc0 100644 --- a/lib/screens/home/search/search_results_channels.dart +++ b/lib/screens/home/search/search_results_channels.dart @@ -4,6 +4,7 @@ import 'package:frosty/constants/constants.dart'; import 'package:frosty/models/channel.dart'; import 'package:frosty/screens/channel/video_chat.dart'; import 'package:frosty/screens/home/stores/search_store.dart'; +import 'package:frosty/widgets/block_report_modal.dart'; import 'package:frosty/widgets/loading_indicator.dart'; import 'package:frosty/widgets/profile_picture.dart'; import 'package:mobx/mobx.dart'; @@ -60,42 +61,56 @@ class SearchResultsChannels extends StatelessWidget { ), ); case FutureStatus.fulfilled: - final List results = future.result; + final results = (future.result as List) + .where((channel) => !searchStore.authStore.user.blockedUsers.map((blockedUser) => blockedUser.userId).contains(channel.id)); return SliverList( delegate: SliverChildListDelegate.fixed([ ...results.map( - (channel) => ListTile( - title: Text( - regexEnglish.hasMatch(channel.displayName) ? channel.displayName : channel.displayName + ' (${channel.broadcasterLogin})'), - leading: ProfilePicture(userLogin: channel.broadcasterLogin), - trailing: channel.isLive - ? ClipRRect( - borderRadius: BorderRadius.circular(5.0), - child: Container( - color: const Color(0xFFF44336), - padding: const EdgeInsets.all(10.0), - child: const Text( - 'LIVE', - style: TextStyle(fontWeight: FontWeight.bold), + (channel) { + final displayName = + regexEnglish.hasMatch(channel.displayName) ? channel.displayName : channel.displayName + ' (${channel.broadcasterLogin})'; + + return ListTile( + title: Text(displayName), + leading: ProfilePicture(userLogin: channel.broadcasterLogin), + trailing: channel.isLive + ? ClipRRect( + borderRadius: BorderRadius.circular(5.0), + child: Container( + color: const Color(0xFFF44336), + padding: const EdgeInsets.all(10.0), + child: const Text( + 'LIVE', + style: TextStyle(fontWeight: FontWeight.bold), + ), ), - ), - ) - : null, - subtitle: channel.isLive - ? Text('Uptime: ${DateTime.now().difference(DateTime.parse(channel.startedAt)).toString().split('.')[0]}') - : null, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => VideoChat( - userId: channel.id, - userName: channel.displayName, - userLogin: channel.broadcasterLogin, + ) + : null, + subtitle: channel.isLive + ? Text('Uptime: ${DateTime.now().difference(DateTime.parse(channel.startedAt)).toString().split('.')[0]}') + : null, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => VideoChat( + userId: channel.id, + userName: channel.displayName, + userLogin: channel.broadcasterLogin, + ), ), ), - ), - ), + onLongPress: () => showModalBottomSheet( + context: context, + builder: (context) => BlockReportModal( + authStore: searchStore.authStore, + name: displayName, + userLogin: channel.broadcasterLogin, + userId: channel.id, + ), + ), + ); + }, ), ListTile( title: Text('Go to channel "$query"'), diff --git a/lib/screens/home/widgets/stream_card.dart b/lib/screens/home/widgets/stream_card.dart index eb7a3578..69e93b8b 100644 --- a/lib/screens/home/widgets/stream_card.dart +++ b/lib/screens/home/widgets/stream_card.dart @@ -4,10 +4,9 @@ import 'package:frosty/constants/constants.dart'; import 'package:frosty/models/stream.dart'; import 'package:frosty/screens/channel/video_chat.dart'; import 'package:frosty/screens/home/stores/list_store.dart'; -import 'package:frosty/widgets/block_button.dart'; +import 'package:frosty/widgets/block_report_modal.dart'; import 'package:frosty/widgets/loading_indicator.dart'; import 'package:frosty/widgets/profile_picture.dart'; -import 'package:frosty/widgets/report_button.dart'; import 'package:intl/intl.dart'; /// A tappable card widget that displays a stream's thumbnail and details. @@ -56,28 +55,11 @@ class StreamCard extends StatelessWidget { ), onLongPress: () => showModalBottomSheet( context: context, - builder: (context) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - width: double.infinity, - child: BlockButton( - authStore: listStore.authStore, - targetUser: streamerName, - targetUserId: streamInfo.userId, - simple: false, - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - width: double.infinity, - child: ReportButton( - userLogin: streamInfo.userLogin, - name: streamerName, - ), - ), - ], + builder: (context) => BlockReportModal( + authStore: listStore.authStore, + name: streamerName, + userLogin: streamInfo.userLogin, + userId: streamInfo.userId, ), ), child: Padding( diff --git a/lib/widgets/block_button.dart b/lib/widgets/block_button.dart index bdada9cb..f4b988f0 100644 --- a/lib/widgets/block_button.dart +++ b/lib/widgets/block_button.dart @@ -27,7 +27,7 @@ class BlockButton extends StatelessWidget { builder: (context) => AlertDialog( title: isBlocked ? const Text('Unblock') : const Text('Block'), content: Text( - 'Are you sure you want to ${isBlocked ? 'unblock $targetUser?' : 'block $targetUser? This will hide them from channel lists and chat messages.'}'), + 'Are you sure you want to ${isBlocked ? 'unblock $targetUser?' : 'block $targetUser? This will remove them from channel lists, search results, and chat messages.'}'), actions: [ TextButton( onPressed: Navigator.of(context).pop, diff --git a/lib/widgets/block_report_modal.dart b/lib/widgets/block_report_modal.dart new file mode 100644 index 00000000..8d88cf7c --- /dev/null +++ b/lib/widgets/block_report_modal.dart @@ -0,0 +1,46 @@ +import 'package:flutter/widgets.dart'; +import 'package:frosty/core/auth/auth_store.dart'; +import 'package:frosty/widgets/block_button.dart'; +import 'package:frosty/widgets/report_button.dart'; + +class BlockReportModal extends StatelessWidget { + final AuthStore authStore; + final String name; + final String userLogin; + final String userId; + + const BlockReportModal({ + Key? key, + required this.authStore, + required this.name, + required this.userLogin, + required this.userId, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + width: double.infinity, + child: BlockButton( + authStore: authStore, + targetUser: name, + targetUserId: userId, + simple: false, + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + width: double.infinity, + child: ReportButton( + userLogin: userLogin, + name: name, + ), + ), + ], + ); + } +}