Refactor user list state management

This commit is contained in:
Tommy Chow 2022-01-06 10:42:35 -05:00
parent 2562375778
commit c338b304dd
3 changed files with 165 additions and 107 deletions

View file

@ -16,8 +16,23 @@ abstract class _ChatDetailsStoreBase with Store {
@readonly
ChatUsers? _chatUsers;
@observable
var showJumpButton = false;
@observable
var filterText = '';
@computed
Iterable<List<String>> get filteredUsers => [
_chatUsers!.chatters.broadcaster,
_chatUsers!.chatters.staff,
_chatUsers!.chatters.admins,
_chatUsers!.chatters.globalMods,
_chatUsers!.chatters.moderators,
_chatUsers!.chatters.vips,
_chatUsers!.chatters.viewers,
].map((e) => e.where((user) => user.contains(filterText)).toList());
@action
Future<void> updateChatters(String userLogin) async {
_chatUsers = await Twitch.getChatters(userLogin: userLogin);
}
Future<void> updateChatters(String userLogin) async => _chatUsers = await Twitch.getChatters(userLogin: userLogin);
}

View file

@ -9,6 +9,14 @@ part of 'chat_details_store.dart';
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
Computed<Iterable<List<String>>>? _$filteredUsersComputed;
@override
Iterable<List<String>> get filteredUsers => (_$filteredUsersComputed ??=
Computed<Iterable<List<String>>>(() => super.filteredUsers,
name: '_ChatDetailsStoreBase.filteredUsers'))
.value;
final _$roomStateAtom = Atom(name: '_ChatDetailsStoreBase.roomState');
@override
@ -41,6 +49,37 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
});
}
final _$showJumpButtonAtom =
Atom(name: '_ChatDetailsStoreBase.showJumpButton');
@override
bool get showJumpButton {
_$showJumpButtonAtom.reportRead();
return super.showJumpButton;
}
@override
set showJumpButton(bool value) {
_$showJumpButtonAtom.reportWrite(value, super.showJumpButton, () {
super.showJumpButton = value;
});
}
final _$filterTextAtom = Atom(name: '_ChatDetailsStoreBase.filterText');
@override
String get filterText {
_$filterTextAtom.reportRead();
return super.filterText;
}
@override
set filterText(String value) {
_$filterTextAtom.reportWrite(value, super.filterText, () {
super.filterText = value;
});
}
final _$updateChattersAsyncAction =
AsyncAction('_ChatDetailsStoreBase.updateChatters');
@ -53,7 +92,10 @@ mixin _$ChatDetailsStore on _ChatDetailsStoreBase, Store {
@override
String toString() {
return '''
roomState: ${roomState}
roomState: ${roomState},
showJumpButton: ${showJumpButton},
filterText: ${filterText},
filteredUsers: ${filteredUsers}
''';
}
}

View file

@ -1,5 +1,6 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/screens/channel/chat/details/chat_details_store.dart';
import 'package:frosty/widgets/scroll_to_top_button.dart';
import 'package:intl/intl.dart';
@ -22,132 +23,132 @@ class _ChattersListState extends State<ChattersList> {
final _scrollController = ScrollController();
final _textController = TextEditingController();
var _showJumpButton = false;
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels > 0.0 && _showJumpButton == false) setState(() => _showJumpButton = true);
if (_scrollController.position.pixels <= 0.0 && _showJumpButton == true) setState(() => _showJumpButton = false);
if (_scrollController.position.atEdge || _scrollController.position.outOfRange) {
widget.chatDetails.showJumpButton = false;
} else {
widget.chatDetails.showJumpButton = true;
}
});
}
@override
Widget build(BuildContext context) {
final chatters = widget.chatDetails.chatUsers?.chatters;
if (chatters != null) {
const textStyle = TextStyle(fontWeight: FontWeight.bold);
const textStyle = TextStyle(fontWeight: FontWeight.bold);
const headers = [
'Broadcaster',
'Staff',
'Admins',
'Global Moderators',
'Moderators',
'VIPs',
'Viewers',
];
const headers = [
'Broadcaster',
'Staff',
'Admins',
'Global Moderators',
'Moderators',
'VIPs',
'Viewers',
];
final userTypes = [
chatters.broadcaster,
chatters.staff,
chatters.admins,
chatters.globalMods,
chatters.moderators,
chatters.vips,
chatters.viewers,
].map((e) => e.where((user) => user.contains(_textController.text)).toList());
final chatDetailStore = widget.chatDetails;
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
controller: _textController,
autocorrect: false,
onChanged: (value) => setState(() {}),
decoration: InputDecoration(
isDense: true,
hintText: 'Filter',
contentPadding: const EdgeInsets.all(10.0),
suffixIcon: IconButton(
tooltip: 'Clear Filter',
onPressed: () => setState(() {
_textController.clear();
FocusManager.instance.primaryFocus?.unfocus();
}),
icon: const Icon(Icons.clear),
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
controller: _textController,
autocorrect: false,
onChanged: (text) => chatDetailStore.filterText = text,
decoration: InputDecoration(
isDense: true,
hintText: 'Filter',
contentPadding: const EdgeInsets.all(10.0),
suffixIcon: IconButton(
tooltip: 'Clear Filter',
onPressed: () {
chatDetailStore.filterText = '';
_textController.clear();
FocusManager.instance.primaryFocus?.unfocus();
},
icon: const Icon(Icons.clear),
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
),
Expanded(
child: RefreshIndicator(
onRefresh: () async {
await widget.chatDetails.updateChatters(widget.userLogin);
setState(() {});
},
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
CustomScrollView(
controller: _scrollController,
slivers: [
SliverPadding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Chatters', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
const SizedBox(height: 5.0),
Text('${NumberFormat().format(widget.chatDetails.chatUsers?.chatterCount)} in chat'),
],
),
Expanded(
child: RefreshIndicator(
onRefresh: () => chatDetailStore.updateChatters(widget.userLogin),
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
Observer(
builder: (context) {
if (chatDetailStore.chatUsers == null) {
return const Center(child: Text('Failed to get chatters :('));
}
return CustomScrollView(
controller: _scrollController,
slivers: [
SliverPadding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Chatters', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
const SizedBox(height: 5.0),
Text('${NumberFormat().format(chatDetailStore.chatUsers?.chatterCount)} in chat'),
],
),
),
),
),
...userTypes.expandIndexed(
(index, type) => [
if (type.isNotEmpty) ...[
SliverPadding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 20.0),
sliver: SliverToBoxAdapter(
child: Text(
headers[index],
style: textStyle,
...chatDetailStore.filteredUsers.expandIndexed(
(index, type) => [
if (type.isNotEmpty) ...[
SliverPadding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 20.0),
sliver: SliverToBoxAdapter(
child: Text(
headers[index],
style: textStyle,
),
),
),
),
SliverPadding(
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,
SliverPadding(
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,
),
),
),
),
]
],
)
],
),
if (_showJumpButton) ScrollToTopButton(scrollController: _scrollController)
],
),
]
],
)
],
);
},
),
Observer(builder: (context) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: chatDetailStore.showJumpButton ? ScrollToTopButton(scrollController: _scrollController) : const SizedBox(),
);
}),
],
),
),
],
),
);
}
return const Center(child: Text('Failed to get chatters :('));
),
],
),
);
}
@override