Merge pull request #122 from tommyxchow/116-transparent-chat-fullscreen-overlay

Add landscape chat overlay, chat side, and size options
This commit is contained in:
Tommy Chow 2022-05-24 02:00:31 -04:00 • committed by GitHub
commit 21cb9820f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 236 additions and 74 deletions

View file

@ -108,7 +108,7 @@ class Chat extends StatelessWidget {
),
),
),
if (chatStore.settings.showBottomBar) ChatBottomBar(chatStore: chatStore),
if (chatStore.settings.showBottomBar && !chatStore.settings.fullScreen) ChatBottomBar(chatStore: chatStore),
if (chatStore.assetsStore.showEmoteMenu)
SizedBox(
height: MediaQuery.of(context).size.height / 3,

View file

@ -73,42 +73,54 @@ class ChatBottomBar extends StatelessWidget {
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
child: TextField(
textInputAction: TextInputAction.send,
focusNode: chatStore.textFieldFocusNode,
minLines: 1,
maxLines: 5,
enabled: chatStore.auth.isLoggedIn ? true : false,
decoration: InputDecoration(
suffixIcon: IconButton(
color: chatStore.assetsStore.showEmoteMenu ? Colors.deepPurple : null,
tooltip: 'Emote Menu',
icon: const Icon(Icons.emoji_emotions_outlined),
onPressed: () {
FocusScope.of(context).unfocus();
chatStore.assetsStore.showEmoteMenu = !chatStore.assetsStore.showEmoteMenu;
},
if (!chatStore.expandChat &&
chatStore.settings.landscapeChatWidth < 0.3 &&
MediaQuery.of(context).orientation == Orientation.landscape)
IconButton(
tooltip: 'Send a Message',
onPressed: () {
chatStore.expandChat = true;
chatStore.textFieldFocusNode.requestFocus();
},
icon: const Icon(Icons.chat))
else
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
child: TextField(
textInputAction: TextInputAction.send,
focusNode: chatStore.textFieldFocusNode,
minLines: 1,
maxLines: 5,
enabled: chatStore.auth.isLoggedIn ? true : false,
decoration: InputDecoration(
suffixIcon: IconButton(
color: chatStore.assetsStore.showEmoteMenu ? Colors.deepPurple : null,
tooltip: 'Emote Menu',
icon: const Icon(Icons.emoji_emotions_outlined),
onPressed: () {
FocusScope.of(context).unfocus();
chatStore.assetsStore.showEmoteMenu = !chatStore.assetsStore.showEmoteMenu;
},
),
hintMaxLines: 1,
contentPadding: const EdgeInsets.all(10.0),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
labelText: chatStore.auth.isLoggedIn ? 'Send a message' : 'Log in to chat',
),
hintMaxLines: 1,
contentPadding: const EdgeInsets.all(10.0),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
labelText: chatStore.auth.isLoggedIn ? 'Send a message' : 'Log in to chat',
controller: chatStore.textController,
onSubmitted: chatStore.sendMessage,
),
controller: chatStore.textController,
onSubmitted: chatStore.sendMessage,
),
),
),
IconButton(
tooltip: 'Send',
icon: const Icon(Icons.send),
onPressed: chatStore.auth.isLoggedIn ? () => chatStore.sendMessage(chatStore.textController.text) : null,
)
if (chatStore.settings.landscapeChatWidth > 0.3 || chatStore.expandChat || MediaQuery.of(context).orientation == Orientation.portrait)
IconButton(
tooltip: 'Send',
icon: const Icon(Icons.send),
onPressed: chatStore.auth.isLoggedIn ? () => chatStore.sendMessage(chatStore.textController.text) : null,
)
],
),
],

View file

