mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
fixes, works on "prefix + bits" form
This commit is contained in:
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,
|
||||
};
|
||||
|
||||
+16
-10
@@ -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
|
||||
|
||||
------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
+124
-23
@@ -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;
|
||||
|
||||
+34
-32
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+35
-30
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
Reference in new issue
Block a user