Profile: support subscribe to home timeline
All checks were successful
/ depoly (push) Successful in 3m0s
All checks were successful
/ depoly (push) Successful in 3m0s
This commit is contained in:
parent
97a1fb9cf1
commit
6705b754d1
1 changed files with 81 additions and 15 deletions
|
@ -17,6 +17,7 @@ import {
|
||||||
AppBar,
|
AppBar,
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
|
Checkbox,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Divider,
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
@ -67,6 +68,8 @@ const Profile: Component = () => {
|
||||||
const time = createTimeSource();
|
const time = createTimeSource();
|
||||||
|
|
||||||
const menuButId = createUniqueId();
|
const menuButId = createUniqueId();
|
||||||
|
const recentTootListId = createUniqueId();
|
||||||
|
const optMenuId = createUniqueId();
|
||||||
|
|
||||||
const [menuOpen, setMenuOpen] = createSignal(false);
|
const [menuOpen, setMenuOpen] = createSignal(false);
|
||||||
|
|
||||||
|
@ -88,17 +91,20 @@ const Profile: Component = () => {
|
||||||
);
|
);
|
||||||
onCleanup(() => obx.disconnect());
|
onCleanup(() => obx.disconnect());
|
||||||
|
|
||||||
const [profileErrorUncaught] = createResource(
|
const [profileUncaught] = createResource(
|
||||||
() => [session().client, params.id] as const,
|
() => [session().client, params.id] as const,
|
||||||
async ([client, id]) => {
|
async ([client, id]) => {
|
||||||
return await client.v1.accounts.$select(id).fetch();
|
return await client.v1.accounts.$select(id).fetch();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const profile = () =>
|
const profile = () => {
|
||||||
catchError(profileErrorUncaught, (err) => {
|
try {
|
||||||
console.error(err);
|
return profileUncaught();
|
||||||
});
|
} catch (reason) {
|
||||||
|
console.error(reason);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isCurrentSessionProfile = () => {
|
const isCurrentSessionProfile = () => {
|
||||||
return session().account?.inf?.url === profile()?.url;
|
return session().account?.inf?.url === profile()?.url;
|
||||||
|
@ -127,6 +133,22 @@ const Profile: Component = () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [relationshipUncaught, { mutate: mutateRelationship }] = createResource(
|
||||||
|
() => [session(), params.id] as const,
|
||||||
|
async ([sess, id]) => {
|
||||||
|
if (!sess.account) return; // No account, no relation
|
||||||
|
const relations = await session().client.v1.accounts.relationships.fetch({
|
||||||
|
id: [id],
|
||||||
|
});
|
||||||
|
return relations.length > 0 ? relations[0] : undefined;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const relationship = () =>
|
||||||
|
catchError(relationshipUncaught, (reason) => {
|
||||||
|
console.error(reason);
|
||||||
|
});
|
||||||
|
|
||||||
const bannerImg = () => profile()?.header;
|
const bannerImg = () => profile()?.header;
|
||||||
const avatarImg = () => profile()?.avatar;
|
const avatarImg = () => profile()?.avatar;
|
||||||
const displayName = () =>
|
const displayName = () =>
|
||||||
|
@ -149,10 +171,27 @@ const Profile: Component = () => {
|
||||||
createRenderEffect(() => (e.innerHTML = sessionDisplayName()));
|
createRenderEffect(() => (e.innerHTML = sessionDisplayName()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleSubscribeHome = async () => {
|
||||||
|
const client = session().client;
|
||||||
|
if (!session().account) return;
|
||||||
|
const isSubscribed = relationship()?.following ?? false;
|
||||||
|
mutateRelationship((x) => Object.assign({ following: !isSubscribed }, x));
|
||||||
|
subscribeMenuState.onClose();
|
||||||
|
|
||||||
|
if (isSubscribed) {
|
||||||
|
const nrel = await client.v1.accounts.$select(params.id).unfollow();
|
||||||
|
mutateRelationship(nrel);
|
||||||
|
} else {
|
||||||
|
const nrel = await client.v1.accounts.$select(params.id).follow();
|
||||||
|
mutateRelationship(nrel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scaffold
|
<Scaffold
|
||||||
topbar={
|
topbar={
|
||||||
<AppBar
|
<AppBar
|
||||||
|
role="navigation"
|
||||||
position="static"
|
position="static"
|
||||||
color={scrolledPastBanner() ? "primary" : "transparent"}
|
color={scrolledPastBanner() ? "primary" : "transparent"}
|
||||||
elevation={scrolledPastBanner() ? undefined : 0}
|
elevation={scrolledPastBanner() ? undefined : 0}
|
||||||
|
@ -186,8 +225,10 @@ const Profile: Component = () => {
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
id={menuButId}
|
id={menuButId}
|
||||||
|
aria-controls={optMenuId}
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={[setMenuOpen, true]}
|
onClick={[setMenuOpen, true]}
|
||||||
|
aria-label="Open Options for the Profile"
|
||||||
>
|
>
|
||||||
<MoreVert />
|
<MoreVert />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -197,11 +238,13 @@ const Profile: Component = () => {
|
||||||
class="Profile"
|
class="Profile"
|
||||||
>
|
>
|
||||||
<Menu
|
<Menu
|
||||||
|
id={optMenuId}
|
||||||
open={menuOpen()}
|
open={menuOpen()}
|
||||||
onClose={[setMenuOpen, false]}
|
onClose={[setMenuOpen, false]}
|
||||||
anchor={() =>
|
anchor={() =>
|
||||||
document.getElementById(menuButId)!.getBoundingClientRect()
|
document.getElementById(menuButId)!.getBoundingClientRect()
|
||||||
}
|
}
|
||||||
|
aria-label="Options for the Profile"
|
||||||
>
|
>
|
||||||
<Show when={session().account}>
|
<Show when={session().account}>
|
||||||
<MenuItem>
|
<MenuItem>
|
||||||
|
@ -296,6 +339,7 @@ const Profile: Component = () => {
|
||||||
"margin-top":
|
"margin-top":
|
||||||
"calc(-1 * (var(--scaffold-topbar-height) + var(--safe-area-inset-top)))",
|
"calc(-1 * (var(--scaffold-topbar-height) + var(--safe-area-inset-top)))",
|
||||||
}}
|
}}
|
||||||
|
role="presentation"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
ref={(e) => obx.observe(e)}
|
ref={(e) => obx.observe(e)}
|
||||||
|
@ -306,6 +350,7 @@ const Profile: Component = () => {
|
||||||
height: "100%",
|
height: "100%",
|
||||||
}}
|
}}
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
|
alt={`Banner image for ${profile()?.displayName || "the user"}`}
|
||||||
onLoad={(event) => {
|
onLoad={(event) => {
|
||||||
const ins = new FastAverageColor();
|
const ins = new FastAverageColor();
|
||||||
const colors = ins.getColor(event.currentTarget);
|
const colors = ins.getColor(event.currentTarget);
|
||||||
|
@ -319,14 +364,21 @@ const Profile: Component = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Menu {...subscribeMenuState}>
|
<Menu {...subscribeMenuState}>
|
||||||
<MenuItem disabled>
|
<MenuItem
|
||||||
|
onClick={toggleSubscribeHome}
|
||||||
|
aria-details="Subscribe or Unsubscribe this account on your home timeline"
|
||||||
|
>
|
||||||
<ListItemAvatar>
|
<ListItemAvatar>
|
||||||
<Avatar src={session().account?.inf?.avatar}></Avatar>
|
<Avatar src={session().account?.inf?.avatar}></Avatar>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText>
|
<ListItemText
|
||||||
|
secondary={relationship()?.following ? "Subscribed" : undefined}
|
||||||
|
>
|
||||||
<span ref={useSessionDisplayName}></span>
|
<span ref={useSessionDisplayName}></span>
|
||||||
<span>'s Home</span>
|
<span>'s Home</span>
|
||||||
</ListItemText>
|
</ListItemText>
|
||||||
|
|
||||||
|
<Checkbox checked={relationship()?.following ?? false} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
|
@ -337,9 +389,10 @@ const Profile: Component = () => {
|
||||||
color: bannerSampledColors()?.text,
|
color: bannerSampledColors()?.text,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="acct-grp">
|
<section class="acct-grp">
|
||||||
<Avatar
|
<Avatar
|
||||||
src={avatarImg()}
|
src={avatarImg()}
|
||||||
|
alt={`${profile()?.displayName || "the user"}'s avatar`}
|
||||||
sx={{
|
sx={{
|
||||||
marginTop: "calc(-16px - 72px / 2)",
|
marginTop: "calc(-16px - 72px / 2)",
|
||||||
width: "72px",
|
width: "72px",
|
||||||
|
@ -351,12 +404,19 @@ const Profile: Component = () => {
|
||||||
ref={(e) =>
|
ref={(e) =>
|
||||||
createRenderEffect(() => (e.innerHTML = displayName()))
|
createRenderEffect(() => (e.innerHTML = displayName()))
|
||||||
}
|
}
|
||||||
|
aria-label="Display name"
|
||||||
></span>
|
></span>
|
||||||
<span>{fullUsername()}</span>
|
<span aria-label="Complete username">{fullUsername()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={!session().account || profileErrorUncaught.loading}>
|
<Match
|
||||||
|
when={
|
||||||
|
!session().account ||
|
||||||
|
profileUncaught.loading ||
|
||||||
|
profileUncaught.error
|
||||||
|
}
|
||||||
|
>
|
||||||
{<></>}
|
{<></>}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={isCurrentSessionProfile()}>
|
<Match when={isCurrentSessionProfile()}>
|
||||||
|
@ -374,20 +434,24 @@ const Profile: Component = () => {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Subscribe
|
{relationship()?.following ? "Subscribed" : "Subscribe"}
|
||||||
</Button>
|
</Button>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
<div
|
<section
|
||||||
class="description"
|
class="description"
|
||||||
|
aria-label={`${profile()?.displayName || "the user"}'s description`}
|
||||||
ref={(e) =>
|
ref={(e) =>
|
||||||
createRenderEffect(() => (e.innerHTML = description() || ""))
|
createRenderEffect(() => (e.innerHTML = description() || ""))
|
||||||
}
|
}
|
||||||
></div>
|
></section>
|
||||||
|
|
||||||
<table class="acct-fields">
|
<table
|
||||||
|
class="acct-fields"
|
||||||
|
aria-label={`${profile()?.displayName || "the user"}'s fields`}
|
||||||
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<For each={profile()?.fields ?? []}>
|
<For each={profile()?.fields ?? []}>
|
||||||
{(item, index) => {
|
{(item, index) => {
|
||||||
|
@ -436,6 +500,7 @@ const Profile: Component = () => {
|
||||||
<Divider />
|
<Divider />
|
||||||
</Show>
|
</Show>
|
||||||
<TootList
|
<TootList
|
||||||
|
id={recentTootListId}
|
||||||
threads={recentToots.list}
|
threads={recentToots.list}
|
||||||
onUnknownThread={recentToots.getPath}
|
onUnknownThread={recentToots.getPath}
|
||||||
onChangeToot={recentToots.set}
|
onChangeToot={recentToots.set}
|
||||||
|
@ -451,6 +516,7 @@ const Profile: Component = () => {
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="Load More"
|
aria-label="Load More"
|
||||||
|
aria-controls={recentTootListId}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={[refetchRecentToots, "prev"]}
|
onClick={[refetchRecentToots, "prev"]}
|
||||||
|
|
Loading…
Reference in a new issue