Compare commits
No commits in common. "a924c80bbe8518ace3939368bd38bc16b1e32048" and "4075c4194291e9126528cb22b9625cca42af882d" have entirely different histories.
a924c80bbe
...
4075c41942
2 changed files with 68 additions and 203 deletions
|
@ -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)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue