102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import type { mastodon } from "masto";
|
|
import { Show, createResource, createSignal, type Component, type Ref } from "solid-js";
|
|
import CompactToot from "./CompactToot";
|
|
import { useTimeSource } from "../platform/timesrc";
|
|
import RegularToot from "./RegularToot";
|
|
import cardStyle from "../material/cards.module.css";
|
|
import { css } from "solid-styled";
|
|
|
|
type TootThreadProps = {
|
|
ref?: Ref<HTMLElement>,
|
|
status: mastodon.v1.Status;
|
|
client: mastodon.rest.Client;
|
|
expanded?: 0 | 1 | 2;
|
|
|
|
onBoost?(client: mastodon.rest.Client, status: mastodon.v1.Status): void;
|
|
onBookmark?(client: mastodon.rest.Client, status: mastodon.v1.Status): void;
|
|
onReply?(client: mastodon.rest.Client, status: mastodon.v1.Status): void
|
|
onExpandChange?(level: 0 | 1 | 2): void;
|
|
};
|
|
|
|
const TootThread: Component<TootThreadProps> = (props) => {
|
|
const status = () => props.status;
|
|
const now = useTimeSource();
|
|
const expanded = () => props.expanded ?? 0;
|
|
|
|
const [inReplyTo] = createResource(
|
|
() => [props.client, status().inReplyToId || null] as const,
|
|
async ([client, replyToId]) => {
|
|
if (!(client && replyToId)) return null;
|
|
return await client.v1.statuses.$select(replyToId).fetch();
|
|
},
|
|
);
|
|
|
|
const boost = (status: mastodon.v1.Status) => {
|
|
props.onBoost?.(props.client, status);
|
|
};
|
|
|
|
const bookmark = (status: mastodon.v1.Status) => {
|
|
props.onBookmark?.(props.client, status);
|
|
};
|
|
|
|
const reply = (status: mastodon.v1.Status) => {
|
|
props.onReply?.(props.client, status)
|
|
}
|
|
|
|
css`
|
|
article {
|
|
transition:
|
|
margin 90ms var(--tutu-anim-curve-sharp),
|
|
var(--tutu-transition-shadow);
|
|
user-select: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.thread-line {
|
|
position: relative;
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 36px;
|
|
top: 16px;
|
|
bottom: 0;
|
|
background-color: var(--tutu-color-secondary);
|
|
width: 2px;
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
.expanded {
|
|
margin-block: 20px;
|
|
box-shadow: var(--tutu-shadow-e9);
|
|
}
|
|
`;
|
|
|
|
const nextExpandLevel = [1, 2, 2] as const;
|
|
|
|
return (
|
|
<article
|
|
ref={props.ref}
|
|
classList={{ "thread-line": !!inReplyTo(), expanded: expanded() > 0 }}
|
|
onClick={() => props.onExpandChange?.(nextExpandLevel[expanded()])}
|
|
>
|
|
<Show when={inReplyTo()}>
|
|
<CompactToot
|
|
status={inReplyTo()!}
|
|
now={now()}
|
|
class={[cardStyle.card, cardStyle.manualMargin].join(" ")}
|
|
/>
|
|
</Show>
|
|
<RegularToot
|
|
status={status()}
|
|
class={cardStyle.card}
|
|
actionable={expanded() > 0}
|
|
onBookmark={(s) => bookmark(s)}
|
|
onRetoot={(s) => boost(s)}
|
|
onReply={s => reply(s)}
|
|
/>
|
|
</article>
|
|
);
|
|
};
|
|
|
|
export default TootThread;
|