@ -78,6 +78,9 @@ abstract class ChatStoreBase with Store {
@readonly
var _userState = const USERSTATE();
@observable
var expandChat = false;
final reactions = <ReactionDisposer>[];
ChatStoreBase({
@ -116,6 +119,8 @@ abstract class ChatStoreBase with Store {
// Add a listener to the textfield focus that will hide the emote menu if it is currently shown.
textFieldFocusNode.addListener(() {
if (textFieldFocusNode.hasFocus && assetsStore.showEmoteMenu) assetsStore.showEmoteMenu = false;
if (!textFieldFocusNode.hasFocus) expandChat = false;
});
// Add a listener to the textfield that will show/hide the autocomplete bar if focused.

View file

@ -81,6 +81,22 @@ mixin _$ChatStore on ChatStoreBase, Store {
});
}
late final _$expandChatAtom =
Atom(name: 'ChatStoreBase.expandChat', context: context);
@override
bool get expandChat {
_$expandChatAtom.reportRead();
return super.expandChat;
}
@override
set expandChat(bool value) {
_$expandChatAtom.reportWrite(value, super.expandChat, () {
super.expandChat = value;
});
}
late final _$ChatStoreBaseActionController =
ActionController(name: 'ChatStoreBase', context: context);
@ -142,7 +158,7 @@ mixin _$ChatStore on ChatStoreBase, Store {
@override
String toString() {
return '''
expandChat: ${expandChat}
''';
}
}

View file

@ -105,6 +105,10 @@ abstract class VideoStoreBase with Store {
_streamInfo = await twitchApi.getStream(userLogin: userLogin, headers: authStore.headersTwitch);
} catch (e) {
debugPrint(e.toString());
_streamInfo = null;
_overlayTimer.cancel();
_overlayVisible = true;
}
}

View file

@ -117,6 +117,13 @@ class VideoOverlay extends StatelessWidget {
),
);
final chatOverlayButton = IconButton(
tooltip: 'Toggle Chat Overlay',
onPressed: () => videoStore.settingsStore.fullScreenChatOverlay = !videoStore.settingsStore.fullScreenChatOverlay,
icon: const Icon(Icons.chat_bubble_outline),
color: Colors.white,
);
final refreshButton = IconButton(
tooltip: 'Refresh',
icon: const Icon(
@ -167,6 +174,7 @@ class VideoOverlay extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (videoStore.settingsStore.fullScreen && orientation == Orientation.landscape) chatOverlayButton,
refreshButton,
if (orientation == Orientation.landscape) fullScreenButton,
],
@ -292,6 +300,7 @@ class VideoOverlay extends StatelessWidget {
),
),
),
if (videoStore.settingsStore.fullScreen) chatOverlayButton,
refreshButton,
if (Platform.isIOS && videoStore.settingsStore.pictureInPicture)
IconButton(

View file

@ -154,45 +154,60 @@ class _VideoChatState extends State<VideoChat> {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
return Observer(
builder: (context) => ColoredBox(
color: settingsStore.showVideo ? Colors.black : Theme.of(context).scaffoldBackgroundColor,
child: SafeArea(
bottom: false,
child: settingsStore.showVideo
? settingsStore.fullScreen
? Stack(
children: [
Visibility(
visible: false,
maintainState: true,
child: chat,
),
video,
],
)
: Row(
children: [
Flexible(
flex: 2,
child: video,
),
Flexible(
flex: 1,
child: ColoredBox(
color: Theme.of(context).scaffoldBackgroundColor,
child: chat,
builder: (context) {
final landscapeChat = AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: _chatStore.expandChat
? MediaQuery.of(context).size.width / 2
: MediaQuery.of(context).size.width * _chatStore.settings.landscapeChatWidth,
curve: Curves.ease,
color: _chatStore.settings.fullScreen ? null : Theme.of(context).scaffoldBackgroundColor,
child: chat,
);
return ColoredBox(
color: settingsStore.showVideo ? Colors.black : Theme.of(context).scaffoldBackgroundColor,
child: SafeArea(
bottom: false,
child: settingsStore.showVideo
? settingsStore.fullScreen
? Stack(
alignment: settingsStore.landscapeChatLeftSide ? Alignment.topLeft : Alignment.topRight,
children: [
video,
IgnorePointer(
ignoring: _videoStore.overlayVisible,
child: Visibility(
visible: settingsStore.fullScreenChatOverlay,
maintainState: true,
child: DefaultTextStyle(
style: const TextStyle(color: Colors.white),
child: landscapeChat,
),
),
),
),
],
)
: Column(
children: [
appBar,
Expanded(child: chat),
],
),
),
),
],
)
: Row(
children: settingsStore.landscapeChatLeftSide
? [
landscapeChat,
Expanded(child: video),
]
: [
Expanded(child: video),
landscapeChat,
],
)
: Column(
children: [
appBar,
Expanded(child: chat),
],
),
),
);
},
);
}

View file

@ -90,6 +90,27 @@ class _ChatSettingsState extends State<ChatSettings> {
.toList(),
),
),
SwitchListTile.adaptive(
title: const Text('Landscape Chat on Left Side'),
value: settingsStore.landscapeChatLeftSide,
onChanged: (newValue) => settingsStore.landscapeChatLeftSide = newValue,
),
ListTile(
title: Row(
children: [
const Text('Landscape Chat Width'),
const Spacer(),
Text('${(settingsStore.landscapeChatWidth * 100).toInt()}%'),
],
),
subtitle: Slider.adaptive(
value: settingsStore.landscapeChatWidth,
min: 0.2,
max: 0.8,
divisions: 12,
onChanged: (newValue) => settingsStore.landscapeChatWidth = newValue,
),
),
const SizedBox(height: 10.0),
const SectionHeader('Message Appearance'),
ExpansionTile(

View file

@ -87,6 +87,10 @@ abstract class _SettingsStoreBase with Store {
static const defaultEmoteAutocomplete = true;
static const defaultLandscapeChatWidth = 0.35;
static const defaultLandscapeChatLeftSide = false;
@JsonKey(defaultValue: defaultShowBottomBar)
@observable
var showBottomBar = defaultShowBottomBar;
@ -135,6 +139,14 @@ abstract class _SettingsStoreBase with Store {
@observable
var emoteAutocomplete = defaultEmoteAutocomplete;
@JsonKey(defaultValue: defaultLandscapeChatWidth)
@observable
var landscapeChatWidth = defaultLandscapeChatWidth;
@JsonKey(defaultValue: defaultLandscapeChatLeftSide)
@observable
var landscapeChatLeftSide = defaultLandscapeChatLeftSide;
// * Other settings
static const defaultSendCrashLogs = true;
@ -147,6 +159,8 @@ abstract class _SettingsStoreBase with Store {
static const defaultExpandInfo = true;
static const defaultFullScreenChatOverlay = false;
@JsonKey(defaultValue: defaultFullScreen)
@observable
var fullScreen = defaultFullScreen;
@ -155,6 +169,10 @@ abstract class _SettingsStoreBase with Store {
@observable
var expandInfo = defaultExpandInfo;
@JsonKey(defaultValue: defaultFullScreenChatOverlay)
@observable
var fullScreenChatOverlay = defaultFullScreenChatOverlay;
@action
void reset() {
// * General Settings
@ -182,6 +200,8 @@ abstract class _SettingsStoreBase with Store {
badgeScale = defaultBadgeScale;
emoteScale = defaultEmoteScale;
emoteAutocomplete = defaultEmoteAutocomplete;
landscapeChatWidth = defaultLandscapeChatWidth;
landscapeChatLeftSide = false;
// * Other settings
sendCrashLogs = defaultSendCrashLogs;

View file

@ -34,9 +34,13 @@ SettingsStore _$SettingsStoreFromJson(Map<String, dynamic> json) =>
..badgeScale = (json['badgeScale'] as num?)?.toDouble() ?? 1.0
..emoteScale = (json['emoteScale'] as num?)?.toDouble() ?? 1.0
..emoteAutocomplete = json['emoteAutocomplete'] as bool? ?? true
..landscapeChatWidth =
(json['landscapeChatWidth'] as num?)?.toDouble() ?? 0.35
..landscapeChatLeftSide = json['landscapeChatLeftSide'] as bool? ?? false
..sendCrashLogs = json['sendCrashLogs'] as bool? ?? true
..fullScreen = json['fullScreen'] as bool? ?? false
..expandInfo = json['expandInfo'] as bool? ?? true;
..expandInfo = json['expandInfo'] as bool? ?? true
..fullScreenChatOverlay = json['fullScreenChatOverlay'] as bool? ?? false;
Map<String, dynamic> _$SettingsStoreToJson(SettingsStore instance) =>
<String, dynamic>{
@ -60,9 +64,12 @@ Map<String, dynamic> _$SettingsStoreToJson(SettingsStore instance) =>
'badgeScale': instance.badgeScale,
'emoteScale': instance.emoteScale,
'emoteAutocomplete': instance.emoteAutocomplete,
'landscapeChatWidth': instance.landscapeChatWidth,
'landscapeChatLeftSide': instance.landscapeChatLeftSide,
'sendCrashLogs': instance.sendCrashLogs,
'fullScreen': instance.fullScreen,
'expandInfo': instance.expandInfo,
'fullScreenChatOverlay': instance.fullScreenChatOverlay,
};
const _$ThemeTypeEnumMap = {
@ -406,6 +413,39 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
late final _$landscapeChatWidthAtom =
Atom(name: '_SettingsStoreBase.landscapeChatWidth', context: context);
@override
double get landscapeChatWidth {
_$landscapeChatWidthAtom.reportRead();
return super.landscapeChatWidth;
}
@override
set landscapeChatWidth(double value) {
_$landscapeChatWidthAtom.reportWrite(value, super.landscapeChatWidth, () {
super.landscapeChatWidth = value;
});
}
late final _$landscapeChatLeftSideAtom =
Atom(name: '_SettingsStoreBase.landscapeChatLeftSide', context: context);
@override
bool get landscapeChatLeftSide {
_$landscapeChatLeftSideAtom.reportRead();
return super.landscapeChatLeftSide;
}
@override
set landscapeChatLeftSide(bool value) {
_$landscapeChatLeftSideAtom.reportWrite(value, super.landscapeChatLeftSide,
() {
super.landscapeChatLeftSide = value;
});
}
late final _$sendCrashLogsAtom =
Atom(name: '_SettingsStoreBase.sendCrashLogs', context: context);
@ -454,6 +494,23 @@ mixin _$SettingsStore on _SettingsStoreBase, Store {
});
}
late final _$fullScreenChatOverlayAtom =
Atom(name: '_SettingsStoreBase.fullScreenChatOverlay', context: context);
@override
bool get fullScreenChatOverlay {
_$fullScreenChatOverlayAtom.reportRead();
return super.fullScreenChatOverlay;
}
@override
set fullScreenChatOverlay(bool value) {
_$fullScreenChatOverlayAtom.reportWrite(value, super.fullScreenChatOverlay,
() {
super.fullScreenChatOverlay = value;
});
}
late final _$_SettingsStoreBaseActionController =
ActionController(name: '_SettingsStoreBase', context: context);
@ -491,9 +548,12 @@ messageScale: ${messageScale},
badgeScale: ${badgeScale},
emoteScale: ${emoteScale},
emoteAutocomplete: ${emoteAutocomplete},
landscapeChatWidth: ${landscapeChatWidth},
landscapeChatLeftSide: ${landscapeChatLeftSide},
sendCrashLogs: ${sendCrashLogs},
fullScreen: ${fullScreen},
expandInfo: ${expandInfo}
expandInfo: ${expandInfo},
fullScreenChatOverlay: ${fullScreenChatOverlay}
''';
}
}