Implement command cooldown management and enhance command execution flow
- Introduced `applyCommandCooldown` function to manage per-command cooldowns after execution, improving command rate limiting. - Updated command execution flow to include cooldown application, ensuring users are informed of cooldown status. - Enhanced error handling in interaction replies, allowing for more graceful error management and user feedback. - Improved the handling of interaction responses across various commands, ensuring consistent user experience. - Added permission checks for new commands, ensuring only authorized users can execute specific actions.
This commit is contained in:
@@ -38,6 +38,10 @@ function toOptions(channels: DiscordChannelOption[]) {
|
||||
}));
|
||||
}
|
||||
|
||||
function normalizeSearch(value: string): string {
|
||||
return value.trim().toLocaleLowerCase();
|
||||
}
|
||||
|
||||
interface ChannelSelectBaseProps {
|
||||
guildId: string;
|
||||
kinds?: ChannelKind[];
|
||||
@@ -62,14 +66,39 @@ export function DiscordChannelSelect({
|
||||
allowClear = true
|
||||
}: DiscordChannelSelectProps) {
|
||||
const t = useTranslations();
|
||||
const { channels, loading } = useGuildChannels(guildId);
|
||||
const { channels, loading, error, reload } = useGuildChannels(guildId);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [query, setQuery] = useState('');
|
||||
|
||||
const options = useMemo(() => toOptions(filterChannels(channels, kinds)), [channels, kinds]);
|
||||
const selected = options.find((option) => option.id === value);
|
||||
const filtered = useMemo(() => {
|
||||
const needle = normalizeSearch(query);
|
||||
if (!needle) {
|
||||
return options;
|
||||
}
|
||||
return options.filter((option) => {
|
||||
const haystack = normalizeSearch(`${option.prefix}${option.name} ${option.id}`);
|
||||
return haystack.includes(needle);
|
||||
});
|
||||
}, [options, query]);
|
||||
|
||||
const triggerLabel = selected
|
||||
? `${selected.prefix}${selected.name}`
|
||||
: value
|
||||
? `#${value}`
|
||||
: (placeholder ?? t('modulePages.commands.channelSearchPlaceholder'));
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={(next) => {
|
||||
setOpen(next);
|
||||
if (!next) {
|
||||
setQuery('');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -79,19 +108,29 @@ export function DiscordChannelSelect({
|
||||
disabled={disabled || loading}
|
||||
className="h-9 w-full justify-between px-3 font-normal"
|
||||
>
|
||||
<span className={cn('truncate', !selected && 'text-muted-foreground')}>
|
||||
{selected
|
||||
? `${selected.prefix}${selected.name}`
|
||||
: (placeholder ?? t('modulePages.commands.channelSearchPlaceholder'))}
|
||||
<span className={cn('truncate', !selected && !value && 'text-muted-foreground')}>
|
||||
{triggerLabel}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder={placeholder ?? t('modulePages.commands.channelSearchPlaceholder')} />
|
||||
<Command shouldFilter={false}>
|
||||
<CommandInput
|
||||
value={query}
|
||||
onValueChange={setQuery}
|
||||
placeholder={placeholder ?? t('modulePages.commands.channelSearchPlaceholder')}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty>{t('common.noResults')}</CommandEmpty>
|
||||
<CommandEmpty>
|
||||
{error ? (
|
||||
<button type="button" className="underline" onClick={() => reload()}>
|
||||
{t('common.saveError')}
|
||||
</button>
|
||||
) : (
|
||||
t('common.noResults')
|
||||
)}
|
||||
</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{allowClear && value ? (
|
||||
<CommandItem
|
||||
@@ -105,10 +144,10 @@ export function DiscordChannelSelect({
|
||||
<span className="text-muted-foreground">{t('common.clearSelection')}</span>
|
||||
</CommandItem>
|
||||
) : null}
|
||||
{options.map((option) => (
|
||||
{filtered.map((option) => (
|
||||
<CommandItem
|
||||
key={option.id}
|
||||
value={`${option.prefix}${option.name} ${option.id}`}
|
||||
value={`${option.name} ${option.id}`}
|
||||
onSelect={() => {
|
||||
onChange(option.id);
|
||||
setOpen(false);
|
||||
|
||||
Reference in New Issue
Block a user