Compare commits
No commits in common. "4075c4194291e9126528cb22b9625cca42af882d" and "e08c978b105e5f2662499d878500fac9ce10bfe5" have entirely different histories.
4075c41942
...
e08c978b10
9 changed files with 45 additions and 315 deletions
|
@ -10,7 +10,7 @@ import {
|
||||||
lazy,
|
lazy,
|
||||||
onCleanup,
|
onCleanup,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { useRootTheme } from "./material/theme.js";
|
import { useRootTheme } from "./material/mui.js";
|
||||||
import {
|
import {
|
||||||
Provider as ClientProvider,
|
Provider as ClientProvider,
|
||||||
createMastoClientFor,
|
createMastoClientFor,
|
||||||
|
@ -149,7 +149,7 @@ const App: Component = () => {
|
||||||
return <UnexpectedError error={err} />;
|
return <UnexpectedError error={err} />;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme()}>
|
||||||
<DateFnScope>
|
<DateFnScope>
|
||||||
<ClientProvider value={clients}>
|
<ClientProvider value={clients}>
|
||||||
<ServiceWorkerProvider
|
<ServiceWorkerProvider
|
||||||
|
|
|
@ -11,15 +11,23 @@ import {
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
|
|
||||||
type Timeline<T extends mastodon.DefaultPaginationParams> = {
|
type Timeline = {
|
||||||
list(params?: T): mastodon.Paginator<mastodon.v1.Status[], unknown>;
|
list(params: {
|
||||||
|
/** Return results older than this ID. */
|
||||||
|
readonly maxId?: string;
|
||||||
|
/** Return results newer than this ID. */
|
||||||
|
readonly sinceId?: string;
|
||||||
|
/** Get a list of items with ID greater than this value excluding this ID */
|
||||||
|
readonly minId?: string;
|
||||||
|
/** Maximum number of results to return per page. Defaults to 40. NOTE: Pagination is done with the Link header from the response. */
|
||||||
|
readonly limit?: number;
|
||||||
|
}): mastodon.Paginator<mastodon.v1.Status[], unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TimelineParamsOf<T> = T extends Timeline<infer P> ? P : never;
|
export function createTimelineSnapshot(
|
||||||
|
timeline: Accessor<Timeline>,
|
||||||
export function createTimelineSnapshot<
|
limit: Accessor<number>,
|
||||||
T extends Timeline<mastodon.DefaultPaginationParams>,
|
) {
|
||||||
>(timeline: Accessor<T>, limit: Accessor<number>) {
|
|
||||||
const [shot, { refetch }] = createResource(
|
const [shot, { refetch }] = createResource(
|
||||||
() => [timeline(), limit()] as const,
|
() => [timeline(), limit()] as const,
|
||||||
async ([tl, limit]) => {
|
async ([tl, limit]) => {
|
||||||
|
@ -72,13 +80,13 @@ export function createTimelineSnapshot<
|
||||||
|
|
||||||
export type TimelineFetchDirection = mastodon.Direction;
|
export type TimelineFetchDirection = mastodon.Direction;
|
||||||
|
|
||||||
export type TimelineChunk<T extends mastodon.DefaultPaginationParams> = {
|
export type TimelineChunk = {
|
||||||
tl: Timeline<T>;
|
tl: Timeline;
|
||||||
rebuilt: boolean;
|
rebuilt: boolean;
|
||||||
chunk: readonly mastodon.v1.Status[];
|
chunk: readonly mastodon.v1.Status[];
|
||||||
done?: boolean;
|
done?: boolean;
|
||||||
direction: TimelineFetchDirection;
|
direction: TimelineFetchDirection;
|
||||||
params: T;
|
limit: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TreeNode<T> = {
|
type TreeNode<T> = {
|
||||||
|
@ -100,21 +108,21 @@ function collectPath<T>(node: TreeNode<T>) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTimelineChunk<T extends Timeline<mastodon.DefaultPaginationParams>>(
|
function createTimelineChunk(
|
||||||
timeline: Accessor<T>,
|
timeline: Accessor<Timeline>,
|
||||||
params: Accessor<TimelineParamsOf<T>>,
|
limit: Accessor<number>,
|
||||||
) {
|
) {
|
||||||
let vpMaxId: string | undefined, vpMinId: string | undefined;
|
let vpMaxId: string | undefined, vpMinId: string | undefined;
|
||||||
|
|
||||||
const fetchExtendingPage = async (
|
const fetchExtendingPage = async (
|
||||||
tl: T,
|
tl: Timeline,
|
||||||
direction: TimelineFetchDirection,
|
direction: TimelineFetchDirection,
|
||||||
params: TimelineParamsOf<T>,
|
limit: number,
|
||||||
) => {
|
) => {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case "next": {
|
case "next": {
|
||||||
const page = await tl
|
const page = await tl
|
||||||
.list({ ...params, sinceId: vpMaxId })
|
.list({ limit, sinceId: vpMaxId })
|
||||||
.setDirection(direction)
|
.setDirection(direction)
|
||||||
.next();
|
.next();
|
||||||
if ((page.value?.length ?? 0) > 0) {
|
if ((page.value?.length ?? 0) > 0) {
|
||||||
|
@ -125,7 +133,7 @@ function createTimelineChunk<T extends Timeline<mastodon.DefaultPaginationParams
|
||||||
|
|
||||||
case "prev": {
|
case "prev": {
|
||||||
const page = await tl
|
const page = await tl
|
||||||
.list({ ...params, maxId: vpMinId })
|
.list({ limit, maxId: vpMinId })
|
||||||
.setDirection(direction)
|
.setDirection(direction)
|
||||||
.next();
|
.next();
|
||||||
if ((page.value?.length ?? 0) > 0) {
|
if ((page.value?.length ?? 0) > 0) {
|
||||||
|
@ -137,11 +145,11 @@ function createTimelineChunk<T extends Timeline<mastodon.DefaultPaginationParams
|
||||||
};
|
};
|
||||||
|
|
||||||
return createResource(
|
return createResource(
|
||||||
() => [timeline(), params()] as const,
|
() => [timeline(), limit()] as const,
|
||||||
async (
|
async (
|
||||||
[tl, params],
|
[tl, limit],
|
||||||
info: ResourceFetcherInfo<
|
info: ResourceFetcherInfo<
|
||||||
Readonly<TimelineChunk<TimelineParamsOf<T>>>,
|
Readonly<TimelineChunk>,
|
||||||
TimelineFetchDirection
|
TimelineFetchDirection
|
||||||
>,
|
>,
|
||||||
) => {
|
) => {
|
||||||
|
@ -152,26 +160,27 @@ function createTimelineChunk<T extends Timeline<mastodon.DefaultPaginationParams
|
||||||
vpMaxId = undefined;
|
vpMaxId = undefined;
|
||||||
vpMinId = undefined;
|
vpMinId = undefined;
|
||||||
}
|
}
|
||||||
const posts = await fetchExtendingPage(tl, direction, params);
|
const posts = await fetchExtendingPage(tl, direction, limit);
|
||||||
return {
|
return {
|
||||||
tl,
|
tl,
|
||||||
rebuilt: rebuildTimeline,
|
rebuilt: rebuildTimeline,
|
||||||
chunk: posts.value ?? [],
|
chunk: posts.value ?? [],
|
||||||
done: posts.done,
|
done: posts.done,
|
||||||
direction,
|
direction,
|
||||||
params,
|
limit,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTimeline<
|
export function createTimeline(
|
||||||
T extends Timeline<mastodon.DefaultPaginationParams>,
|
timeline: Accessor<Timeline>,
|
||||||
>(timeline: Accessor<T>, params: Accessor<TimelineParamsOf<T>>) {
|
limit: Accessor<number>,
|
||||||
|
) {
|
||||||
const lookup = new ReactiveMap<string, TreeNode<mastodon.v1.Status>>();
|
const lookup = new ReactiveMap<string, TreeNode<mastodon.v1.Status>>();
|
||||||
const [threads, setThreads] = createStore([] as mastodon.v1.Status["id"][]);
|
const [threads, setThreads] = createStore([] as mastodon.v1.Status["id"][]);
|
||||||
|
|
||||||
const [chunk, { refetch }] = createTimelineChunk(timeline, params);
|
const [chunk, { refetch }] = createTimelineChunk(timeline, limit);
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const chk = catchError(chunk, (e) => console.error(e));
|
const chk = catchError(chunk, (e) => console.error(e));
|
||||||
|
|
|
@ -1,136 +0,0 @@
|
||||||
import { useWindowSize } from "@solid-primitives/resize-observer";
|
|
||||||
import { MenuList } from "@suid/material";
|
|
||||||
import {
|
|
||||||
createEffect,
|
|
||||||
createSignal,
|
|
||||||
type JSX,
|
|
||||||
type ParentComponent,
|
|
||||||
} from "solid-js";
|
|
||||||
import { ANIM_CURVE_STD } from "./theme";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
open?: boolean;
|
|
||||||
onClose?: JSX.EventHandlerUnion<HTMLDialogElement, Event>;
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Menu: ParentComponent<Props> = (props) => {
|
|
||||||
let root: HTMLDialogElement;
|
|
||||||
const [pos, setPos] = createSignal<[number, number]>([0, 0]);
|
|
||||||
const windowSize = useWindowSize();
|
|
||||||
|
|
||||||
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 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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
animateClose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<dialog
|
|
||||||
ref={root!}
|
|
||||||
onClose={props.onClose}
|
|
||||||
onClick={(e) => {
|
|
||||||
if (e.target === root) {
|
|
||||||
if (props.onClose) {
|
|
||||||
if (Array.isArray(props.onClose)) {
|
|
||||||
props.onClose[0](props.onClose[1], e);
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
props.onClose as (
|
|
||||||
event: Event & { currentTarget: HTMLDialogElement },
|
|
||||||
) => void
|
|
||||||
)(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
left: `${pos()[0]}px`,
|
|
||||||
top: `${pos()[1]}px`,
|
|
||||||
border: "none",
|
|
||||||
padding: 0,
|
|
||||||
"max-width": "560px",
|
|
||||||
width: "max-content",
|
|
||||||
/*"min-width": "20vw", */
|
|
||||||
"box-shadow": "var(--tutu-shadow-e8)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
background: "var(--tutu-color-surface)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuList>{props.children}</MenuList>
|
|
||||||
</div>
|
|
||||||
</dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Menu;
|
|
|
@ -2,9 +2,6 @@ import { Theme, createTheme } from "@suid/material/styles";
|
||||||
import { deepPurple, amber } from "@suid/material/colors";
|
import { deepPurple, amber } from "@suid/material/colors";
|
||||||
import { Accessor } from "solid-js";
|
import { Accessor } from "solid-js";
|
||||||
|
|
||||||
/**
|
|
||||||
* The MUI theme.
|
|
||||||
*/
|
|
||||||
export function useRootTheme(): Accessor<Theme> {
|
export function useRootTheme(): Accessor<Theme> {
|
||||||
return () =>
|
return () =>
|
||||||
createTheme({
|
createTheme({
|
||||||
|
@ -18,8 +15,3 @@ export function useRootTheme(): Accessor<Theme> {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ANIM_CURVE_STD = "cubic-bezier(0.4, 0, 0.2, 1)";
|
|
||||||
export const ANIM_CURVE_DECELERATION = "cubic-bezier(0, 0, 0.2, 1)";
|
|
||||||
export const ANIM_CURVE_ACELERATION = "cubic-bezier(0.4, 0, 1, 1)";
|
|
||||||
export const ANIM_CURVE_SHARP = "cubic-bezier(0.4, 0, 0.6, 1)";
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
import { Close as CloseIcon, ContentCopy } from "@suid/icons-material";
|
import { Close as CloseIcon, ContentCopy } from "@suid/icons-material";
|
||||||
import { Title } from "../material/typography";
|
import { Title } from "../material/typography";
|
||||||
import { render } from "solid-js/web";
|
import { render } from "solid-js/web";
|
||||||
import { useRootTheme } from "../material/theme";
|
import { useRootTheme } from "../material/mui";
|
||||||
|
|
||||||
const ShareBottomSheet: Component<{
|
const ShareBottomSheet: Component<{
|
||||||
data?: ShareData;
|
data?: ShareData;
|
||||||
|
|
|
@ -19,8 +19,8 @@ import { useWindowSize } from "@solid-primitives/resize-observer";
|
||||||
import { css } from "solid-styled";
|
import { css } from "solid-styled";
|
||||||
import { createTimeline } from "../masto/timelines";
|
import { createTimeline } from "../masto/timelines";
|
||||||
import TootList from "../timelines/TootList";
|
import TootList from "../timelines/TootList";
|
||||||
|
import { createIntersectionObserver } from "@solid-primitives/intersection-observer";
|
||||||
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
|
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
|
||||||
import TootFilterButton from "./TootFilterButton";
|
|
||||||
|
|
||||||
const Profile: Component = () => {
|
const Profile: Component = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
@ -48,6 +48,7 @@ const Profile: Component = () => {
|
||||||
threshold: 0.1,
|
threshold: 0.1,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
onCleanup(() => obx.disconnect());
|
onCleanup(() => obx.disconnect());
|
||||||
|
|
||||||
const [profile] = createResource(
|
const [profile] = createResource(
|
||||||
|
@ -57,25 +58,16 @@ const Profile: Component = () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const [recentTootFilter, setRecentTootFilter] = createSignal({
|
|
||||||
boost: true,
|
|
||||||
reply: true,
|
|
||||||
original: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [recentToots] = createTimeline(
|
const [recentToots] = createTimeline(
|
||||||
() => session().client.v1.accounts.$select(params.id).statuses,
|
() => session().client.v1.accounts.$select(params.id).statuses,
|
||||||
() => {
|
() => 20,
|
||||||
const { boost, reply } = recentTootFilter();
|
|
||||||
return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply };
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const bannerImg = () => profile()?.header;
|
const bannerImg = () => profile()?.header;
|
||||||
const avatarImg = () => profile()?.avatar;
|
const avatarImg = () => profile()?.avatar;
|
||||||
const displayName = () =>
|
const displayName = () =>
|
||||||
resolveCustomEmoji(profile()?.displayName || "", profile()?.emojis ?? []);
|
resolveCustomEmoji(profile()?.displayName || "", profile()?.emojis ?? []);
|
||||||
const fullUsername = () => `@${profile()?.acct ?? ""}`; // TODO: full user name
|
const fullUsername = () => `@${profile()?.acct ?? "..."}`; // TODO: full user name
|
||||||
const description = () => profile()?.note;
|
const description = () => profile()?.note;
|
||||||
|
|
||||||
css`
|
css`
|
||||||
|
@ -134,9 +126,7 @@ const Profile: Component = () => {
|
||||||
variant="dense"
|
variant="dense"
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
color: scrolledPastBanner()
|
color: scrolledPastBanner() ? undefined : bannerSampledColors()?.text,
|
||||||
? undefined
|
|
||||||
: bannerSampledColors()?.text,
|
|
||||||
paddingTop: "var(--safe-area-inset-top)",
|
paddingTop: "var(--safe-area-inset-top)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -248,19 +238,6 @@ const Profile: Component = () => {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<TootFilterButton
|
|
||||||
options={{
|
|
||||||
boost: "Boosteds",
|
|
||||||
reply: "Replies",
|
|
||||||
original: "Originals",
|
|
||||||
}}
|
|
||||||
applied={recentTootFilter()}
|
|
||||||
onApply={setRecentTootFilter}
|
|
||||||
disabledKeys={["original"]}
|
|
||||||
></TootFilterButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TimeSourceProvider value={time}>
|
<TimeSourceProvider value={time}>
|
||||||
<TootList
|
<TootList
|
||||||
threads={recentToots.list}
|
threads={recentToots.list}
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
MenuItem,
|
|
||||||
Checkbox,
|
|
||||||
ListItemText,
|
|
||||||
} from "@suid/material";
|
|
||||||
import {
|
|
||||||
createMemo,
|
|
||||||
createSignal,
|
|
||||||
createUniqueId,
|
|
||||||
For,
|
|
||||||
} from "solid-js";
|
|
||||||
import Menu from "../material/Menu";
|
|
||||||
import { FilterList, FilterListOff } from "@suid/icons-material";
|
|
||||||
|
|
||||||
type Props<Filters extends Record<string, string>> = {
|
|
||||||
options: Filters;
|
|
||||||
applied: Record<keyof Filters, boolean | undefined>;
|
|
||||||
disabledKeys?: (keyof Filters)[];
|
|
||||||
|
|
||||||
onApply(value: Record<keyof Filters, boolean | undefined>): void;
|
|
||||||
};
|
|
||||||
|
|
||||||
function TootFilterButton<F extends Record<string, string>>(props: Props<F>) {
|
|
||||||
const buttonId = createUniqueId();
|
|
||||||
const [open, setOpen] = createSignal(false);
|
|
||||||
|
|
||||||
const getTextForMultipleEntities = (texts: string[]) => {
|
|
||||||
switch (texts.length) {
|
|
||||||
case 0:
|
|
||||||
return "Nothing";
|
|
||||||
case 1:
|
|
||||||
return texts[0];
|
|
||||||
case 2:
|
|
||||||
return `${texts[0]} and ${texts[1]}`;
|
|
||||||
case 3:
|
|
||||||
return `${texts[0]}, ${texts[1]} and ${texts[2]}`;
|
|
||||||
default:
|
|
||||||
return `${texts[0]} and ${texts.length - 1} other${texts.length > 2 ? "s" : ""}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const optionKeys = () => Object.keys(props.options);
|
|
||||||
|
|
||||||
const appliedKeys = createMemo(() => {
|
|
||||||
const applied = props.applied;
|
|
||||||
return optionKeys().filter((k) => applied[k]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const text = () => {
|
|
||||||
const keys = optionKeys();
|
|
||||||
const napplied = appliedKeys().length;
|
|
||||||
switch (napplied) {
|
|
||||||
case keys.length:
|
|
||||||
return "All";
|
|
||||||
default:
|
|
||||||
return getTextForMultipleEntities(
|
|
||||||
appliedKeys().map((k) => props.options[k]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleKey = (key: keyof F) => {
|
|
||||||
props.onApply(
|
|
||||||
Object.assign({}, props.applied, {
|
|
||||||
[key]: !props.applied[key],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Button size="large" onClick={[setOpen, true]} id={buttonId}>
|
|
||||||
{appliedKeys().length === optionKeys().length ? (
|
|
||||||
<FilterListOff />
|
|
||||||
) : (
|
|
||||||
<FilterList />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<span style={{ "margin-left": "0.5em" }}>{text()}</span>
|
|
||||||
</Button>
|
|
||||||
<Menu
|
|
||||||
open={open()}
|
|
||||||
onClose={[setOpen, false]}
|
|
||||||
anchor={() =>
|
|
||||||
document.getElementById(buttonId)!.getBoundingClientRect()
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<For each={Object.keys(props.options)}>
|
|
||||||
{(item, idx) => (
|
|
||||||
<>
|
|
||||||
<MenuItem
|
|
||||||
data-sort={idx()}
|
|
||||||
onClick={[toggleKey, item]}
|
|
||||||
disabled={props.disabledKeys?.includes(item)}
|
|
||||||
>
|
|
||||||
<ListItemText>{props.options[item]}</ListItemText>
|
|
||||||
<Checkbox
|
|
||||||
checked={props.applied[item]}
|
|
||||||
sx={{ marginRight: "-8px" }}
|
|
||||||
disabled={props.disabledKeys?.includes(item)}
|
|
||||||
></Checkbox>
|
|
||||||
</MenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Menu>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TootFilterButton;
|
|
|
@ -32,7 +32,7 @@ const TimelinePanel: Component<{
|
||||||
|
|
||||||
const [timeline, snapshot, { refetch: refetchTimeline }] = createTimeline(
|
const [timeline, snapshot, { refetch: refetchTimeline }] = createTimeline(
|
||||||
() => props.client.v1.timelines[props.name],
|
() => props.client.v1.timelines[props.name],
|
||||||
() => ({limit: 20}),
|
() => 20,
|
||||||
);
|
);
|
||||||
const [expandedThreadId, setExpandedThreadId] = createSignal<string>();
|
const [expandedThreadId, setExpandedThreadId] = createSignal<string>();
|
||||||
const [typing, setTyping] = createSignal(false);
|
const [typing, setTyping] = createSignal(false);
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default defineConfig(({ mode }) => ({
|
||||||
strategies: "injectManifest",
|
strategies: "injectManifest",
|
||||||
registerType: "autoUpdate",
|
registerType: "autoUpdate",
|
||||||
devOptions: {
|
devOptions: {
|
||||||
enabled: !["production", "staging"].includes(mode),
|
enabled: mode === "staging" || mode === "dev",
|
||||||
},
|
},
|
||||||
srcDir: "src/serviceworker",
|
srcDir: "src/serviceworker",
|
||||||
filename: "main.ts",
|
filename: "main.ts",
|
||||||
|
|
Loading…
Reference in a new issue