122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
|
import {
|
||
|
Button,
|
||
|
Checkbox,
|
||
|
List,
|
||
|
ListItemButton,
|
||
|
ListItemText,
|
||
|
Radio,
|
||
|
} from "@suid/material";
|
||
|
import type { mastodon } from "masto";
|
||
|
import {
|
||
|
createEffect,
|
||
|
createRenderEffect,
|
||
|
createSignal,
|
||
|
Index,
|
||
|
Show,
|
||
|
type Component,
|
||
|
} from "solid-js";
|
||
|
import BottomSheet, { type BottomSheetProps } from "~material/BottomSheet";
|
||
|
import Scaffold from "~material/Scaffold";
|
||
|
import { resolveCustomEmoji } from "../../masto/toot";
|
||
|
import "./TootPollDialog.css";
|
||
|
|
||
|
export type TootPollDialogPoll = {
|
||
|
open?: boolean;
|
||
|
options: Readonly<mastodon.v1.Poll["options"]>;
|
||
|
initialVotes?: readonly number[];
|
||
|
multiple?: boolean;
|
||
|
|
||
|
onVote(votes: readonly number[]): void | Promise<void>;
|
||
|
onClose?: BottomSheetProps["onClose"] &
|
||
|
((reason: "cancel" | "success") => void);
|
||
|
};
|
||
|
|
||
|
const TootPollDialog: Component<TootPollDialogPoll> = (props) => {
|
||
|
const [votes, setVotes] = createSignal([] as readonly number[]);
|
||
|
const [inProgress, setInProgress] = createSignal(false);
|
||
|
|
||
|
createEffect(() => {
|
||
|
setVotes(props.initialVotes || []);
|
||
|
});
|
||
|
|
||
|
const toggleVote = (i: number) => {
|
||
|
if (props.multiple) {
|
||
|
setVotes((o) => [...o.filter((x) => x === i), i]);
|
||
|
} else {
|
||
|
setVotes([i]);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const sendVote = async () => {
|
||
|
setInProgress(true);
|
||
|
try {
|
||
|
await props.onVote(votes());
|
||
|
} catch (reason) {
|
||
|
console.error(reason);
|
||
|
props.onClose?.("cancel");
|
||
|
return;
|
||
|
} finally {
|
||
|
setInProgress(false);
|
||
|
}
|
||
|
props.onClose?.("success");
|
||
|
};
|
||
|
|
||
|
return (
|
||
|
<BottomSheet open={props.open} onClose={props.onClose} bottomUp>
|
||
|
<Scaffold
|
||
|
class="TootPollDialog"
|
||
|
bottom={
|
||
|
<div class="actions">
|
||
|
<Button
|
||
|
color="error"
|
||
|
onClick={props.onClose ? [props.onClose, "cancel"] : undefined}
|
||
|
disabled={inProgress()}
|
||
|
>
|
||
|
Cancel
|
||
|
</Button>
|
||
|
<Button onClick={sendVote} disabled={inProgress()}>
|
||
|
Confirm
|
||
|
</Button>
|
||
|
</div>
|
||
|
}
|
||
|
>
|
||
|
<List>
|
||
|
<Index each={props.options}>
|
||
|
{(option, index) => {
|
||
|
return (
|
||
|
<ListItemButton
|
||
|
onClick={[toggleVote, index]}
|
||
|
disabled={inProgress()}
|
||
|
>
|
||
|
<ListItemText>
|
||
|
<span
|
||
|
ref={(e) =>
|
||
|
createRenderEffect(
|
||
|
() =>
|
||
|
(e.innerHTML = resolveCustomEmoji(
|
||
|
option().title,
|
||
|
option().emojis,
|
||
|
)),
|
||
|
)
|
||
|
}
|
||
|
></span>
|
||
|
</ListItemText>
|
||
|
|
||
|
<Show
|
||
|
when={props.multiple}
|
||
|
fallback={<Radio checked={votes().includes(index)} />}
|
||
|
>
|
||
|
<Checkbox checked={votes().includes(index)} />
|
||
|
</Show>
|
||
|
</ListItemButton>
|
||
|
);
|
||
|
}}
|
||
|
</Index>
|
||
|
</List>
|
||
|
</Scaffold>
|
||
|
</BottomSheet>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export default TootPollDialog;
|