Revert "Improve chat performance/battery usage"

This commit is contained in:
Tommy Chow 2021-09-23 17:32:41 -04:00 • committed by GitHub
parent 992a7d1b30
commit 9fc9fd5de5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 259 additions and 196 deletions

View file

@ -6,17 +6,20 @@ part of 'badges.dart';
// JsonSerializableGenerator
// **************************************************************************
BadgeImagesTwitch _$BadgeImagesTwitchFromJson(Map<String, dynamic> json) =>
BadgeImagesTwitch(
json['id'] as String,
json['image_url_1x'] as String,
json['image_url_2x'] as String,
json['image_url_4x'] as String,
);
BadgeImagesTwitch _$BadgeImagesTwitchFromJson(Map<String, dynamic> json) {
return BadgeImagesTwitch(
json['id'] as String,
json['image_url_1x'] as String,
json['image_url_2x'] as String,
json['image_url_4x'] as String,
);
}
BadgesTwitch _$BadgesTwitchFromJson(Map<String, dynamic> json) => BadgesTwitch(
json['set_id'] as String,
(json['versions'] as List<dynamic>)
.map((e) => BadgeImagesTwitch.fromJson(e as Map<String, dynamic>))
.toList(),
);
BadgesTwitch _$BadgesTwitchFromJson(Map<String, dynamic> json) {
return BadgesTwitch(
json['set_id'] as String,
(json['versions'] as List<dynamic>)
.map((e) => BadgeImagesTwitch.fromJson(e as Map<String, dynamic>))
.toList(),
);
}

View file

@ -6,20 +6,22 @@ part of 'channel.dart';
// JsonSerializableGenerator
// **************************************************************************
Channel _$ChannelFromJson(Map<String, dynamic> json) => Channel(
id: json['id'] as String,
userId: json['user_id'] as String,
userLogin: json['user_login'] as String,
userName: json['user_name'] as String,
gameId: json['game_id'] as String,
gameName: json['game_name'] as String,
type: json['type'] as String,
title: json['title'] as String,
viewerCount: json['viewer_count'] as int,
startedAt: json['started_at'] as String,
language: json['language'] as String,
thumbnailUrl: json['thumbnail_url'] as String,
tagIds:
(json['tag_ids'] as List<dynamic>?)?.map((e) => e as String).toList(),
isMature: json['is_mature'] as bool,
);
Channel _$ChannelFromJson(Map<String, dynamic> json) {
return Channel(
id: json['id'] as String,
userId: json['user_id'] as String,
userLogin: json['user_login'] as String,
userName: json['user_name'] as String,
gameId: json['game_id'] as String,
gameName: json['game_name'] as String,
type: json['type'] as String,
title: json['title'] as String,
viewerCount: json['viewer_count'] as int,
startedAt: json['started_at'] as String,
language: json['language'] as String,
thumbnailUrl: json['thumbnail_url'] as String,
tagIds:
(json['tag_ids'] as List<dynamic>?)?.map((e) => e as String).toList(),
isMature: json['is_mature'] as bool,
);
}

View file

