Skip to content

Commit

Permalink
Merge pull request alveusgg#51 from MattIPv4/MattIPv4/repeated-command
Browse files Browse the repository at this point in the history
Allow same command repeatedly, use callback instead of return
  • Loading branch information
abdullahmorrison authored Feb 5, 2023
2 parents 2925b1e + f991234 commit f4e8ca9
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 48 deletions.
62 changes: 33 additions & 29 deletions src/pages/overlay/components/overlay/Overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// utils
import { useEffect, useRef, useReducer } from 'react'
import { useEffect, useRef, useReducer, useCallback, useState } from 'react'
import { ACTIONS, OverlayReducer } from './overlay.reducer'

//components & hooks
Expand All @@ -26,37 +26,41 @@ interface OverlayProps {
export default function Overlay(props: OverlayProps) {
const { sleeping, awoken, wake, settings } = props

const [chosenAmbassador, setChosenAmbassador] = useState<string | undefined>(undefined)
const [{showAmbassadorList, showAlveusIntro}, dispatch] = useReducer(OverlayReducer, {
showAmbassadorList: false,
showAlveusIntro: false
})

const command = useChatCommand()
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined)
const awakingRef = useRef(false)
const commandDelay = 8000

// When a chat command is run, show the list and auto-dismiss it after 6s
useEffect(() => {
if (command !== undefined && !settings.disableChatPopup) {
if(command === '!welcome')
dispatch({type: ACTIONS.SHOW_ALVEUS_INTRO})
else{
// Show the list, and dismiss it after 6s
dispatch({type: ACTIONS.SHOW_AMBASSADOR_LIST})
timeoutRef.current = setTimeout(() => {dispatch({type: ACTIONS.HIDE_AMBASSADOR_LIST})}, 6000)
// When a chat command is run, wake the overlay
useChatCommand(useCallback((command: string) => {
if (!settings.disableChatPopup) {
if (command !== '!welcome') setChosenAmbassador(command.slice(1))

// Track that we're waking up, so that we don't immediately clear the timeout
awakingRef.current = true
}
// Show the card
dispatch({type: command === '!welcome' ? ACTIONS.SHOW_ALVEUS_INTRO : ACTIONS.SHOW_AMBASSADOR_LIST})

// Wake the overlay for 8s
wake(8000)
// Dismiss the overlay after a delay
if (timeoutRef.current) clearTimeout(timeoutRef.current)
timeoutRef.current = setTimeout(() => {
dispatch({type: command === '!welcome' ? ACTIONS.HIDE_ALVEUS_INTRO : ACTIONS.HIDE_AMBASSADOR_LIST})
}, commandDelay)

// Track that we're waking up, so that we don't immediately clear the timeout, and wake the overlay
awakingRef.current = true
wake(commandDelay)
}
}, [settings.disableChatPopup, commandDelay, wake]))

// Ensure we clean up the timer when we unmount
useEffect(() => {
return () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current)
}
}, [command, settings.disableChatPopup, wake])
}, [])

