import Input from "@tensamin/markdown/input"; import { Card, CardHeader, Popover, PopoverContent, PopoverTrigger, } from "@tensamin/ui"; import { useStorage } from "@tensamin/storage/context"; import * as React from "react"; import { Button } from "@tensamin/ui"; import { Plus, Laugh, FileVideo } from "lucide-react"; import { useChat } from "../context"; import { useTTP } from "@tensamin/ttp"; import { log, toast } from "@tensamin/shared/log"; import { cn, useIsMobile } from "@tensamin/ui"; import { encryptText } from "@tensamin/crypto/worker"; import { useSession } from "@tensamin/storage/session"; import GifPicker from "./gifPicker"; export default function InputComponent({ value, setValue, }: { value: string; setValue: (value: string) => void; }) { const [invertEnterBehavior, setInvertEnterBehavior] = React.useState(false); const { send } = useTTP(); const { addLiveMessage, sharedSecret, userId, inputBoxRef } = useChat(); const { load, save } = useStorage(); const { moveUserIdToTop } = useSession(); const gifPopoverRef = React.useRef(null); const [gifPopoverOpen, setGifPopoverOpen] = React.useState(false); const [gifPopoverSize, setGifPopoverSize] = React.useState<{ width: number; height: number; }>(); React.useEffect(() => { void load("settings.reverse_enter_behavior").then((shouldInvert) => { setInvertEnterBehavior(shouldInvert); }); }, [load]); React.useEffect(() => { void load("chat_picker_size").then((size) => { if (size) { setGifPopoverSize(size); } }); }, [load]); async function handleSubmit(content = value, preserveContent = false) { if (content.trim() === "") return; const time = Date.now(); const currentValue = content; if (!Number.isSafeInteger(userId) || userId <= 0) { toast("error", "No conversation selected"); return; } if (!sharedSecret) { toast("error", "Still getting shared secret..."); return; } const reference = addLiveMessage({ height: 0, not_encrypted: true, send_time: time, content: currentValue, sent_by_self: true, message_state: "awaiting", }); const encryptedContext = await encryptText(sharedSecret, currentValue); send("message_send", { height: 0, content: encryptedContext, receiver_id: userId, send_time: time, }).catch((e) => { log(0, "Chat", "red", "Failed to send message", e, { content: currentValue, encryptedContext, receiver_id: userId, send_time: time, }); reference.setFailed(true); toast("error", "Failed to send message"); }); moveUserIdToTop(userId); if (!preserveContent) { setValue(""); } } const isMobile = useIsMobile(); function handleGifPopoverResizeStart( event: React.PointerEvent, ) { const popover = gifPopoverRef.current; if (!popover) return; event.preventDefault(); event.stopPropagation(); const rect = popover.getBoundingClientRect(); const startX = event.clientX; const startY = event.clientY; const startWidth = rect.width; const startHeight = rect.height; const minSize = 40; const maxSize = 720; function clampSize(size: number) { return Math.min(Math.max(size, minSize), maxSize); } function handlePointerMove(moveEvent: PointerEvent) { const nextSize = { width: clampSize(startWidth + startX - moveEvent.clientX), height: clampSize(startHeight + startY - moveEvent.clientY), }; setGifPopoverSize(nextSize); } function handlePointerUp() { const popover = gifPopoverRef.current; if (popover) { const rect = popover.getBoundingClientRect(); void save("chat_picker_size", { width: clampSize(rect.width), height: clampSize(rect.height), }); } window.removeEventListener("pointermove", handlePointerMove); window.removeEventListener("pointerup", handlePointerUp); } window.addEventListener("pointermove", handlePointerMove); window.addEventListener("pointerup", handlePointerUp); } return (

Files list

} /> event.stopPropagation()} onWheelCapture={(event) => event.stopPropagation()} style={{ ...gifPopoverSize, maxHeight: gifPopoverSize?.height, }} >
{ void handleSubmit(url, true); setGifPopoverOpen(false); }} />
); }