Skip to content

Commit

Permalink
feat: completed channel point redemption event config (#222)
Browse files Browse the repository at this point in the history
* Added print line to chat_history.dart

Print line checks if message received is prefixed by '!'. This is done through isCommand property of TwitchMessageModel.

* Update chat_history.dart

* Add channel_point.dart

* feat: channel point redemption event config

* fix: merge conflict errors

* added case for channel points in getExpiration

* add selector widget for channel point redemption model and config

* fix: add missing notifyListener

* fix: add toJson for channel point

* fix: resolve conflicts

* remove pinnable switchlisttile

* feat: add pin to manual clear unfulfilled rewards

* style: change switchlisttile title and subtitle

* style: comment unused variable

* style: comment unused import

* check if status is cancelled

* fix: status typo

changed from 'cancelled' to 'canceled'

* style: remove unused import

* removed manual clear function and include slider for additional duration

* fix: use enums instead of string comparison

* test: string to enum

* fix: added string to enum parser

* fix: parse correct argument type for status
  • Loading branch information
ryansng authored Sep 8, 2021
1 parent cbc0ebd commit 3759020
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 20 deletions.
13 changes: 7 additions & 6 deletions lib/components/chat_history/message.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
Expand Down Expand Up @@ -37,8 +36,6 @@ class ChatHistoryMessage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final m = message;
final enableInlineEvents =
RemoteConfig.instance.getBool('inline_events_enabled');
if (m is TwitchMessageModel) {
return Consumer<LayoutModel>(builder: (context, layoutModel, child) {
final child = Padding(
Expand Down Expand Up @@ -184,9 +181,13 @@ class ChatHistoryMessage extends StatelessWidget {
config.showEvent ? TwitchPollEventWidget(m) : Container(),
);
} else if (m is TwitchChannelPointRedemptionEventModel) {
return enableInlineEvents
? TwitchChannelPointRedemptionEventWidget(m)
: Container();
return Selector<EventSubConfigurationModel,
ChannelPointRedemptionEventConfig>(
selector: (_, model) => model.channelPointRedemptionEventConfig,
builder: (_, config, __) => config.showEvent
? TwitchChannelPointRedemptionEventWidget(m)
: Container(),
);
} else if (m is TwitchHypeTrainEventModel) {
return Selector<EventSubConfigurationModel, HypetrainEventConfig>(
selector: (_, model) => model.hypetrainEventConfig,
Expand Down
6 changes: 5 additions & 1 deletion lib/components/chat_history/twitch/channel_point_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ class TwitchChannelPointRedemptionEventWidget extends StatelessWidget {
TextSpan(
text: model.redeemerUsername,
style: Theme.of(context).textTheme.subtitle2),
const TextSpan(text: " redeemed "),
TextSpan(
text: "${model.rewardName} ",
style: Theme.of(context).textTheme.subtitle2),
TextSpan(
text:
" redeemed ${model.rewardName} for ${model.rewardCost} points. ${model.userInput ?? ''}"),
"for ${model.rewardCost} points. ${model.userInput ?? ''}"),
],
),
),
Expand Down
18 changes: 18 additions & 0 deletions lib/components/chat_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:rtchat/components/pinnable/scroll_view.dart';
import 'package:rtchat/components/style_model_theme.dart';
import 'package:rtchat/models/channels.dart';
import 'package:rtchat/models/messages/message.dart';
import 'package:rtchat/models/messages/twitch/channel_point_redemption_event.dart';
import 'package:rtchat/models/messages/twitch/event.dart';
import 'package:rtchat/models/messages/twitch/eventsub_configuration.dart';
import 'package:rtchat/models/messages/twitch/hype_train_event.dart';
Expand Down Expand Up @@ -106,6 +107,23 @@ DateTime? _getExpiration(
return null;
}
return model.endTimestamp.add(pollEventConfig.eventDuration);
} else if (model is TwitchChannelPointRedemptionEventModel) {
final channelPointRedemptionEventConfig =
eventSubConfigurationModel.channelPointRedemptionEventConfig;
final unfulfilledDuration =
channelPointRedemptionEventConfig.eventDuration +
channelPointRedemptionEventConfig.unfulfilledAdditionalDuration;

if (model.status == TwitchChannelPointRedemptionStatus.unfulfilled &&
unfulfilledDuration > Duration.zero) {
return model.timestamp.add(unfulfilledDuration);
}
if (model.status == TwitchChannelPointRedemptionStatus.fulfilled &&
channelPointRedemptionEventConfig.eventDuration > Duration.zero) {
return model.timestamp
.add(channelPointRedemptionEventConfig.eventDuration);
}
return null;
} else if (model is TwitchHypeTrainEventModel) {
final hypetrainEventConfig =
eventSubConfigurationModel.hypetrainEventConfig;
Expand Down
3 changes: 3 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import 'package:rtchat/screens/settings/events/poll.dart';
import 'package:rtchat/screens/settings/events/prediction.dart';
import 'package:rtchat/screens/settings/events/raid.dart';
import 'package:rtchat/screens/settings/events/subscription.dart';
import 'package:rtchat/screens/settings/events/channel_point.dart';
import 'package:rtchat/screens/settings/quick_links.dart';
import 'package:rtchat/screens/settings/settings.dart';
import 'package:rtchat/screens/settings/tts.dart';
Expand Down Expand Up @@ -296,6 +297,8 @@ class _AppState extends State<App> {
'/settings/events/subscription': (context) =>
const SubscriptionEventScreen(),
'/settings/events/raid': (context) => const RaidEventScreen(),
'/settings/events/channel-point': (context) =>
const ChannelPointRedemptionEventScreen(),
'/settings/events/poll': (context) => const PollEventScreen(),
'/settings/events/hypetrain': (context) =>
const HypetrainEventScreen(),
Expand Down
36 changes: 30 additions & 6 deletions lib/models/messages/twitch/channel_point_redemption_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,32 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rtchat/models/messages/message.dart';

enum TwitchChannelPointRedemptionStatus {
fulfilled,
canceled,
unfulfilled,
unknown
}

extension TwitchChannelPointRedemptionStatusParser
on TwitchChannelPointRedemptionStatus {
static TwitchChannelPointRedemptionStatus toEnum(String value) {
switch (value) {
case 'fulfilled':
return TwitchChannelPointRedemptionStatus.fulfilled;
case 'canceled':
return TwitchChannelPointRedemptionStatus.canceled;
case 'unfulfilled':
return TwitchChannelPointRedemptionStatus.unfulfilled;
default:
return TwitchChannelPointRedemptionStatus.unknown;
}
}
}

class TwitchChannelPointRedemptionEventModel extends MessageModel {
final String redeemerUsername;
final String status;
final TwitchChannelPointRedemptionStatus status;
final String rewardName;
final int rewardCost;
final String? userInput;
Expand All @@ -25,21 +48,22 @@ class TwitchChannelPointRedemptionEventModel extends MessageModel {
timestamp: data['timestamp'].toDate(),
messageId: "channel.point-redemption-${data['event']['id']}",
redeemerUsername: data['event']['user_name'],
status: data['event']['status'],
status: TwitchChannelPointRedemptionStatusParser.toEnum(
data['event']['status']),
rewardName: data['event']['reward']['title'],
rewardCost: data['event']['reward']['cost'],
userInput: data['event']['user_input']);
}

IconData get icon {
switch (status) {
case "fulfilled":
case TwitchChannelPointRedemptionStatus.fulfilled:
return Icons.done;
case "cancelled":
case TwitchChannelPointRedemptionStatus.canceled:
return Icons.close;
case "unfulfilled":
case TwitchChannelPointRedemptionStatus.unfulfilled:
return Icons.timer;
case "unknown":
case TwitchChannelPointRedemptionStatus.unknown:
return Icons.help;
default:
return Icons.done;
Expand Down
49 changes: 45 additions & 4 deletions lib/models/messages/twitch/eventsub_configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,39 @@ class RaidEventConfig {
};
}

class PollEventConfig {
class ChannelPointRedemptionEventConfig {
bool showEvent;
Duration eventDuration;
Duration unfulfilledAdditionalDuration;

PollEventConfig(this.showEvent, this.eventDuration);
ChannelPointRedemptionEventConfig(
this.showEvent, this.eventDuration, this.unfulfilledAdditionalDuration);

PollEventConfig.fromJson(Map<String, dynamic> json)
ChannelPointRedemptionEventConfig.fromJson(Map<String, dynamic> json)
: showEvent = json['showEvent'],
eventDuration = Duration(seconds: json['eventDuration'].toInt());
eventDuration = Duration(seconds: json['eventDuration'].toInt()),
unfulfilledAdditionalDuration =
Duration(seconds: json['unfulfilledAdditionalDuration'].toInt());

Map<String, dynamic> toJson() => {
"showEvent": showEvent,
"eventDuration": eventDuration.inSeconds.toInt(),
"unfulfilledAdditionalDuration":
unfulfilledAdditionalDuration.inSeconds.toInt(),
};
}

class PollEventConfig {
bool showEvent;
Duration eventDuration;

PollEventConfig(this.showEvent, this.eventDuration);

PollEventConfig.fromJson(Map<String, dynamic> json)
: showEvent = json['showEvent'],
eventDuration = Duration(seconds: json['eventDuration'].toInt());
}

class HypetrainEventConfig {
bool showEvent;
Duration eventDuration;
Expand Down Expand Up @@ -125,6 +142,9 @@ class EventSubConfigurationModel extends ChangeNotifier {
CheerEventConfig(true, const Duration(seconds: 6));
RaidEventConfig raidEventConfig =
RaidEventConfig(true, const Duration(seconds: 6));
ChannelPointRedemptionEventConfig channelPointRedemptionEventConfig =
ChannelPointRedemptionEventConfig(
true, const Duration(seconds: 6), const Duration(seconds: 0));
PollEventConfig pollEventConfig =
PollEventConfig(true, const Duration(seconds: 6));
HypetrainEventConfig hypetrainEventConfig =
Expand Down Expand Up @@ -179,6 +199,21 @@ class EventSubConfigurationModel extends ChangeNotifier {
notifyListeners();
}

setChannelPointRedemptionEventDuration(Duration value) {
channelPointRedemptionEventConfig.eventDuration = value;
notifyListeners();
}

setChannelPointRedemptionEventShowable(bool value) {
channelPointRedemptionEventConfig.showEvent = value;
notifyListeners();
}

setChannelPointRedemptionEventUnfulfilledAdditionalDuration(Duration value) {
channelPointRedemptionEventConfig.unfulfilledAdditionalDuration = value;
notifyListeners();
}

setPollEventDuration(Duration duration) {
pollEventConfig.eventDuration = duration;
notifyListeners();
Expand Down Expand Up @@ -223,6 +258,11 @@ class EventSubConfigurationModel extends ChangeNotifier {
if (json['raidEventConfig'] != null) {
raidEventConfig = RaidEventConfig.fromJson(json['raidEventConfig']);
}
if (json['channelPointRedemptionEventConfig'] != null) {
channelPointRedemptionEventConfig =
ChannelPointRedemptionEventConfig.fromJson(
json['channelPointRedemptionEventConfig']);
}
if (json['pollEventConfig'] != null) {
pollEventConfig = PollEventConfig.fromJson(json['pollEventConfig']);
}
Expand All @@ -242,6 +282,7 @@ class EventSubConfigurationModel extends ChangeNotifier {
"cheerEventConfig": cheerEventConfig,
"raidEventConfig": raidEventConfig,
"pollEventConfig": pollEventConfig,
"channelPointsRedemptionEventConfig": channelPointRedemptionEventConfig,
"hypeTrainConfig": hypetrainEventConfig,
"predictionEventConfig": predictionEventConfig
};
Expand Down
24 changes: 23 additions & 1 deletion lib/screens/settings/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import 'package:rtchat/components/chat_history/twitch/poll_event.dart';
import 'package:rtchat/components/chat_history/twitch/prediction_event.dart';
import 'package:rtchat/components/chat_history/twitch/raid_event.dart';
import 'package:rtchat/components/chat_history/twitch/subscription_event.dart';
import 'package:rtchat/components/chat_history/twitch/channel_point_event.dart';
import 'package:rtchat/components/style_model_theme.dart';
import 'package:rtchat/models/layout.dart';
import 'package:rtchat/models/messages/twitch/channel_point_redemption_event.dart';
import 'package:rtchat/models/messages/twitch/event.dart';
import 'package:rtchat/models/messages/twitch/hype_train_event.dart';
import 'package:rtchat/models/messages/twitch/prediction_event.dart';
Expand Down Expand Up @@ -169,7 +171,27 @@ class EventsScreen extends StatelessWidget {
onTap: () {
Navigator.pushNamed(context, '/settings/events/prediction');
},
)
),
Padding(
padding: const EdgeInsets.all(16),
child: StyleModelTheme(
child: TwitchChannelPointRedemptionEventWidget(
TwitchChannelPointRedemptionEventModel(
messageId: '',
timestamp: DateTime.now(),
redeemerUsername: 'muxfd',
status: TwitchChannelPointRedemptionStatus.fulfilled,
rewardName: 'do a backflip',
rewardCost: 1000,
userInput: 'Infront of Topaz!',
)))),
ListTile(
title: const Text('Channel point redemption event config'),
subtitle:
const Text("Customize your channel point redemption event"),
onTap: () {
Navigator.pushNamed(context, "/settings/events/channel-point");
}),
]);
}),
);
Expand Down
77 changes: 77 additions & 0 deletions lib/screens/settings/events/channel_point.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:rtchat/models/messages/twitch/eventsub_configuration.dart';

class ChannelPointRedemptionEventScreen extends StatelessWidget {
const ChannelPointRedemptionEventScreen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Channel Point Configuration"),
),
body: Consumer<EventSubConfigurationModel>(
builder: (context, model, child) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Pin Duration",
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.bold,
)),
Slider.adaptive(
value: model.channelPointRedemptionEventConfig
.eventDuration.inSeconds
.toDouble(),
min: 0,
max: 30,
divisions: 15,
label:
"${model.channelPointRedemptionEventConfig.eventDuration.inSeconds} seconds",
onChanged: (value) {
model.setChannelPointRedemptionEventDuration(
Duration(seconds: value.toInt()));
},
),
Text("Additional Pin Duration for Unfulfilled Rewards",
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.bold,
)),
Slider.adaptive(
value: model.channelPointRedemptionEventConfig
.unfulfilledAdditionalDuration.inSeconds
.toDouble(),
min: 0,
max: 30,
divisions: 15,
label:
"${model.channelPointRedemptionEventConfig.unfulfilledAdditionalDuration.inSeconds} seconds",
onChanged: (value) {
model
.setChannelPointRedemptionEventUnfulfilledAdditionalDuration(
Duration(seconds: value.toInt()));
},
),
SwitchListTile.adaptive(
title: const Text('Enable event'),
subtitle: const Text('Show event in chat history'),
value: model.channelPointRedemptionEventConfig.showEvent,
onChanged: (value) {
model.setChannelPointRedemptionEventShowable(value);
},
),
],
),
)
],
);
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void main() {
timestamp: DateTime.now(),
messageId: "testMessageId",
redeemerUsername: "automux",
status: "fulfilled",
status: TwitchChannelPointRedemptionStatus.fulfilled,
rewardName: "Sprint",
rewardCost: 100,
userInput: null);
Expand All @@ -38,7 +38,7 @@ void main() {
timestamp: DateTime.now(),
messageId: "testMessageId",
redeemerUsername: "automux",
status: "unfulfilled",
status: TwitchChannelPointRedemptionStatus.unfulfilled,
rewardName: "WaTeER",
rewardCost: 350,
userInput: "user input Kappa");
Expand Down

0 comments on commit 3759020

Please sign in to comment.