Compare commits
No commits in common. "b133a9b9a779f9a21d0b4a021502c1b5ffe7b726" and "35f51db294e48eb4129089cd46ba112370137364" have entirely different histories.
b133a9b9a7
...
35f51db294
8 changed files with 19 additions and 164 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -36,9 +36,7 @@
|
||||||
"@suid/icons-material": "^0.7.0",
|
"@suid/icons-material": "^0.7.0",
|
||||||
"@suid/material": "^0.16.0",
|
"@suid/material": "^0.16.0",
|
||||||
"blurhash": "^2.0.5",
|
"blurhash": "^2.0.5",
|
||||||
"colorjs.io": "^0.5.2",
|
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"fast-average-color": "^9.4.0",
|
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"masto": "^6.8.0",
|
"masto": "^6.8.0",
|
||||||
"nanostores": "^0.9.5",
|
"nanostores": "^0.9.5",
|
||||||
|
|
|
@ -43,7 +43,7 @@ const MOVE_SPEED = 1400; // 1400px/s, bottom sheet is big and a bit heavier than
|
||||||
const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
|
const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
|
||||||
let element: HTMLDialogElement;
|
let element: HTMLDialogElement;
|
||||||
let animation: Animation | undefined;
|
let animation: Animation | undefined;
|
||||||
const [hero, setHero] = useHeroSignal(HERO);
|
const hero = useHeroSignal(HERO);
|
||||||
const [cache, setCache] = createSignal<ResolvedChildren | undefined>();
|
const [cache, setCache] = createSignal<ResolvedChildren | undefined>();
|
||||||
const ochildren = children(() => props.children);
|
const ochildren = children(() => props.children);
|
||||||
|
|
||||||
|
@ -70,13 +70,11 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
|
||||||
const animation = animateHero(startRect, endRect, element, true);
|
const animation = animateHero(startRect, endRect, element, true);
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
element.close();
|
element.close();
|
||||||
setHero();
|
|
||||||
};
|
};
|
||||||
animation.addEventListener("finish", onClose);
|
animation.addEventListener("finish", onClose);
|
||||||
animation.addEventListener("cancel", onClose);
|
animation.addEventListener("cancel", onClose);
|
||||||
} else {
|
} else {
|
||||||
element.close();
|
element.close();
|
||||||
setHero();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,7 @@ export type HeroSource = {
|
||||||
[key: string | symbol | number]: DOMRect | undefined;
|
[key: string | symbol | number]: DOMRect | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const HeroSourceContext = createContext<Signal<HeroSource>>(
|
const HeroSourceContext = createContext<Signal<HeroSource>>(/* __@PURE__ */undefined);
|
||||||
/* __@PURE__ */ undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
export const HeroSourceProvider = HeroSourceContext.Provider;
|
export const HeroSourceProvider = HeroSourceContext.Provider;
|
||||||
|
|
||||||
|
@ -27,7 +25,7 @@ function useHeroSource() {
|
||||||
*/
|
*/
|
||||||
export function useHeroSignal(
|
export function useHeroSignal(
|
||||||
key: string | symbol | number,
|
key: string | symbol | number,
|
||||||
): Signal<DOMRect | undefined> {
|
): Accessor<DOMRect | undefined> {
|
||||||
const source = useHeroSource();
|
const source = useHeroSource();
|
||||||
if (source) {
|
if (source) {
|
||||||
const [get, set] = createSignal<DOMRect>();
|
const [get, set] = createSignal<DOMRect>();
|
||||||
|
@ -44,8 +42,8 @@ export function useHeroSignal(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return [get, set];
|
return get;
|
||||||
} else {
|
} else {
|
||||||
return [() => undefined, () => undefined];
|
return () => undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ const CompactToot: Component<CompactTootProps> = (props) => {
|
||||||
const toot = () => props.status;
|
const toot = () => props.status;
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
class={[tootStyle.toot, tootStyle.compact, props.class || ""].join(" ")}
|
class={[tootStyle.compact, props.class || ""].join(" ")}
|
||||||
lang={toot().language || undefined}
|
lang={toot().language || undefined}
|
||||||
>
|
>
|
||||||
<Img
|
<Img
|
||||||
|
|
|
@ -146,7 +146,7 @@ const TimelinePanel: Component<{
|
||||||
expanded={item.id === expandedThreadId() ? 1 : 0}
|
expanded={item.id === expandedThreadId() ? 1 : 0}
|
||||||
onExpandChange={(x) => {
|
onExpandChange={(x) => {
|
||||||
if (item.id !== expandedThreadId()) {
|
if (item.id !== expandedThreadId()) {
|
||||||
setExpandedThreadId((x) => (x ? undefined : item.id));
|
setExpandedThreadId(item.id);
|
||||||
} else if (x === 2) {
|
} else if (x === 2) {
|
||||||
props.openFullScreenToot(item, element);
|
props.openFullScreenToot(item, element);
|
||||||
}
|
}
|
||||||
|
@ -198,16 +198,8 @@ const Home: ParentComponent = (props) => {
|
||||||
const settings$ = useStore($settings);
|
const settings$ = useStore($settings);
|
||||||
|
|
||||||
const [profiles] = useSignedInProfiles();
|
const [profiles] = useSignedInProfiles();
|
||||||
const profile = () => {
|
const profile = () => profiles()[0].inf;
|
||||||
const all = profiles();
|
const client = () => profiles()[0].client;
|
||||||
if (all.length > 0) {
|
|
||||||
return all[0].inf;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const client = () => {
|
|
||||||
const all = profiles();
|
|
||||||
return all?.[0]?.client;
|
|
||||||
};
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [heroSrc, setHeroSrc] = createSignal<HeroSource>({});
|
const [heroSrc, setHeroSrc] = createSignal<HeroSource>({});
|
||||||
|
|
|
@ -5,19 +5,11 @@ import {
|
||||||
type JSX,
|
type JSX,
|
||||||
Show,
|
Show,
|
||||||
createRenderEffect,
|
createRenderEffect,
|
||||||
createSignal,
|
|
||||||
createEffect,
|
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import tootStyle from "./toot.module.css";
|
import tootStyle from "./toot.module.css";
|
||||||
import { formatRelative } from "date-fns";
|
import { formatRelative } from "date-fns";
|
||||||
import Img from "../material/Img.js";
|
import Img from "../material/Img.js";
|
||||||
import {
|
import { Body2 } from "../material/typography.js";
|
||||||
Body1,
|
|
||||||
Body2,
|
|
||||||
Caption,
|
|
||||||
Subheading,
|
|
||||||
Title,
|
|
||||||
} from "../material/typography.js";
|
|
||||||
import { css } from "solid-styled";
|
import { css } from "solid-styled";
|
||||||
import {
|
import {
|
||||||
BookmarkAddOutlined,
|
BookmarkAddOutlined,
|
||||||
|
@ -34,8 +26,6 @@ import { Divider } from "@suid/material";
|
||||||
import cardStyle from "../material/cards.module.css";
|
import cardStyle from "../material/cards.module.css";
|
||||||
import Button from "../material/Button.js";
|
import Button from "../material/Button.js";
|
||||||
import MediaAttachmentGrid from "./MediaAttachmentGrid.js";
|
import MediaAttachmentGrid from "./MediaAttachmentGrid.js";
|
||||||
import { FastAverageColor } from "fast-average-color";
|
|
||||||
import Color from "colorjs.io";
|
|
||||||
|
|
||||||
type TootContentViewProps = {
|
type TootContentViewProps = {
|
||||||
source?: string;
|
source?: string;
|
||||||
|
@ -197,67 +187,6 @@ function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TootPreviewCard(props: { src: mastodon.v1.PreviewCard }) {
|
|
||||||
let root: HTMLAnchorElement;
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
if (!props.src.width) return;
|
|
||||||
const width = root.getBoundingClientRect().width;
|
|
||||||
if (width > props.src.width) {
|
|
||||||
root.classList.add(tootStyle.compact);
|
|
||||||
} else {
|
|
||||||
root.classList.remove(tootStyle.compact);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const onImgLoad = (event: Event & { currentTarget: HTMLImageElement }) => {
|
|
||||||
// TODO: better extraction algorithm
|
|
||||||
// I'd like to use a pattern panel and match one in the panel from the extracted color
|
|
||||||
const fac = new FastAverageColor();
|
|
||||||
const result = fac.getColor(event.currentTarget);
|
|
||||||
if (result.error) {
|
|
||||||
console.error(result.error);
|
|
||||||
fac.destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
root.style.setProperty("--tutu-color-surface", result.hex);
|
|
||||||
const focusSurface = result.isDark
|
|
||||||
? new Color(result.hex).darken(0.2)
|
|
||||||
: new Color(result.hex).lighten(0.2);
|
|
||||||
root.style.setProperty("--tutu-color-surface-d", focusSurface.toString());
|
|
||||||
const textColor = result.isDark ? "white" : "black";
|
|
||||||
const secondaryTextColor = new Color(textColor);
|
|
||||||
secondaryTextColor.alpha = 0.75;
|
|
||||||
root.style.setProperty("--tutu-color-on-surface", textColor);
|
|
||||||
root.style.setProperty(
|
|
||||||
"--tutu-color-secondary-text-on-surface",
|
|
||||||
secondaryTextColor.toString(),
|
|
||||||
);
|
|
||||||
fac.destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
ref={root!}
|
|
||||||
class={tootStyle.previewCard}
|
|
||||||
href={props.src.url}
|
|
||||||
target="_blank"
|
|
||||||
referrerPolicy="unsafe-url"
|
|
||||||
>
|
|
||||||
<Show when={props.src.image}>
|
|
||||||
<img
|
|
||||||
crossOrigin="anonymous"
|
|
||||||
src={props.src.image!}
|
|
||||||
onLoad={onImgLoad}
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<Title component="h1">{props.src.title}</Title>
|
|
||||||
<Body1 component="p">{props.src.description}</Body1>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RegularToot: Component<TootCardProps> = (props) => {
|
const RegularToot: Component<TootCardProps> = (props) => {
|
||||||
let rootRef: HTMLElement;
|
let rootRef: HTMLElement;
|
||||||
const [managed, managedActionGroup, rest] = splitProps(
|
const [managed, managedActionGroup, rest] = splitProps(
|
||||||
|
@ -312,9 +241,6 @@ const RegularToot: Component<TootCardProps> = (props) => {
|
||||||
emojis={toot().emojis}
|
emojis={toot().emojis}
|
||||||
class={tootStyle.tootContent}
|
class={tootStyle.tootContent}
|
||||||
/>
|
/>
|
||||||
<Show when={toot().card}>
|
|
||||||
<TootPreviewCard src={toot().card!} />
|
|
||||||
</Show>
|
|
||||||
<Show when={toot().mediaAttachments.length > 0}>
|
<Show when={toot().mediaAttachments.length > 0}>
|
||||||
<MediaAttachmentGrid attachments={toot().mediaAttachments} />
|
<MediaAttachmentGrid attachments={toot().mediaAttachments} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -104,64 +104,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.previewCard {
|
.compact {
|
||||||
composes: cardGutSkip from "../material/cards.module.css";
|
|
||||||
display: block;
|
|
||||||
border: 1px solid #eeeeee;
|
|
||||||
background-color: var(--tutu-color-surface);
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1.5em;
|
|
||||||
color: var(--tutu-color-secondary-text-on-surface);
|
|
||||||
transition: color 220ms var(--tutu-anim-curve-std), background-color 220ms var(--tutu-anim-curve-std);
|
|
||||||
padding-bottom: 8px;
|
|
||||||
|
|
||||||
>img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus-visible {
|
|
||||||
background-color: var(--tutu-color-surface-d);
|
|
||||||
color: var(--tutu-color-on-surface);
|
|
||||||
|
|
||||||
>h1 {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
>h1 {
|
|
||||||
color: var(--tutu-color-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
>h1,
|
|
||||||
>p {
|
|
||||||
margin-left: 16px;
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.compact {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(10%, 30%) 1fr;
|
|
||||||
padding-left: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
|
|
||||||
>img:first-child {
|
|
||||||
grid-row: 1 / 3;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
>h1,
|
|
||||||
>p {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.toot.compact {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
Loading…
Reference in a new issue