Restructure folders (#184)

* Split up `video_chat.dart`

* Restructure home folder

* Restructure settings folder

* Restructure channel folder

* More restructuring
This commit is contained in:
Tommy Chow 2022-08-31 14:11:45 -04:00 • committed by GitHub
parent c07fe5337c
commit 53fabd067a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 451 additions and 440 deletions

View file

@ -1,7 +1,7 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/badges.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/models/channel.dart';

View file

@ -4,13 +4,13 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:frosty/api/bttv_api.dart';
import 'package:frosty/api/ffz_api.dart';
import 'package:frosty/api/seventv_api.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/bttv_api.dart';
import 'package:frosty/apis/ffz_api.dart';
import 'package:frosty/apis/seventv_api.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/home/home.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:http/http.dart';
import 'package:mobx/mobx.dart';

View file

@ -1,4 +1,4 @@
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:json_annotation/json_annotation.dart';
part 'emotes.g.dart';

View file

@ -3,10 +3,10 @@ import 'package:collection/collection.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/badges.dart';
import 'package:frosty/models/emotes.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_assets_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:intl/intl.dart';
import 'package:url_launcher/url_launcher.dart';

View file

@ -0,0 +1,315 @@
import 'dart:io';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/apis/bttv_api.dart';
import 'package:frosty/apis/ffz_api.dart';
import 'package:frosty/apis/seventv_api.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/main.dart';
import 'package:frosty/screens/channel/chat/chat.dart';
import 'package:frosty/screens/channel/chat/details/chat_details_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/channel/video/video.dart';
import 'package:frosty/screens/channel/video/video_overlay.dart';
import 'package:frosty/screens/channel/video/video_store.dart';
import 'package:frosty/screens/settings/settings.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/modal.dart';
import 'package:provider/provider.dart';
/// Creates a widget that shows the video stream (if live) and chat of the given user.
class VideoChat extends StatefulWidget {
final String userId;
final String userName;
final String userLogin;
const VideoChat({
Key? key,
required this.userId,
required this.userName,
required this.userLogin,
}) : super(key: key);
@override
State<VideoChat> createState() => _VideoChatState();
}
class _VideoChatState extends State<VideoChat> {
final _videoKey = GlobalKey();
final _chatKey = GlobalKey();
late final ChatStore _chatStore = ChatStore(
channelName: widget.userLogin,
channelId: widget.userId,
displayName: widget.userName,
auth: context.read<AuthStore>(),
settings: context.read<SettingsStore>(),
chatDetailsStore: ChatDetailsStore(
twitchApi: context.read<TwitchApi>(),
channelName: widget.userLogin,
),
assetsStore: ChatAssetsStore(
twitchApi: context.read<TwitchApi>(),
ffzApi: context.read<FFZApi>(),
bttvApi: context.read<BTTVApi>(),
sevenTVApi: context.read<SevenTVApi>(),
),
);
late final VideoStore _videoStore = VideoStore(
userLogin: widget.userLogin,
twitchApi: context.read<TwitchApi>(),
authStore: context.read<AuthStore>(),
settingsStore: context.read<SettingsStore>(),
);
@override
Widget build(BuildContext context) {
final settingsStore = _chatStore.settings;
void showSettings() {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => FrostyModal(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.app_settings_alt),
title: const Text('App settings'),
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => FrostyModal(
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: Settings(settingsStore: settingsStore),
),
),
),
),
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Reconnect to chat'),
onTap: () {
_chatStore.updateNotification('Reconnecting to chat...');
_chatStore.connectToChat();
},
),
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Refresh badges and emotes'),
onTap: () async {
await _chatStore.getAssets();
_chatStore.updateNotification('Badges and emotes refreshed');
},
),
],
),
),
);
}
final appBar = AppBar(
title: Text(
regexEnglish.hasMatch(_chatStore.displayName) ? _chatStore.displayName : '${_chatStore.displayName} (${_chatStore.channelName})',
style: const TextStyle(fontSize: 20),
),
actions: [
IconButton(
tooltip: 'Settings',
icon: const Icon(Icons.settings),
onPressed: showSettings,
),
],
);
final player = GestureDetector(
onLongPress: _videoStore.handleToggleOverlay,
child: Video(
key: _videoKey,
videoStore: _videoStore,
),
);
final videoOverlay = VideoOverlay(
videoStore: _videoStore,
onSettingsPressed: showSettings,
);
final overlay = GestureDetector(
onLongPress: _videoStore.handleToggleOverlay,
onDoubleTap: MediaQuery.of(context).orientation == Orientation.landscape ? () => settingsStore.fullScreen = !settingsStore.fullScreen : null,
onTap: () {
if (_chatStore.assetsStore.showEmoteMenu) {
_chatStore.assetsStore.showEmoteMenu = false;
} else {
if (_chatStore.textFieldFocusNode.hasFocus) {
_chatStore.textFieldFocusNode.unfocus();
} else {
_videoStore.handleVideoTap();
}
}
},
child: Observer(
builder: (_) {
if (_videoStore.paused || _videoStore.streamInfo == null) return videoOverlay;
return AnimatedOpacity(
opacity: _videoStore.overlayVisible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: ColoredBox(
color: Colors.black.withOpacity(settingsStore.overlayOpacity),
child: IgnorePointer(
ignoring: !_videoStore.overlayVisible,
child: videoOverlay,
),
),
);
},
),
);
final video = Observer(
builder: (context) {
if (!_videoStore.settingsStore.showOverlay) return player;
return Stack(
children: [
player,
overlay,
],
);
},
);
final chat = Chat(
key: _chatKey,
chatStore: _chatStore,
);
final videoChat = Scaffold(
body: OrientationBuilder(
builder: (context, orientation) {
if (orientation == Orientation.landscape) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
return Observer(
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.chatWidth,
curve: Curves.ease,
color: _chatStore.settings.fullScreen
? Colors.black.withOpacity(_chatStore.settings.fullScreenChatOverlayOpacity)
: Theme.of(context).scaffoldBackgroundColor,
child: chat,
);
final overlayChat = Visibility(
visible: settingsStore.fullScreenChatOverlay,
maintainState: true,
child: Theme(
data: darkTheme,
child: DefaultTextStyle(
style: DefaultTextStyle.of(context).style.copyWith(color: Colors.white),
child: landscapeChat,
),
),
);
return ColoredBox(
color: settingsStore.showVideo ? Colors.black : Theme.of(context).scaffoldBackgroundColor,
child: SafeArea(
bottom: false,
left:
(settingsStore.landscapeCutout == LandscapeCutoutType.both || settingsStore.landscapeCutout == LandscapeCutoutType.left) ? false : true,
right: (settingsStore.landscapeCutout == LandscapeCutoutType.both || settingsStore.landscapeCutout == LandscapeCutoutType.right)
? false
: true,
child: settingsStore.showVideo
? settingsStore.fullScreen
? Stack(
children: [
player,
if (settingsStore.showOverlay)
Row(
children: settingsStore.landscapeChatLeftSide
? [overlayChat, Expanded(child: overlay)]
: [Expanded(child: overlay), overlayChat],
)
],
)
: Row(
children:
settingsStore.landscapeChatLeftSide ? [landscapeChat, Expanded(child: video)] : [Expanded(child: video), landscapeChat],
)
: Column(
children: [appBar, Expanded(child: chat)],
),
),
);
},
);
}
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
return SafeArea(
child: Column(
children: [
Observer(
builder: (_) {
if (!settingsStore.showVideo) return appBar;
return AspectRatio(aspectRatio: 16 / 9, child: video);
},
),
Expanded(child: chat),
],
),
);
},
),
);
// If on Android, use PiPSwitcher to enable PiP functionality.
if (Platform.isAndroid) {
return PiPSwitcher(
floating: _videoStore.floating,
childWhenEnabled: player,
childWhenDisabled: videoChat,
);
}
return videoChat;
}
@override
void dispose() {
_chatStore.dispose();
_videoStore.dispose();
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
SystemChrome.setPreferredOrientations([]);
super.dispose();
}
}

