The emote menu will now have disabled buttons for whenever the emote category is empty. Sub emotes for the channel will also be disabled if the user is not subbed. Closes #47, closes #56, closes #57, closes #58, closes #59
This commit is contained in:
parent
60cdf87219
commit
f288f0d566
5 changed files with 117 additions and 95 deletions
|
|
@ -19,51 +19,50 @@ class Chat extends StatefulWidget {
|
|||
class _ChatState extends State<Chat> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final chatStore = widget.chatStore;
|
||||
return FutureBuilder(
|
||||
future: widget.chatStore.getAssets(),
|
||||
future: chatStore.getAssets(),
|
||||
builder: (_, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return GestureDetector(
|
||||
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
child: Observer(
|
||||
builder: (context) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.bottomCenter,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return ListView.builder(
|
||||
addAutomaticKeepAlives: false,
|
||||
addRepaintBoundaries: false,
|
||||
itemCount: widget.chatStore.messages.length,
|
||||
controller: widget.chatStore.scrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return widget.chatStore.renderChatMessage(widget.chatStore.messages[index], context);
|
||||
},
|
||||
);
|
||||
builder: (context) => Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.bottomCenter,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => ListView.builder(
|
||||
addAutomaticKeepAlives: false,
|
||||
addRepaintBoundaries: false,
|
||||
itemCount: chatStore.messages.length,
|
||||
controller: chatStore.scrollController,
|
||||
itemBuilder: (context, index) {
|
||||
return chatStore.renderChatMessage(chatStore.messages[index], context);
|
||||
},
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return Visibility(
|
||||
visible: !widget.chatStore.autoScroll,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: widget.chatStore.resumeScroll,
|
||||
child: const Text('Resume Scroll'),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return Visibility(
|
||||
visible: !chatStore.autoScroll,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: chatStore.resumeScroll,
|
||||
child: const Text('Resume Scroll'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (context.read<AuthStore>().isLoggedIn)
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
|
|
@ -73,69 +72,66 @@ class _ChatState extends State<Chat> {
|
|||
context: context,
|
||||
builder: (context) {
|
||||
return ChatStats(
|
||||
chatStore: widget.chatStore,
|
||||
chatStore: chatStore,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
if (context.read<AuthStore>().isLoggedIn) ...[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
|
||||
child: TextField(
|
||||
minLines: 1,
|
||||
maxLines: 5,
|
||||
onTap: () => widget.chatStore.showEmoteMenu = false,
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.emoji_emotions_outlined),
|
||||
onPressed: () {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
widget.chatStore.showEmoteMenu = !widget.chatStore.showEmoteMenu;
|
||||
},
|
||||
),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.all(10.0),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
hintText: 'Send a message',
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
|
||||
child: TextField(
|
||||
minLines: 1,
|
||||
maxLines: 5,
|
||||
onTap: () => chatStore.showEmoteMenu = false,
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.emoji_emotions_outlined),
|
||||
onPressed: () {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
chatStore.showEmoteMenu = !chatStore.showEmoteMenu;
|
||||
},
|
||||
),
|
||||
controller: widget.chatStore.textController,
|
||||
onSubmitted: (string) => widget.chatStore.sendMessage(string),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.all(10.0),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
hintText: 'Send a message',
|
||||
),
|
||||
controller: chatStore.textController,
|
||||
onSubmitted: chatStore.sendMessage,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.send),
|
||||
onPressed: () => widget.chatStore.sendMessage(widget.chatStore.textController.text),
|
||||
)
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.send),
|
||||
onPressed: () => chatStore.sendMessage(chatStore.textController.text),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (widget.chatStore.showEmoteMenu) ...[
|
||||
IndexedStack(
|
||||
index: widget.chatStore.emoteMenuIndex,
|
||||
children: EmoteType.values.map((type) => EmoteMenu(chatStore: widget.chatStore, emoteType: type)).toList(),
|
||||
),
|
||||
SingleChildScrollView(
|
||||
if (chatStore.showEmoteMenu) ...[
|
||||
Observer(builder: (_) => EmoteMenu(chatStore: chatStore, emoteType: EmoteType.values[chatStore.emoteMenuIndex])),
|
||||
Observer(
|
||||
builder: (_) => SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: EmoteType.values
|
||||
.asMap()
|
||||
.entries
|
||||
.map((e) => TextButton(
|
||||
style: e.key == widget.chatStore.emoteMenuIndex ? null : TextButton.styleFrom(primary: Colors.grey),
|
||||
onPressed: () => widget.chatStore.emoteMenuIndex = e.key,
|
||||
child: Text(widget.chatStore.emoteMenuTitle(e.value)),
|
||||
))
|
||||
.toList()),
|
||||
)
|
||||
]
|
||||
],
|
||||
);
|
||||
},
|
||||
children: EmoteType.values.asMap().entries.map((e) {
|
||||
final emotes = chatStore.emoteToObject.values.toList().where((emote) => emote.type == EmoteType.values[e.key]).toList();
|
||||
|
||||
return TextButton(
|
||||
style: e.key == chatStore.emoteMenuIndex ? null : TextButton.styleFrom(primary: Colors.grey),
|
||||
onPressed: emotes.isEmpty ? null : () => chatStore.emoteMenuIndex = e.key,
|
||||
child: Text(chatStore.emoteMenuTitle(e.value)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,9 +47,6 @@ abstract class _ChatStoreBase with Store {
|
|||
/// The provided setting store to account for any user-defined behaviors.
|
||||
final SettingsStore settings;
|
||||
|
||||
/// The logged-in user's appearance in chat.
|
||||
var _userState = const USERSTATE();
|
||||
|
||||
/// Requested message to be sent by the user. Will only be sent on receival of a USERNOTICE command.
|
||||
IRCMessage? toSend;
|
||||
|
||||
|
|
@ -65,6 +62,10 @@ abstract class _ChatStoreBase with Store {
|
|||
@readonly
|
||||
var _autoScroll = true;
|
||||
|
||||
/// The logged-in user's appearance in chat.
|
||||
@readonly
|
||||
var _userState = const USERSTATE();
|
||||
|
||||
/// The rules and modes being used in the chat.
|
||||
@readonly
|
||||
var _roomState = const ROOMSTATE();
|
||||
|
|
@ -378,7 +379,7 @@ abstract class _ChatStoreBase with Store {
|
|||
case EmoteType.twitchChannel:
|
||||
return 'Twitch Channel';
|
||||
case EmoteType.ffzGlobal:
|
||||
return 'Twitch Global';
|
||||
return 'FFZ Global';
|
||||
case EmoteType.ffzChannel:
|
||||
return 'FFZ Channel';
|
||||
case EmoteType.bttvGlobal:
|
||||
|
|
|
|||
|
|
@ -56,6 +56,23 @@ mixin _$ChatStore on _ChatStoreBase, Store {
|
|||
});
|
||||
}
|
||||
|
||||
final _$_userStateAtom = Atom(name: '_ChatStoreBase._userState');
|
||||
|
||||
USERSTATE get userState {
|
||||
_$_userStateAtom.reportRead();
|
||||
return super._userState;
|
||||
}
|
||||
|
||||
@override
|
||||
USERSTATE get _userState => userState;
|
||||
|
||||
@override
|
||||
set _userState(USERSTATE value) {
|
||||
_$_userStateAtom.reportWrite(value, super._userState, () {
|
||||
super._userState = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_roomStateAtom = Atom(name: '_ChatStoreBase._roomState');
|
||||
|
||||
ROOMSTATE get roomState {
|
||||
|
|
|
|||
|
|
@ -21,15 +21,20 @@ class EmoteMenu extends StatelessWidget {
|
|||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
),
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: emotes.length,
|
||||
itemBuilder: (context, index) {
|
||||
return GestureDetector(
|
||||
onTap: () => chatStore.textController.text += ' ' + emotes[index].name,
|
||||
onTap: emoteType == EmoteType.twitchChannel && !chatStore.userState.subscriber
|
||||
? null
|
||||
: () => chatStore.textController.text += ' ' + emotes[index].name,
|
||||
child: Tooltip(
|
||||
message: emotes[index].name,
|
||||
preferBelow: false,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: emotes[index].url,
|
||||
color: emoteType == EmoteType.twitchChannel && !chatStore.userState.subscriber ? const Color.fromRGBO(255, 255, 255, 0.5) : null,
|
||||
colorBlendMode: BlendMode.modulate,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -52,11 +52,14 @@ class VideoChat extends StatelessWidget {
|
|||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Settings(settingsStore: context.read<SettingsStore>());
|
||||
},
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
fullscreenDialog: true,
|
||||
builder: (context) {
|
||||
return Settings(settingsStore: context.read<SettingsStore>());
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue