import { createRenderEffect, createResource, createSignal, For, onCleanup, Show, type Component, } from "solid-js"; import Scaffold from "../material/Scaffold"; import { AppBar, Avatar, Button, IconButton, Toolbar } from "@suid/material"; import { Close, MoreVert, Verified } from "@suid/icons-material"; 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"; import { css } from "solid-styled"; import { createTimeline } from "../masto/timelines"; import TootList from "../timelines/TootList"; import { createIntersectionObserver } from "@solid-primitives/intersection-observer"; import { createTimeSource, TimeSourceProvider } from "../platform/timesrc"; 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(); 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()); const [profile] = createResource( () => [session().client, params.id] as const, async ([client, id]) => { return await client.v1.accounts.$select(id).fetch(); }, ); const [recentToots] = createTimeline( () => session().client.v1.accounts.$select(params.id).statuses, () => 20, ); const bannerImg = () => profile()?.header; const avatarImg = () => profile()?.avatar; const displayName = () => resolveCustomEmoji(profile()?.displayName || "", profile()?.emojis ?? []); const fullUsername = () => `@${profile()?.acct ?? "..."}`; // TODO: full user name const description = () => profile()?.note; css` .intro { background-color: var(--tutu-color-surface-d); color: var(--tutu-color-on-surface); padding: 16px 12px; display: flex; flex-flow: column nowrap; gap: 16px; } .acct-grp { display: grid; grid-template-columns: auto 1fr auto; gap: 16px; align-items: center; } .name-grp { display: flex; flex-flow: column nowrap; } table.acct-fields { & td > :global(a) { display: inline-flex; min-height: 44px; align-items: center; color: inherit; } & :global(a > .invisible) { display: none; } & :global(svg) { vertical-align: middle; } } .page-title { flex-grow: 1; } `; return ( createRenderEffect(() => (e.innerHTML = displayName())) } > } >
obx.observe(e)} src={bannerImg()} style={{ "object-fit": "contain", width: "100%", height: "100%", }} crossOrigin="anonymous" onLoad={async (event) => { const ins = new FastAverageColor(); const colors = ins.getColor(event.currentTarget); setBannerSampledColors({ average: colors.hex, text: colors.isDark ? "white" : "black", }); }} >
createRenderEffect(() => (e.innerHTML = displayName())) } > {fullUsername()}
createRenderEffect(() => (e.innerHTML = description() || "")) } >
{(item, index) => { return ( ); }}
{item.name} { createRenderEffect(() => (e.innerHTML = item.value)); }} >
); }; export default Profile;