Compare commits

..

No commits in common. "a924c80bbe8518ace3939368bd38bc16b1e32048" and "4075c4194291e9126528cb22b9625cca42af882d" have entirely different histories.

2 changed files with 68 additions and 203 deletions

View file

@ -14,84 +14,56 @@ type Props = {
anchor: () => DOMRect; anchor: () => DOMRect;
}; };
function px(n?: number) { function adjustMenuPosition(
if (n) { rect: DOMRect,
return `${n}px`; [left, top]: [number, number],
} else { { width, height }: { width: number; height: number },
return undefined; ) {
} 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];
} }
const Menu: ParentComponent<Props> = (props) => { const Menu: ParentComponent<Props> = (props) => {
let root: HTMLDialogElement; let root: HTMLDialogElement;
const [pos, setPos] = createSignal<[number, number]>([0, 0]);
const windowSize = useWindowSize(); const windowSize = useWindowSize();
const [anchorPos, setAnchorPos] = createSignal<{
left?: number;
top?: number;
}>({});
let openAnimationOrigin: "lt" | "rt" = "lt";
createEffect(() => { createEffect(() => {
if (props.open) { if (props.open) {
const a = props.anchor(); const a = props.anchor();
if (!root.open) { if (!root.open) {
root.showModal(); root.showModal();
const rend = root.getBoundingClientRect(); const rend = root.getBoundingClientRect();
const { width } = windowSize; setPos(adjustMenuPosition(rend, [a.left, a.top], windowSize));
const { left, top, right } = a;
if (left > width / 2) {
openAnimationOrigin = "rt";
setAnchorPos({
left: right - rend.width,
top,
});
const overflow = root.style.overflow; const overflow = root.style.overflow;
root.style.overflow = "hidden"; root.style.overflow = "hidden";
const duration = (rend.height / 1600) * 1000; const duration = (rend.height / 1600) * 1000;
const easing = ANIM_CURVE_STD; const easing = ANIM_CURVE_STD;
const animation = root.animate( const animation = root.animate(
{ {
height: [`${rend.height / 2}px`, `${rend.height}px`], height: [`${rend.height / 2}px`, `${rend.height}px`],
}, width: [`${rend.width / 4 * 3}px`, `${rend.width}px`],
{ },
duration, {
easing, duration,
}, easing,
); },
animation.addEventListener( );
"finish", animation.addEventListener(
() => (root.style.overflow = overflow), "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 { } else {
// TODO: update the pos setPos(
adjustMenuPosition(
root.getBoundingClientRect(),
[a.left, a.top],
windowSize,
),
);
} }
} else { } else {
animateClose(); animateClose();
@ -100,40 +72,22 @@ const Menu: ParentComponent<Props> = (props) => {
const animateClose = () => { const animateClose = () => {
const rend = root.getBoundingClientRect(); const rend = root.getBoundingClientRect();
if (openAnimationOrigin === "lt") { const overflow = root.style.overflow;
const overflow = root.style.overflow; root.style.overflow = "hidden";
root.style.overflow = "hidden"; const animation = root.animate(
const animation = root.animate( {
{ height: [`${rend.height}px`, `${rend.height / 2}px`],
height: [`${rend.height}px`, `${rend.height / 2}px`], width: [`${rend.width}px`, `${rend.width / 4 * 3}px`],
width: [`${rend.width}px`, `${(rend.width / 4) * 3}px`], },
}, {
{ duration: (rend.height / 2 / 1600) * 1000,
duration: (rend.height / 2 / 1600) * 1000, easing: ANIM_CURVE_STD,
easing: ANIM_CURVE_STD, },
}, );
); animation.addEventListener("finish", () => {
animation.addEventListener("finish", () => { root.style.overflow = overflow;
root.style.overflow = overflow; root.close();
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 ( return (
@ -157,15 +111,14 @@ const Menu: ParentComponent<Props> = (props) => {
} }
}} }}
style={{ style={{
position: "fixed", position: "absolute",
left: px(anchorPos().left), left: `${pos()[0]}px`,
top: px(anchorPos().top), top: `${pos()[1]}px`,
border: "none", border: "none",
"border-radius": "2px",
padding: 0, padding: 0,
"max-width": "560px", "max-width": "560px",
width: "max-content", width: "max-content",
/* FIXME: the content may be overflow */ /*"min-width": "20vw", */
"box-shadow": "var(--tutu-shadow-e8)", "box-shadow": "var(--tutu-shadow-e8)",
}} }}
> >

View file

@ -2,34 +2,14 @@ import {
createRenderEffect, createRenderEffect,
createResource, createResource,
createSignal, createSignal,
createUniqueId,
For, For,
onCleanup, onCleanup,
Show, Show,
type Component, type Component,
} from "solid-js"; } from "solid-js";
import Scaffold from "../material/Scaffold"; import Scaffold from "../material/Scaffold";
import { import { AppBar, Avatar, Button, IconButton, Toolbar } from "@suid/material";
AppBar, import { Close, MoreVert, Verified } from "@suid/icons-material";
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 { Title } from "../material/typography";
import { useNavigate, useParams } from "@solidjs/router"; import { useNavigate, useParams } from "@solidjs/router";
import { useSessionForAcctStr } from "../masto/clients"; import { useSessionForAcctStr } from "../masto/clients";
@ -41,8 +21,6 @@ import { createTimeline } from "../masto/timelines";
import TootList from "../timelines/TootList"; import TootList from "../timelines/TootList";
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc"; import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
import TootFilterButton from "./TootFilterButton"; import TootFilterButton from "./TootFilterButton";
import Menu from "../material/Menu";
import { share } from "../platform/share";
const Profile: Component = () => { const Profile: Component = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -56,10 +34,6 @@ const Profile: Component = () => {
const windowSize = useWindowSize(); const windowSize = useWindowSize();
const time = createTimeSource(); const time = createTimeSource();
const menuButId = createUniqueId();
const [menuOpen, setMenuOpen] = createSignal(false);
const [scrolledPastBanner, setScrolledPastBanner] = createSignal(false); const [scrolledPastBanner, setScrolledPastBanner] = createSignal(false);
const obx = new IntersectionObserver( const obx = new IntersectionObserver(
(entries) => { (entries) => {
@ -84,19 +58,18 @@ const Profile: Component = () => {
); );
const [recentTootFilter, setRecentTootFilter] = createSignal({ const [recentTootFilter, setRecentTootFilter] = createSignal({
boost: false, boost: true,
reply: true, reply: true,
original: true, original: true,
}); });
const [recentToots, recentTootChunk, { refetch: refetchRecentToots }] = const [recentToots] = createTimeline(
createTimeline( () => session().client.v1.accounts.$select(params.id).statuses,
() => session().client.v1.accounts.$select(params.id).statuses, () => {
() => { const { boost, reply } = recentTootFilter();
const { boost, reply } = recentTootFilter(); return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply };
return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply }; },
}, );
);
const bannerImg = () => profile()?.header; const bannerImg = () => profile()?.header;
const avatarImg = () => profile()?.avatar; const avatarImg = () => profile()?.avatar;
@ -167,11 +140,7 @@ const Profile: Component = () => {
paddingTop: "var(--safe-area-inset-top)", paddingTop: "var(--safe-area-inset-top)",
}} }}
> >
<IconButton <IconButton color="inherit" onClick={[navigate, -1]}>
color="inherit"
onClick={[navigate, -1]}
aria-label="Close"
>
<Close /> <Close />
</IconButton> </IconButton>
<Title <Title
@ -184,52 +153,13 @@ const Profile: Component = () => {
createRenderEffect(() => (e.innerHTML = displayName())) createRenderEffect(() => (e.innerHTML = displayName()))
} }
></Title> ></Title>
<IconButton color="inherit">
<IconButton
id={menuButId}
color="inherit"
onClick={[setMenuOpen, true]}
>
<MoreVert /> <MoreVert />
</IconButton> </IconButton>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
} }
> >
<Menu
open={menuOpen()}
onClose={[setMenuOpen, false]}
anchor={() =>
document.getElementById(menuButId)!.getBoundingClientRect()
}
>
<MenuItem disabled>
<ListItemIcon>
<Edit />
</ListItemIcon>
<ListItemText>Edit...</ListItemText>
</MenuItem>
<Divider />
<MenuItem disabled>
<ListItemIcon>
<Send />
</ListItemIcon>
<ListItemText>Mention {profile()?.displayName || ""}...</ListItemText>
</MenuItem>
<Divider />
<MenuItem component={"a"} href={profile()?.url} target="_blank" rel="noopener noreferrer">
<ListItemIcon>
<OpenInBrowser />
</ListItemIcon>
<ListItemText>Open in browser...</ListItemText>
</MenuItem>
<MenuItem onClick={() => share({ url: profile()?.url })}>
<ListItemIcon>
<Share />
</ListItemIcon>
<ListItemText>Share...</ListItemText>
</MenuItem>
</Menu>
<div <div
style={{ style={{
width: "100%", width: "100%",
@ -321,7 +251,7 @@ const Profile: Component = () => {
<div> <div>
<TootFilterButton <TootFilterButton
options={{ options={{
boost: "Boosts", boost: "Boosteds",
reply: "Replies", reply: "Replies",
original: "Originals", original: "Originals",
}} }}
@ -338,24 +268,6 @@ const Profile: Component = () => {
onChangeToot={recentToots.set} onChangeToot={recentToots.set}
/> />
</TimeSourceProvider> </TimeSourceProvider>
<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"]}
>
<ExpandMore />
</IconButton>
</div>
</Show>
</Scaffold> </Scaffold>
); );
}; };