diff --git a/src/App.tsx b/src/App.tsx index 8f7ba44..e7ca588 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { Route, Router } from "@solidjs/router"; +import { Route } from "@solidjs/router"; import { ThemeProvider } from "@suid/material"; import { Component, @@ -17,7 +17,12 @@ import { } from "./masto/clients.js"; import { $accounts, updateAcctInf } from "./accounts/stores.js"; import { useStore } from "@nanostores/solid"; -import { DateFnScope, useLanguage } from "./platform/i18n.jsx"; +import { + AppLocaleProvider, + createCurrentLanguage, + createCurrentRegion, + createDateFnLocaleResource, +} from "./platform/i18n.jsx"; import { useRegisterSW } from "virtual:pwa-register/solid"; import { isJSONRPCResult, @@ -67,7 +72,9 @@ const Routing: Component = () => { const App: Component = () => { const theme = useRootTheme(); const accts = useStore($accounts); - const lang = useLanguage(); + const lang = createCurrentLanguage(); + const region = createCurrentRegion(); + const dateFnLocale = createDateFnLocaleResource(region); const [serviceWorker, setServiceWorker] = createSignal< ServiceWorker | undefined >(undefined, { name: "serviceWorker" }); @@ -150,7 +157,13 @@ const App: Component = () => { }} > - + { - + ); diff --git a/src/platform/i18n.tsx b/src/platform/i18n.tsx index ade3868..b64b920 100644 --- a/src/platform/i18n.tsx +++ b/src/platform/i18n.tsx @@ -1,12 +1,12 @@ import { - ParentComponent, + catchError, createContext, createMemo, createResource, useContext, } from "solid-js"; import { match } from "@formatjs/intl-localematcher"; -import { Accessor, createEffect, createSignal } from "solid-js"; +import { Accessor } from "solid-js"; import { $settings } from "../settings/stores"; import { enGB } from "date-fns/locale/en-GB"; import { useStore } from "@nanostores/solid"; @@ -17,13 +17,6 @@ import { type Template, } from "@solid-primitives/i18n"; -async function synchronised( - name: string, - callback: () => Promise | void, -): Promise { - await navigator.locks.request(name, callback); -} - export const SUPPORTED_LANGS = ["en", "zh-Hans"] as const; export const SUPPORTED_REGIONS = ["en_US", "en_GB", "zh_CN"] as const; @@ -38,14 +31,6 @@ export function autoMatchLangTag() { return match(Array.from(navigator.languages), SUPPORTED_LANGS, DEFAULT_LANG); } -const DateFnLocaleCx = /* __@PURE__ */ createContext>( - () => enGB, -); - -const cachedDateFnLocale: Record = { - enGB, -}; - export function autoMatchRegion() { const specifiers = navigator.languages.map((x) => x.split("-")); @@ -70,7 +55,7 @@ export function autoMatchRegion() { return "en_GB"; } -export function useRegion() { +export function createCurrentRegion() { const appSettings = useStore($settings); return createMemo( @@ -100,53 +85,6 @@ async function importDateFnLocale(tag: string): Promise { } } -/** - * Provides runtime values and fetch dependencies for date-fns locale - */ -export const DateFnScope: ParentComponent = (props) => { - const [dateFnLocale, setDateFnLocale] = createSignal(enGB, { - name: "dateFnLocale", - }); - const region = useRegion(); - - createEffect(() => { - const dateFnLocaleName = region(); - - if (cachedDateFnLocale[dateFnLocaleName]) { - setDateFnLocale(cachedDateFnLocale[dateFnLocaleName]); - } else { - synchronised("i18n-wrapper-load-date-fns-locale", async () => { - if (cachedDateFnLocale[dateFnLocaleName]) { - setDateFnLocale(cachedDateFnLocale[dateFnLocaleName]); - return; - } - const target = `date-fns/locale/${dateFnLocaleName}`; - try { - const mod = await importDateFnLocale(dateFnLocaleName); - cachedDateFnLocale[dateFnLocaleName] = mod; - setDateFnLocale(mod); - } catch (reason) { - console.error( - { - act: "load-date-fns-locale", - stat: "failed", - reason, - target, - }, - "failed to load date-fns locale", - ); - } - }); - } - }); - - return ( - - {props.children} - - ); -}; - /** * Get the {@link Locale} object for date-fns. * @@ -155,11 +93,11 @@ export const DateFnScope: ParentComponent = (props) => { * @returns Accessor for Locale */ export function useDateFnLocale(): Accessor { - const cx = useContext(DateFnLocaleCx); - return cx; + const { dateFn } = useAppLocale(); + return dateFn; } -export function useLanguage() { +export function createCurrentLanguage() { const settings = useStore($settings); return () => settings().language || autoMatchLangTag(); } @@ -179,7 +117,7 @@ type MergedImportedModule = T extends [] export function createStringResource< T extends ImportFn | undefined>>[], >(...importFns: T) { - const language = useLanguage(); // TODO: this function costs to much, provide a global cache + const language = createCurrentLanguage(); const cache: Record> = {}; return createResource( @@ -209,3 +147,38 @@ export function createTranslator< return [translator(res[0], resolveTemplate), res] as const; } + +export type AppLocale = { + dateFn: () => Locale; + language: () => string; + region: () => string; +}; + +const AppLocaleContext = /* @__PURE__ */ createContext(); + +export const AppLocaleProvider = AppLocaleContext.Provider; + +export function useAppLocale() { + const l = useContext(AppLocaleContext); + if (!l) { + throw new TypeError("app locale not found"); + } + return l; +} + +export function createDateFnLocaleResource(region: () => string) { + const [localeUncaught] = createResource( + region, + async (region) => { + return await importDateFnLocale(region); + }, + { initialValue: enGB }, + ); + + return createMemo( + () => + catchError(localeUncaught, (reason) => { + console.error("fetch date-fns locale", reason); + }) ?? enGB, + ); +} diff --git a/src/timelines/CompactToot.tsx b/src/timelines/CompactToot.tsx deleted file mode 100644 index 52bd551..0000000 --- a/src/timelines/CompactToot.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import type { mastodon } from "masto"; -import { Show, type Component } from "solid-js"; -import tootStyle from "./toot.module.css"; -import { formatRelative } from "date-fns"; -import Img from "~material/Img"; -import { Body2 } from "~material/typography"; -import { appliedCustomEmoji } from "../masto/toot"; -import { TootPreviewCard } from "./RegularToot"; - -type CompactTootProps = { - status: mastodon.v1.Status; - now: Date; - class?: string; -}; - -const CompactToot: Component = (props) => { - const toot = () => props.status; - return ( -
- -
- { - appliedCustomEmoji( - e, - toot().account.displayName, - toot().account.emojis, - ); - }} - > - - @{toot().account.username}@{new URL(toot().account.url).hostname} - - -
-
{ - appliedCustomEmoji(e, toot().content, toot().emojis); - }} - class={[tootStyle.compactTootContent].join(" ")} - >
- - - -
- ); -}; - -export default CompactToot; diff --git a/src/timelines/RegularToot.css b/src/timelines/RegularToot.css new file mode 100644 index 0000000..d7a50c3 --- /dev/null +++ b/src/timelines/RegularToot.css @@ -0,0 +1,78 @@ +.RegularToot { + --card-pad: 16px; + --card-gut: 16px; + --toot-avatar-size: 40px; + margin-block: 0; + position: relative; + contain: layout style; + cursor: pointer; + + + transition: + margin-top 60ms var(--tutu-anim-curve-sharp), + margin-bottom 60ms var(--tutu-anim-curve-sharp), + height 60ms var(--tutu-anim-curve-sharp), + var(--tutu-transition-shadow); + border-radius: 0; + + time { + color: var(--tutu-color-secondary-text-on-surface); + } + + >.retoot-grp { + display: flex; + gap: 0.25em; + margin-bottom: 8px; + align-items: center; + + > :first-child { + margin-right: 0.25em; + } + } + + & .custom-emoji { + height: 1em; + object-fit: contain; + } + + &.expanded { + margin-block: 20px; + box-shadow: var(--tutu-shadow-e9); + } + + &.thread-top, + &.thread-mid, + &.thread-btm { + position: relative; + + &::before { + content: ""; + position: absolute; + left: 36px; + background-color: var(--tutu-color-secondary); + width: 2px; + display: block; + } + } + + &.thread-mid { + &::before { + top: 0; + bottom: 0; + } + } + + &.thread-top { + &::before { + top: 16px; + bottom: 0; + } + } + + &.thread-btm { + &::before { + top: 0; + height: 16px; + } + } +} diff --git a/src/timelines/RegularToot.tsx b/src/timelines/RegularToot.tsx index 68c0f72..fd5c80d 100644 --- a/src/timelines/RegularToot.tsx +++ b/src/timelines/RegularToot.tsx @@ -7,63 +7,62 @@ import { createRenderEffect, createSignal, type Setter, + createContext, + useContext, } from "solid-js"; import tootStyle from "./toot.module.css"; -import { formatRelative, parseISO } from "date-fns"; +import { formatRelative } from "date-fns"; import Img from "~material/Img.js"; import { Body2 } from "~material/typography.js"; -import { css } from "solid-styled"; -import { - BookmarkAddOutlined, - Repeat, - ReplyAll, - Star, - StarOutline, - Bookmark, - Share, - SmartToySharp, - Lock, -} from "@suid/icons-material"; +import { SmartToySharp, Lock } from "@suid/icons-material"; import { useTimeSource } from "~platform/timesrc.js"; import { resolveCustomEmoji } from "../masto/toot.js"; import { Divider } from "@suid/material"; import cardStyle from "~material/cards.module.css"; -import Button from "~material/Button.js"; import MediaAttachmentGrid from "./toots/MediaAttachmentGrid.jsx"; import { useDateFnLocale } from "~platform/i18n"; -import { canShare, share } from "~platform/share"; import { makeAcctText, useDefaultSession } from "../masto/clients"; import TootContent from "./toots/TootContent"; import BoostIcon from "./toots/BoostIcon"; import PreviewCard from "./toots/PreviewCard"; import TootPoll from "./toots/TootPoll"; +import TootActionGroup from "./toots/TootActionGroup.js"; +import "./RegularToot.css"; -type TootActionGroupProps = { - onRetoot?: (value: T) => void; - onFavourite?: (value: T) => void; - onBookmark?: (value: T) => void; - onReply?: ( - value: T, +export type TootEnv = { + boost: (value: mastodon.v1.Status) => void; + favourite: (value: mastodon.v1.Status) => void; + bookmark: (value: mastodon.v1.Status) => void; + reply?: ( + value: mastodon.v1.Status, event: MouseEvent & { currentTarget: HTMLButtonElement }, ) => void; + vote: ( + status: mastodon.v1.Status, + votes: readonly number[], + ) => void | Promise; }; +const TootEnvContext = /* @__PURE__ */ createContext(); + +export const TootEnvProvider = TootEnvContext.Provider; + +export function useTootEnv() { + const env = useContext(TootEnvContext); + if (!env) { + throw new TypeError( + "environment not found, use TootEnvProvider to provide", + ); + } + return env; +} + type RegularTootProps = { status: mastodon.v1.Status; actionable?: boolean; evaluated?: boolean; thread?: "top" | "bottom" | "middle"; - - onVote?: (value: { - status: mastodon.v1.Status; - votes: readonly number[]; - }) => void | Promise; -} & TootActionGroupProps & - JSX.HTMLElementTags["article"]; - -function isolatedCallback(e: MouseEvent) { - e.stopPropagation(); -} +} & JSX.HTMLElementTags["article"]; export function findRootToot(element: HTMLElement) { let current: HTMLElement | null = element; @@ -78,73 +77,6 @@ export function findRootToot(element: HTMLElement) { return current; } -function TootActionGroup( - props: TootActionGroupProps & { value: T }, -) { - let actGrpElement: HTMLDivElement; - const toot = () => props.value; - return ( -
- - - - - - - - - - -
- ); -} - function TootAuthorGroup( props: { status: mastodon.v1.Status; @@ -220,6 +152,8 @@ function onToggleReveal(setValue: Setter, event: Event) { * this component under a `` with correct * session. * + * This component requires be under ``. + * * **Handling Clicks** * There are multiple actions supported in the component. Some handlers * are passed in, some should be handled as the click event. @@ -241,80 +175,39 @@ function onToggleReveal(setValue: Setter, event: Event) { * You can extract the intent from the attributes of the "actionable" element. * The action type is the dataset's `action`. */ -const RegularToot: Component = (props) => { +const RegularToot: Component = (oprops) => { let rootRef: HTMLElement; - const [managed, managedActionGroup, pollProps, rest] = splitProps( - props, - ["status", "lang", "class", "actionable", "evaluated", "thread"], - ["onRetoot", "onFavourite", "onBookmark", "onReply"], - ["onVote"], - ); + const [props, rest] = splitProps(oprops, [ + "status", + "lang", + "class", + "actionable", + "evaluated", + "thread", + ]); const now = useTimeSource(); - const status = () => managed.status; + const status = () => props.status; const toot = () => status().reblog ?? status(); const session = useDefaultSession(); const [reveal, setReveal] = createSignal(false); - css` - .reply-sep { - margin-left: calc(var(--toot-avatar-size) + var(--card-pad) + 8px); - margin-block: 8px; - } - - .thread-top, - .thread-mid, - .thread-btm { - position: relative; - - &::before { - content: ""; - position: absolute; - left: 36px; - background-color: var(--tutu-color-secondary); - width: 2px; - display: block; - } - } - - .thread-mid { - &::before { - top: 0; - bottom: 0; - } - } - - .thread-top { - &::before { - top: 16px; - bottom: 0; - } - } - - .thread-btm { - &::before { - top: 0; - height: 16px; - } - } - `; - return ( <>
-
+
{ @@ -360,27 +253,14 @@ const RegularToot: Component = (props) => { /> - pollProps.onVote?.({ status: status(), votes })} - /> + - + - +
diff --git a/src/timelines/TootBottomSheet.tsx b/src/timelines/TootBottomSheet.tsx index d38d34f..09465db 100644 --- a/src/timelines/TootBottomSheet.tsx +++ b/src/timelines/TootBottomSheet.tsx @@ -13,7 +13,10 @@ import { Title } from "~material/typography"; import { Close as CloseIcon } from "@suid/icons-material"; import { useSessionForAcctStr } from "../masto/clients"; import { resolveCustomEmoji } from "../masto/toot"; -import RegularToot, { findElementActionable } from "./RegularToot"; +import RegularToot, { + findElementActionable, + TootEnvProvider, +} from "./RegularToot"; import type { mastodon } from "masto"; import cards from "~material/cards.module.css"; import { css } from "solid-styled"; @@ -169,6 +172,33 @@ const TootBottomSheet: Component = (props) => { return Array.from(new Set(values).keys()); }; + const vote = async (status: mastodon.v1.Status, votes: readonly number[]) => { + const client = session()?.client; + if (!client) return; + + const toot = status.reblog ?? status; + if (!toot.poll) return; + + const npoll = await client.v1.polls.$select(toot.poll.id).votes.create({ + choices: votes, + }); + + if (status.reblog) { + setRemoteToot({ + ...status, + reblog: { + ...status.reblog, + poll: npoll, + }, + }); + } else { + setRemoteToot({ + ...status, + poll: npoll, + }); + } + }; + const handleMainTootClick = ( event: MouseEvent & { currentTarget: HTMLElement }, ) => { @@ -255,23 +285,29 @@ const TootBottomSheet: Component = (props) => {
- + > + +
diff --git a/src/timelines/TootComposer.tsx b/src/timelines/TootComposer.tsx index 3c6b696..2895c67 100644 --- a/src/timelines/TootComposer.tsx +++ b/src/timelines/TootComposer.tsx @@ -42,7 +42,7 @@ import { import type { Account } from "../accounts/stores"; import "./TootComposer.css"; import BottomSheet from "~material/BottomSheet"; -import { useLanguage } from "~platform/i18n"; +import { useAppLocale } from "~platform/i18n"; import iso639_1 from "iso-639-1"; import ChooseTootLang from "./ChooseTootLang"; import type { mastodon } from "masto"; @@ -98,7 +98,8 @@ const TootVisibilityPickerDialog: Component<{ style={{ "border-top": "1px solid #ddd", background: "var(--tutu-color-surface)", - padding: "8px 16px calc(8px + var(--safe-area-inset-bottom, 0px))", + padding: + "8px 16px calc(8px + var(--safe-area-inset-bottom, 0px))", width: "100%", "text-align": "end", }} @@ -232,7 +233,7 @@ const TootComposer: Component<{ const [permPicker, setPermPicker] = createSignal(false); const [language, setLanguage] = createSignal("en"); const [langPickerOpen, setLangPickerOpen] = createSignal(false); - const appLanguage = useLanguage(); + const { language: appLanguage } = useAppLocale(); const [openMenu, menuState] = createManagedMenuState(); const randomPlaceholder = useRandomChoice(() => [ diff --git a/src/timelines/TootList.tsx b/src/timelines/TootList.tsx index e72c360..b35dcce 100644 --- a/src/timelines/TootList.tsx +++ b/src/timelines/TootList.tsx @@ -6,6 +6,7 @@ import { createSelector, Index, createMemo, + For, } from "solid-js"; import { type mastodon } from "masto"; import { vibrate } from "~platform/hardware"; @@ -14,6 +15,7 @@ import { setCache as setTootBottomSheetCache } from "./TootBottomSheet"; import RegularToot, { findElementActionable, findRootToot, + TootEnvProvider, } from "./RegularToot"; import cardStyle from "~material/cards.module.css"; import type { ThreadNode } from "../masto/timelines"; @@ -232,13 +234,10 @@ const TootList: Component<{ openFullScreenToot(status, element, true); }; - const vote = async ({ - status, - votes, - }: { - status: mastodon.v1.Status; - votes: readonly number[]; - }) => { + const vote = async ( + status: mastodon.v1.Status, + votes: readonly number[] + ) => { const client = session()?.client; if (!client) return; @@ -263,7 +262,6 @@ const TootList: Component<{ poll: npoll, }); } - }; return ( @@ -273,49 +271,50 @@ const TootList: Component<{ return

Oops: {String(err)}

; }} > -
- - {(threadId, threadIdx) => { - const thread = createMemo(() => - props.onUnknownThread(threadId())?.reverse(), - ); + +
+ + {(threadId, threadIdx) => { + const thread = createMemo(() => + props.onUnknownThread(threadId)?.reverse(), + ); - const threadLength = () => thread()?.length ?? 0; + const threadLength = () => thread()?.length ?? 0; - return ( - - {(threadNode, index) => { - const status = () => threadNode().value; + return ( + + {(threadNode, index) => { + const status = () => threadNode().value; - return ( - 1 - ? positionTootInThread(index, threadLength()) - : undefined - } - class={cardStyle.card} - evaluated={checkIsExpended(status())} - actionable={checkIsExpended(status())} - onBookmark={onBookmark} - onRetoot={toggleBoost} - onFavourite={toggleFavourite} - onReply={reply} - onVote={vote} - onClick={[onItemClick, status()]} - /> - ); - }} - - ); - }} - -
+ return ( + 1 + ? positionTootInThread(index, threadLength()) + : undefined + } + class={cardStyle.card} + evaluated={checkIsExpended(status())} + actionable={checkIsExpended(status())} + onClick={[onItemClick, status()]} + /> + ); + }} +
+ ); + }} + +
+
); }; diff --git a/src/timelines/toot.module.css b/src/timelines/toot.module.css index bb10964..e666f66 100644 --- a/src/timelines/toot.module.css +++ b/src/timelines/toot.module.css @@ -1,41 +1,3 @@ -.toot { - --card-pad: 16px; - --card-gut: 16px; - --toot-avatar-size: 40px; - margin-block: 0; - position: relative; - contain: content; - cursor: pointer; - - &.toot { - /* fix composition ordering: I think the css module processor should aware the overriding and behaves, but no */ - transition: - margin-top 60ms var(--tutu-anim-curve-sharp), - margin-bottom 60ms var(--tutu-anim-curve-sharp), - height 60ms var(--tutu-anim-curve-sharp), - var(--tutu-transition-shadow); - border-radius: 0; - } - - &>.toot { - box-shadow: none; - } - - time { - color: var(--tutu-color-secondary-text-on-surface); - } - - & :global(.custom-emoji) { - height: 1em; - object-fit: contain; - } - - &.expanded { - margin-block: 20px; - box-shadow: var(--tutu-shadow-e9); - } -} - .tootAuthorGrp { display: flex; align-items: flex-start; @@ -88,90 +50,3 @@ border: 1px solid var(--tutu-color-surface); background-color: var(--tutu-color-surface-d); } - -.toot.compact { - display: grid; - grid-template-columns: auto 1fr; - gap: 8px; - row-gap: 0; - padding-block: var(--card-gut, 16px); - padding-inline: var(--card-pad, 16px); - - > :first-child { - grid-row: 1/3; - } - - > :last-child { - grid-column: 2 /3; - } -} - -.compactAuthorGroup { - display: flex; - gap: 8px; - align-items: center; - margin-bottom: 8px; - flex-flow: row wrap; - justify-content: flex-end; - - >.compactAuthorUsername { - color: var(--tutu-color-secondary-text-on-surface); - flex-grow: 1; - } - - >time { - color: var(--tutu-color-secondary-text-on-surface); - } -} - -.tootRetootGrp { - display: flex; - gap: 0.25em; - margin-bottom: 8px; - align-items: center; - - > :first-child { - margin-right: 0.25em; - } -} - -.tootBottomActionGrp { - composes: cardGutSkip from "~material/cards.module.css"; - padding-block: calc((var(--card-gut) - 10px) / 2); - - animation: 225ms var(--tutu-anim-curve-std) tootBottomExpanding; - display: flex; - flex-flow: row wrap; - justify-content: space-evenly; - - >button { - color: var(--tutu-color-on-surface); - padding: 10px 8px; - - >svg { - font-size: 20px; - } - } -} - -.tootActionWithCount { - display: flex; - align-items: center; - gap: 8px; -} - -.tootAction { - display: flex; - align-items: center; - justify-content: center; -} - -@keyframes tootBottomExpanding { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } -} \ No newline at end of file diff --git a/src/timelines/toots/TootActionGroup.css b/src/timelines/toots/TootActionGroup.css new file mode 100644 index 0000000..b82432c --- /dev/null +++ b/src/timelines/toots/TootActionGroup.css @@ -0,0 +1,41 @@ +.TootActionGroup { + padding-block: calc((var(--card-gut) - 10px) / 2); + contain: layout style; + + animation: 225ms var(--tutu-anim-curve-std) TootActionGroup_fade-in; + display: flex; + flex-flow: row wrap; + justify-content: space-evenly; + + >button { + color: var(--tutu-color-on-surface); + padding: 10px 8px; + + >svg { + font-size: 20px; + } + } + + >* { + display: flex; + align-items: center; + } + + >.with-count { + gap: 8px; + } + + >.plain { + justify-content: center; + } +} + +@keyframes TootActionGroup_fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/src/timelines/toots/TootActionGroup.tsx b/src/timelines/toots/TootActionGroup.tsx new file mode 100644 index 0000000..fef75ef --- /dev/null +++ b/src/timelines/toots/TootActionGroup.tsx @@ -0,0 +1,89 @@ +import type { mastodon } from "masto"; +import { useTootEnv } from "../RegularToot"; +import { Button } from "@suid/material"; +import { Show } from "solid-js"; +import { + Bookmark, + BookmarkAddOutlined, + Repeat, + ReplyAll, + Share, + Star, + StarOutline, +} from "@suid/icons-material"; +import { canShare, share } from "~platform/share"; +import "./TootActionGroup.css"; + +async function shareContent(toot: mastodon.v1.Status) { + return await share({ + url: toot.url ?? undefined, + }); +} + +function isolatedCallback(e: MouseEvent) { + e.stopPropagation(); +} + +function TootActionGroup(props: { + value: T; + class?: string; +}) { + const { reply, boost, favourite, bookmark } = useTootEnv(); + let actGrpElement: HTMLDivElement; + const toot = () => props.value; + return ( +
+ + + + + + + + + + +
+ ); +} + +export default TootActionGroup; diff --git a/src/timelines/toots/TootPoll.tsx b/src/timelines/toots/TootPoll.tsx index ce972b0..a148fcb 100644 --- a/src/timelines/toots/TootPoll.tsx +++ b/src/timelines/toots/TootPoll.tsx @@ -28,21 +28,17 @@ import { useTimeSource } from "~platform/timesrc"; import { useDateFnLocale } from "~platform/i18n"; import TootPollDialog from "./TootPollDialog"; import { ANIM_CURVE_STD } from "~material/theme"; +import { useTootEnv } from "../RegularToot"; type TootPollProps = { - options: Readonly; - multiple?: boolean; - votesCount: number; - expired?: boolean; - expiredAt?: Date; - voted?: boolean; - ownVotes?: readonly number[]; - - onVote(votes: readonly number[]): void | Promise; + value: mastodon.v1.Poll + status: mastodon.v1.Status }; const TootPoll: Component = (props) => { let list: HTMLUListElement; + const {vote}= useTootEnv() + const now = useTimeSource(); const dateFnLocale = useDateFnLocale(); const [mustShowResult, setMustShowResult] = createSignal(); @@ -50,24 +46,26 @@ const TootPoll: Component = (props) => { const [initialVote, setInitialVote] = createSignal(0); + const poll = () => props.value + const isShowResult = () => { const n = mustShowResult(); if (typeof n !== "undefined") { return n; } - return props.expired || props.voted; + return poll().expired || poll().voted; }; const isOwnVote = createSelector( - () => props.ownVotes, + () => poll().ownVotes, (idx: number, votes) => votes?.includes(idx) || false, ); const openVote = (i: number, event: Event) => { event.stopPropagation(); - if (props.expired || props.voted) { + if (poll().expired || poll().voted) { return; } @@ -100,13 +98,13 @@ const TootPoll: Component = (props) => { return (
- {props.votesCount} votes in total - + {poll().votesCount} votes in total + Poll is ended
- + {(option, index) => { return ( <> @@ -140,7 +138,7 @@ const TootPoll: Component = (props) => { = (props) => { - + - {isBefore(now(), props.expiredAt!) ? "Expire in" : "Expired"} + {isBefore(now(), poll().expiresAt!) ? "Expire in" : "Expired"} -