From 71c0b96091a323d9e29f08960dad613d2691422a Mon Sep 17 00:00:00 2001 From: Ash Keel Date: Fri, 5 Nov 2021 22:54:42 +0100 Subject: [PATCH] Timers! --- frontend/src/lib/react-utils.ts | 3 +- frontend/src/locale/en/translation.json | 5 +- frontend/src/ui/components/Interval.tsx | 33 +++- frontend/src/ui/pages/loyalty/Rewards.tsx | 2 +- frontend/src/ui/pages/twitch/Commands.tsx | 2 +- frontend/src/ui/pages/twitch/Timers.tsx | 203 ++++++++++++++++------ modules/twitch/modules.go | 1 + modules/twitch/modules.timer.go | 15 +- 8 files changed, 192 insertions(+), 72 deletions(-) diff --git a/frontend/src/lib/react-utils.ts b/frontend/src/lib/react-utils.ts index 0ddc0e7..10f751b 100644 --- a/frontend/src/lib/react-utils.ts +++ b/frontend/src/lib/react-utils.ts @@ -1,5 +1,5 @@ import { ActionCreatorWithOptionalPayload, AsyncThunk } from '@reduxjs/toolkit'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { KilovoltMessage, @@ -8,7 +8,6 @@ import { import { RootState } from '../store'; import apiReducer, { getUserPoints } from '../store/api/reducer'; import { APIState, LoyaltyStorage } from '../store/api/types'; -import { getInterval } from './time-utils'; export function useModule({ key, diff --git a/frontend/src/locale/en/translation.json b/frontend/src/locale/en/translation.json index 12b8b51..393c0cb 100644 --- a/frontend/src/locale/en/translation.json +++ b/frontend/src/locale/en/translation.json @@ -218,7 +218,10 @@ "minimum-delay": "Interval", "minimum-delay-help": "How many time must pass between each repeat.", "minimum-activity": "Minimum chat activity", - "minimum-activity-post": "messages in the last 5 minutes" + "minimum-activity-post": "messages in the last 5 minutes", + "condition-text": "every {{time}}, at least {{messages}} messages in the last 5 minutes", + "err-twitchbot-disabled": "Twitch bot must be enabled in order to use timers!", + "enable": "Enable timers" } } } diff --git a/frontend/src/ui/components/Interval.tsx b/frontend/src/ui/components/Interval.tsx index 6f034a9..6a8a578 100644 --- a/frontend/src/ui/components/Interval.tsx +++ b/frontend/src/ui/components/Interval.tsx @@ -2,27 +2,40 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { getInterval } from '../../lib/time-utils'; +export interface TimeUnit { + multiplier: number; + unit: string; +} + +export const seconds = { multiplier: 1, unit: 'form-common.time.seconds' }; +export const minutes = { multiplier: 60, unit: 'form-common.time.minutes' }; +export const hours = { multiplier: 3600, unit: 'form-common.time.hours' }; + export interface IntervalProps { active: boolean; value: number; min?: number; + units?: TimeUnit[]; onChange?: (value: number) => void; } -function Interval({ active, value, min, onChange }: IntervalProps) { +function Interval({ active, value, min, units, onChange }: IntervalProps) { const { t } = useTranslation(); + const timeUnits = units ?? [seconds, minutes, hours]; + const [numInitialValue, multInitialValue] = getInterval(value); const [num, setNum] = useState(numInitialValue); const [mult, setMult] = useState(multInitialValue); useEffect(() => { - const seconds = num * mult; - if (min && seconds < min) { - setNum(5); - setMult(1); + const total = num * mult; + if (min && total < min) { + const [minNum, minMult] = getInterval(min); + setNum(minNum); + setMult(minMult); } - onChange(Math.max(min ?? 0, seconds)); + onChange(Math.max(min ?? 0, total)); }, [num, mult]); return ( @@ -57,9 +70,11 @@ function Interval({ active, value, min, onChange }: IntervalProps) { setMult(intMult); }} > - - - + {timeUnits.map((unit) => ( + + ))}

diff --git a/frontend/src/ui/pages/loyalty/Rewards.tsx b/frontend/src/ui/pages/loyalty/Rewards.tsx index ad12ee2..9ba58b8 100644 --- a/frontend/src/ui/pages/loyalty/Rewards.tsx +++ b/frontend/src/ui/pages/loyalty/Rewards.tsx @@ -90,7 +90,7 @@ function RewardItem({ {t('actions.test')} {' '} - {t(item.enabled ? 'actions.disable' : 'actions.enable')} + {item.enabled ? t('actions.disable') : t('actions.enable')} {' '} {t('actions.edit')} diff --git a/frontend/src/ui/pages/twitch/Commands.tsx b/frontend/src/ui/pages/twitch/Commands.tsx index db4278b..72644e3 100644 --- a/frontend/src/ui/pages/twitch/Commands.tsx +++ b/frontend/src/ui/pages/twitch/Commands.tsx @@ -54,7 +54,7 @@ function CommandItem({
{item.response}
- {item.enabled ? 'Disable' : 'Enable'} + {item.enabled ? t('actions.disable') : t('actions.enable')} {' '} {t('actions.edit')} diff --git a/frontend/src/ui/pages/twitch/Timers.tsx b/frontend/src/ui/pages/twitch/Timers.tsx index 68065f3..f13e1dc 100644 --- a/frontend/src/ui/pages/twitch/Timers.tsx +++ b/frontend/src/ui/pages/twitch/Timers.tsx @@ -2,12 +2,13 @@ import { RouteComponentProps } from '@reach/router'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; +import prettyTime from 'pretty-ms'; import { useModule } from '../../../lib/react-utils'; import { modules } from '../../../store/api/reducer'; import Modal from '../../components/Modal'; import { TwitchBotTimer } from '../../../store/api/types'; import Field from '../../components/Field'; -import Interval from '../../components/Interval'; +import Interval, { hours, minutes } from '../../components/Interval'; interface TimerItemProps { item: TwitchBotTimer; @@ -24,7 +25,14 @@ function TimerItem({ item, onToggleState, onEdit, onDelete }: TimerItemProps) {
{item.enabled ? ( - {item.name} + <> + {item.name} ( + {t('twitch.timers.condition-text', { + time: prettyTime(item.minimum_delay * 1000), + messages: item.minimum_chat_activity, + })} + ) + ) : ( {item.name} @@ -44,12 +52,12 @@ function TimerItem({ item, onToggleState, onEdit, onDelete }: TimerItemProps) { {expanded ? (
{t('twitch.timers.messages')}:{' '} - {item.messages.map((message) => ( -
{message}
+ {item.messages.map((message, index) => ( +
{message}
))}
- {item.enabled ? 'Disable' : 'Enable'} + {item.enabled ? t('actions.disable') : t('actions.enable')} {' '} {t('actions.edit')} @@ -91,15 +99,16 @@ function TimerModal({ ); const { t } = useTranslation(); - const validForm = name !== '' && messages.length > 0 && messages[0] !== ''; + const validForm = + name !== '' && messages.length > 0 && messages.every((msg) => msg !== ''); const confirm = () => { if (onConfirm) { onConfirm(name, { name, messages, - minimum_chat_activity: 0, - minimum_delay: 0, + minimum_chat_activity: minActivity, + minimum_delay: minDelay, enabled: initialData?.enabled ?? false, }); } @@ -126,7 +135,7 @@ function TimerModal({
-

+

setName(ev.target.value)} /> -

+
@@ -145,7 +154,8 @@ function TimerModal({ value={minDelay} onChange={setMinDelay} active={active} - min={5} + min={60} + units={[minutes, hours]} />
@@ -153,7 +163,7 @@ function TimerModal({
-

+

-

+

{t('twitch.timers.minimum-activity-post')} @@ -180,18 +190,49 @@ function TimerModal({

- {messages.map((message, index) => ( -
+
+ {messages.map((message, index) => ( +
0 ? '0.5rem' : '' }} + > +

+ setMessageIndex(ev.target.value, index)} + value={message} + className={message !== '' ? 'input' : 'input is-danger'} + style={{ width: '28rem' }} + /> +

+

+ +

+
+ ))} +

-