import { createEffect, createSignal, onMount, Show, type Component, } from "solid-js"; import Scaffold from "../material/Scaffold"; import { Avatar, Button, IconButton, List, ListItemButton, ListItemIcon, ListItemSecondaryAction, ListItemText, Radio, Switch, Divider, } 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"; 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 ( <BottomSheet open={props.open} onClose={props.onClose} bottomUp> <Scaffold bottom={ <div style={{ "border-top": "1px solid #ddd", background: "var(--tutu-color-surface)", padding: "8px 16px", width: "100%", "text-align": "end", }} > <Button onClick={props.onClose}>Confirm</Button> </div> } > <List dense> <ListItemButton onClick={[setKind, "public"]}> <ListItemIcon> <PublicIcon /> </ListItemIcon> <ListItemText primary="Public" secondary="Everyone can see this toot" ></ListItemText> <ListItemSecondaryAction> <Radio checked={kind() == "public"}></Radio> </ListItemSecondaryAction> </ListItemButton> <ListItemButton sx={{ paddingLeft: "40px" }} disabled={kind() !== "public"} onClick={() => setDiscoverable((x) => !x)} > <ListItemIcon> <ListAltIcon /> </ListItemIcon> <ListItemText primary="Discoverable" secondary="The others can discover it on the exploration." ></ListItemText> <ListItemSecondaryAction> <Switch checked={discoverable()} disabled={kind() !== "public"} ></Switch> </ListItemSecondaryAction> </ListItemButton> <Divider /> <ListItemButton onClick={[setKind, "private"]}> <ListItemIcon> <PeopleIcon /> </ListItemIcon> <ListItemText primary="Only Followers" secondary="Visibile for followers only" ></ListItemText> <ListItemSecondaryAction> <Radio checked={kind() == "private"}></Radio> </ListItemSecondaryAction> </ListItemButton> <Divider /> <ListItemButton onClick={[setKind, "direct"]}> <ListItemIcon> <ThreePIcon /> </ListItemIcon> <ListItemText primary="Only Mentions" secondary="Visible for mentioned users only" ></ListItemText> <ListItemSecondaryAction> <Radio checked={kind() == "direct"}></Radio> </ListItemSecondaryAction> </ListItemButton> </List> </Scaffold> </BottomSheet> ); }; const TootLanguagePickerDialog: Component<{ open?: boolean; onClose: () => void; code: string; onCodeChange: (nval: string) => void; }> = (props) => { return ( <BottomSheet open={props.open} onClose={props.onClose}> <Show when={props.open}> <ChooseTootLang code={props.code} onCodeChange={props.onCodeChange} onClose={props.onClose} /> </Show> </BottomSheet> ); }; const ReplyEditor: Component<{ profile: Account; replyToDisplayName: string; isTyping?: boolean; onTypingChange: (value: boolean) => void; }> = (props) => { let inputRef: HTMLTextAreaElement; const typing = () => props.isTyping; const setTyping = (v: boolean) => props.onTypingChange(v); const [visibility, setVisibility] = createSignal<TootVisibility>("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)); }); const containerStyle = () => typing() || permPicker() ? { position: "sticky" as const, top: "var(--scaffold-topbar-height, 0)", bottom: "var(--safe-area-inset-bottom, 0)", "z-index": 2, } : undefined; const visibilityText = () => { switch (visibility()) { case "public": return "Discoverable"; case "unlisted": return "Public"; case "private": return "Only Followers"; case "direct": return "Only Mentions"; } }; return ( <div class={tootComposers.composer} style={containerStyle()} onClick={(e) => inputRef.focus()} > <div class={tootComposers.replyInput}> <Avatar src={props.profile.inf?.avatar} sx={{marginLeft: '-0.25em'}} /> <textarea ref={inputRef!} placeholder={`Reply to ${props.replyToDisplayName}...`} style={{ width: "100%", border: "none" }} ></textarea> <IconButton sx={{marginRight: '-0.5em'}}> <Send /> </IconButton> </div> <div style={{ display: "flex", "justify-content": "space-between", "margin-top": "8px", gap: "16px", "flex-flow": "row wrap", }} > <Button onClick={[setLangPickerOpen, true]}> <Translate sx={{ marginTop: "-0.25em", marginRight: "0.25em" }} /> {iso639_1.getNativeName(language())} <ArrowDropDown sx={{ marginTop: "-0.25em" }} /> </Button> <Button onClick={[setPermPicker, true]}> <Visibility sx={{ marginTop: "-0.15em", marginRight: "0.25em" }} /> {visibilityText()} <ArrowDropDown sx={{ marginTop: "-0.25em" }} /> </Button> </div> <TootVisibilityPickerDialog open={permPicker()} onClose={() => setPermPicker(false)} visibility={visibility()} onVisibilityChange={setVisibility} /> <TootLanguagePickerDialog open={langPickerOpen()} onClose={() => setLangPickerOpen(false)} code={language()} onCodeChange={setLanguage} /> </div> ); }; export default ReplyEditor;