@ -2,6 +2,8 @@ import 'package:json_annotation/json_annotation.dart';
part 'emotes.g.dart';
// TODO: Add 7TV emotes: https://github.com/SevenTV/ServerGo/blob/master/docs/rest-api.md
// * Twitch Emotes *
@JsonSerializable(createToJson: false)
class ImagesTwitch {

View file

@ -6,113 +6,134 @@ part of 'emotes.dart';
// JsonSerializableGenerator
// **************************************************************************
ImagesTwitch _$ImagesTwitchFromJson(Map<String, dynamic> json) => ImagesTwitch(
json['url_1x'] as String,
json['url_2x'] as String,
json['url_4x'] as String,
);
ImagesTwitch _$ImagesTwitchFromJson(Map<String, dynamic> json) {
return ImagesTwitch(
json['url_1x'] as String,
json['url_2x'] as String,
json['url_4x'] as String,
);
}
EmoteTwitch _$EmoteTwitchFromJson(Map<String, dynamic> json) => EmoteTwitch(
json['id'] as String,
json['name'] as String,
ImagesTwitch.fromJson(json['images'] as Map<String, dynamic>),
json['tier'] as String?,
json['emote_type'] as String?,
json['emote_set_id'] as String?,
(json['format'] as List<dynamic>).map((e) => e as String).toList(),
(json['scale'] as List<dynamic>).map((e) => e as String).toList(),
(json['theme_mode'] as List<dynamic>).map((e) => e as String).toList(),
);
EmoteTwitch _$EmoteTwitchFromJson(Map<String, dynamic> json) {
return EmoteTwitch(
json['id'] as String,
json['name'] as String,
ImagesTwitch.fromJson(json['images'] as Map<String, dynamic>),
json['tier'] as String?,
json['emote_type'] as String?,
json['emote_set_id'] as String?,
(json['format'] as List<dynamic>).map((e) => e as String).toList(),
(json['scale'] as List<dynamic>).map((e) => e as String).toList(),
(json['theme_mode'] as List<dynamic>).map((e) => e as String).toList(),
);
}
EmoteBTTVGlobal _$EmoteBTTVGlobalFromJson(Map<String, dynamic> json) =>
EmoteBTTVGlobal(
json['id'] as String,
json['code'] as String,
json['imageType'] as String,
json['userId'] as String,
);
EmoteBTTVGlobal _$EmoteBTTVGlobalFromJson(Map<String, dynamic> json) {
return EmoteBTTVGlobal(
json['id'] as String,
json['code'] as String,
json['imageType'] as String,
json['userId'] as String,
);
}
UserBTTV _$UserBTTVFromJson(Map<String, dynamic> json) => UserBTTV(
json['id'] as String,
json['name'] as String,
json['displayName'] as String,
json['providerId'] as String,
);
UserBTTV _$UserBTTVFromJson(Map<String, dynamic> json) {
return UserBTTV(
json['id'] as String,
json['name'] as String,
json['displayName'] as String,
json['providerId'] as String,
);
}
EmoteBTTVShared _$EmoteBTTVSharedFromJson(Map<String, dynamic> json) =>
EmoteBTTVShared(
json['id'] as String,
json['code'] as String,
json['imageType'] as String,
UserBTTV.fromJson(json['user'] as Map<String, dynamic>),
);
EmoteBTTVShared _$EmoteBTTVSharedFromJson(Map<String, dynamic> json) {
return EmoteBTTVShared(
json['id'] as String,
json['code'] as String,
json['imageType'] as String,
UserBTTV.fromJson(json['user'] as Map<String, dynamic>),
);
}
EmoteBTTVChannel _$EmoteBTTVChannelFromJson(Map<String, dynamic> json) =>
EmoteBTTVChannel(
json['id'] as String,
(json['bots'] as List<dynamic>).map((e) => e as String).toList(),
(json['channelEmotes'] as List<dynamic>)
.map((e) => EmoteBTTVGlobal.fromJson(e as Map<String, dynamic>))
.toList(),
(json['sharedEmotes'] as List<dynamic>)
.map((e) => EmoteBTTVShared.fromJson(e as Map<String, dynamic>))
.toList(),
);
EmoteBTTVChannel _$EmoteBTTVChannelFromJson(Map<String, dynamic> json) {
return EmoteBTTVChannel(
json['id'] as String,
(json['bots'] as List<dynamic>).map((e) => e as String).toList(),
(json['channelEmotes'] as List<dynamic>)
.map((e) => EmoteBTTVGlobal.fromJson(e as Map<String, dynamic>))
.toList(),
(json['sharedEmotes'] as List<dynamic>)
.map((e) => EmoteBTTVShared.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
UserFFZ _$UserFFZFromJson(Map<String, dynamic> json) => UserFFZ(
json['id'] as int,
json['name'] as String,
json['displayName'] as String,
);
UserFFZ _$UserFFZFromJson(Map<String, dynamic> json) {
return UserFFZ(
json['id'] as int,
json['name'] as String,
json['displayName'] as String,
);
}
ImagesFFZ _$ImagesFFZFromJson(Map<String, dynamic> json) => ImagesFFZ(
json['1x'] as String,
json['2x'] as String?,
json['4x'] as String?,
);
ImagesFFZ _$ImagesFFZFromJson(Map<String, dynamic> json) {
return ImagesFFZ(
json['1x'] as String,
json['2x'] as String?,
json['4x'] as String?,
);
}
EmoteFFZ _$EmoteFFZFromJson(Map<String, dynamic> json) => EmoteFFZ(
json['id'] as int,
UserFFZ.fromJson(json['user'] as Map<String, dynamic>),
json['code'] as String,
ImagesFFZ.fromJson(json['images'] as Map<String, dynamic>),
json['imageType'] as String,
);
EmoteFFZ _$EmoteFFZFromJson(Map<String, dynamic> json) {
return EmoteFFZ(
json['id'] as int,
UserFFZ.fromJson(json['user'] as Map<String, dynamic>),
json['code'] as String,
ImagesFFZ.fromJson(json['images'] as Map<String, dynamic>),
json['imageType'] as String,
);
}
Role7TV _$Role7TVFromJson(Map<String, dynamic> json) => Role7TV(
json['id'] as String,
json['name'] as String,
json['position'] as int,
json['color'] as int,
json['allowed'] as int,
json['denied'] as int,
json['default'] as bool?,
);
Role7TV _$Role7TVFromJson(Map<String, dynamic> json) {
return Role7TV(
json['id'] as String,
json['name'] as String,
json['position'] as int,
json['color'] as int,
json['allowed'] as int,
json['denied'] as int,
json['default'] as bool?,
);
}
Owner7TV _$Owner7TVFromJson(Map<String, dynamic> json) => Owner7TV(
json['id'] as String,
json['twitch_id'] as String,
json['login'] as String,
json['display_name'] as String,
Role7TV.fromJson(json['role'] as Map<String, dynamic>),
);
Owner7TV _$Owner7TVFromJson(Map<String, dynamic> json) {
return Owner7TV(
json['id'] as String,
json['twitch_id'] as String,
json['login'] as String,
json['display_name'] as String,
Role7TV.fromJson(json['role'] as Map<String, dynamic>),
);
}
Emote7TV _$Emote7TVFromJson(Map<String, dynamic> json) => Emote7TV(
json['id'] as String,
json['name'] as String,
json['owner'] == null
? null
: Owner7TV.fromJson(json['owner'] as Map<String, dynamic>),
json['visibility'] as int,
(json['visibility_simple'] as List<dynamic>)
.map((e) => e as String)
.toList(),
json['mime'] as String,
json['status'] as int,
(json['tags'] as List<dynamic>).map((e) => e as String).toList(),
(json['width'] as List<dynamic>).map((e) => e as int).toList(),
(json['height'] as List<dynamic>).map((e) => e as int).toList(),
(json['urls'] as List<dynamic>)
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
.toList(),
);
Emote7TV _$Emote7TVFromJson(Map<String, dynamic> json) {
return Emote7TV(
json['id'] as String,
json['name'] as String,
json['owner'] == null
? null
: Owner7TV.fromJson(json['owner'] as Map<String, dynamic>),
json['visibility'] as int,
(json['visibility_simple'] as List<dynamic>)
.map((e) => e as String)
.toList(),
json['mime'] as String,
json['status'] as int,
(json['tags'] as List<dynamic>).map((e) => e as String).toList(),
(json['width'] as List<dynamic>).map((e) => e as int).toList(),
(json['height'] as List<dynamic>).map((e) => e as int).toList(),
(json['urls'] as List<dynamic>)
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
.toList(),
);
}

View file

@ -6,15 +6,17 @@ part of 'user.dart';
// JsonSerializableGenerator
// **************************************************************************
UserTwitch _$UserTwitchFromJson(Map<String, dynamic> json) => UserTwitch(
json['id'] as String,
json['login'] as String,
json['display_name'] as String,
json['type'] as String,
json['broadcaster_type'] as String,
json['description'] as String,
json['profile_image_url'] as String,
json['offline_image_url'] as String,
json['view_count'] as int,
json['created_at'] as String,
);
UserTwitch _$UserTwitchFromJson(Map<String, dynamic> json) {
return UserTwitch(
json['id'] as String,
json['login'] as String,
json['display_name'] as String,
json['type'] as String,
json['broadcaster_type'] as String,
json['description'] as String,
json['profile_image_url'] as String,
json['offline_image_url'] as String,
json['view_count'] as int,
json['created_at'] as String,
);
}

View file

@ -1,4 +1,5 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:frosty/models/channel.dart';
import 'package:frosty/providers/authentication_provider.dart';
@ -11,6 +12,7 @@ class ChatProvider extends ChangeNotifier {
final channel = WebSocketChannel.connect(Uri.parse('wss://irc-ws.chat.twitch.tv:443'));
final messages = <Widget>[];
final scrollController = ScrollController();
final _assetToUrl = <String, String>{};
final _emoteIdToWord = <String, String>{};
@ -31,6 +33,16 @@ class ChatProvider extends ChangeNotifier {
for (final command in commands) {
channel.sink.add(command);
}
scrollController.addListener(() {
if (!scrollController.position.atEdge) {
autoScroll = false;
notifyListeners();
} else if (scrollController.position.atEdge && scrollController.position.pixels != scrollController.position.minScrollExtent) {
autoScroll = true;
notifyListeners();
}
});
}
Future<void> getEmotes() async {
@ -96,17 +108,16 @@ class ChatProvider extends ChangeNotifier {
case 'GLOBALUSERSTATE':
break;
case 'PRIVMSG':
if (messages.length >= 200) {
debugPrint('remove');
messages.removeRange(0, 20);
}
final message = splitMessage.sublist(3).join(' ').substring(1);
messages
..add(const SizedBox(height: 10))
..add(ChatMessage(
key: Key(mappedTags['id']!),
children: privateMessage(tags: mappedTags, chatMessage: message),
));
messages.add(const SizedBox(height: 10));
messages.add(ChatMessage(
children: privateMessage(tags: mappedTags, chatMessage: message),
));
if (autoScroll) {
SchedulerBinding.instance?.addPostFrameCallback((_) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
});
}
break;
case 'ROOMSTATE':
break;
@ -210,6 +221,14 @@ class ChatProvider extends ChangeNotifier {
return result;
}
void resumeScroll() {
autoScroll = true;
scrollController.jumpTo(scrollController.position.maxScrollExtent);
SchedulerBinding.instance?.addPostFrameCallback((_) {
scrollController.jumpTo(scrollController.position.maxScrollExtent);
});
}
@override
void dispose() {
channel.sink.close();

View file

@ -12,29 +12,50 @@ class Chat extends StatelessWidget {
Widget build(BuildContext context) {
debugPrint('build');
final viewModel = context.read<ChatProvider>();
return FutureBuilder(
future: viewModel.getEmotes(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return StreamBuilder(
stream: viewModel.channel.stream,
builder: (context, snapshot) {
viewModel.handleWebsocketData(snapshot.data);
if (viewModel.messages.isNotEmpty) {
return ListView(
reverse: true,
padding: const EdgeInsets.all(5.0),
children: viewModel.messages.reversed.toList(),
return SafeArea(
child: FutureBuilder(
future: viewModel.getEmotes(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return StreamBuilder(
stream: viewModel.channel.stream,
builder: (context, snapshot) {
viewModel.handleWebsocketData(snapshot.data);
return Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
ListView.builder(
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,
physics: const ClampingScrollPhysics(),
itemCount: viewModel.messages.length,
controller: viewModel.scrollController,
padding: const EdgeInsets.all(5.0),
itemBuilder: (context, index) {
return viewModel.messages[index];
},
),
Consumer<ChatProvider>(
builder: (context, viewModel, child) {
return Visibility(
visible: !viewModel.autoScroll,
child: ElevatedButton(
onPressed: () => viewModel.resumeScroll(),
child: const Text('Resume Scroll'),
),
);
},
),
],
);
}
return const SizedBox();
},
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
},
),
);
}
}

View file

@ -9,7 +9,7 @@ class Video extends StatelessWidget {
@override
Widget build(BuildContext context) {
return WebView(
initialUrl: 'https://player.twitch.tv/?channel=$channelName&muted=false&parent=frosty',
initialUrl: 'https://player.twitch.tv/?channel=$channelName&muted=false&parent=localhost.com',
javascriptMode: JavascriptMode.unrestricted,
allowsInlineMediaPlayback: true,
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,

View file

@ -7,21 +7,21 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "26.0.0"
version: "22.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "1.7.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.2.0"
async:
dependency: transitive
description:
@ -323,7 +323,7 @@ packages:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.0.0"
http:
dependency: "direct main"
description:
@ -379,7 +379,7 @@ packages:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.1"
version: "4.1.4"
lints:
dependency: transitive
description:
@ -393,7 +393,7 @@ packages:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.1"
matcher:
dependency: transitive
description:
@ -435,7 +435,7 @@ packages:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.0.0"
path:
dependency: transitive
description:
@ -449,14 +449,14 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.3"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.0.2"
path_provider_macos:
dependency: transitive
description:
@ -526,14 +526,14 @@ packages:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.0.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.0.0"
rxdart:
dependency: transitive
description:
@ -566,14 +566,7 @@ packages:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
source_helper:
dependency: transitive
description:
name: source_helper
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
version: "1.0.3"
source_span:
dependency: transitive
description:
@ -692,7 +685,7 @@ packages:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.14"
version: "2.0.13"
win32:
dependency: transitive
description:
@ -715,5 +708,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"
dart: ">=2.13.0 <3.0.0"
flutter: ">=2.0.0"

View file

@ -54,7 +54,7 @@ dev_dependencies:
# rules and activating additional ones.
flutter_lints: ^1.0.0
build_runner: ^2.1.1
json_serializable: ^5.0.1
json_serializable: ^4.1.4
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec