From 8834c4d3aed437f2d21371fa39f478fbd0886512 Mon Sep 17 00:00:00 2001 From: thislight Date: Fri, 25 Oct 2024 22:37:50 +0800 Subject: [PATCH 1/2] Profile: add menu and load more button --- src/profiles/Profile.tsx | 114 ++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 13 deletions(-) diff --git a/src/profiles/Profile.tsx b/src/profiles/Profile.tsx index 64cf0b5..c9e7785 100644 --- a/src/profiles/Profile.tsx +++ b/src/profiles/Profile.tsx @@ -2,14 +2,34 @@ import { createRenderEffect, createResource, createSignal, + createUniqueId, 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 { + AppBar, + Avatar, + Button, + Divider, + IconButton, + ListItemIcon, + ListItemText, + MenuItem, + Toolbar, +} from "@suid/material"; +import { + Close, + Edit, + ExpandMore, + MoreVert, + OpenInBrowser, + Send, + Share, + Verified, +} from "@suid/icons-material"; import { Title } from "../material/typography"; import { useNavigate, useParams } from "@solidjs/router"; import { useSessionForAcctStr } from "../masto/clients"; @@ -21,6 +41,8 @@ import { createTimeline } from "../masto/timelines"; import TootList from "../timelines/TootList"; import { createTimeSource, TimeSourceProvider } from "../platform/timesrc"; import TootFilterButton from "./TootFilterButton"; +import Menu from "../material/Menu"; +import { share } from "../platform/share"; const Profile: Component = () => { const navigate = useNavigate(); @@ -34,6 +56,10 @@ const Profile: Component = () => { const windowSize = useWindowSize(); const time = createTimeSource(); + const menuButId = createUniqueId(); + + const [menuOpen, setMenuOpen] = createSignal(false); + const [scrolledPastBanner, setScrolledPastBanner] = createSignal(false); const obx = new IntersectionObserver( (entries) => { @@ -58,18 +84,19 @@ const Profile: Component = () => { ); const [recentTootFilter, setRecentTootFilter] = createSignal({ - boost: true, + boost: false, reply: true, original: true, }); - const [recentToots] = createTimeline( - () => session().client.v1.accounts.$select(params.id).statuses, - () => { - const { boost, reply } = recentTootFilter(); - return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply }; - }, - ); + 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 bannerImg = () => profile()?.header; const avatarImg = () => profile()?.avatar; @@ -140,7 +167,11 @@ const Profile: Component = () => { paddingTop: "var(--safe-area-inset-top)", }} > - + { createRenderEffect(() => (e.innerHTML = displayName())) } > - + + } > + + document.getElementById(menuButId)!.getBoundingClientRect() + } + > + + + + + Edit... + + + + + + + Mention {profile()?.displayName || ""}... + + + + + + + Open in browser... + + share({ url: profile()?.url })}> + + + + Share... + +
{
{ onChangeToot={recentToots.set} /> + + +
+ + + +
+
); }; From a924c80bbe8518ace3939368bd38bc16b1e32048 Mon Sep 17 00:00:00 2001 From: thislight Date: Fri, 25 Oct 2024 22:40:07 +0800 Subject: [PATCH 2/2] Menu: support right-top origin --- src/material/Menu.tsx | 157 +++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 55 deletions(-) diff --git a/src/material/Menu.tsx b/src/material/Menu.tsx index 365d090..6c08dad 100644 --- a/src/material/Menu.tsx +++ b/src/material/Menu.tsx @@ -14,56 +14,84 @@ type Props = { anchor: () => DOMRect; }; -function adjustMenuPosition( - rect: DOMRect, - [left, top]: [number, number], - { width, height }: { width: number; height: number }, -) { - const ntop = rect.bottom > height ? top - (rect.bottom - height) : top; - const nleft = rect.right > width ? left - (rect.right - width) : left; - return [nleft, ntop] as [number, number]; +function px(n?: number) { + if (n) { + return `${n}px`; + } else { + return undefined; + } } const Menu: ParentComponent = (props) => { let root: HTMLDialogElement; - const [pos, setPos] = createSignal<[number, number]>([0, 0]); const windowSize = useWindowSize(); + const [anchorPos, setAnchorPos] = createSignal<{ + left?: number; + top?: number; + }>({}); + + let openAnimationOrigin: "lt" | "rt" = "lt"; + createEffect(() => { if (props.open) { const a = props.anchor(); + if (!root.open) { root.showModal(); const rend = root.getBoundingClientRect(); - setPos(adjustMenuPosition(rend, [a.left, a.top], windowSize)); + const { width } = windowSize; + const { left, top, right } = a; + if (left > width / 2) { + openAnimationOrigin = "rt"; + setAnchorPos({ + left: right - rend.width, + top, + }); - const overflow = root.style.overflow; - root.style.overflow = "hidden"; - const duration = (rend.height / 1600) * 1000; - const easing = ANIM_CURVE_STD; - const animation = root.animate( - { - height: [`${rend.height / 2}px`, `${rend.height}px`], - width: [`${rend.width / 4 * 3}px`, `${rend.width}px`], - }, - { - duration, - easing, - }, - ); - animation.addEventListener( - "finish", - () => (root.style.overflow = overflow), - ); + const overflow = root.style.overflow; + root.style.overflow = "hidden"; + const duration = (rend.height / 1600) * 1000; + const easing = ANIM_CURVE_STD; + const animation = root.animate( + { + height: [`${rend.height / 2}px`, `${rend.height}px`], + }, + { + duration, + easing, + }, + ); + animation.addEventListener( + "finish", + () => (root.style.overflow = overflow), + ); + } else { + openAnimationOrigin = "lt"; + setAnchorPos({ left, top }); + + const overflow = root.style.overflow; + root.style.overflow = "hidden"; + const duration = (rend.height / 1600) * 1000; + const easing = ANIM_CURVE_STD; + const animation = root.animate( + { + height: [`${rend.height / 2}px`, `${rend.height}px`], + width: [`${(rend.width / 4) * 3}px`, `${rend.width}px`], + }, + { + duration, + easing, + }, + ); + animation.addEventListener( + "finish", + () => (root.style.overflow = overflow), + ); + } } else { - setPos( - adjustMenuPosition( - root.getBoundingClientRect(), - [a.left, a.top], - windowSize, - ), - ); + // TODO: update the pos } } else { animateClose(); @@ -72,22 +100,40 @@ const Menu: ParentComponent = (props) => { const animateClose = () => { const rend = root.getBoundingClientRect(); - const overflow = root.style.overflow; - root.style.overflow = "hidden"; - const animation = root.animate( - { - height: [`${rend.height}px`, `${rend.height / 2}px`], - width: [`${rend.width}px`, `${rend.width / 4 * 3}px`], - }, - { - duration: (rend.height / 2 / 1600) * 1000, - easing: ANIM_CURVE_STD, - }, - ); - animation.addEventListener("finish", () => { - root.style.overflow = overflow; - root.close(); - }); + if (openAnimationOrigin === "lt") { + const overflow = root.style.overflow; + root.style.overflow = "hidden"; + const animation = root.animate( + { + height: [`${rend.height}px`, `${rend.height / 2}px`], + width: [`${rend.width}px`, `${(rend.width / 4) * 3}px`], + }, + { + duration: (rend.height / 2 / 1600) * 1000, + easing: ANIM_CURVE_STD, + }, + ); + animation.addEventListener("finish", () => { + root.style.overflow = overflow; + root.close(); + }); + } else { + const overflow = root.style.overflow; + root.style.overflow = "hidden"; + const animation = root.animate( + { + height: [`${rend.height}px`, `${rend.height / 2}px`], + }, + { + duration: (rend.height / 2 / 1600) * 1000, + easing: ANIM_CURVE_STD, + }, + ); + animation.addEventListener("finish", () => { + root.style.overflow = overflow; + root.close(); + }); + } }; return ( @@ -111,14 +157,15 @@ const Menu: ParentComponent = (props) => { } }} style={{ - position: "absolute", - left: `${pos()[0]}px`, - top: `${pos()[1]}px`, + position: "fixed", + left: px(anchorPos().left), + top: px(anchorPos().top), border: "none", + "border-radius": "2px", padding: 0, "max-width": "560px", width: "max-content", - /*"min-width": "20vw", */ + /* FIXME: the content may be overflow */ "box-shadow": "var(--tutu-shadow-e8)", }} >