/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ import {Trans, useLingui} from '@lingui/react/macro'; import {ClockIcon, XCircleIcon} from '@phosphor-icons/react'; import {observer} from 'mobx-react-lite'; import React from 'react'; import * as GuildMemberActionCreators from '~/actions/GuildMemberActionCreators'; import * as ModalActionCreators from '~/actions/ModalActionCreators'; import {modal} from '~/actions/ModalActionCreators'; import * as ToastActionCreators from '~/actions/ToastActionCreators'; import { MenuBottomSheet, type MenuGroupType, type MenuItemType, type MenuRadioType, } from '~/components/uikit/MenuBottomSheet/MenuBottomSheet'; import type {GuildMemberRecord} from '~/records/GuildMemberRecord'; import type {UserRecord} from '~/records/UserRecord'; import {RemoveTimeoutModal} from './RemoveTimeoutModal'; import {getTimeoutDurationOptions, type TimeoutDurationOption} from './TimeoutMemberOptions'; import styles from './TimeoutMemberSheet.module.css'; interface TimeoutMemberSheetProps { isOpen: boolean; onClose: () => void; guildId: string; targetUser: UserRecord; targetMember: GuildMemberRecord; } export const TimeoutMemberSheet: React.FC = observer( ({isOpen, onClose, guildId, targetUser, targetMember}) => { const {t} = useLingui(); const isCurrentlyTimedOut = targetMember.isTimedOut(); const timeoutOptions = React.useMemo(() => getTimeoutDurationOptions(t), [t]); const [timeoutDuration, setTimeoutDuration] = React.useState(timeoutOptions[3].value); const [isSubmitting, setIsSubmitting] = React.useState(false); const handleTimeout = React.useCallback(async () => { setIsSubmitting(true); try { const timeoutUntil = new Date(Date.now() + timeoutDuration * 1000).toISOString(); await GuildMemberActionCreators.timeout(guildId, targetUser.id, timeoutUntil); ToastActionCreators.createToast({ type: 'success', children: Successfully timed out {targetUser.tag}, }); onClose(); } catch (error) { console.error('Failed to timeout member:', error); ToastActionCreators.createToast({ type: 'error', children: Failed to timeout member. Please try again., }); } finally { setIsSubmitting(false); } }, [guildId, onClose, targetUser.id, timeoutDuration, targetUser.tag]); const durationItems: Array = React.useMemo(() => { return timeoutOptions.map((option: TimeoutDurationOption) => ({ label: option.label, selected: option.value === timeoutDuration, onSelect: () => setTimeoutDuration(option.value), })); }, [timeoutDuration, timeoutOptions]); const actionItems: Array = React.useMemo(() => { const items: Array = [ { id: 'timeout', icon: , label: t`Timeout`, onClick: handleTimeout, danger: true, disabled: isSubmitting, }, ]; if (isCurrentlyTimedOut) { items.push({ id: 'remove-timeout', icon: , label: t`Remove Timeout`, onClick: () => { onClose(); ModalActionCreators.push(modal(() => )); }, danger: true, disabled: isSubmitting, }); } return items; }, [guildId, handleTimeout, isCurrentlyTimedOut, isSubmitting, onClose, targetUser]); const headerContent = (

{isCurrentlyTimedOut ? ( {targetUser.tag} is currently timed out. You can update their timeout duration or remove the timeout. ) : ( Prevent {targetUser.tag} from sending messages, reacting, and connecting to voice channels for the specified duration. )}

); const groups: Array = [{items: durationItems}, {items: actionItems}]; return ( ); }, );