added composing new toots
This commit is contained in:
parent
74770649c3
commit
e0d1136230
3 changed files with 55 additions and 15 deletions
|
@ -11,7 +11,7 @@ import {
|
||||||
Suspense,
|
Suspense,
|
||||||
Match,
|
Match,
|
||||||
Switch as JsSwitch,
|
Switch as JsSwitch,
|
||||||
ErrorBoundary
|
ErrorBoundary,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { useDocumentTitle } from "../utils";
|
import { useDocumentTitle } from "../utils";
|
||||||
import { type mastodon } from "masto";
|
import { type mastodon } from "masto";
|
||||||
|
@ -47,6 +47,7 @@ import { HeroSourceProvider, type HeroSource } from "../platform/anim";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
import { useSignedInProfiles } from "../masto/acct";
|
import { useSignedInProfiles } from "../masto/acct";
|
||||||
import { setCache as setTootBottomSheetCache } from "./TootBottomSheet";
|
import { setCache as setTootBottomSheetCache } from "./TootBottomSheet";
|
||||||
|
import TootComposer from "./TootComposer";
|
||||||
|
|
||||||
const TimelinePanel: Component<{
|
const TimelinePanel: Component<{
|
||||||
client: mastodon.rest.Client;
|
client: mastodon.rest.Client;
|
||||||
|
@ -72,6 +73,7 @@ const TimelinePanel: Component<{
|
||||||
{ fullRefresh: props.fullRefetch },
|
{ fullRefresh: props.fullRefetch },
|
||||||
);
|
);
|
||||||
const [expandedThreadId, setExpandedThreadId] = createSignal<string>();
|
const [expandedThreadId, setExpandedThreadId] = createSignal<string>();
|
||||||
|
const [typing, setTyping] = createSignal(false);
|
||||||
|
|
||||||
const tlEndObserver = new IntersectionObserver(() => {
|
const tlEndObserver = new IntersectionObserver(() => {
|
||||||
if (untrack(() => props.prefetch) && !snapshot.loading)
|
if (untrack(() => props.prefetch) && !snapshot.loading)
|
||||||
|
@ -126,9 +128,11 @@ const TimelinePanel: Component<{
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary fallback={(err, reset) => {
|
<ErrorBoundary
|
||||||
return <p>Oops: {String(err)}</p>
|
fallback={(err, reset) => {
|
||||||
}}>
|
return <p>Oops: {String(err)}</p>;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<PullDownToRefresh
|
<PullDownToRefresh
|
||||||
linkedElement={scrollLinked()}
|
linkedElement={scrollLinked()}
|
||||||
loading={snapshot.loading}
|
loading={snapshot.loading}
|
||||||
|
@ -141,6 +145,17 @@ const TimelinePanel: Component<{
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<Show when={props.name === "home"}>
|
||||||
|
<TootComposer
|
||||||
|
style={{
|
||||||
|
"--scaffold-topbar-height": "0px",
|
||||||
|
}}
|
||||||
|
isTyping={typing()}
|
||||||
|
onTypingChange={setTyping}
|
||||||
|
client={props.client}
|
||||||
|
onSent={() => refetchTimeline({direction: "new"})}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
<For each={timeline}>
|
<For each={timeline}>
|
||||||
{(item, index) => {
|
{(item, index) => {
|
||||||
let element: HTMLElement | undefined;
|
let element: HTMLElement | undefined;
|
||||||
|
|
|
@ -23,7 +23,7 @@ import cards from "../material/cards.module.css";
|
||||||
import { css } from "solid-styled";
|
import { css } from "solid-styled";
|
||||||
import { vibrate } from "../platform/hardware";
|
import { vibrate } from "../platform/hardware";
|
||||||
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
|
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
|
||||||
import ReplyEditor from "./ReplyEditor";
|
import TootComposer from "./TootComposer";
|
||||||
|
|
||||||
let cachedEntry: [string, mastodon.v1.Status] | undefined;
|
let cachedEntry: [string, mastodon.v1.Status] | undefined;
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ const TootBottomSheet: Component = (props) => {
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<Show when={session()!.account}>
|
<Show when={session()!.account}>
|
||||||
<ReplyEditor
|
<TootComposer
|
||||||
isTyping={isInTyping()}
|
isTyping={isInTyping()}
|
||||||
onTypingChange={setInTyping}
|
onTypingChange={setInTyping}
|
||||||
mentions={defaultMentions()}
|
mentions={defaultMentions()}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import {
|
||||||
onMount,
|
onMount,
|
||||||
Show,
|
Show,
|
||||||
type Component,
|
type Component,
|
||||||
|
type JSX,
|
||||||
|
type Ref,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import Scaffold from "../material/Scaffold";
|
import Scaffold from "../material/Scaffold";
|
||||||
import {
|
import {
|
||||||
|
@ -177,15 +179,26 @@ const TootLanguagePickerDialog: Component<{
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReplyEditor: Component<{
|
function randomChoose<T extends any[]>(
|
||||||
profile: Account;
|
rn: number,
|
||||||
replyToDisplayName: string;
|
K: T,
|
||||||
|
): T extends Array<infer E> ? E : never {
|
||||||
|
const idx = Math.round(rn * K.length);
|
||||||
|
return K[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
const TootComposer: Component<{
|
||||||
|
ref?: Ref<HTMLDivElement>;
|
||||||
|
style?: JSX.CSSProperties;
|
||||||
|
profile?: Account;
|
||||||
|
replyToDisplayName?: string;
|
||||||
mentions?: readonly string[];
|
mentions?: readonly string[];
|
||||||
isTyping?: boolean;
|
isTyping?: boolean;
|
||||||
onTypingChange: (value: boolean) => void;
|
onTypingChange: (value: boolean) => void;
|
||||||
client?: mastodon.rest.Client;
|
client?: mastodon.rest.Client;
|
||||||
inReplyToId?: string;
|
inReplyToId?: string;
|
||||||
onSent?: (status: mastodon.v1.Status) => void;
|
onSent?: (status: mastodon.v1.Status) => void;
|
||||||
|
inputProps?: JSX.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
let inputRef: HTMLTextAreaElement;
|
let inputRef: HTMLTextAreaElement;
|
||||||
let sendKey: string | undefined;
|
let sendKey: string | undefined;
|
||||||
|
@ -223,6 +236,7 @@ const ReplyEditor: Component<{
|
||||||
top: "var(--scaffold-topbar-height, 0)",
|
top: "var(--scaffold-topbar-height, 0)",
|
||||||
bottom: "var(--safe-area-inset-bottom, 0)",
|
bottom: "var(--safe-area-inset-bottom, 0)",
|
||||||
"z-index": 2,
|
"z-index": 2,
|
||||||
|
...props.style,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
@ -274,20 +288,31 @@ const ReplyEditor: Component<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={props.ref}
|
||||||
class={tootComposers.composer}
|
class={tootComposers.composer}
|
||||||
style={containerStyle()}
|
style={containerStyle()}
|
||||||
onClick={(e) => inputRef.focus()}
|
onClick={(e) => inputRef.focus()}
|
||||||
>
|
>
|
||||||
<div class={tootComposers.replyInput}>
|
<div class={tootComposers.replyInput}>
|
||||||
<Avatar
|
<Show when={props.profile}>
|
||||||
src={props.profile.inf?.avatar}
|
<Avatar
|
||||||
sx={{ marginLeft: "-0.25em" }}
|
src={props.profile!.inf?.avatar}
|
||||||
/>
|
sx={{ marginLeft: "-0.25em" }}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
<textarea
|
<textarea
|
||||||
ref={inputRef!}
|
ref={inputRef!}
|
||||||
placeholder={`Reply to ${props.replyToDisplayName}...`}
|
placeholder={
|
||||||
|
props.replyToDisplayName
|
||||||
|
? `Reply to ${props.replyToDisplayName}...`
|
||||||
|
: randomChoose(Math.random(), [
|
||||||
|
"What's happening?",
|
||||||
|
"What do your think?",
|
||||||
|
])
|
||||||
|
}
|
||||||
style={{ width: "100%", border: "none" }}
|
style={{ width: "100%", border: "none" }}
|
||||||
disabled={sending()}
|
disabled={sending()}
|
||||||
|
{...props.inputProps}
|
||||||
></textarea>
|
></textarea>
|
||||||
<Show when={props.client}>
|
<Show when={props.client}>
|
||||||
<Show
|
<Show
|
||||||
|
@ -351,4 +376,4 @@ const ReplyEditor: Component<{
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ReplyEditor;
|
export default TootComposer;
|
Loading…
Reference in a new issue