fixes, works on "prefix + bits" form

This commit is contained in:
Brandon Thomas committed 2022-02-24 23:28:47 -05:00
1 parent 596b50cb05
commit acf187be16
7 files changed
+302 -127

No files matched your search

@@ -14,6 +14,7 @@ import { EventResponseType } from './event_response_builder/EventResponseType';
import { EventResponseComponent } from './event_response_builder/EventResponseComponent';
import { BitsRuleType } from './event_match_predicate_builder/types/BitsRuleType';
import { EventMatchPredicateBuilderComponent } from './event_match_predicate_builder/EventMatchPredicateBuilderComponent';
import { CheerState } from './event_match_predicate_builder/CheerState';
interface Props {
sessionWrapper: SessionWrapper,
@@ -37,36 +38,20 @@ function TtsConfigsEditRulePage(props: Props) {
const [newEventMatchPredicate, setNewEventMatchPredicate] = useState<EventMatchPredicate>({});
const [newEventResponse, setNewEventResponse] = useState<EventResponse>({});
const [cheerState, setCheerState] = useState<CheerState>({});
// ========== ??? ==========
/// TODO GET RID OF THIS
// TODO: GET RID OF THIS
const [eventResponseType, setEventResponseType] = useState<EventResponseType>(EventResponseType.TtsSingleVoice);
// ===== Predicate Field values (editing) =====
// Used in:
// BitsCheermoteNameExactMatch
// BitsCheermotePrefixSpendThreshold
const [cheerNameOrPrefix, setCheerNameOrPrefix] = useState('');
// Used in:
// BitsSpendThreshold
// BitsCheermotePrefixSpendThreshold
const [minimumBitsSpent, setMinimumBitsSpent] = useState(1);
// Used in:
// ChannelPointsRewardNameExactMatch
const [rewardName, setRewardName] = useState('');
// ===== Response Field values (editing) =====
// Used in:
// TtsSingleVoice
// TODO: GET RID OF THIS
const [ttsModelToken, setTtsModelToken] = useState('');
// ===== Field values (final) =====
// TODO: GET RID OF non-"new" (see above)
const [eventMatchPredicate, setEventMatchPredicate] = useState<EventMatchPredicate>({});
const [eventResponse, setEventResponse] = useState<EventResponse>({});
const [ruleIsDisabled, setRuleIsDisabled] = useState(false);
@@ -92,15 +77,15 @@ function TtsConfigsEditRulePage(props: Props) {
serverBitsRuleType = BitsRuleType.BitsCheermoteNameExactMatch;
} else if (!!response.twitch_event_rule.event_match_predicate.bits_cheermote_prefix_spend_threshold) {
serverBitsRuleType = BitsRuleType.BitsCheermotePrefixSpendThreshold;
setMinimumBitsSpent(response.twitch_event_rule.event_match_predicate.bits_cheermote_prefix_spend_threshold.minimum_bits_spent);
//setMinimumBitsSpent(response.twitch_event_rule.event_match_predicate.bits_cheermote_prefix_spend_threshold.minimum_bits_spent);
} else if (!!response.twitch_event_rule.event_match_predicate.bits_spend_threshold) {
serverBitsRuleType = BitsRuleType.BitsSpendThreshold;
setMinimumBitsSpent(response.twitch_event_rule.event_match_predicate.bits_spend_threshold.minimum_bits_spent);
//setMinimumBitsSpent(response.twitch_event_rule.event_match_predicate.bits_spend_threshold.minimum_bits_spent);
}
break;
case TwitchEventCategory.ChannelPoints: // NB: Only one rule type
if (!!response.twitch_event_rule.event_match_predicate.channel_points_reward_name_exact_match) {
setRewardName(response.twitch_event_rule.event_match_predicate.channel_points_reward_name_exact_match.reward_name);
//setRewardName(response.twitch_event_rule.event_match_predicate.channel_points_reward_name_exact_match.reward_name);
}
break;
case TwitchEventCategory.ChatCommand: // TODO: Not yet supported
@@ -187,7 +172,7 @@ function TtsConfigsEditRulePage(props: Props) {
updated_at: twitchEventRule.updated_at,
// Updated in UI
event_match_predicate: newEventMatchPredicate,
event_match_predicate: eventMatchPredicate,
event_response: eventResponse,
rule_is_disabled: ruleIsDisabled,
};
@@ -3,6 +3,7 @@ import { CHEER_LOOKUP_MAP, TwitchCheerPrefix } from "../../../twitch/Cheers";
import { CheerUtil } from "../../../twitch/CheerUtil";
import { BitsRuleType } from "./types/BitsRuleType";
// TODO: This needs tests badly.
export interface CheerStateOfficial {
cheerPrefix?: TwitchCheerPrefix,
@@ -51,17 +52,21 @@ export function convertExactMatchToCheerState(freeformText: string) : CheerState
}
// Convert "prefix + bits" into CheerState
export function parsePrefixAndBitsToCheerState(freeformText: string, bits?: number) : CheerState {
const trimmed = freeformText.trim();
export function convertPrefixAndBitsToCheerState(freeformText?: string, bits?: number) : CheerState {
const trimmed = !!freeformText ? freeformText.trim() : '';
let { cheerPrefix, bitValue } = CheerUtil.parseCheerString(trimmed);
let maybeCheer = CHEER_LOOKUP_MAP.get(cheerPrefix || '');
// NB: Prefer function args over parsed value since we might be parsing
// custom cheers such as "Sus11" or "1984" (7tv.app)
let maybeBits = bits || bitValue;
if (maybeCheer !== undefined) {
return {
cheerPrefix: maybeCheer,
bits: bitValue,
bits: maybeBits,
}
}
@@ -69,21 +74,20 @@ export function parsePrefixAndBitsToCheerState(freeformText: string, bits?: numb
// eg. "1984"
return {
cheerFull: freeformText,
bits: bitValue,
bits: maybeBits,
};
}
// Turn a cheer state into a predicate
export function predicateToCheerState(predicate: EventMatchPredicate) : CheerState
{
if (!!predicate.bits_cheermote_name_exact_match) {
return convertExactMatchToCheerState(predicate.bits_cheermote_name_exact_match.cheermote_name)
} else if (!!predicate.bits_cheermote_prefix_spend_threshold) {
return {
cheerFull: predicate.bits_cheermote_prefix_spend_threshold.cheermote_prefix,
bits: predicate.bits_cheermote_prefix_spend_threshold.minimum_bits_spent,
}
return convertPrefixAndBitsToCheerState(
predicate.bits_cheermote_prefix_spend_threshold.cheermote_prefix,
predicate.bits_cheermote_prefix_spend_threshold.minimum_bits_spent,
);
} else if (!!predicate.bits_spend_threshold) {
return {
bits: predicate.bits_spend_threshold.minimum_bits_spent,
@@ -126,8 +130,10 @@ export function cheerStateToPredicate(
/*
Truth table-ish breakdown (incomplete).
"Corgo" is an official cheer
"Zombo" is a custom cheer
"Zombo", "Sus12", and "1984" are custom cheers
------------------------------------------------------------------------------------------------
@@ -8,6 +8,8 @@ import { BitsCheermotePrefixSpendThresholdForm } from './subforms/BitsCheermoteP
import { BitsSpendThresholdForm } from './subforms/BitsSpendThresholdForm';
import { ChannelPointsRewardNameExactMatchForm } from './subforms/ChannelPointsRewardNameExactMatchForm';
import { ChannelPointsRuleType } from './types/ChannelPointsRuleType';
import { CheerUtil } from '../../../twitch/CheerUtil';
import { CheerState, predicateToCheerState } from './CheerState';
// TODO: Don't duplicate
const CHEER_REGEX = /^([A-Za-z]+)(\d+)?$/;
@@ -33,15 +35,33 @@ function EventMatchPredicateBuilderComponent(props: EventMatchPredicateBuilderCo
// ========== Cached Values for Editing ==========
// New, and remove everything else:
const [cheerState, setCheerState] = useState<CheerState>({});
// Used in:
// BitsCheermoteNameExactMatch
// BitsCheermotePrefixSpendThreshold
const [cheerNameOrPrefix, setCheerNameOrPrefix] = useState('');
const [cheerNameOrPrefix, setCheerNameOrPrefix] = useState(''); // TODO: DIE
// Used in:
// // Used in:
// // BitsSpendThreshold
// // BitsCheermotePrefixSpendThreshold
// const [minimumBitsSpent, setMinimumBitsSpent] = useState(1); // TODO: DIE
// Shared state for :
// BitsSpendThreshold
// BitsCheermoteNameExactMatch
// BitsCheermotePrefixSpendThreshold
const [minimumBitsSpent, setMinimumBitsSpent] = useState(1);
const [bitsValue, setBitsValue] = useState(1);
const [cheerPrefix, setCheerPrefix] = useState('');
// Used in:
// ChannelPointsRewardNameExactMatch
@@ -55,18 +75,27 @@ function EventMatchPredicateBuilderComponent(props: EventMatchPredicateBuilderCo
let newBitsRuleType = BitsRuleType.BitsCheermoteNameExactMatch;
let newChannelPointsRuleType = ChannelPointsRuleType.ChannelPointsRewardNameExactMatch;
let cheerAndBitsV = undefined;
let bitsV = undefined;
let cheerPrefixV = undefined;
let newCheerState = predicateToCheerState(props.eventMatchPredicate);
switch (props.twitchEventCategory) {
case TwitchEventCategory.Bits:
if (!!props.eventMatchPredicate.bits_cheermote_name_exact_match) {
newBitsRuleType = BitsRuleType.BitsCheermoteNameExactMatch;
setCheerNameOrPrefix(props.eventMatchPredicate.bits_cheermote_name_exact_match.cheermote_name);
cheerAndBitsV = props.eventMatchPredicate.bits_cheermote_name_exact_match.cheermote_name;
} else if (!!props.eventMatchPredicate.bits_cheermote_prefix_spend_threshold) {
newBitsRuleType = BitsRuleType.BitsCheermotePrefixSpendThreshold;
setMinimumBitsSpent(props.eventMatchPredicate.bits_cheermote_prefix_spend_threshold.minimum_bits_spent);
setCheerNameOrPrefix(props.eventMatchPredicate.bits_cheermote_prefix_spend_threshold.cheermote_prefix);
cheerPrefixV = props.eventMatchPredicate.bits_cheermote_prefix_spend_threshold.cheermote_prefix;
bitsV = props.eventMatchPredicate.bits_cheermote_prefix_spend_threshold.minimum_bits_spent;
} else if (!!props.eventMatchPredicate.bits_spend_threshold) {
newBitsRuleType = BitsRuleType.BitsSpendThreshold;
setMinimumBitsSpent(props.eventMatchPredicate.bits_spend_threshold.minimum_bits_spent);
bitsV = props.eventMatchPredicate.bits_spend_threshold.minimum_bits_spent;
}
break;
case TwitchEventCategory.ChannelPoints: // NB: Only one rule type
@@ -80,15 +109,59 @@ function EventMatchPredicateBuilderComponent(props: EventMatchPredicateBuilderCo
break;
}
setCheerState(newCheerState);
console.table(props.eventMatchPredicate)
console.log('cheerState', newCheerState);
if (!!cheerAndBitsV) {
let { cheerPrefix, bitValue } = CheerUtil.parseCheerString(cheerAndBitsV);
setCheerPrefix(cheerPrefix || '');
setBitsValue(bitValue || 1);
setCheerNameOrPrefix(cheerAndBitsV); // TODO: DIE
console.log('parsed', cheerPrefix, bitValue);
}
if (!!bitsV) {
setBitsValue(bitsV || 1);
}
if (!!cheerPrefixV) {
setCheerPrefix(cheerPrefixV || '');
}
setBitsRuleType(newBitsRuleType);
setChannelPointsRuleType(newChannelPointsRuleType);
}, [props.twitchEventCategory, props.eventMatchPredicate]);
// TODO: We need to recalculate the model.
const handleChangedBitsRuleType = (ev: React.FormEvent<HTMLSelectElement>) : boolean => {
const value = (ev.target as HTMLSelectElement).value;
const ruleType = value as BitsRuleType;
setBitsRuleType(ruleType);
const newRuleType = value as BitsRuleType;
let predicate : EventMatchPredicate = {};
switch (newRuleType) {
case BitsRuleType.BitsCheermoteNameExactMatch:
predicate.bits_cheermote_name_exact_match = {
cheermote_name: cheerNameOrPrefix,
}
break;
case BitsRuleType.BitsCheermotePrefixSpendThreshold:
predicate.bits_cheermote_prefix_spend_threshold = {
cheermote_prefix: cheerNameOrPrefix,
minimum_bits_spent: bitsValue,
}
break;
case BitsRuleType.BitsSpendThreshold:
predicate.bits_spend_threshold = {
minimum_bits_spent: bitsValue,
}
break;
}
props.updateEventMatchPredicate(predicate);
setBitsRuleType(newRuleType);
return true;
}
@@ -120,24 +193,52 @@ function EventMatchPredicateBuilderComponent(props: EventMatchPredicateBuilderCo
// break;
// }
const handleChangedCheerNameOrPrefix = (nameOrPrefix: string) => {
//const handleChangedCheerNameOrPrefix = (prefix: string) => {
const handleChangedCheerPrefix = (prefix: string) => {
let predicate : EventMatchPredicate = {};
switch (bitsRuleType) {
case BitsRuleType.BitsCheermoteNameExactMatch:
let joined = CheerUtil.joinCheerAndPrefix(prefix, bitsValue);
predicate.bits_cheermote_name_exact_match = {
cheermote_name: joined, // New value
}
break;
case BitsRuleType.BitsCheermotePrefixSpendThreshold:
predicate.bits_cheermote_prefix_spend_threshold = {
cheermote_prefix: prefix, // New value
minimum_bits_spent: bitsValue,
}
break;
case BitsRuleType.BitsSpendThreshold:
predicate.bits_spend_threshold = {
minimum_bits_spent: bitsValue,
}
break;
}
props.updateEventMatchPredicate(predicate);
}
const handleChangedCheerName = (name: string) => {
let predicate : EventMatchPredicate = {};
switch (bitsRuleType) {
case BitsRuleType.BitsCheermoteNameExactMatch:
predicate.bits_cheermote_name_exact_match = {
cheermote_name: nameOrPrefix, // New value
cheermote_name: name, // New value
}
break;
case BitsRuleType.BitsCheermotePrefixSpendThreshold:
let { cheerPrefix, bitValue } = CheerUtil.parseCheerString(name);
predicate.bits_cheermote_prefix_spend_threshold = {
cheermote_prefix: nameOrPrefix, // New value
minimum_bits_spent: minimumBitsSpent,
cheermote_prefix: cheerPrefix || name, // New value
minimum_bits_spent: bitValue || bitsValue,
}
break;
case BitsRuleType.BitsSpendThreshold:
predicate.bits_spend_threshold = {
minimum_bits_spent: minimumBitsSpent,
minimum_bits_spent: bitsValue,
}
break;
}
@@ -150,13 +251,14 @@ function EventMatchPredicateBuilderComponent(props: EventMatchPredicateBuilderCo
switch (bitsRuleType) {
case BitsRuleType.BitsCheermoteNameExactMatch:
let joined = CheerUtil.joinCheerAndPrefix(cheerPrefix, minimumSpent);
predicate.bits_cheermote_name_exact_match = {
cheermote_name: cheerNameOrPrefix, // Untouched existing value
cheermote_name: joined, // Combined value
}
break;
case BitsRuleType.BitsCheermotePrefixSpendThreshold:
predicate.bits_cheermote_prefix_spend_threshold = {
cheermote_prefix: cheerNameOrPrefix, // Untouched existing value
cheermote_prefix: cheerPrefix, // Untouched existing value
minimum_bits_spent: minimumSpent, // New value
}
break;
@@ -210,23 +312,22 @@ function EventMatchPredicateBuilderComponent(props: EventMatchPredicateBuilderCo
switch (bitsRuleType) {
case BitsRuleType.BitsCheermoteNameExactMatch:
matchingRulesForm = <BitsCheermoteNameExactMatchForm
cheerName={cheerNameOrPrefix}
minimumBitsSpent={minimumBitsSpent}
updateCheerNameOrPrefix={handleChangedCheerNameOrPrefix}
cheerState={cheerState}
updateCheerName={handleChangedCheerName}
updateCheerPrefix={handleChangedCheerPrefix}
updateMinimumBitsSpent={handleChangedMinimumBitsSpent} // NB: Technically not a field, but we can parse it out!
/>;
break;
case BitsRuleType.BitsCheermotePrefixSpendThreshold:
matchingRulesForm = <BitsCheermotePrefixSpendThresholdForm
cheerPrefix={cheerNameOrPrefix}
updateCheerNameOrPrefix={handleChangedCheerNameOrPrefix}
minimumBitsSpent={minimumBitsSpent}
cheerState={cheerState}
updateCheerPrefix={handleChangedCheerPrefix}
updateMinimumBitsSpent={handleChangedMinimumBitsSpent}
/>
break;
case BitsRuleType.BitsSpendThreshold:
matchingRulesForm = <BitsSpendThresholdForm
minimumBitsSpent={minimumBitsSpent}
cheerState={cheerState}
updateMinimumBitsSpent={handleChangedMinimumBitsSpent}
/>;
break;
@@ -1,63 +1,65 @@
import React, { useEffect, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faGem } from '@fortawesome/free-solid-svg-icons';
import { CHEER_BIT_LEVELS, CHEER_PREFIXES } from '../../../../twitch/Cheers';
import { CHEER_BIT_LEVELS, CHEER_PREFIXES, CHEER_PREFIX_TO_STRING_MAP } from '../../../../twitch/Cheers';
import { CheerState, CheerStateIsCustom, CheerStateIsOfficial } from '../CheerState';
// TODO: Don't duplicate
const CHEER_REGEX = /^([A-Za-z]+)(\d+)?$/;
interface BitsCheermoteNameExactMatchProps {
cheerName: string,
updateCheerNameOrPrefix: (cheerNameOrPrefix: string) => void,
cheerState: CheerState,
updateCheerName: (cheerNameOrPrefix: string) => void,
updateCheerPrefix: (cheerNameOrPrefix: string) => void,
// NB: 'minimumBitsSpent' and its update are technically not a field here, but we can parse it out
// to communicate back upstream in case the field changes, or infer it from a context switch to this
// field. Basically, we're trying not to lose information.
minimumBitsSpent: number,
updateMinimumBitsSpent: (minimumSpent: number) => void,
};
function BitsCheermoteNameExactMatchForm(props: BitsCheermoteNameExactMatchProps) {
// The two dropdowns
const [cheerPrefix, setCheerPrefix] = useState<string>(props.cheerName);
const [bitsValue, setBitsValue] = useState<number>(props.minimumBitsSpent);
const [cheerPrefix, setCheerPrefix] = useState<string>('');
const [bitsValue, setBitsValue] = useState<number>(1);
// The freeform text input
const [manualCheerValue, setManualCheerValue] = useState<string>(props.cheerName);
const [manualCheerValue, setManualCheerValue] = useState<string>('');
// NB: useState is not always setting from props correctly (after several re-renders)
// The following answers suggests using useEffect:
// https://stackoverflow.com/a/54866051 (less clear by also using useState(), but good comments)
// https://stackoverflow.com/a/62982753
useEffect(() => {
let newBits = props.minimumBitsSpent;
let newManualCheerValue = props.cheerName;
let joinBitsAndCheer = true;
let newPrefix = '';
let newBits = 1;
let newFullName = '';
// Cheer name is the full value, eg. 'Corgo100'
const matches = props.cheerName.trim().match(CHEER_REGEX)
if (CheerStateIsOfficial(props.cheerState)) {
if (!!props.cheerState.cheerPrefix) {
newPrefix = CHEER_PREFIX_TO_STRING_MAP.get((props.cheerState.cheerPrefix)) || '';
}
if (!!props.cheerState.bits && !isNaN(props.cheerState.bits) && props.cheerState.bits > 0) {
newBits = props.cheerState.bits;
newFullName = `${newPrefix}${newBits}`;
} else {
newFullName = newPrefix;
}
if (!!matches && matches.length > 1) {
setCheerPrefix(matches[1]); // First match group
if (matches.length == 3 && matches[2] !== undefined) {
// If we detect a number, let's not conjoin bits and cheer.
joinBitsAndCheer = false;
// NB(1): We're getting this value from a fullly named cheer, eg 'Corgo5000'.
// NB(2): The second match group can be 'undefined' if no number is present. (Optional matching, I guess.)
let maybeBits = parseInt(matches[2]);
if (!isNaN(maybeBits)) {
newBits = maybeBits;
}
} else if (CheerStateIsCustom(props.cheerState)) {
newFullName = props.cheerState.cheerFull || '';
if (!!props.cheerState.bits && !isNaN(props.cheerState.bits) && props.cheerState.bits > 0) {
newBits = props.cheerState.bits;
}
}
// NB: This is for arriving from "CheermotePrefixSpendThreshold", where the values are disjoint.
if (joinBitsAndCheer) {
newManualCheerValue = `${cheerPrefix}${newBits}`;
}
setManualCheerValue(newManualCheerValue); // The freeform text field
setCheerPrefix(newPrefix);
setBitsValue(newBits);
}, [props.cheerName, props.minimumBitsSpent]);
setManualCheerValue(newFullName);
}, [props.cheerState]);
const updateCheerPrefix = (ev: React.FormEvent<HTMLSelectElement>) : boolean => {
const value = (ev.target as HTMLSelectElement).value;
@@ -96,7 +98,7 @@ function BitsCheermoteNameExactMatchForm(props: BitsCheermoteNameExactMatchProps
}
setManualCheerValue(value);
props.updateCheerNameOrPrefix(value);
props.updateCheerName(value);
return true;
}
@@ -107,7 +109,7 @@ function BitsCheermoteNameExactMatchForm(props: BitsCheermoteNameExactMatchProps
}
const cheerValue = `${prefix}${bits}`;
setManualCheerValue(cheerValue);
props.updateCheerNameOrPrefix(cheerValue);
props.updateCheerName(cheerValue);
props.updateMinimumBitsSpent(bits); // NB: Technically not in this form, but helps when switching
}
@@ -1,24 +1,25 @@
import React, { useEffect, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faGem } from '@fortawesome/free-solid-svg-icons';
import { CHEER_BIT_LEVELS, CHEER_PREFIXES } from '../../../../twitch/Cheers';
import { CHEER_BIT_LEVELS, CHEER_PREFIXES, CHEER_PREFIX_TO_STRING_MAP } from '../../../../twitch/Cheers';
import { CheerState, CheerStateIsCustom, CheerStateIsOfficial } from '../CheerState';
// TODO: Don't duplicate
const CHEER_REGEX = /^([A-Za-z]+)(\d+)?$/;
interface BitsCheermotePrefixSpendThresholdProps {
cheerPrefix: string,
updateCheerNameOrPrefix: (cheerNameOrPrefix: string) => void,
minimumBitsSpent: number,
cheerState: CheerState,
updateCheerPrefix: (cheerNameOrPrefix: string) => void,
updateMinimumBitsSpent: (minimumSpent: number) => void,
};
function BitsCheermotePrefixSpendThresholdForm(props: BitsCheermotePrefixSpendThresholdProps) {
const [cheerPrefix, setCheerPrefix] = useState<string>(props.cheerPrefix);
const [customCheerPrefix, setCustomCheerPrefix] = useState<string>(props.cheerPrefix);
const [cheerPrefix, setCheerPrefix] = useState<string>('');
const [customCheerPrefix, setCustomCheerPrefix] = useState<string>('');
const [bitsValue, setBitsValue] = useState<number>(props.minimumBitsSpent);
const [customBitsValue, setCustomBitsValue] = useState<number>(props.minimumBitsSpent);
const [bitsValue, setBitsValue] = useState<number>(1);
const [customBitsValue, setCustomBitsValue] = useState<number>(1);
// This handles initialization after mount.
// NB: useState is not always setting from props correctly (after several re-renders)
@@ -26,33 +27,37 @@ function BitsCheermotePrefixSpendThresholdForm(props: BitsCheermotePrefixSpendTh
// https://stackoverflow.com/a/54866051 (less clear by also using useState(), but good comments)
// https://stackoverflow.com/a/62982753
useEffect(() => {
let newBitsValue = 1;
let newPrefix = '';
let newCustomPrefix = '';
let newBits = 1;
let newCustomBits = 1;
// Cheer prefix could be just a prefix (eg. 'Corgo') OR the full name (eg. 'Corgo100')
// This depends on what the previous view was.
const matches = props.cheerPrefix.trim().match(CHEER_REGEX)
if (CheerStateIsOfficial(props.cheerState)) {
if (!!props.cheerState.cheerPrefix) {
newPrefix = CHEER_PREFIX_TO_STRING_MAP.get((props.cheerState.cheerPrefix)) || '';
newCustomPrefix = CHEER_PREFIX_TO_STRING_MAP.get((props.cheerState.cheerPrefix)) || '';
}
if (!!props.cheerState.bits && !isNaN(props.cheerState.bits) && props.cheerState.bits > 0) {
newBits = props.cheerState.bits;
newCustomBits = props.cheerState.bits;
}
if (!!matches && matches.length > 1) {
// First match group
setCheerPrefix(matches[1]);
setCustomCheerPrefix(matches[1]);
if (matches.length == 3 && matches[2] !== undefined) {
// NB: Second match group can be 'undefined' if no number is present. (Zero-width matching?)
let maybeValid = parseInt(matches[2]);
if (!isNaN(maybeValid)) {
newBitsValue = maybeValid;
}
} else if (CheerStateIsCustom(props.cheerState)) {
newCustomPrefix = props.cheerState.cheerFull || '';
if (!!props.cheerState.bits && !isNaN(props.cheerState.bits) && props.cheerState.bits > 0) {
newBits = props.cheerState.bits;
newCustomBits = props.cheerState.bits;
}
}
if (!!props.minimumBitsSpent && !isNaN(props.minimumBitsSpent) && props.minimumBitsSpent > 0) {
newBitsValue = props.minimumBitsSpent;
}
setCheerPrefix(newPrefix);
setCustomCheerPrefix(newCustomPrefix);
setBitsValue(newBits);
setCustomBitsValue(newCustomBits);
setBitsValue(newBitsValue);
setCustomBitsValue(newBitsValue);
}, [props.cheerPrefix, props.minimumBitsSpent]);
}, [props.cheerState]);
const updateCheerPrefix = (ev: React.FormEvent<HTMLSelectElement>) : boolean => {
const value = (ev.target as HTMLSelectElement).value;
@@ -64,7 +69,7 @@ function BitsCheermotePrefixSpendThresholdForm(props: BitsCheermotePrefixSpendTh
const updateTextCheerValue = (ev: React.FormEvent<HTMLInputElement>) : boolean => {
const value = (ev.target as HTMLInputElement).value;
setCustomCheerPrefix(value);
props.updateCheerNameOrPrefix(value);
props.updateCheerPrefix(value);
return true;
}
@@ -2,27 +2,28 @@ import React, { useEffect, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faGem } from '@fortawesome/free-solid-svg-icons';
import { CHEER_BIT_LEVELS } from '../../../../twitch/Cheers';
import { CheerState } from '../CheerState';
interface BitsSpendThresholdFormProps {
minimumBitsSpent: number,
cheerState: CheerState,
updateMinimumBitsSpent: (minimumSpent: number) => void,
};
function BitsSpendThresholdForm(props: BitsSpendThresholdFormProps) {
// The dropdown
const [bitsValue, setBitsValue] = useState<number>(props.minimumBitsSpent);
const [bitsValue, setBitsValue] = useState<number>(1);
// The freeform text input
const [customAmount, setCustomAmount] = useState<number>(props.minimumBitsSpent);
const [customAmount, setCustomAmount] = useState<number>(1);
// NB: useState is not always setting from props correctly (after several re-renders) // The following answers suggests using useEffect:
// https://stackoverflow.com/a/54866051 (less clear by also using useState(), but good comments)
// https://stackoverflow.com/a/62982753
useEffect(() => {
if (!isNaN(props.minimumBitsSpent)) {
setBitsValue(props.minimumBitsSpent)
setCustomAmount(props.minimumBitsSpent)
if (!!props.cheerState.bits && !isNaN(props.cheerState.bits) && props.cheerState.bits > 0) {
setBitsValue(props.cheerState.bits);
setCustomAmount(props.cheerState.bits);
}
}, [props.minimumBitsSpent]);
}, [props.cheerState]);
const handleBitSelect = (ev: React.FormEvent<HTMLSelectElement>) : boolean => {
const value = (ev.target as HTMLSelectElement).value;
+75
View File
@@ -1,5 +1,39 @@
export enum TwitchCheerPrefix {
Cheer = "Cheer",
DoodleCheer = "DoodleCheer",
BibleThump = "BibleThump",
Cheerwhal = "cheerwhal",
Corgo = "Corgo",
Uni = "uni",
ShowLove = "ShowLove",
Party = "Party",
SeemsGood = "SeemsGood",
Pride = "Pride",
Kappa = "Kappa",
FrankerZ = "FrankerZ",
HeyGuys = "HeyGuys",
DansGame = "DansGame",
EleGiggle = "EleGiggle",
TriHard = "TriHard",
Kreygasm = "Kreygasm",
FourHead = "4Head",
SwiftRage = "SwiftRage",
NotLikeThis = "NotLikeThis",
FailFish = "FailFish",
VoHiYo = "VoHiYo",
PjSalt = "PJSalt",
MrDestructoid = "MrDestructoid",
BDay = "bday",
RipChear = "RIPCheer",
Shamrock = "Shamrock",
BitBoss = "BitBoss",
Streamlabs = "Streamlabs",
Muxy = "Muxy",
HolidayCheer = "HolidayCheer",
}
// TODO: Immutability
/// This list seems pretty comprehensive
/// https://github.com/nossebro/TwitchPubSubMirror/blob/master/TwitchPubSubMirror_StreamlabsSystem.py
export const CHEER_PREFIXES : string[] = [
@@ -36,5 +70,46 @@ export const CHEER_PREFIXES : string[] = [
"HolidayCheer"
];
// TODO: Immutability
export const CHEER_LOOKUP_MAP : Map<string, TwitchCheerPrefix> = new Map([
["Cheer", TwitchCheerPrefix.Cheer],
["DoodleCheer", TwitchCheerPrefix.DoodleCheer],
["BibleThump", TwitchCheerPrefix.BibleThump],
["cheerwhal", TwitchCheerPrefix.Cheerwhal],
["Corgo", TwitchCheerPrefix.Corgo],
["uni", TwitchCheerPrefix.Uni],
["ShowLove", TwitchCheerPrefix.ShowLove],
["Party", TwitchCheerPrefix.Party],
["SeemsGood", TwitchCheerPrefix.SeemsGood],
["Pride", TwitchCheerPrefix.Pride],
["Kappa", TwitchCheerPrefix.Kappa],
["FrankerZ", TwitchCheerPrefix.FrankerZ],
["HeyGuys", TwitchCheerPrefix.HeyGuys],
["DansGame", TwitchCheerPrefix.DansGame],
["EleGiggle", TwitchCheerPrefix.EleGiggle],
["TriHard", TwitchCheerPrefix.TriHard],
["Kreygasm", TwitchCheerPrefix.Kreygasm],
["4Head", TwitchCheerPrefix.FourHead],
["SwiftRage", TwitchCheerPrefix.SwiftRage],
["NotLikeThis", TwitchCheerPrefix.NotLikeThis],
["FailFish", TwitchCheerPrefix.NotLikeThis],
["VoHiYo", TwitchCheerPrefix.VoHiYo],
["PJSalt", TwitchCheerPrefix.PjSalt],
["MrDestructoid", TwitchCheerPrefix.MrDestructoid],
["bday", TwitchCheerPrefix.BDay],
["RIPCheer", TwitchCheerPrefix.BDay],
["Shamrock", TwitchCheerPrefix.Shamrock],
["BitBoss", TwitchCheerPrefix.BitBoss],
["Streamlabs", TwitchCheerPrefix.Streamlabs],
["Muxy", TwitchCheerPrefix.Muxy],
["HolidayCheer", TwitchCheerPrefix.HolidayCheer],
]);
// TODO: Immutability
export const CHEER_PREFIX_TO_STRING_MAP : Map<TwitchCheerPrefix, string> = new Map(
Array.from(CHEER_LOOKUP_MAP, entry => [entry[1], entry[0]])
)
// TODO: Immutability
/// Levels of cheering
export const CHEER_BIT_LEVELS = [1, 100, 1000, 5000, 10000];