From a6688a992c05cd2f7c5f90d8c0be2834673c0e6d Mon Sep 17 00:00:00 2001 From: tommyxchow Date: Wed, 24 Nov 2021 04:39:05 -0500 Subject: [PATCH] Add stream request & viewercount on video overlay --- lib/api/twitch_api.dart | 18 +++ lib/screens/channel/video/video_overlay.dart | 140 +++++++++++-------- lib/screens/channel/video/video_store.dart | 32 ++++- lib/screens/channel/video/video_store.g.dart | 51 ++++++- lib/screens/channel/video_chat.dart | 5 +- 5 files changed, 177 insertions(+), 69 deletions(-) diff --git a/lib/api/twitch_api.dart b/lib/api/twitch_api.dart index 8f043bcf..2a3c6ded 100644 --- a/lib/api/twitch_api.dart +++ b/lib/api/twitch_api.dart @@ -187,6 +187,24 @@ class Twitch { } } + /// Returns the stream info given the user login. + static Future getStream({required String userLogin, required Map? headers}) async { + final uri = Uri.parse('https://api.twitch.tv/helix/streams?user_login=$userLogin'); + + final response = await http.get(uri, headers: headers); + + if (response.statusCode == 200) { + final decoded = jsonDecode(response.body); + final data = decoded['data'] as List; + + if (data.isEmpty) return null; + + return StreamTwitch.fromJson(data.first); + } else { + debugPrint('Failed to update top streams'); + } + } + /// Returns a user's info given their login name. static Future getUser({required String userLogin, required Map? headers}) async { final response = await http.get(Uri.parse('https://api.twitch.tv/helix/users?login=$userLogin'), headers: headers); diff --git a/lib/screens/channel/video/video_overlay.dart b/lib/screens/channel/video/video_overlay.dart index 623cd363..ca271db2 100644 --- a/lib/screens/channel/video/video_overlay.dart +++ b/lib/screens/channel/video/video_overlay.dart @@ -3,6 +3,7 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:frosty/core/settings/settings.dart'; import 'package:frosty/core/settings/settings_store.dart'; import 'package:frosty/screens/channel/video/video_store.dart'; +import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; class VideoOverlay extends StatelessWidget { @@ -31,75 +32,90 @@ class VideoOverlay extends StatelessWidget { color: Colors.black.withOpacity(0.5), child: IgnorePointer( ignoring: !videoStore.menuVisible, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Stack( children: [ - Row( + Column( children: [ - IconButton( - icon: const Icon( - Icons.arrow_back, - color: Colors.white, - ), - onPressed: () => Navigator.of(context).pop(), - ), - const Spacer(), - IconButton( - icon: const Icon( - Icons.settings, - color: Colors.white, - ), - onPressed: () { - showModalBottomSheet( - context: context, - builder: (context) { - return Settings(settingsStore: context.read()); - }, - ); - }, - ), - ], - ), - IconButton( - icon: videoStore.paused - ? const Icon( - Icons.play_arrow, - color: Colors.white, - ) - : const Icon( - Icons.pause, - color: Colors.white, + Row( + children: [ + IconButton( + icon: Icon( + Icons.adaptive.arrow_back, + color: Colors.white, + ), + onPressed: () => Navigator.of(context).pop(), ), - onPressed: videoStore.handlePausePlay, - ), - Row( - children: [ - const SizedBox(width: 10), - Text( - userName, - style: const TextStyle( - color: Colors.white, - fontSize: 20, - fontWeight: FontWeight.bold, - ), + const Spacer(), + IconButton( + icon: const Icon( + Icons.settings, + color: Colors.white, + ), + onPressed: () { + showModalBottomSheet( + context: context, + builder: (context) { + return Settings(settingsStore: context.read()); + }, + ); + }, + ), + ], ), const Spacer(), - IconButton( - icon: const Icon( - Icons.picture_in_picture_alt_rounded, - color: Colors.white, - ), - onPressed: videoStore.enterPictureInPicture, - ), - IconButton( - icon: const Icon( - Icons.fullscreen, - color: Colors.white, - ), - onPressed: videoStore.requestFullscreen, + Row( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + userName, + style: const TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 5.0), + if (videoStore.streamInfo != null) Text('${NumberFormat().format(videoStore.streamInfo?.viewerCount)} viewers'), + ], + ), + ), + const Spacer(), + IconButton( + icon: const Icon( + Icons.picture_in_picture_alt_rounded, + color: Colors.white, + ), + onPressed: videoStore.enterPictureInPicture, + ), + IconButton( + icon: const Icon( + Icons.fullscreen, + color: Colors.white, + ), + onPressed: videoStore.requestFullscreen, + ) + ], ) ], - ) + ), + Center( + child: IconButton( + icon: videoStore.paused + ? const Icon( + Icons.play_arrow, + color: Colors.white, + ) + : const Icon( + Icons.pause, + color: Colors.white, + ), + onPressed: videoStore.handlePausePlay, + ), + ), ], ), ), diff --git a/lib/screens/channel/video/video_store.dart b/lib/screens/channel/video/video_store.dart index 36af8564..edbb46c0 100644 --- a/lib/screens/channel/video/video_store.dart +++ b/lib/screens/channel/video/video_store.dart @@ -1,5 +1,8 @@ import 'dart:async'; import 'dart:io'; +import 'package:frosty/api/twitch_api.dart'; +import 'package:frosty/core/auth/auth_store.dart'; +import 'package:frosty/models/stream.dart'; import 'package:mobx/mobx.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -12,16 +15,25 @@ abstract class _VideoStoreBase with Store { late Timer timer; - _VideoStoreBase() { - timer = Timer(const Duration(seconds: 3), () => menuVisible = false); - } - @observable var menuVisible = true; @observable var paused = false; + @observable + StreamTwitch? streamInfo; + + final String userLogin; + + final AuthStore authStore; + + _VideoStoreBase({required this.userLogin, required this.authStore}) { + timer = Timer(const Duration(seconds: 3), () => menuVisible = false); + updateStreamInfo(); + } + + @action void handlePausePlay() { if (paused) { controller.runJavascript('document.getElementsByTagName("video")[0].play();'); @@ -32,7 +44,8 @@ abstract class _VideoStoreBase with Store { paused = !paused; } - void handleVideoTap() async { + @action + void handleVideoTap() { if (menuVisible) { timer.cancel(); @@ -44,6 +57,15 @@ abstract class _VideoStoreBase with Store { }); menuVisible = true; + updateStreamInfo(); + } + } + + @action + Future updateStreamInfo() async { + final updatedStreamInfo = await Twitch.getStream(userLogin: userLogin, headers: authStore.headersTwitch); + if (updatedStreamInfo != null) { + streamInfo = updatedStreamInfo; } } diff --git a/lib/screens/channel/video/video_store.g.dart b/lib/screens/channel/video/video_store.g.dart index 7208a73e..ebf56b2d 100644 --- a/lib/screens/channel/video/video_store.g.dart +++ b/lib/screens/channel/video/video_store.g.dart @@ -39,11 +39,60 @@ mixin _$VideoStore on _VideoStoreBase, Store { }); } + final _$streamInfoAtom = Atom(name: '_VideoStoreBase.streamInfo'); + + @override + StreamTwitch? get streamInfo { + _$streamInfoAtom.reportRead(); + return super.streamInfo; + } + + @override + set streamInfo(StreamTwitch? value) { + _$streamInfoAtom.reportWrite(value, super.streamInfo, () { + super.streamInfo = value; + }); + } + + final _$updateStreamInfoAsyncAction = + AsyncAction('_VideoStoreBase.updateStreamInfo'); + + @override + Future updateStreamInfo() { + return _$updateStreamInfoAsyncAction.run(() => super.updateStreamInfo()); + } + + final _$_VideoStoreBaseActionController = + ActionController(name: '_VideoStoreBase'); + + @override + void handlePausePlay() { + final _$actionInfo = _$_VideoStoreBaseActionController.startAction( + name: '_VideoStoreBase.handlePausePlay'); + try { + return super.handlePausePlay(); + } finally { + _$_VideoStoreBaseActionController.endAction(_$actionInfo); + } + } + + @override + void handleVideoTap() { + final _$actionInfo = _$_VideoStoreBaseActionController.startAction( + name: '_VideoStoreBase.handleVideoTap'); + try { + return super.handleVideoTap(); + } finally { + _$_VideoStoreBaseActionController.endAction(_$actionInfo); + } + } + @override String toString() { return ''' menuVisible: ${menuVisible}, -paused: ${paused} +paused: ${paused}, +streamInfo: ${streamInfo} '''; } } diff --git a/lib/screens/channel/video_chat.dart b/lib/screens/channel/video_chat.dart index 90565143..3827a372 100644 --- a/lib/screens/channel/video_chat.dart +++ b/lib/screens/channel/video_chat.dart @@ -37,7 +37,10 @@ class VideoChat extends StatelessWidget { title: title, userName: userName, userLogin: userLogin, - videoStore: VideoStore(), + videoStore: VideoStore( + userLogin: userLogin, + authStore: context.read(), + ), ), ); }