2024-06-24 18:16:55 -04:00
|
|
|
import axios from "axios";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
|
|
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
2024-08-25 17:35:46 -04:00
|
|
|
import { Label } from "@/components/ui/label";
|
2024-06-24 18:16:55 -04:00
|
|
|
import { HelpCircleIcon, Trash2Icon } from "lucide-react";
|
|
|
|
import {
|
|
|
|
Tooltip,
|
|
|
|
TooltipContent,
|
|
|
|
TooltipProvider,
|
|
|
|
TooltipTrigger,
|
2024-08-25 17:35:46 -04:00
|
|
|
} from "@/components/ui/tooltip"
|
2024-06-24 18:16:55 -04:00
|
|
|
|
|
|
|
interface Redemption {
|
|
|
|
id: string | undefined
|
|
|
|
redemptionId: string | undefined
|
|
|
|
actionName: string
|
2024-08-14 16:33:40 -04:00
|
|
|
numbering: number,
|
2024-06-24 18:16:55 -04:00
|
|
|
edit: boolean
|
|
|
|
showEdit: boolean
|
|
|
|
isNew: boolean
|
|
|
|
actions: string[]
|
|
|
|
twitchRedemptions: { id: string, title: string }[]
|
|
|
|
adder: (id: string, actionName: string, redemptionId: string, order: number) => void
|
|
|
|
remover: (redemption: { id: string, redemptionId: string, actionName: string, order: number }) => void
|
|
|
|
}
|
|
|
|
|
|
|
|
const OBSRedemption = ({
|
|
|
|
id,
|
|
|
|
redemptionId,
|
|
|
|
actionName,
|
2024-08-14 16:33:40 -04:00
|
|
|
numbering,
|
2024-06-24 18:16:55 -04:00
|
|
|
edit,
|
|
|
|
showEdit,
|
|
|
|
isNew,
|
|
|
|
actions,
|
|
|
|
twitchRedemptions,
|
|
|
|
adder,
|
|
|
|
remover
|
|
|
|
}: Redemption) => {
|
|
|
|
const [actionOpen, setActionOpen] = useState(false)
|
|
|
|
const [redemptionOpen, setRedemptionOpen] = useState(false)
|
|
|
|
const [twitchRedemption, setTwitchRedemption] = useState<{ id: string, title: string } | undefined>(undefined)
|
|
|
|
const [action, setAction] = useState<string | undefined>(actionName)
|
2024-08-14 16:33:40 -04:00
|
|
|
const [order, setOrder] = useState<number>(numbering)
|
2024-06-24 18:16:55 -04:00
|
|
|
const [isEditable, setIsEditable] = useState(edit)
|
|
|
|
const [oldData, setOldData] = useState<{ r: { id: string, title: string } | undefined, a: string | undefined, o: number } | undefined>(undefined)
|
2024-08-25 17:35:46 -04:00
|
|
|
const [error, setError] = useState<string | undefined>(undefined)
|
2024-06-24 18:16:55 -04:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setTwitchRedemption(twitchRedemptions.find(r => r.id == redemptionId))
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
function Save() {
|
2024-08-25 17:35:46 -04:00
|
|
|
setError(undefined)
|
|
|
|
if (!isNew && !id) {
|
|
|
|
setError('Something went wrong. Refresh the page.')
|
2024-06-24 18:16:55 -04:00
|
|
|
return
|
2024-08-25 17:35:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!action) {
|
|
|
|
setError('An action must be selected.')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (!actions.some(a => a == action)) {
|
|
|
|
setError('Ensure the action selected still exists.')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!twitchRedemption) {
|
|
|
|
setError('A Twitch redemption must be selected.')
|
2024-06-24 18:16:55 -04:00
|
|
|
return
|
2024-08-25 17:35:46 -04:00
|
|
|
}
|
|
|
|
if (!twitchRedemptions.some(r => r.id == twitchRedemption.id)) {
|
|
|
|
setError('Ensure the action selected still exists.')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isNaN(order)) {
|
|
|
|
setError('The order cannot be NaN.')
|
|
|
|
setOrder(0)
|
|
|
|
return
|
|
|
|
}
|
2024-06-24 18:16:55 -04:00
|
|
|
|
|
|
|
if (isNew) {
|
|
|
|
axios.post("/api/settings/redemptions", {
|
|
|
|
actionName: action,
|
|
|
|
redemptionId: twitchRedemption?.id,
|
|
|
|
order: order,
|
|
|
|
state: true
|
|
|
|
}).then(d => {
|
2024-08-14 16:33:40 -04:00
|
|
|
adder(d.data.id, action, twitchRedemption.id, order)
|
2024-06-24 18:16:55 -04:00
|
|
|
setAction(undefined)
|
|
|
|
setTwitchRedemption(undefined)
|
|
|
|
setOrder(0)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
axios.put("/api/settings/redemptions", {
|
|
|
|
id: id,
|
|
|
|
actionName: action,
|
|
|
|
redemptionId: twitchRedemption?.id,
|
|
|
|
order: order,
|
|
|
|
state: true
|
|
|
|
}).then(d => {
|
|
|
|
setIsEditable(false)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function Cancel() {
|
|
|
|
if (!oldData)
|
|
|
|
return
|
|
|
|
|
2024-08-25 17:35:46 -04:00
|
|
|
setError(undefined)
|
2024-06-24 18:16:55 -04:00
|
|
|
setAction(oldData.a)
|
|
|
|
setTwitchRedemption(oldData.r)
|
|
|
|
setOrder(oldData.o)
|
|
|
|
setIsEditable(false)
|
|
|
|
setOldData(undefined)
|
|
|
|
}
|
|
|
|
|
|
|
|
function Delete() {
|
|
|
|
axios.delete("/api/settings/redemptions?id=" + id)
|
|
|
|
.then(d => {
|
|
|
|
remover(d.data)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className="bg-orange-300 p-5 border-2 border-orange-400 rounded-lg w-[830px]">
|
|
|
|
<div
|
|
|
|
className="pb-4">
|
|
|
|
<Label
|
|
|
|
className="mr-2"
|
|
|
|
htmlFor="redemption">
|
|
|
|
Twitch Redemption
|
|
|
|
</Label>
|
|
|
|
{!isEditable &&
|
|
|
|
<Input
|
|
|
|
className="inline-block w-[290px] justify-between"
|
|
|
|
name="redemption"
|
|
|
|
value={twitchRedemption?.title}
|
|
|
|
readOnly />
|
|
|
|
|| isEditable &&
|
|
|
|
<Popover
|
|
|
|
open={redemptionOpen}
|
|
|
|
onOpenChange={setRedemptionOpen}>
|
|
|
|
<PopoverTrigger asChild>
|
|
|
|
<Button
|
|
|
|
variant="outline"
|
|
|
|
role="combobox"
|
|
|
|
aria-expanded={actionOpen}
|
|
|
|
className="w-[290px] justify-between"
|
|
|
|
>{!twitchRedemption ? "Select one..." : twitchRedemption.title}</Button>
|
|
|
|
</PopoverTrigger>
|
|
|
|
<PopoverContent>
|
|
|
|
<Command>
|
|
|
|
<CommandInput
|
|
|
|
placeholder="Filter redemptions..."
|
|
|
|
autoFocus={true} />
|
|
|
|
<CommandList>
|
|
|
|
<CommandEmpty>No redemption found.</CommandEmpty>
|
|
|
|
<CommandGroup>
|
|
|
|
{twitchRedemptions.map((redemption) => (
|
|
|
|
<CommandItem
|
|
|
|
value={redemption.title}
|
|
|
|
key={redemption.id}
|
|
|
|
onSelect={(value) => {
|
2024-08-25 17:35:46 -04:00
|
|
|
console.log('tr', value, redemption.id, redemption.title)
|
|
|
|
setTwitchRedemption(redemption)
|
2024-06-24 18:16:55 -04:00
|
|
|
setRedemptionOpen(false)
|
|
|
|
}}>
|
|
|
|
{redemption.title}
|
|
|
|
</CommandItem>
|
|
|
|
))}
|
|
|
|
</CommandGroup>
|
|
|
|
</CommandList>
|
|
|
|
</Command>
|
|
|
|
</PopoverContent>
|
|
|
|
</Popover>
|
|
|
|
}
|
|
|
|
<Label
|
|
|
|
className="ml-10 mr-2"
|
|
|
|
htmlFor="action">
|
|
|
|
Action
|
|
|
|
</Label>
|
|
|
|
{!isEditable &&
|
|
|
|
<Input
|
|
|
|
className="inline-block w-[290px] justify-between"
|
|
|
|
name="action"
|
|
|
|
value={action}
|
|
|
|
readOnly />
|
|
|
|
|| isEditable &&
|
|
|
|
<Popover
|
|
|
|
open={actionOpen}
|
|
|
|
onOpenChange={setActionOpen}>
|
|
|
|
<PopoverTrigger asChild>
|
|
|
|
<Button
|
|
|
|
variant="outline"
|
|
|
|
role="combobox"
|
|
|
|
aria-expanded={actionOpen}
|
|
|
|
className="w-[290px] justify-between">
|
|
|
|
{!action ? "Select one..." : action}
|
|
|
|
</Button>
|
|
|
|
</PopoverTrigger>
|
|
|
|
<PopoverContent>
|
|
|
|
<Command>
|
|
|
|
<CommandInput
|
|
|
|
placeholder="Filter actions..."
|
|
|
|
autoFocus={true} />
|
|
|
|
<CommandList>
|
|
|
|
<CommandEmpty>No action found.</CommandEmpty>
|
|
|
|
<CommandGroup>
|
|
|
|
{actions.map((action) => (
|
|
|
|
<CommandItem
|
|
|
|
value={action}
|
|
|
|
key={action}
|
|
|
|
onSelect={(value) => {
|
|
|
|
let a = actions.find(v => v == action)
|
|
|
|
if (a)
|
|
|
|
setAction(a)
|
|
|
|
setActionOpen(false)
|
|
|
|
}}>
|
|
|
|
{action}
|
|
|
|
</CommandItem>
|
|
|
|
))}
|
|
|
|
</CommandGroup>
|
|
|
|
</CommandList>
|
|
|
|
</Command>
|
|
|
|
</PopoverContent>
|
|
|
|
</Popover>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
className="pb-4">
|
|
|
|
<Label
|
|
|
|
className="mr-2"
|
|
|
|
htmlFor="order">
|
|
|
|
Order
|
|
|
|
</Label>
|
|
|
|
<Input
|
|
|
|
className="inline-block w-[300px]"
|
|
|
|
id="name"
|
|
|
|
placeholder="Enter an order number for this action"
|
2024-08-25 17:35:46 -04:00
|
|
|
onChange={e => setOrder(d => {
|
|
|
|
let temp = order
|
|
|
|
const v = parseInt(e.target.value)
|
|
|
|
if (e.target.value.length == 0 || Number.isNaN(v)) {
|
|
|
|
temp = 0
|
|
|
|
} else if (!Number.isNaN(v) && Number.isSafeInteger(v)) {
|
|
|
|
temp = v
|
|
|
|
}
|
|
|
|
return temp
|
|
|
|
})}
|
2024-06-24 18:16:55 -04:00
|
|
|
value={order}
|
|
|
|
readOnly={!isEditable} />
|
|
|
|
<TooltipProvider>
|
|
|
|
<Tooltip>
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
<HelpCircleIcon
|
2024-08-25 17:35:46 -04:00
|
|
|
className="inline-block ml-3" />
|
2024-06-24 18:16:55 -04:00
|
|
|
</TooltipTrigger>
|
|
|
|
<TooltipContent>
|
2024-08-25 17:35:46 -04:00
|
|
|
<p>This decides when this action will be done relative to other actions for this Twitch redemption.<br />
|
|
|
|
Action start from lowest to highest order number.<br />
|
|
|
|
Equal order numbers cannot be guaranteed proper order.</p>
|
2024-06-24 18:16:55 -04:00
|
|
|
</TooltipContent>
|
|
|
|
</Tooltip>
|
|
|
|
</TooltipProvider>
|
|
|
|
</div>
|
2024-08-25 17:35:46 -04:00
|
|
|
{error &&
|
|
|
|
<div className="text-red-600 font-bold">
|
|
|
|
{error}
|
|
|
|
</div>
|
|
|
|
}
|
2024-06-24 18:16:55 -04:00
|
|
|
<div>
|
|
|
|
{isEditable &&
|
|
|
|
<Button
|
|
|
|
className="m-3"
|
|
|
|
onClick={() => Save()}>
|
|
|
|
{isNew ? "Add" : "Save"}
|
|
|
|
</Button>
|
|
|
|
}
|
|
|
|
{isEditable && !isNew &&
|
|
|
|
<Button
|
|
|
|
className="m-3"
|
|
|
|
onClick={() => Cancel()}>
|
|
|
|
Cancel
|
|
|
|
</Button>
|
|
|
|
}
|
|
|
|
{showEdit && !isEditable &&
|
|
|
|
<Button
|
|
|
|
className="m-3"
|
|
|
|
onClick={() => {
|
|
|
|
setOldData({ a: actionName, r: twitchRedemption, o: order })
|
|
|
|
setIsEditable(true)
|
|
|
|
}}>
|
|
|
|
Edit
|
|
|
|
</Button>
|
|
|
|
}
|
|
|
|
{!isEditable &&
|
|
|
|
<Button
|
|
|
|
className="m-3 bg-red-500 hover:bg-red-600 align-bottom"
|
|
|
|
onClick={() => Delete()}>
|
|
|
|
<Trash2Icon />
|
|
|
|
</Button>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default OBSRedemption;
|