Equicord/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx
2026-10-03 11:16:17 -04:00

159 lines
5.6 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Button } from "@components/Button";
import ErrorBoundary from "@components/ErrorBoundary";
import { Heading } from "@components/Heading";
import { classNameFactory } from "@utils/css";
import { RenderModalProps } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, closeModal, DraftType, Modal, openModal, showToast, TextInput, UploadManager, useState } from "@webpack/common";
import { ScheduledAttachment } from "../types";
import { addScheduledMessage, getChannelDisplayInfo } from "../utils";
import { ErrorIcon } from "./Icons";
const cl = classNameFactory("vc-scheduled-msg-");
const ComponentDispatch = findByPropsLazy("dispatchToLastSubscribed");
function ScheduleTimeModalInner({ channelId, content, attachments, rootProps, close }: {
channelId: string;
content: string;
attachments?: ScheduledAttachment[];
rootProps: RenderModalProps;
close: () => void;
}) {
const [scheduleType, setScheduleType] = useState<"delay" | "time">("delay");
const [delayMinutes, setDelayMinutes] = useState("5");
const [scheduledDateTime, setScheduledDateTime] = useState("");
const [error, setError] = useState("");
const { name, avatar } = getChannelDisplayInfo(channelId);
const channel = ChannelStore.getChannel(channelId);
if (!channel) return null;
const isDM = channel.isPrivate();
const handleSchedule = async () => {
let scheduledTime: number;
if (scheduleType === "delay") {
const minutes = parseInt(delayMinutes, 10);
if (isNaN(minutes) || minutes < 1) {
setError("Please enter a valid delay (minimum 1 minute)");
return;
}
scheduledTime = Date.now() + minutes * 60 * 1000;
} else {
const dateTime = new Date(scheduledDateTime).getTime();
if (isNaN(dateTime) || dateTime <= Date.now()) {
setError("Please select a future date and time");
return;
}
scheduledTime = dateTime;
}
const result = await addScheduledMessage(channelId, content, scheduledTime, attachments);
if (result.success) {
ComponentDispatch.dispatchToLastSubscribed("CLEAR_TEXT");
UploadManager.clearAll(channelId, DraftType.ChannelMessage);
showToast("Message scheduled!", "success");
close();
} else {
setError(result.error ?? "Failed to schedule message");
}
};
return (
<Modal
{...rootProps}
size="sm"
title="Schedule Message"
actions={[
{
text: "Schedule",
variant: "primary",
onClick: handleSchedule
},
{
text: "Cancel",
variant: "secondary",
onClick: close
}
]}
>
<div className={cl("channel-info")}>
{avatar && <img src={avatar} className={cl("channel-avatar")} alt="" />}
<span className={cl("channel-text")}>
Scheduling for: <strong>{isDM ? name : `#${name}`}</strong>
</span>
</div>
<Heading tag="h5" className={cl("field-label")}>Schedule Type</Heading>
<div className={cl("schedule-type-buttons")}>
<Button
size="small"
variant={scheduleType === "delay" ? "primary" : "secondary"}
onClick={() => setScheduleType("delay")}
>
Delay
</Button>
<Button
size="small"
variant={scheduleType === "time" ? "primary" : "secondary"}
onClick={() => setScheduleType("time")}
>
Specific Time
</Button>
</div>
{scheduleType === "delay" ? (
<>
<Heading tag="h5" className={cl("field-label")}>Delay (minutes)</Heading>
<TextInput
value={delayMinutes}
onChange={setDelayMinutes}
placeholder="5"
type="number"
/>
</>
) : (
<>
<Heading tag="h5" className={cl("field-label")}>Date & Time</Heading>
<input
type="datetime-local"
className={cl("datetime-input")}
value={scheduledDateTime}
onChange={e => setScheduledDateTime(e.target.value)}
min={new Date().toISOString().slice(0, 16)}
/>
</>
)}
{error && (
<div className={cl("error")}>
<ErrorIcon />
<span>{error}</span>
</div>
)}
</Modal>
);
}
export const ScheduleTimeModal = ErrorBoundary.wrap(ScheduleTimeModalInner, { noop: true });
export function openScheduleTimeModal(channelId: string, content: string, attachments?: ScheduledAttachment[]): void {
const key = openModal(props => (
<ScheduleTimeModal
channelId={channelId}
content={content}
attachments={attachments}
rootProps={props}
close={() => closeModal(key)}
/>
));
}