import { createEffect, createSignal, createUniqueId, onMount, Show, type Component, type JSX, type Ref, } from "solid-js"; import Scaffold from "../material/Scaffold"; import { Avatar, Button, IconButton, List, ListItemButton, ListItemIcon, ListItemSecondaryAction, ListItemText, Radio, Switch, Divider, CircularProgress, } from "@suid/material"; import { ArrowDropDown, Public as PublicIcon, Send, People as PeopleIcon, ThreeP as ThreePIcon, ListAlt as ListAltIcon, Visibility, Translate, } from "@suid/icons-material"; import type { Account } from "../accounts/stores"; import tootComposers from "./TootComposer.module.css"; import { makeEventListener } from "@solid-primitives/event-listener"; import BottomSheet from "../material/BottomSheet"; import { useLanguage } from "../platform/i18n"; import iso639_1 from "iso-639-1"; import ChooseTootLang from "./ChooseTootLang"; import type { mastodon } from "masto"; type TootVisibility = "public" | "unlisted" | "private" | "direct"; const TootVisibilityPickerDialog: Component<{ open?: boolean; onClose: () => void; visibility: TootVisibility; onVisibilityChange: (value: TootVisibility) => void; }> = (props) => { type Kind = "public" | "private" | "direct"; const kind = () => props.visibility === "public" || props.visibility === "unlisted" ? "public" : props.visibility; const setKind = (nv: Kind) => { if (nv == "public") { props.onVisibilityChange(discoverable() ? "public" : "unlisted"); } else { props.onVisibilityChange(nv); } }; const discoverable = () => { return props.visibility === "public"; }; const setDiscoverable = (setter: (v: boolean) => boolean) => { const nval = setter(discoverable()); props.onVisibilityChange(nval ? "public" : "unlisted"); // trigger change }; return ( } > setDiscoverable((x) => !x)} > ); }; const TootLanguagePickerDialog: Component<{ open?: boolean; onClose: () => void; code: string; onCodeChange: (nval: string) => void; }> = (props) => { return ( ); }; function randomChoose( rn: number, K: T, ): T extends Array ? E : never { const idx = Math.round(rn * K.length); return K[idx]; } const TootComposer: Component<{ ref?: Ref; style?: JSX.CSSProperties; profile?: Account; replyToDisplayName?: string; mentions?: readonly string[]; isTyping?: boolean; onTypingChange: (value: boolean) => void; client?: mastodon.rest.Client; inReplyToId?: string; onSent?: (status: mastodon.v1.Status) => void; inputProps?: JSX.TextareaHTMLAttributes; }> = (props) => { let inputRef: HTMLTextAreaElement; let sendKey: string | undefined; const typing = () => props.isTyping; const setTyping = (v: boolean) => props.onTypingChange(v); const [sending, setSending] = createSignal(false); const [visibility, setVisibility] = createSignal("public"); const [permPicker, setPermPicker] = createSignal(false); const [language, setLanguage] = createSignal("en"); const [langPickerOpen, setLangPickerOpen] = createSignal(false); const appLanguage = useLanguage(); createEffect(() => { const lang = appLanguage().split("-")[0]; setLanguage(lang); }); onMount(() => { makeEventListener(inputRef, "focus", () => setTyping(true)); }); createEffect(() => { if (inputRef.value !== "") return; if (props.mentions) { const prepText = props.mentions.join(" ") + " "; inputRef.value = prepText; } }); const containerStyle = () => typing() || permPicker() ? { position: "sticky" as const, top: "var(--scaffold-topbar-height, 0)", bottom: "var(--safe-area-inset-bottom, 0)", "z-index": 2, ...props.style, } : undefined; const visibilityText = () => { switch (visibility()) { case "public": return "Discoverable"; case "unlisted": return "Public"; case "private": return "Only Followers"; case "direct": return "Only Mentions"; } }; const getOrGenSendKey = () => { if (sendKey === undefined) { sendKey = window.crypto.randomUUID(); } return sendKey; }; const send = async () => { setSending(true); try { const status = await props.client!.v1.statuses.create( { status: inputRef.value, language: language(), visibility: visibility(), inReplyToId: props.inReplyToId, }, { requestInit: { headers: { ["Idempotency-Key"]: getOrGenSendKey(), }, }, }, ); props.onSent?.(status); inputRef.value = ""; } finally { setSending(false); } }; return (
inputRef.focus()} >
} >
setPermPicker(false)} visibility={visibility()} onVisibilityChange={setVisibility} /> setLangPickerOpen(false)} code={language()} onCodeChange={setLanguage} />
); }; export default TootComposer;