2024-10-18 19:15:35 +08:00
|
|
|
import {
|
2024-10-30 19:25:58 +08:00
|
|
|
catchError,
|
2024-10-18 19:15:35 +08:00
|
|
|
createRenderEffect,
|
|
|
|
createResource,
|
|
|
|
createSignal,
|
2024-10-25 22:37:50 +08:00
|
|
|
createUniqueId,
|
2024-10-18 19:15:35 +08:00
|
|
|
For,
|
2024-11-03 20:50:31 +08:00
|
|
|
Switch,
|
|
|
|
Match,
|
2024-10-18 19:15:35 +08:00
|
|
|
onCleanup,
|
|
|
|
Show,
|
|
|
|
type Component,
|
2024-11-04 15:57:53 +08:00
|
|
|
createMemo,
|
2024-10-18 19:15:35 +08:00
|
|
|
} from "solid-js";
|
|
|
|
import Scaffold from "../material/Scaffold";
|
2024-10-25 22:37:50 +08:00
|
|
|
import {
|
|
|
|
AppBar,
|
|
|
|
Avatar,
|
|
|
|
Button,
|
2024-10-28 13:56:04 +08:00
|
|
|
CircularProgress,
|
2024-10-25 22:37:50 +08:00
|
|
|
Divider,
|
|
|
|
IconButton,
|
2024-11-03 23:52:26 +08:00
|
|
|
ListItemAvatar,
|
2024-10-25 22:37:50 +08:00
|
|
|
ListItemIcon,
|
|
|
|
ListItemText,
|
|
|
|
MenuItem,
|
|
|
|
Toolbar,
|
|
|
|
} from "@suid/material";
|
|
|
|
import {
|
|
|
|
Close,
|
|
|
|
Edit,
|
|
|
|
ExpandMore,
|
2024-11-03 18:00:13 +08:00
|
|
|
Group,
|
2024-10-25 22:37:50 +08:00
|
|
|
MoreVert,
|
|
|
|
OpenInBrowser,
|
2024-11-03 18:00:13 +08:00
|
|
|
PersonOff,
|
|
|
|
PlaylistAdd,
|
2024-10-25 22:37:50 +08:00
|
|
|
Send,
|
|
|
|
Share,
|
2024-11-03 18:00:13 +08:00
|
|
|
Translate,
|
2024-10-25 22:37:50 +08:00
|
|
|
Verified,
|
|
|
|
} from "@suid/icons-material";
|
2024-10-18 19:15:35 +08:00
|
|
|
import { Title } from "../material/typography";
|
|
|
|
import { useNavigate, useParams } from "@solidjs/router";
|
|
|
|
import { useSessionForAcctStr } from "../masto/clients";
|
|
|
|
import { resolveCustomEmoji } from "../masto/toot";
|
|
|
|
import { FastAverageColor } from "fast-average-color";
|
|
|
|
import { useWindowSize } from "@solid-primitives/resize-observer";
|
2024-10-31 00:18:47 +08:00
|
|
|
import { createTimeline, createTimelineSnapshot } from "../masto/timelines";
|
2024-10-18 19:15:35 +08:00
|
|
|
import TootList from "../timelines/TootList";
|
|
|
|
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
|
2024-10-24 23:47:44 +08:00
|
|
|
import TootFilterButton from "./TootFilterButton";
|
2024-11-03 20:50:31 +08:00
|
|
|
import Menu, { createManagedMenuState } from "../material/Menu";
|
2024-10-25 22:37:50 +08:00
|
|
|
import { share } from "../platform/share";
|
2024-11-03 20:50:31 +08:00
|
|
|
import "./Profile.css";
|
2024-10-18 19:15:35 +08:00
|
|
|
|
|
|
|
const Profile: Component = () => {
|
|
|
|
const navigate = useNavigate();
|
|
|
|
const params = useParams<{ acct: string; id: string }>();
|
|
|
|
const acctText = () => decodeURIComponent(params.acct);
|
|
|
|
const session = useSessionForAcctStr(acctText);
|
|
|
|
const [bannerSampledColors, setBannerSampledColors] = createSignal<{
|
|
|
|
average: string;
|
|
|
|
text: string;
|
|
|
|
}>();
|
|
|
|
const windowSize = useWindowSize();
|
|
|
|
const time = createTimeSource();
|
|
|
|
|
2024-10-25 22:37:50 +08:00
|
|
|
const menuButId = createUniqueId();
|
|
|
|
|
|
|
|
const [menuOpen, setMenuOpen] = createSignal(false);
|
2024-11-03 20:50:31 +08:00
|
|
|
|
|
|
|
const [openSubscribeMenu, subscribeMenuState] = createManagedMenuState();
|
2024-10-25 22:37:50 +08:00
|
|
|
|
2024-10-18 19:15:35 +08:00
|
|
|
const [scrolledPastBanner, setScrolledPastBanner] = createSignal(false);
|
|
|
|
const obx = new IntersectionObserver(
|
|
|
|
(entries) => {
|
|
|
|
const ent = entries[0];
|
|
|
|
if (ent.intersectionRatio < 0.1) {
|
|
|
|
setScrolledPastBanner(true);
|
|
|
|
} else {
|
|
|
|
setScrolledPastBanner(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
threshold: 0.1,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
onCleanup(() => obx.disconnect());
|
|
|
|
|
2024-10-30 19:25:58 +08:00
|
|
|
const [profileErrorUncaught] = createResource(
|
2024-10-18 19:15:35 +08:00
|
|
|
() => [session().client, params.id] as const,
|
|
|
|
async ([client, id]) => {
|
|
|
|
return await client.v1.accounts.$select(id).fetch();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2024-10-30 19:25:58 +08:00
|
|
|
const profile = () =>
|
|
|
|
catchError(profileErrorUncaught, (err) => {
|
|
|
|
console.error(err);
|
|
|
|
});
|
|
|
|
|
2024-11-03 18:00:13 +08:00
|
|
|
const isCurrentSessionProfile = () => {
|
|
|
|
return session().account?.inf?.url === profile()?.url;
|
|
|
|
};
|
|
|
|
|
2024-10-24 23:47:44 +08:00
|
|
|
const [recentTootFilter, setRecentTootFilter] = createSignal({
|
2024-10-31 00:18:47 +08:00
|
|
|
pinned: true,
|
2024-10-25 22:37:50 +08:00
|
|
|
boost: false,
|
2024-10-24 23:47:44 +08:00
|
|
|
reply: true,
|
|
|
|
original: true,
|
|
|
|
});
|
|
|
|
|
2024-10-25 22:37:50 +08:00
|
|
|
const [recentToots, recentTootChunk, { refetch: refetchRecentToots }] =
|
|
|
|
createTimeline(
|
|
|
|
() => session().client.v1.accounts.$select(params.id).statuses,
|
|
|
|
() => {
|
|
|
|
const { boost, reply } = recentTootFilter();
|
|
|
|
return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply };
|
|
|
|
},
|
|
|
|
);
|
2024-10-18 19:15:35 +08:00
|
|
|
|
2024-10-31 00:18:47 +08:00
|
|
|
const [pinnedToots, pinnedTootChunk] = createTimelineSnapshot(
|
|
|
|
() => session().client.v1.accounts.$select(params.id).statuses,
|
|
|
|
() => {
|
|
|
|
return { limit: 20, pinned: true };
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2024-10-18 19:15:35 +08:00
|
|
|
const bannerImg = () => profile()?.header;
|
|
|
|
const avatarImg = () => profile()?.avatar;
|
|
|
|
const displayName = () =>
|
|
|
|
resolveCustomEmoji(profile()?.displayName || "", profile()?.emojis ?? []);
|
2024-10-28 13:56:04 +08:00
|
|
|
const fullUsername = () => (profile()?.acct ? `@${profile()!.acct!}` : ""); // TODO: full user name
|
2024-10-18 19:15:35 +08:00
|
|
|
const description = () => profile()?.note;
|
|
|
|
|
2024-10-31 00:18:47 +08:00
|
|
|
const isTootListLoading = () =>
|
|
|
|
recentTootChunk.loading ||
|
|
|
|
(recentTootFilter().pinned && pinnedTootChunk.loading);
|
|
|
|
|
2024-11-04 15:57:53 +08:00
|
|
|
const sessionDisplayName = createMemo(() =>
|
|
|
|
resolveCustomEmoji(
|
|
|
|
session().account?.inf?.displayName || "",
|
|
|
|
session().account?.inf?.emojis ?? [],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
const useSessionDisplayName = (e: HTMLElement) => {
|
|
|
|
createRenderEffect(() => (e.innerHTML = sessionDisplayName()));
|
|
|
|
};
|
|
|
|
|
2024-10-18 19:15:35 +08:00
|
|
|
return (
|
|
|
|
<Scaffold
|
|
|
|
topbar={
|
|
|
|
<AppBar
|
|
|
|
position="static"
|
|
|
|
color={scrolledPastBanner() ? "primary" : "transparent"}
|
|
|
|
elevation={scrolledPastBanner() ? undefined : 0}
|
|
|
|
>
|
|
|
|
<Toolbar
|
|
|
|
variant="dense"
|
|
|
|
sx={{
|
|
|
|
display: "flex",
|
2024-10-24 23:47:44 +08:00
|
|
|
color: scrolledPastBanner()
|
|
|
|
? undefined
|
|
|
|
: bannerSampledColors()?.text,
|
2024-10-18 19:15:35 +08:00
|
|
|
paddingTop: "var(--safe-area-inset-top)",
|
|
|
|
}}
|
|
|
|
>
|
2024-10-25 22:37:50 +08:00
|
|
|
<IconButton
|
|
|
|
color="inherit"
|
|
|
|
onClick={[navigate, -1]}
|
|
|
|
aria-label="Close"
|
|
|
|
>
|
2024-10-18 19:15:35 +08:00
|
|
|
<Close />
|
|
|
|
</IconButton>
|
|
|
|
<Title
|
2024-11-03 20:50:31 +08:00
|
|
|
class="Profile__page-title"
|
2024-10-18 19:15:35 +08:00
|
|
|
style={{
|
|
|
|
visibility: scrolledPastBanner() ? undefined : "hidden",
|
|
|
|
}}
|
|
|
|
ref={(e: HTMLElement) =>
|
|
|
|
createRenderEffect(() => (e.innerHTML = displayName()))
|
|
|
|
}
|
|
|
|
></Title>
|
2024-10-25 22:37:50 +08:00
|
|
|
|
|
|
|
<IconButton
|
|
|
|
id={menuButId}
|
|
|
|
color="inherit"
|
|
|
|
onClick={[setMenuOpen, true]}
|
|
|
|
>
|
2024-10-18 19:15:35 +08:00
|
|
|
<MoreVert />
|
|
|
|
</IconButton>
|
|
|
|
</Toolbar>
|
|
|
|
</AppBar>
|
|
|
|
}
|
2024-11-03 20:50:31 +08:00
|
|
|
class="Profile"
|
2024-10-18 19:15:35 +08:00
|
|
|
>
|
2024-10-25 22:37:50 +08:00
|
|
|
<Menu
|
|
|
|
open={menuOpen()}
|
|
|
|
onClose={[setMenuOpen, false]}
|
|
|
|
anchor={() =>
|
|
|
|
document.getElementById(menuButId)!.getBoundingClientRect()
|
|
|
|
}
|
|
|
|
>
|
2024-11-04 15:57:53 +08:00
|
|
|
<Show when={session().account}>
|
|
|
|
<MenuItem>
|
|
|
|
<ListItemAvatar>
|
|
|
|
<Avatar src={session().account?.inf?.avatar} />
|
|
|
|
</ListItemAvatar>
|
|
|
|
<ListItemText secondary={"Default account"}>
|
|
|
|
<span ref={useSessionDisplayName}></span>
|
|
|
|
</ListItemText>
|
|
|
|
{/* <ArrowRight /> // for future */}
|
|
|
|
</MenuItem>
|
|
|
|
</Show>
|
2024-11-03 23:52:26 +08:00
|
|
|
<Show when={session().account && profile()}>
|
2024-11-03 20:50:31 +08:00
|
|
|
<Show
|
|
|
|
when={isCurrentSessionProfile()}
|
|
|
|
fallback={
|
2024-11-03 23:52:26 +08:00
|
|
|
<MenuItem
|
|
|
|
onClick={(event) => {
|
|
|
|
const { left, right, top } =
|
|
|
|
event.currentTarget.getBoundingClientRect();
|
|
|
|
openSubscribeMenu({
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
top,
|
|
|
|
e: 1,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
>
|
2024-11-03 20:50:31 +08:00
|
|
|
<ListItemIcon>
|
|
|
|
<PlaylistAdd />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText>Subscribe...</ListItemText>
|
|
|
|
</MenuItem>
|
|
|
|
}
|
|
|
|
>
|
2024-11-03 18:00:13 +08:00
|
|
|
<MenuItem disabled>
|
|
|
|
<ListItemIcon>
|
2024-11-03 20:50:31 +08:00
|
|
|
<Edit />
|
2024-11-03 18:00:13 +08:00
|
|
|
</ListItemIcon>
|
2024-11-03 20:50:31 +08:00
|
|
|
<ListItemText>Edit...</ListItemText>
|
2024-11-03 18:00:13 +08:00
|
|
|
</MenuItem>
|
2024-11-03 20:50:31 +08:00
|
|
|
</Show>
|
|
|
|
<Divider />
|
2024-11-03 18:00:13 +08:00
|
|
|
</Show>
|
2024-10-25 22:37:50 +08:00
|
|
|
<MenuItem disabled>
|
|
|
|
<ListItemIcon>
|
2024-11-03 18:00:13 +08:00
|
|
|
<Group />
|
2024-10-25 22:37:50 +08:00
|
|
|
</ListItemIcon>
|
2024-11-03 18:00:13 +08:00
|
|
|
<ListItemText>Subscribers</ListItemText>
|
|
|
|
</MenuItem>
|
|
|
|
<MenuItem disabled>
|
|
|
|
<ListItemIcon>
|
|
|
|
<PersonOff />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText>Blocklist</ListItemText>
|
|
|
|
</MenuItem>
|
|
|
|
<MenuItem disabled>
|
|
|
|
<ListItemIcon>
|
|
|
|
<Translate />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText>Translate Name and Bio...</ListItemText>
|
2024-10-25 22:37:50 +08:00
|
|
|
</MenuItem>
|
|
|
|
<MenuItem disabled>
|
|
|
|
<ListItemIcon>
|
|
|
|
<Send />
|
|
|
|
</ListItemIcon>
|
2024-11-03 18:00:13 +08:00
|
|
|
<ListItemText>Mention in...</ListItemText>
|
2024-10-25 22:37:50 +08:00
|
|
|
</MenuItem>
|
|
|
|
<Divider />
|
2024-10-28 13:56:04 +08:00
|
|
|
<MenuItem
|
|
|
|
component={"a"}
|
|
|
|
href={profile()?.url}
|
|
|
|
target="_blank"
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
>
|
2024-10-25 22:37:50 +08:00
|
|
|
<ListItemIcon>
|
|
|
|
<OpenInBrowser />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText>Open in browser...</ListItemText>
|
|
|
|
</MenuItem>
|
|
|
|
<MenuItem onClick={() => share({ url: profile()?.url })}>
|
|
|
|
<ListItemIcon>
|
|
|
|
<Share />
|
|
|
|
</ListItemIcon>
|
|
|
|
<ListItemText>Share...</ListItemText>
|
|
|
|
</MenuItem>
|
|
|
|
</Menu>
|
2024-10-18 19:15:35 +08:00
|
|
|
<div
|
|
|
|
style={{
|
|
|
|
width: "100%",
|
|
|
|
height: `${268 * (Math.min(560, windowSize.width) / 560)}px`,
|
|
|
|
"margin-top":
|
|
|
|
"calc(-1 * (var(--scaffold-topbar-height) + var(--safe-area-inset-top)))",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<img
|
|
|
|
ref={(e) => obx.observe(e)}
|
|
|
|
src={bannerImg()}
|
|
|
|
style={{
|
2024-10-30 19:25:58 +08:00
|
|
|
"object-fit": "cover",
|
2024-10-18 19:15:35 +08:00
|
|
|
width: "100%",
|
|
|
|
height: "100%",
|
|
|
|
}}
|
|
|
|
crossOrigin="anonymous"
|
2024-11-04 15:57:53 +08:00
|
|
|
onLoad={(event) => {
|
2024-10-18 19:15:35 +08:00
|
|
|
const ins = new FastAverageColor();
|
|
|
|
const colors = ins.getColor(event.currentTarget);
|
|
|
|
setBannerSampledColors({
|
|
|
|
average: colors.hex,
|
|
|
|
text: colors.isDark ? "white" : "black",
|
|
|
|
});
|
2024-10-29 19:42:15 +08:00
|
|
|
ins.destroy();
|
2024-10-18 19:15:35 +08:00
|
|
|
}}
|
|
|
|
></img>
|
|
|
|
</div>
|
|
|
|
|
2024-11-03 20:50:31 +08:00
|
|
|
<Menu {...subscribeMenuState}>
|
|
|
|
<MenuItem disabled>
|
2024-11-03 23:52:26 +08:00
|
|
|
<ListItemAvatar>
|
|
|
|
<Avatar src={session().account?.inf?.avatar}></Avatar>
|
|
|
|
</ListItemAvatar>
|
2024-11-03 20:50:31 +08:00
|
|
|
<ListItemText>
|
2024-11-04 15:57:53 +08:00
|
|
|
<span ref={useSessionDisplayName}></span>
|
2024-11-03 23:52:26 +08:00
|
|
|
<span>'s Home</span>
|
2024-11-03 20:50:31 +08:00
|
|
|
</ListItemText>
|
|
|
|
</MenuItem>
|
|
|
|
</Menu>
|
|
|
|
|
2024-10-18 19:15:35 +08:00
|
|
|
<div
|
|
|
|
class="intro"
|
|
|
|
style={{
|
|
|
|
"background-color": bannerSampledColors()?.average,
|
|
|
|
color: bannerSampledColors()?.text,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<div class="acct-grp">
|
|
|
|
<Avatar
|
|
|
|
src={avatarImg()}
|
|
|
|
sx={{
|
|
|
|
marginTop: "calc(-16px - 72px / 2)",
|
|
|
|
width: "72px",
|
|
|
|
height: "72px",
|
|
|
|
}}
|
|
|
|
></Avatar>
|
|
|
|
<div class="name-grp">
|
|
|
|
<span
|
|
|
|
ref={(e) =>
|
|
|
|
createRenderEffect(() => (e.innerHTML = displayName()))
|
|
|
|
}
|
|
|
|
></span>
|
|
|
|
<span>{fullUsername()}</span>
|
|
|
|
</div>
|
|
|
|
<div>
|
2024-11-03 20:50:31 +08:00
|
|
|
<Switch>
|
2024-11-03 23:52:26 +08:00
|
|
|
<Match when={!session().account || profileErrorUncaught.loading}>
|
|
|
|
{<></>}
|
|
|
|
</Match>
|
2024-11-03 20:50:31 +08:00
|
|
|
<Match when={isCurrentSessionProfile()}>
|
|
|
|
<IconButton color="inherit">
|
|
|
|
<Edit />
|
|
|
|
</IconButton>
|
|
|
|
</Match>
|
|
|
|
<Match when={true}>
|
|
|
|
<Button
|
|
|
|
variant="contained"
|
|
|
|
color="secondary"
|
|
|
|
onClick={(event) => {
|
|
|
|
openSubscribeMenu(
|
|
|
|
event.currentTarget.getBoundingClientRect(),
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
Subscribe
|
|
|
|
</Button>
|
|
|
|
</Match>
|
|
|
|
</Switch>
|
2024-10-18 19:15:35 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div
|
2024-10-29 19:48:15 +08:00
|
|
|
class="description"
|
2024-10-18 19:15:35 +08:00
|
|
|
ref={(e) =>
|
|
|
|
createRenderEffect(() => (e.innerHTML = description() || ""))
|
|
|
|
}
|
|
|
|
></div>
|
2024-10-28 13:56:04 +08:00
|
|
|
|
2024-10-18 19:15:35 +08:00
|
|
|
<table class="acct-fields">
|
|
|
|
<tbody>
|
|
|
|
<For each={profile()?.fields ?? []}>
|
|
|
|
{(item, index) => {
|
|
|
|
return (
|
|
|
|
<tr data-field-index={index()}>
|
|
|
|
<td>{item.name}</td>
|
|
|
|
<td>
|
|
|
|
<Show when={item.verifiedAt}>
|
|
|
|
<Verified />
|
|
|
|
</Show>
|
|
|
|
</td>
|
|
|
|
<td
|
|
|
|
ref={(e) => {
|
|
|
|
createRenderEffect(() => (e.innerHTML = item.value));
|
|
|
|
}}
|
|
|
|
></td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
</For>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
|
2024-10-31 00:00:07 +08:00
|
|
|
<div class="toot-list-toolbar">
|
2024-10-24 23:47:44 +08:00
|
|
|
<TootFilterButton
|
|
|
|
options={{
|
2024-10-31 00:18:47 +08:00
|
|
|
pinned: "Pinneds",
|
2024-10-25 22:37:50 +08:00
|
|
|
boost: "Boosts",
|
2024-10-24 23:47:44 +08:00
|
|
|
reply: "Replies",
|
|
|
|
original: "Originals",
|
|
|
|
}}
|
|
|
|
applied={recentTootFilter()}
|
|
|
|
onApply={setRecentTootFilter}
|
|
|
|
disabledKeys={["original"]}
|
|
|
|
></TootFilterButton>
|
|
|
|
</div>
|
|
|
|
|
2024-10-18 19:15:35 +08:00
|
|
|
<TimeSourceProvider value={time}>
|
2024-11-03 18:23:24 +08:00
|
|
|
<Show when={recentTootFilter().pinned && pinnedToots.list.length > 0}>
|
2024-10-31 00:18:47 +08:00
|
|
|
<TootList
|
|
|
|
threads={pinnedToots.list}
|
|
|
|
onUnknownThread={pinnedToots.getPath}
|
|
|
|
onChangeToot={pinnedToots.set}
|
|
|
|
/>
|
|
|
|
<Divider />
|
|
|
|
</Show>
|
2024-10-18 19:15:35 +08:00
|
|
|
<TootList
|
|
|
|
threads={recentToots.list}
|
|
|
|
onUnknownThread={recentToots.getPath}
|
|
|
|
onChangeToot={recentToots.set}
|
|
|
|
/>
|
|
|
|
</TimeSourceProvider>
|
2024-10-25 22:37:50 +08:00
|
|
|
|
|
|
|
<Show when={!recentTootChunk()?.done}>
|
|
|
|
<div
|
|
|
|
style={{
|
|
|
|
"text-align": "center",
|
|
|
|
"padding-bottom": "var(--safe-area-inset-bottom)",
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<IconButton
|
|
|
|
aria-label="Load More"
|
|
|
|
size="large"
|
|
|
|
color="primary"
|
|
|
|
onClick={[refetchRecentToots, "prev"]}
|
2024-10-31 00:18:47 +08:00
|
|
|
disabled={isTootListLoading()}
|
2024-10-25 22:37:50 +08:00
|
|
|
>
|
2024-10-31 00:18:47 +08:00
|
|
|
<Show when={isTootListLoading()} fallback={<ExpandMore />}>
|
2024-10-28 13:56:04 +08:00
|
|
|
<CircularProgress sx={{ width: "24px", height: "24px" }} />
|
|
|
|
</Show>
|
2024-10-25 22:37:50 +08:00
|
|
|
</IconButton>
|
|
|
|
</div>
|
|
|
|
</Show>
|
2024-10-18 19:15:35 +08:00
|
|
|
</Scaffold>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default Profile;
|