import { catchError, createRenderEffect, createResource, createSignal, createUniqueId, For, Switch, Match, onCleanup, Show, type Component, createMemo, } from "solid-js"; import Scaffold from "../material/Scaffold"; import { AppBar, Avatar, Button, Checkbox, CircularProgress, Divider, IconButton, ListItemAvatar, ListItemIcon, ListItemText, MenuItem, Toolbar, } from "@suid/material"; import { Close, Edit, ExpandMore, Group, MoreVert, OpenInBrowser, PersonOff, PlaylistAdd, Send, Share, SmartToy, SmartToySharp, Translate, Verified, } from "@suid/icons-material"; import { Body2, 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 { createTimeline, createTimelineSnapshot } from "../masto/timelines"; import TootList from "../timelines/TootList"; import { createTimeSource, TimeSourceProvider } from "../platform/timesrc"; import TootFilterButton from "./TootFilterButton"; import Menu, { createManagedMenuState } from "../material/Menu"; import { share } from "../platform/share"; import "./Profile.css"; 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 menuButId = createUniqueId(); const recentTootListId = createUniqueId(); const optMenuId = createUniqueId(); const [menuOpen, setMenuOpen] = createSignal(false); const [openSubscribeMenu, subscribeMenuState] = createManagedMenuState(); 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 [profileUncaught] = createResource( () => [session().client, params.id] as const, async ([client, id]) => { return await client.v1.accounts.$select(id).fetch(); }, ); const profile = () => { try { return profileUncaught(); } catch (reason) { console.error(reason); } }; const isCurrentSessionProfile = () => { return session().account?.inf?.url === profile()?.url; }; const [recentTootFilter, setRecentTootFilter] = createSignal({ pinned: true, boost: false, reply: true, original: true, }); 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 }; }, ); const [pinnedToots, pinnedTootChunk] = createTimelineSnapshot( () => session().client.v1.accounts.$select(params.id).statuses, () => { return { limit: 20, pinned: true }; }, ); 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 avatarImg = () => profile()?.avatar; const displayName = () => resolveCustomEmoji(profile()?.displayName || "", profile()?.emojis ?? []); const fullUsername = () => (profile()?.acct ? `@${profile()!.acct!}` : ""); // TODO: full user name const description = () => profile()?.note; const isTootListLoading = () => recentTootChunk.loading || (recentTootFilter().pinned && pinnedTootChunk.loading); const sessionDisplayName = createMemo(() => resolveCustomEmoji( session().account?.inf?.displayName || "", session().account?.inf?.emojis ?? [], ), ); const useSessionDisplayName = (e: HTMLElement) => { 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 ( createRenderEffect(() => (e.innerHTML = displayName())) } > } class="Profile" > document.getElementById(menuButId)!.getBoundingClientRect() } aria-label="Options for the Profile" > {/* // for future */} { const { left, right, top } = event.currentTarget.getBoundingClientRect(); openSubscribeMenu({ left, right, top, e: 1, }); }} > Subscribe... } > Edit... Subscribers Blocklist Translate Name and Bio... Mention in... Open in browser... share({ url: profile()?.url })}> Share... 's Home
createRenderEffect(() => (e.innerHTML = displayName())) } aria-label="Display name" >
{fullUsername()}
{<>}
createRenderEffect(() => (e.innerHTML = description() || "")) } >
{(item, index) => { return ( ); }}
{item.name} { createRenderEffect(() => (e.innerHTML = item.value)); }} >
0}>
}>
); }; export default Profile;