fix #35: pass correct value to TootActionGroup
All checks were successful
/ depoly (push) Successful in 1m19s
All checks were successful
/ depoly (push) Successful in 1m19s
* add createDefaultTootEnv
This commit is contained in:
parent
6dd6065711
commit
5742932c86
7 changed files with 179 additions and 235 deletions
|
@ -26,8 +26,8 @@ export type Account = AccountKey & {
|
|||
inf?: mastodon.v1.AccountCredentials;
|
||||
};
|
||||
|
||||
export function isAccount(object: AccountKey) {
|
||||
return !!(object as Record<string, unknown>)["tokenType"];
|
||||
export function isAccount(object: RemoteServer) {
|
||||
return isAccountKey(object) && !!(object as Record<string, unknown>)["tokenType"];
|
||||
}
|
||||
|
||||
export const $accounts = persistentAtom<Account[]>("accounts", [], {
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
untrack,
|
||||
useContext,
|
||||
} from "solid-js";
|
||||
import { Account } from "../accounts/stores";
|
||||
import { Account, type RemoteServer } from "../accounts/stores";
|
||||
import { createRestAPIClient, mastodon } from "masto";
|
||||
import { useLocation } from "@solidjs/router";
|
||||
import { useNavigator } from "~platform/StackedRouter";
|
||||
|
@ -131,8 +131,8 @@ export function useSessionForAcctStr(acct: Accessor<string>) {
|
|||
return (
|
||||
authedSession ?? {
|
||||
client: createUnauthorizedClient(inputSite),
|
||||
account: { site: inputSite }, // TODO: we need some security checks here?
|
||||
}
|
||||
account: { site: inputSite } as RemoteServer, // TODO: we need some security checks here?
|
||||
} as const
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
catchError,
|
||||
createRenderEffect,
|
||||
createResource,
|
||||
createSignal,
|
||||
createUniqueId,
|
||||
|
@ -14,7 +13,6 @@ import {
|
|||
} from "solid-js";
|
||||
import Scaffold from "~material/Scaffold";
|
||||
import {
|
||||
AppBar,
|
||||
Avatar,
|
||||
Button,
|
||||
Checkbox,
|
||||
|
@ -26,7 +24,6 @@ import {
|
|||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
Toolbar,
|
||||
} from "@suid/material";
|
||||
import {
|
||||
Close,
|
||||
|
@ -63,6 +60,7 @@ import {
|
|||
default as ItemSelectionProvider,
|
||||
} from "../timelines/toots/ItemSelectionProvider";
|
||||
import AppTopBar from "~material/AppTopBar";
|
||||
import type { Account } from "../accounts/stores";
|
||||
|
||||
const Profile: Component = () => {
|
||||
const { pop } = useNavigator();
|
||||
|
@ -117,7 +115,7 @@ const Profile: Component = () => {
|
|||
};
|
||||
|
||||
const isCurrentSessionProfile = () => {
|
||||
return session().account?.inf?.url === profile()?.url;
|
||||
return (session().account as Account).inf?.url === profile()?.url;
|
||||
};
|
||||
|
||||
const [recentTootFilter, setRecentTootFilter] = createSignal({
|
||||
|
@ -172,8 +170,8 @@ const Profile: Component = () => {
|
|||
|
||||
const sessionDisplayName = createMemo(() =>
|
||||
resolveCustomEmoji(
|
||||
session().account?.inf?.displayName || "",
|
||||
session().account?.inf?.emojis ?? [],
|
||||
(session().account as Account).inf?.displayName || "",
|
||||
(session().account as Account).inf?.emojis ?? [],
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -244,7 +242,7 @@ const Profile: Component = () => {
|
|||
<Show when={session().account}>
|
||||
<MenuItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar src={session().account?.inf?.avatar} />
|
||||
<Avatar src={(session().account as Account).inf?.avatar} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText secondary={"Default account"}>
|
||||
<span innerHTML={sessionDisplayName()}></span>
|
||||
|
@ -367,7 +365,7 @@ const Profile: Component = () => {
|
|||
aria-label={`${relationship()?.following ? "Unfollow" : "Follow"} on your home timeline`}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar src={session().account?.inf?.avatar}></Avatar>
|
||||
<Avatar src={(session().account as Account).inf?.avatar}></Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
secondary={
|
||||
|
|
|
@ -23,6 +23,7 @@ import TootPoll from "./toots/TootPoll";
|
|||
import TootActionGroup from "./toots/TootActionGroup.js";
|
||||
import TootAuthorGroup from "./toots/TootAuthorGroup.js";
|
||||
import "./RegularToot.css";
|
||||
import { vibrate } from "~platform/hardware.js";
|
||||
|
||||
export type TootEnv = {
|
||||
boost: (value: mastodon.v1.Status) => void;
|
||||
|
@ -52,6 +53,107 @@ export function useTootEnv() {
|
|||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default toot env.
|
||||
*
|
||||
* This function does not provides the "reply" action.
|
||||
*/
|
||||
export function createDefaultTootEnv(
|
||||
client: () => mastodon.rest.Client | undefined,
|
||||
setToot: (id: string, status: mastodon.v1.Status) => void,
|
||||
): TootEnv {
|
||||
return {
|
||||
async bookmark(status: mastodon.v1.Status) {
|
||||
const c = client();
|
||||
if (!c) return;
|
||||
|
||||
const result = await (status.bookmarked
|
||||
? c.v1.statuses.$select(status.id).unbookmark()
|
||||
: c.v1.statuses.$select(status.id).bookmark());
|
||||
|
||||
setToot(result.id, result);
|
||||
},
|
||||
|
||||
async boost(status: mastodon.v1.Status) {
|
||||
const c = client();
|
||||
if (!c) return;
|
||||
|
||||
vibrate(50);
|
||||
const rootStatus = status.reblog ? status.reblog : status;
|
||||
const reblogged = rootStatus.reblogged;
|
||||
if (status.reblog) {
|
||||
setToot(status.id, {
|
||||
...status,
|
||||
reblog: { ...status.reblog!, reblogged: !reblogged },
|
||||
reblogged: !reblogged,
|
||||
});
|
||||
} else {
|
||||
setToot(status.id, {
|
||||
...status,
|
||||
reblogged: !reblogged,
|
||||
});
|
||||
}
|
||||
// modified the original
|
||||
|
||||
const result = reblogged
|
||||
? await c.v1.statuses.$select(status.id).unreblog()
|
||||
: (await c.v1.statuses.$select(status.id).reblog());
|
||||
|
||||
if (status.reblog && !reblogged) {
|
||||
// When calling /reblog, the result is the boost object (the actor
|
||||
// is the calling account); for /unreblog, the result is the original
|
||||
// toot. So we only do this trick only on the reblogging.
|
||||
setToot(status.id, {
|
||||
...status,
|
||||
reblog: result.reblog,
|
||||
});
|
||||
} else {
|
||||
setToot(status.id, result);
|
||||
}
|
||||
},
|
||||
|
||||
async favourite(status: mastodon.v1.Status) {
|
||||
const c = client();
|
||||
if (!c) return;
|
||||
|
||||
const ovalue = status.favourited;
|
||||
setToot(status.id, { ...status, favourited: !ovalue });
|
||||
|
||||
const result = ovalue
|
||||
? await c.v1.statuses.$select(status.id).unfavourite()
|
||||
: await c.v1.statuses.$select(status.id).favourite();
|
||||
setToot(status.id, result);
|
||||
},
|
||||
|
||||
async vote(status: mastodon.v1.Status, votes: readonly number[]) {
|
||||
const c = client();
|
||||
if (!c) return;
|
||||
|
||||
const toot = status.reblog ?? status;
|
||||
if (!toot.poll) return;
|
||||
|
||||
const npoll = await c.v1.polls.$select(toot.poll.id).votes.create({
|
||||
choices: votes,
|
||||
});
|
||||
|
||||
if (status.reblog) {
|
||||
setToot(status.id, {
|
||||
...status,
|
||||
reblog: {
|
||||
...status.reblog,
|
||||
poll: npoll,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setToot(status.id, {
|
||||
...status,
|
||||
poll: npoll,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type RegularTootProps = {
|
||||
status: mastodon.v1.Status;
|
||||
actionable?: boolean;
|
||||
|
@ -203,7 +305,7 @@ const RegularToot: Component<RegularTootProps> = (oprops) => {
|
|||
class={cardStyle.cardNoPad}
|
||||
style={{ "margin-top": "8px" }}
|
||||
/>
|
||||
<TootActionGroup value={toot()} class={cardStyle.cardGutSkip} />
|
||||
<TootActionGroup value={status()} class={cardStyle.cardGutSkip} />
|
||||
</Show>
|
||||
</article>
|
||||
</>
|
||||
|
|
|
@ -1,23 +1,17 @@
|
|||
import { useParams } from "@solidjs/router";
|
||||
import {
|
||||
catchError,
|
||||
createResource,
|
||||
Show,
|
||||
type Component,
|
||||
} from "solid-js";
|
||||
import { catchError, createResource, Show, type Component } from "solid-js";
|
||||
import Scaffold from "~material/Scaffold";
|
||||
import { CircularProgress } from "@suid/material";
|
||||
import { Title } from "~material/typography";
|
||||
import { useSessionForAcctStr } from "../masto/clients";
|
||||
import { resolveCustomEmoji } from "../masto/toot";
|
||||
import RegularToot, {
|
||||
createDefaultTootEnv,
|
||||
findElementActionable,
|
||||
TootEnvProvider,
|
||||
} from "./RegularToot";
|
||||
import type { mastodon } from "masto";
|
||||
import cards from "~material/cards.module.css";
|
||||
import { css } from "solid-styled";
|
||||
import { vibrate } from "~platform/hardware";
|
||||
import { createTimeSource, TimeSourceProvider } from "~platform/timesrc";
|
||||
import TootComposer from "./TootComposer";
|
||||
import { useDocumentTitle } from "../utils";
|
||||
|
@ -53,7 +47,7 @@ const TootBottomSheet: Component = (props) => {
|
|||
|
||||
const [tootContextErrorUncaught, { refetch: refetchContext }] =
|
||||
createResource(
|
||||
() => [session().client, params.id] as const,
|
||||
() => [session().client, toot()?.reblog?.id ?? params.id] as const,
|
||||
async ([client, id]) => {
|
||||
return await client.v1.statuses.$select(id).context.fetch();
|
||||
},
|
||||
|
@ -89,49 +83,10 @@ const TootBottomSheet: Component = (props) => {
|
|||
return s.account ? s : undefined;
|
||||
};
|
||||
|
||||
const onBookmark = async () => {
|
||||
const status = remoteToot()!;
|
||||
const client = actSession()!.client;
|
||||
setRemoteToot(
|
||||
Object.assign({}, status, {
|
||||
bookmarked: !status.bookmarked,
|
||||
}),
|
||||
);
|
||||
const result = await (status.bookmarked
|
||||
? client.v1.statuses.$select(status.id).unbookmark()
|
||||
: client.v1.statuses.$select(status.id).bookmark());
|
||||
setRemoteToot(result);
|
||||
};
|
||||
|
||||
const onBoost = async () => {
|
||||
const status = remoteToot()!;
|
||||
const client = actSession()!.client;
|
||||
vibrate(50);
|
||||
setRemoteToot(
|
||||
Object.assign({}, status, {
|
||||
reblogged: !status.reblogged,
|
||||
}),
|
||||
);
|
||||
const result = await (status.reblogged
|
||||
? client.v1.statuses.$select(status.id).unreblog()
|
||||
: client.v1.statuses.$select(status.id).reblog());
|
||||
vibrate([20, 30]);
|
||||
setRemoteToot(result.reblog!);
|
||||
};
|
||||
|
||||
const onFav = async () => {
|
||||
const status = remoteToot()!;
|
||||
const client = actSession()!.client;
|
||||
setRemoteToot(
|
||||
Object.assign({}, status, {
|
||||
favourited: !status.favourited,
|
||||
}),
|
||||
);
|
||||
const result = await (status.favourited
|
||||
? client.v1.statuses.$select(status.id).favourite()
|
||||
: client.v1.statuses.$select(status.id).unfavourite());
|
||||
setRemoteToot(result);
|
||||
};
|
||||
const mainTootEnv = createDefaultTootEnv(
|
||||
() => actSession()?.client,
|
||||
(_, status) => setRemoteToot(status),
|
||||
);
|
||||
|
||||
const defaultMentions = () => {
|
||||
const tootAcct = remoteToot()?.reblog?.account ?? remoteToot()?.account;
|
||||
|
@ -145,33 +100,6 @@ 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 },
|
||||
) => {
|
||||
|
@ -237,58 +165,51 @@ const TootBottomSheet: Component = (props) => {
|
|||
>
|
||||
<div class="Scrollable">
|
||||
<TimeSourceProvider value={time}>
|
||||
<TootList
|
||||
threads={ancestors.list}
|
||||
onUnknownThread={ancestors.getPath}
|
||||
onChangeToot={ancestors.set}
|
||||
/>
|
||||
|
||||
<article>
|
||||
<Show when={toot()}>
|
||||
<TootEnvProvider
|
||||
value={{
|
||||
bookmark: onBookmark,
|
||||
boost: onBoost,
|
||||
favourite: onFav,
|
||||
vote,
|
||||
}}
|
||||
>
|
||||
<RegularToot
|
||||
id={`toot-${toot()!.id}`}
|
||||
class={cards.card}
|
||||
style={{
|
||||
"scroll-margin-top":
|
||||
"calc(var(--scaffold-topbar-height) + 20px)",
|
||||
cursor: "auto",
|
||||
"user-select": "auto",
|
||||
}}
|
||||
status={toot()!}
|
||||
actionable={!!actSession()}
|
||||
evaluated={true}
|
||||
onClick={handleMainTootClick}
|
||||
></RegularToot>
|
||||
</TootEnvProvider>
|
||||
</Show>
|
||||
</article>
|
||||
|
||||
<Show when={(session().account as Account).inf}>
|
||||
<TootComposer
|
||||
mentions={defaultMentions()}
|
||||
profile={(session().account! as Account).inf}
|
||||
replyToDisplayName={toot()?.account?.displayName || ""}
|
||||
client={session().client}
|
||||
onSent={() => refetchContext()}
|
||||
inReplyToId={remoteToot()?.reblog?.id ?? remoteToot()?.id}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<Show when={tootContextErrorUncaught.loading}>
|
||||
<div class="progress-line">
|
||||
<CircularProgress style="width: 1.5em; height: 1.5em;" />
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<ItemSelectionProvider value={selectionState}>
|
||||
<TootList
|
||||
threads={ancestors.list}
|
||||
onUnknownThread={ancestors.getPath}
|
||||
onChangeToot={ancestors.set}
|
||||
/>
|
||||
|
||||
<article>
|
||||
<Show when={toot()}>
|
||||
<TootEnvProvider value={mainTootEnv}>
|
||||
<RegularToot
|
||||
id={`toot-${toot()!.id}`}
|
||||
class={cards.card}
|
||||
style={{
|
||||
"scroll-margin-top":
|
||||
"calc(var(--scaffold-topbar-height) + 20px)",
|
||||
cursor: "auto",
|
||||
"user-select": "auto",
|
||||
}}
|
||||
status={toot()!}
|
||||
actionable={!!actSession()}
|
||||
evaluated={true}
|
||||
onClick={handleMainTootClick}
|
||||
></RegularToot>
|
||||
</TootEnvProvider>
|
||||
</Show>
|
||||
</article>
|
||||
|
||||
<Show when={(session().account as Account).inf}>
|
||||
<TootComposer
|
||||
mentions={defaultMentions()}
|
||||
profile={(session().account! as Account).inf}
|
||||
replyToDisplayName={toot()?.account?.displayName || ""}
|
||||
client={session().client}
|
||||
onSent={() => refetchContext()}
|
||||
inReplyToId={remoteToot()?.reblog?.id ?? remoteToot()?.id}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<Show when={tootContextErrorUncaught.loading}>
|
||||
<div class="progress-line">
|
||||
<CircularProgress style="width: 1.5em; height: 1.5em;" />
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<TootList
|
||||
threads={descendants.list}
|
||||
onUnknownThread={descendants.getPath}
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import {
|
||||
Component,
|
||||
createSignal,
|
||||
ErrorBoundary,
|
||||
type Ref,
|
||||
createSelector,
|
||||
Index,
|
||||
createMemo,
|
||||
For,
|
||||
createUniqueId,
|
||||
} from "solid-js";
|
||||
import { type mastodon } from "masto";
|
||||
import { vibrate } from "~platform/hardware";
|
||||
import { useDefaultSession } from "../masto/clients";
|
||||
import { setCache as setTootBottomSheetCache } from "./TootBottomSheet";
|
||||
import RegularToot, {
|
||||
createDefaultTootEnv,
|
||||
findElementActionable,
|
||||
findRootToot,
|
||||
TootEnvProvider,
|
||||
|
@ -62,62 +59,12 @@ const TootList: Component<{
|
|||
const [isExpanded, setExpanded] = useItemSelection();
|
||||
const { push } = useNavigator();
|
||||
|
||||
const onBookmark = async (status: mastodon.v1.Status) => {
|
||||
const client = session()?.client;
|
||||
if (!client) return;
|
||||
const tootEnv = createDefaultTootEnv(
|
||||
() => session()?.client,
|
||||
(...args) => props.onChangeToot(...args),
|
||||
);
|
||||
|
||||
const result = await (status.bookmarked
|
||||
? client.v1.statuses.$select(status.id).unbookmark()
|
||||
: client.v1.statuses.$select(status.id).bookmark());
|
||||
props.onChangeToot(result.id, result);
|
||||
};
|
||||
|
||||
const toggleBoost = async (status: mastodon.v1.Status) => {
|
||||
const client = session()?.client;
|
||||
if (!client) return;
|
||||
|
||||
vibrate(50);
|
||||
const rootStatus = status.reblog ? status.reblog : status;
|
||||
const reblogged = rootStatus.reblogged;
|
||||
if (status.reblog) {
|
||||
props.onChangeToot(status.id, {
|
||||
...status,
|
||||
reblog: { ...status.reblog, reblogged: !reblogged },
|
||||
});
|
||||
} else {
|
||||
props.onChangeToot(status.id, {
|
||||
...status,
|
||||
reblogged: !reblogged,
|
||||
});
|
||||
}
|
||||
|
||||
const result = reblogged
|
||||
? await client.v1.statuses.$select(status.id).unreblog()
|
||||
: (await client.v1.statuses.$select(status.id).reblog()).reblog!;
|
||||
|
||||
if (status.reblog) {
|
||||
props.onChangeToot(status.id, {
|
||||
...status,
|
||||
reblog: result,
|
||||
});
|
||||
} else {
|
||||
props.onChangeToot(status.id, result);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleFavourite = async (status: mastodon.v1.Status) => {
|
||||
const client = session()?.client;
|
||||
if (!client) return;
|
||||
const ovalue = status.favourited;
|
||||
props.onChangeToot(status.id, { ...status, favourited: !ovalue });
|
||||
|
||||
const result = ovalue
|
||||
? await client.v1.statuses.$select(status.id).unfavourite()
|
||||
: await client.v1.statuses.$select(status.id).favourite();
|
||||
props.onChangeToot(status.id, result);
|
||||
};
|
||||
|
||||
const openFullScreenToot = (
|
||||
const openFullScreenToot = async (
|
||||
toot: mastodon.v1.Status,
|
||||
srcElement: HTMLElement,
|
||||
reply?: boolean,
|
||||
|
@ -235,33 +182,6 @@ const TootList: Component<{
|
|||
openFullScreenToot(status, element, true);
|
||||
};
|
||||
|
||||
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) {
|
||||
props.onChangeToot(status.id, {
|
||||
...status,
|
||||
reblog: {
|
||||
...status.reblog,
|
||||
poll: npoll,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
props.onChangeToot(status.id, {
|
||||
...status,
|
||||
poll: npoll,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ErrorBoundary
|
||||
fallback={(err, reset) => {
|
||||
|
@ -271,11 +191,8 @@ const TootList: Component<{
|
|||
>
|
||||
<TootEnvProvider
|
||||
value={{
|
||||
boost: toggleBoost,
|
||||
bookmark: onBookmark,
|
||||
favourite: toggleFavourite,
|
||||
...tootEnv,
|
||||
reply: reply,
|
||||
vote: vote,
|
||||
}}
|
||||
>
|
||||
<div ref={props.ref} id={props.id} class="toot-list">
|
||||
|
|
|
@ -24,13 +24,19 @@ function isolatedCallback(e: MouseEvent) {
|
|||
e.stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* The actions of the toot card.
|
||||
*
|
||||
* The `value` must be the original toot (contains `reblog` if
|
||||
* it's a boost), since the value will be passed to the callbacks.
|
||||
*/
|
||||
function TootActionGroup<T extends mastodon.v1.Status>(props: {
|
||||
value: T;
|
||||
class?: string;
|
||||
}) {
|
||||
const { reply, boost, favourite, bookmark } = useTootEnv();
|
||||
let actGrpElement: HTMLDivElement;
|
||||
const toot = () => props.value;
|
||||
const toot = () => props.value.reblog ?? props.value;
|
||||
return (
|
||||
<div
|
||||
ref={actGrpElement!}
|
||||
|
|
Loading…
Reference in a new issue