// If the user interacts with the overlay, clear the auto-dismiss timer
useEffect(() => {
Expand All @@ -70,17 +74,17 @@ export default function Overlay(props: OverlayProps) {

return (
<div className={`${styles.overlay} ${sleeping ? styles.hidden : styles.visible}`} >
<ActivationButtons
toggleShowAmbassadorList={() => dispatch({type: showAmbassadorList ? ACTIONS.HIDE_AMBASSADOR_LIST : ACTIONS.SHOW_AMBASSADOR_LIST})}
toggleShowAlveusIntro={() => dispatch({type: showAlveusIntro ? ACTIONS.HIDE_ALVEUS_INTRO : ACTIONS.SHOW_ALVEUS_INTRO})}
/>
<AlveusIntro
showAlveusIntro={showAlveusIntro}
/>
<AmbassadorList
showAmbassadorList={showAmbassadorList}
chatChosenAmbassador={command?.slice(1)}
/>
<ActivationButtons
toggleShowAmbassadorList={() => dispatch({type: showAmbassadorList ? ACTIONS.HIDE_AMBASSADOR_LIST : ACTIONS.SHOW_AMBASSADOR_LIST})}
toggleShowAlveusIntro={() => dispatch({type: showAlveusIntro ? ACTIONS.HIDE_ALVEUS_INTRO : ACTIONS.SHOW_ALVEUS_INTRO})}
/>
<AlveusIntro
showAlveusIntro={showAlveusIntro}
/>
<AmbassadorList
showAmbassadorList={showAmbassadorList}
chatChosenAmbassador={chosenAmbassador}
/>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import useChatCommand from "../../../../utils/chatCommand";
import styles from './ambassadorPanel.module.css'

//data
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react"
import AmbassadorData from "../../../../assets/ambassadors.json";


export default function AmbassadorPanel() {
const [ambassadors] = useState(AmbassadorData)
const [ambassadorCard, setAmbassadorCard] = useState("") //name of ambassador that will show up as a modal
const chosenAmbassador = useChatCommand()?.slice(1)

const [chosenAmbassador, setChosenAmbassador] = useState<string | undefined>(undefined)
useChatCommand(useCallback((command: string) => {
setChosenAmbassador(command.slice(1))
}, []))

useEffect(() => {
if (chosenAmbassador !== undefined)
Expand Down
45 changes: 28 additions & 17 deletions src/utils/chatCommand.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useState, useMemo } from 'react'
import tmi, { ChatUserstate } from 'tmi.js'
import AmbassadorData from '../assets/ambassadors.json'

Expand Down Expand Up @@ -30,10 +30,22 @@ const getMapOfAmbassadorWithDiacritics = (): Map<string, string> => {
return diacriticMap
}

export default function useChatCommand() {
const [command, setCommand] = useState<string>()
const [ambassadorNames] = useState(AmbassadorData.map((ambassador) => ambassador.name.split(' ')[0].toLowerCase()))
const [diacriticsMap] = useState<Map<string, string>>(getMapOfAmbassadorWithDiacritics())
export default function useChatCommand(callback: (command: string) => void) {
const commandsMap = useMemo<Map<string, string>>(() => {
// Map the normalized names to their original names
const commands = getMapOfAmbassadorWithDiacritics()

// Add the original names to the map, pointing to themselves
AmbassadorData.forEach((ambassador) => {
const name = ambassador.name.split(' ')[0].toLowerCase()
commands.set(name, name)
})

// Welcome is also a valid command
commands.set('welcome', 'welcome')

return commands
}, [])

const [client] = useState(new tmi.Client({
connection: {
Expand All @@ -53,25 +65,24 @@ export default function useChatCommand() {
// Ignore echoed messages (messages sent by the bot) and messages that don't start with '!'
if (self || !msg.trim().startsWith('!')) return

const commandName = msg.trim().toLowerCase()
if(commandName === '!welcome') {
setCommand(commandName)
}else if (ambassadorNames.find((name) => name === commandName.slice(1))) {
setCommand(commandName)
} else if (diacriticsMap.get(commandName.slice(1))) { // Check if a user typed a name without diacritics (Ex: !jalapeno should be !Jalapeño)
setCommand("!"+diacriticsMap.get(commandName.slice(1)))
const commandName = msg.trim().toLowerCase().slice(1)
const command = commandsMap.get(commandName)
if (command) callback('!'+command)
}, [commandsMap, callback])

useEffect(() => {
client.addListener('message', messageHandler)
return () => {
client.removeListener('message', messageHandler)
}
}, [ambassadorNames, diacriticsMap])
}, [client, messageHandler])

const connectedHandler = useCallback(() => {
console.log('*Twitch extension is connected to chat*')
}, [])

useEffect(() => {
client.on('message', messageHandler)
client.on('connected', connectedHandler)
client.connect()
}, [client, messageHandler, connectedHandler])

return command
}, [client, connectedHandler])
}

0 comments on commit f4e8ca9

Please sign in to comment.