View file

@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/widgets/chat_bottom_bar.dart';
import 'package:frosty/screens/channel/chat/widgets/chat_message.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/widgets/button.dart';
import 'package:frosty/widgets/notification.dart';

View file

@ -1,9 +1,9 @@
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/screens/channel/chat/details/chat_modes.dart';
import 'package:frosty/screens/channel/chat/details/chat_users_list.dart';
import 'package:frosty/screens/channel/stores/chat_details_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/widgets/modal.dart';
class ChatDetails extends StatefulWidget {

View file

@ -1,7 +1,7 @@
import 'dart:io';
import 'package:flutter/widgets.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/chatters.dart';
import 'package:frosty/models/irc.dart';
import 'package:mobx/mobx.dart';

View file

@ -2,11 +2,11 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/screens/channel/chat/details/chat_details_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/widgets/chat_user_modal.dart';
import 'package:frosty/screens/channel/stores/chat_details_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/button.dart';
import 'package:frosty/widgets/loading_indicator.dart';

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu_panel.dart';
import 'package:frosty/screens/channel/chat/emote_menu/recent_emotes_panel.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/widgets/button.dart';
class EmoteMenu extends StatefulWidget {

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:frosty/models/emotes.dart';
import 'package:frosty/screens/channel/chat/emote_menu/emote_menu_section.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/widgets/section_header.dart';
class EmoteMenuPanel extends StatelessWidget {

View file

@ -1,8 +1,8 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/emotes.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:provider/provider.dart';

View file

@ -1,7 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/button.dart';

View file

@ -1,9 +1,9 @@
import 'dart:convert';
import 'package:frosty/api/bttv_api.dart';
import 'package:frosty/api/ffz_api.dart';
import 'package:frosty/api/seventv_api.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/apis/bttv_api.dart';
import 'package:frosty/apis/ffz_api.dart';
import 'package:frosty/apis/seventv_api.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/badges.dart';
import 'package:frosty/models/emotes.dart';
import 'package:mobx/mobx.dart';

View file

@ -1,13 +1,13 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/badges.dart';
import 'package:frosty/models/emotes.dart';
import 'package:frosty/models/irc.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/stores/chat_details_store.dart';
import 'package:frosty/screens/channel/chat/details/chat_details_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_assets_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:mobx/mobx.dart';
import 'package:wakelock/wakelock.dart';

View file

@ -1,9 +1,9 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/channel/chat/details/chat_details.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/widgets/button.dart';
class ChatBottomBar extends StatelessWidget {

View file

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/models/irc.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/widgets/chat_user_modal.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
class ChatMessage extends StatelessWidget {
final IRCMessage ircMessage;

View file

@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/channel/chat/stores/chat_store.dart';
import 'package:frosty/screens/channel/chat/widgets/chat_message.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/block_report_modal.dart';
import 'package:frosty/widgets/button.dart';

View file

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:frosty/screens/channel/video/video_store.dart';
import 'package:webview_flutter/webview_flutter.dart';
/// Creates a [WebView] widget that shows a channel's video stream.
class Video extends StatelessWidget {
final VideoStore videoStore;
const Video({
Key? key,
required this.videoStore,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return WebView(
backgroundColor: Colors.black,
initialUrl: videoStore.videoUrl,
javascriptMode: JavascriptMode.unrestricted,
allowsInlineMediaPlayback: true,
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
onWebViewCreated: (controller) => videoStore.controller = controller,
onPageFinished: (string) => videoStore.initVideo(),
navigationDelegate: videoStore.handleNavigation,
javascriptChannels: videoStore.javascriptChannels,
);
}
}

View file

@ -1,354 +1,22 @@
import 'dart:async';
import 'dart:io';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/bttv_api.dart';
import 'package:frosty/api/ffz_api.dart';
import 'package:frosty/api/seventv_api.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/main.dart';
import 'package:frosty/screens/channel/chat/chat.dart';
import 'package:frosty/screens/channel/stores/chat_assets_store.dart';
import 'package:frosty/screens/channel/stores/chat_details_store.dart';
import 'package:frosty/screens/channel/stores/chat_store.dart';
import 'package:frosty/screens/channel/stores/video_store.dart';
import 'package:frosty/screens/settings/settings.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/screens/channel/video/video_store.dart';
import 'package:frosty/widgets/button.dart';
import 'package:frosty/widgets/dialog.dart';
import 'package:frosty/widgets/modal.dart';
import 'package:frosty/widgets/profile_picture.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:webview_flutter/webview_flutter.dart';
/// Creates a widget that shows the video stream (if live) and chat of the given user.
class VideoChat extends StatefulWidget {
final String userId;
final String userName;
final String userLogin;
const VideoChat({
Key? key,
required this.userId,
required this.userName,
required this.userLogin,
}) : super(key: key);
@override
State<VideoChat> createState() => _VideoChatState();
}
class _VideoChatState extends State<VideoChat> {
final _videoKey = GlobalKey();
final _chatKey = GlobalKey();
late final ChatStore _chatStore = ChatStore(
channelName: widget.userLogin,
channelId: widget.userId,
displayName: widget.userName,
auth: context.read<AuthStore>(),
settings: context.read<SettingsStore>(),
chatDetailsStore: ChatDetailsStore(
twitchApi: context.read<TwitchApi>(),
channelName: widget.userLogin,
),
assetsStore: ChatAssetsStore(
twitchApi: context.read<TwitchApi>(),
ffzApi: context.read<FFZApi>(),
bttvApi: context.read<BTTVApi>(),
sevenTVApi: context.read<SevenTVApi>(),
),
);
late final VideoStore _videoStore = VideoStore(
userLogin: widget.userLogin,
twitchApi: context.read<TwitchApi>(),
authStore: context.read<AuthStore>(),
settingsStore: context.read<SettingsStore>(),
);
@override
Widget build(BuildContext context) {
final settingsStore = _chatStore.settings;
void showSettings() {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => FrostyModal(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.app_settings_alt),
title: const Text('App settings'),
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => FrostyModal(
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: Settings(settingsStore: settingsStore),
),
),
),
),
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Reconnect to chat'),
onTap: () {
_chatStore.updateNotification('Reconnecting to chat...');
_chatStore.connectToChat();
},
),
ListTile(
leading: const Icon(Icons.refresh),
title: const Text('Refresh badges and emotes'),
onTap: () async {
await _chatStore.getAssets();
_chatStore.updateNotification('Badges and emotes refreshed');
},
),
],
),
),
);
}
final appBar = AppBar(
title: Text(
regexEnglish.hasMatch(_chatStore.displayName) ? _chatStore.displayName : '${_chatStore.displayName} (${_chatStore.channelName})',
style: const TextStyle(fontSize: 20),
),
actions: [
IconButton(
tooltip: 'Settings',
icon: const Icon(Icons.settings),
onPressed: showSettings,
),
],
);
final player = GestureDetector(
onLongPress: _videoStore.handleToggleOverlay,
child: _Video(
key: _videoKey,
videoStore: _videoStore,
),
);
final videoOverlay = _VideoOverlay(
videoStore: _videoStore,
onSettingsPressed: showSettings,
);
final overlay = GestureDetector(
onLongPress: _videoStore.handleToggleOverlay,
onDoubleTap: MediaQuery.of(context).orientation == Orientation.landscape ? () => settingsStore.fullScreen = !settingsStore.fullScreen : null,
onTap: () {
if (_chatStore.assetsStore.showEmoteMenu) {
_chatStore.assetsStore.showEmoteMenu = false;
} else {
if (_chatStore.textFieldFocusNode.hasFocus) {
_chatStore.textFieldFocusNode.unfocus();
} else {
_videoStore.handleVideoTap();
}
}
},
child: Observer(
builder: (_) {
if (_videoStore.paused || _videoStore.streamInfo == null) return videoOverlay;
return AnimatedOpacity(
opacity: _videoStore.overlayVisible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: ColoredBox(
color: Colors.black.withOpacity(settingsStore.overlayOpacity),
child: IgnorePointer(
ignoring: !_videoStore.overlayVisible,
child: videoOverlay,
),
),
);
},
),
);
final video = Observer(
builder: (context) {
if (!_videoStore.settingsStore.showOverlay) return player;
return Stack(
children: [
player,
overlay,
],
);
},
);
final chat = Chat(
key: _chatKey,
chatStore: _chatStore,
);
final videoChat = Scaffold(
body: OrientationBuilder(
builder: (context, orientation) {
if (orientation == Orientation.landscape) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
return Observer(
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.chatWidth,
curve: Curves.ease,
color: _chatStore.settings.fullScreen
? Colors.black.withOpacity(_chatStore.settings.fullScreenChatOverlayOpacity)
: Theme.of(context).scaffoldBackgroundColor,
child: chat,
);
final overlayChat = Visibility(
visible: settingsStore.fullScreenChatOverlay,
maintainState: true,
child: Theme(
data: darkTheme,
child: DefaultTextStyle(
style: DefaultTextStyle.of(context).style.copyWith(color: Colors.white),
child: landscapeChat,
),
),
);
return ColoredBox(
color: settingsStore.showVideo ? Colors.black : Theme.of(context).scaffoldBackgroundColor,
child: SafeArea(
bottom: false,
left:
(settingsStore.landscapeCutout == LandscapeCutoutType.both || settingsStore.landscapeCutout == LandscapeCutoutType.left) ? false : true,
right: (settingsStore.landscapeCutout == LandscapeCutoutType.both || settingsStore.landscapeCutout == LandscapeCutoutType.right)
? false
: true,
child: settingsStore.showVideo
? settingsStore.fullScreen
? Stack(
children: [
player,
if (settingsStore.showOverlay)
Row(
children: settingsStore.landscapeChatLeftSide
? [overlayChat, Expanded(child: overlay)]
: [Expanded(child: overlay), overlayChat],
)
],
)
: Row(
children:
settingsStore.landscapeChatLeftSide ? [landscapeChat, Expanded(child: video)] : [Expanded(child: video), landscapeChat],
)
: Column(
children: [appBar, Expanded(child: chat)],
),
),
);
},
);
}
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
return SafeArea(
child: Column(
children: [
Observer(
builder: (_) {
if (!settingsStore.showVideo) return appBar;
return AspectRatio(aspectRatio: 16 / 9, child: video);
},
),
Expanded(child: chat),
],
),
);
},
),
);
// If on Android, use PiPSwitcher to enable PiP functionality.
if (Platform.isAndroid) {
return PiPSwitcher(
floating: _videoStore.floating,
childWhenEnabled: player,
childWhenDisabled: videoChat,
);
}
return videoChat;
}
@override
void dispose() {
_chatStore.dispose();
_videoStore.dispose();
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: SystemUiOverlay.values,
);
SystemChrome.setPreferredOrientations([]);
super.dispose();
}
}
/// Creates a [WebView] widget that shows a channel's video stream.
class _Video extends StatelessWidget {
final VideoStore videoStore;
const _Video({
Key? key,
required this.videoStore,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return WebView(
backgroundColor: Colors.black,
initialUrl: videoStore.videoUrl,
javascriptMode: JavascriptMode.unrestricted,
allowsInlineMediaPlayback: true,
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
onWebViewCreated: (controller) => videoStore.controller = controller,
onPageFinished: (string) => videoStore.initVideo(),
navigationDelegate: videoStore.handleNavigation,
javascriptChannels: videoStore.javascriptChannels,
);
}
}
/// Creates a widget containing controls which enable interactions with an underlying [_Video] widget.
class _VideoOverlay extends StatelessWidget {
/// Creates a widget containing controls which enable interactions with an underlying [Video] widget.
class VideoOverlay extends StatelessWidget {
final VideoStore videoStore;
final void Function() onSettingsPressed;
const _VideoOverlay({
const VideoOverlay({
Key? key,
required this.videoStore,
required this.onSettingsPressed,

View file

@ -5,9 +5,9 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:floating/floating.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/stream.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:mobx/mobx.dart';
import 'package:webview_flutter/webview_flutter.dart';

View file

@ -1,12 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/home/home_store.dart';
import 'package:frosty/screens/home/search/search.dart';
import 'package:frosty/screens/home/stores/home_store.dart';
import 'package:frosty/screens/home/stores/list_store.dart';
import 'package:frosty/screens/home/top/top_section.dart';
import 'package:frosty/screens/home/widgets/streams_list.dart';
import 'package:frosty/screens/home/stream_list/stream_list_store.dart';
import 'package:frosty/screens/home/stream_list/streams_list.dart';
import 'package:frosty/screens/home/top/top.dart';
import 'package:frosty/screens/settings/settings.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/button.dart';
import 'package:frosty/widgets/dialog.dart';

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:mobx/mobx.dart';
part 'home_store.g.dart';

View file

@ -1,11 +1,11 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/screens/home/search/search_results_categories.dart';
import 'package:frosty/screens/home/search/search_results_channels.dart';
import 'package:frosty/screens/home/stores/search_store.dart';
import 'package:frosty/screens/home/search/search_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/section_header.dart';
import 'package:provider/provider.dart';

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/screens/home/stores/search_store.dart';
import 'package:frosty/screens/home/widgets/category_card.dart';
import 'package:frosty/screens/home/search/search_store.dart';
import 'package:frosty/screens/home/top/categories/category_card.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/loading_indicator.dart';
import 'package:mobx/mobx.dart';

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/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/screens/channel/channel.dart';
import 'package:frosty/screens/home/search/search_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/animate_scale.dart';
import 'package:frosty/widgets/block_report_modal.dart';

View file

@ -1,8 +1,8 @@
import 'package:flutter/widgets.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/models/channel.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';

View file

@ -3,11 +3,11 @@ import 'dart:math';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/models/stream.dart';
import 'package:frosty/screens/channel/video_chat.dart';
import 'package:frosty/screens/home/top/category_streams.dart';
import 'package:frosty/screens/channel/channel.dart';
import 'package:frosty/screens/home/top/categories/category_streams.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/widgets/animate_scale.dart';
import 'package:frosty/widgets/block_report_modal.dart';
import 'package:frosty/widgets/loading_indicator.dart';

View file

@ -1,12 +1,12 @@
import 'dart:io';
import 'package:flutter/widgets.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/stream.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:mobx/mobx.dart';
part 'list_store.g.dart';
part 'stream_list_store.g.dart';
class ListStore = ListStoreBase with _$ListStore;

View file

@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'list_store.dart';
part of 'stream_list_store.dart';
// **************************************************************************
// StoreGenerator

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/home/stores/list_store.dart';
import 'package:frosty/screens/home/widgets/stream_card.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/screens/home/stream_list/stream_card.dart';
import 'package:frosty/screens/home/stream_list/stream_list_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/loading_indicator.dart';

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/home/stores/categories_store.dart';
import 'package:frosty/screens/home/widgets/category_card.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/screens/home/top/categories/categories_store.dart';
import 'package:frosty/screens/home/top/categories/category_card.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/loading_indicator.dart';
import 'package:provider/provider.dart';

View file

@ -1,8 +1,8 @@
import 'dart:io';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:mobx/mobx.dart';
part 'categories_store.g.dart';

View file

@ -1,7 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/screens/home/top/category_streams.dart';
import 'package:frosty/screens/home/top/categories/category_streams.dart';
import 'package:frosty/widgets/animate_scale.dart';
import 'package:frosty/widgets/loading_indicator.dart';

View file

@ -2,11 +2,11 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/category.dart';
import 'package:frosty/screens/home/stores/list_store.dart';
import 'package:frosty/screens/home/widgets/stream_card.dart';
import 'package:frosty/screens/home/stream_list/stream_card.dart';
import 'package:frosty/screens/home/stream_list/stream_list_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/loading_indicator.dart';

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:frosty/screens/home/stores/home_store.dart';
import 'package:frosty/screens/home/stores/list_store.dart';
import 'package:frosty/screens/home/top/categories.dart';
import 'package:frosty/screens/home/widgets/streams_list.dart';
import 'package:frosty/screens/home/home_store.dart';
import 'package:frosty/screens/home/stream_list/stream_list_store.dart';
import 'package:frosty/screens/home/stream_list/streams_list.dart';
import 'package:frosty/screens/home/top/categories/categories.dart';
class TopSection extends StatefulWidget {
final HomeStore homeStore;

View file

@ -2,8 +2,8 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/block_button.dart';

View file

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/section_header.dart';

View file

@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/widgets/alert_message.dart';
import 'package:frosty/widgets/button.dart';

View file

@ -2,13 +2,13 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/settings/sections/account_settings.dart';
import 'package:frosty/screens/settings/sections/chat_settings.dart';
import 'package:frosty/screens/settings/sections/general_settings.dart';
import 'package:frosty/screens/settings/sections/other_settings.dart';
import 'package:frosty/screens/settings/sections/video_settings.dart';
import 'package:frosty/screens/settings/account_settings.dart';
import 'package:frosty/screens/settings/chat_settings.dart';
import 'package:frosty/screens/settings/general_settings.dart';
import 'package:frosty/screens/settings/other_settings.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/screens/settings/stores/settings_store.dart';
import 'package:frosty/screens/settings/video_settings.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';

View file

@ -2,10 +2,10 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/constants/constants.dart';
import 'package:frosty/core/user/user_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/constants.dart';
import 'package:frosty/main.dart';
import 'package:frosty/screens/settings/stores/user_store.dart';
import 'package:mobx/mobx.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:webview_flutter/webview_flutter.dart';

View file

@ -1,4 +1,4 @@
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/user.dart';
import 'package:mobx/mobx.dart';

View file

@ -9,7 +9,7 @@ class AlertMessage extends StatelessWidget {
const AlertMessage({
Key? key,
required this.message,
final this.icon = Icons.info,
this.icon = Icons.info,
this.color,
}) : super(key: key);

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/widgets/button.dart';
import 'package:frosty/widgets/dialog.dart';

View file

@ -1,7 +1,7 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:frosty/widgets/block_button.dart';
import 'package:frosty/widgets/modal.dart';
import 'package:frosty/widgets/report_button.dart';

View file

@ -36,8 +36,8 @@ class _ButtonState extends State<Button> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final buttonStyle = ElevatedButton.styleFrom(
primary: widget.color == null || widget.fill ? widget.color : Colors.transparent,
onPrimary: widget.fill ? null : widget.color,
foregroundColor: widget.fill ? null : widget.color,
backgroundColor: widget.color == null || widget.fill ? widget.color : Colors.transparent,
padding: widget.padding,
splashFactory: Platform.isIOS ? NoSplash.splashFactory : null,
textStyle: TextStyle(fontWeight: FontWeight.w600, fontSize: widget.fontSize),

View file

@ -1,8 +1,8 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:frosty/api/twitch_api.dart';
import 'package:frosty/core/auth/auth_store.dart';
import 'package:frosty/apis/twitch_api.dart';
import 'package:frosty/models/user.dart';
import 'package:frosty/screens/settings/stores/auth_store.dart';
import 'package:provider/provider.dart';
class ProfilePicture extends StatelessWidget {