Compare commits

...

4 commits

Author SHA1 Message Date
thislight
85cb7ad081
App: try fix gap at bottom on iOS standalone
All checks were successful
/ depoly (push) Successful in 1m17s
2024-11-10 22:29:49 +08:00
thislight
8066f699bd
TootBottomSheet: test scrollable 2024-11-10 21:39:54 +08:00
thislight
34dd95e959
Settings: fix safe area insets emulation 2024-11-10 21:23:03 +08:00
thislight
7e698ebe4c
bump to v1.1.0 2024-11-10 16:56:41 +08:00
5 changed files with 112 additions and 70 deletions

View file

@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package", "$schema": "https://json.schemastore.org/package",
"name": "tutu", "name": "tutu",
"version": "1.0.8", "version": "1.1.0",
"description": "", "description": "",
"private": true, "private": true,
"type": "module", "type": "module",

View file

@ -10,6 +10,11 @@
overscroll-behavior-block: none; overscroll-behavior-block: none;
} }
body, #root {
min-height: 100vh;
min-height: 100dvh;
}
.custom-emoji { .custom-emoji {
width: 1em; width: 1em;
} }

View file

@ -98,7 +98,14 @@ const safeAreaInsets: Record<string, SafeAreaInsets> = {
let screenOrientationCallback: (() => void) | undefined; let screenOrientationCallback: (() => void) | undefined;
function removeSafeAreaEmulation(root: HTMLElement) {
for (const name of ["top", "right", "bottom", "left"]) {
root.style.removeProperty(`--safe-area-inset-${name}`);
}
}
function applySafeAreaEmulation(root: HTMLElement, insets: Inset) { function applySafeAreaEmulation(root: HTMLElement, insets: Inset) {
removeSafeAreaEmulation(root);
for (const key of Object.keys(insets) as (keyof Inset)[]) { for (const key of Object.keys(insets) as (keyof Inset)[]) {
const value = insets[key]; const value = insets[key];
if (!value || value === 0) continue; if (!value || value === 0) continue;
@ -109,19 +116,22 @@ function applySafeAreaEmulation(root: HTMLElement, insets: Inset) {
function setupSafeAreaEmulation(name: string) { function setupSafeAreaEmulation(name: string) {
const insets = safeAreaInsets[name]; const insets = safeAreaInsets[name];
const root = document.querySelector(":root")! as HTMLElement; const root = document.querySelector(":root")! as HTMLElement;
if (!insets) {
if (screenOrientationCallback) { if (screenOrientationCallback) {
window.screen.orientation.removeEventListener( window.screen.orientation.removeEventListener(
"change", "change",
screenOrientationCallback, screenOrientationCallback,
); );
screenOrientationCallback = undefined; screenOrientationCallback = undefined;
} }
for (const name of ["top", "right", "bottom", "left"]) {
root.style.removeProperty(`--safe-area-inset-${name}`); removeSafeAreaEmulation(root);
}
} else { if (insets) {
screenOrientationCallback = () => { screenOrientationCallback = () => {
console.debug(
`safe area emulation target: ${window.screen.orientation.type}`,
);
if (window.screen.orientation.type === "portrait-primary") { if (window.screen.orientation.type === "portrait-primary") {
console.debug("safe area emulation: protrait"); console.debug("safe area emulation: protrait");
applySafeAreaEmulation(root, insets.protrait); applySafeAreaEmulation(root, insets.protrait);
@ -138,6 +148,16 @@ function setupSafeAreaEmulation(name: string) {
} }
} }
if (import.meta.hot) {
import.meta.hot.accept((mod) => {
if (!mod) return;
if (screenOrientationCallback) {
mod["screenOrientationCallback"] = screenOrientationCallback;
setTimeout(screenOrientationCallback, 0);
}
});
}
type Strings = { type Strings = {
["lang.auto"]: Template<{ detected: string }>; ["lang.auto"]: Template<{ detected: string }>;
} & Record<string, string | undefined>; } & Record<string, string | undefined>;

View file

@ -0,0 +1,18 @@
.TootBottomSheet {
overflow: hidden;
.Scrollable {
padding-bottom: var(--safe-area-inset-bottom, 0);
overflow-y: auto;
overscroll-behavior-y: contain;
max-height: calc(100vh - var(--scaffold-topbar-height, 0px));
max-height: calc(100dvh - var(--scaffold-topbar-height, 0px));
}
.progress-line {
display: flex;
justify-content: center;
margin-top: 12px;
margin-bottom: 12px;
}
}

View file

@ -11,9 +11,7 @@ import {
import Scaffold from "../material/Scaffold"; import Scaffold from "../material/Scaffold";
import { AppBar, CircularProgress, IconButton, Toolbar } from "@suid/material"; import { AppBar, CircularProgress, IconButton, Toolbar } from "@suid/material";
import { Title } from "../material/typography"; import { Title } from "../material/typography";
import { import { Close as CloseIcon } from "@suid/icons-material";
Close as CloseIcon,
} from "@suid/icons-material";
import { useSessionForAcctStr } from "../masto/clients"; import { useSessionForAcctStr } from "../masto/clients";
import { resolveCustomEmoji } from "../masto/toot"; import { resolveCustomEmoji } from "../masto/toot";
import RegularToot, { findElementActionable } from "./RegularToot"; import RegularToot, { findElementActionable } from "./RegularToot";
@ -26,6 +24,7 @@ import TootComposer from "./TootComposer";
import { useDocumentTitle } from "../utils"; import { useDocumentTitle } from "../utils";
import { createTimelineControlsForArray } from "../masto/timelines"; import { createTimelineControlsForArray } from "../masto/timelines";
import TootList from "./TootList"; import TootList from "./TootList";
import "./TootBottomSheet.css";
let cachedEntry: [string, mastodon.v1.Status] | undefined; let cachedEntry: [string, mastodon.v1.Status] | undefined;
@ -245,64 +244,64 @@ const TootBottomSheet: Component = (props) => {
</Toolbar> </Toolbar>
</AppBar> </AppBar>
} }
class="TootBottomSheet"
> >
<TimeSourceProvider value={time}> <div
<TootList class="Scrollable"
threads={ancestors.list} >
onUnknownThread={ancestors.getPath} <TimeSourceProvider value={time}>
onChangeToot={ancestors.set} <TootList
/> threads={ancestors.list}
onUnknownThread={ancestors.getPath}
<article> onChangeToot={ancestors.set}
<Show when={toot()}>
<RegularToot
id={`toot-${toot()!.id}`}
class={cards.card}
style={{
"scroll-margin-top":
"calc(var(--scaffold-topbar-height) + 20px)",
}}
status={toot()!}
actionable={!!actSession()}
evaluated={true}
onBookmark={onBookmark}
onRetoot={onBoost}
onFavourite={onFav}
onClick={handleMainTootClick}
></RegularToot>
</Show>
</article>
<Show when={session()!.account}>
<TootComposer
mentions={defaultMentions()}
profile={session().account!}
replyToDisplayName={toot()?.account?.displayName || ""}
client={session().client}
onSent={() => refetchContext()}
inReplyToId={remoteToot()?.reblog?.id ?? remoteToot()?.id}
/> />
</Show>
<Show when={tootContextErrorUncaught.loading}> <article>
<div <Show when={toot()}>
style={{ <RegularToot
display: "flex", id={`toot-${toot()!.id}`}
"justify-content": "center", class={cards.card}
"margin-block": "12px", style={{
}} "scroll-margin-top":
> "calc(var(--scaffold-topbar-height) + 20px)",
<CircularProgress style="width: 1.5em; height: 1.5em;" /> }}
</div> status={toot()!}
</Show> actionable={!!actSession()}
evaluated={true}
onBookmark={onBookmark}
onRetoot={onBoost}
onFavourite={onFav}
onClick={handleMainTootClick}
></RegularToot>
</Show>
</article>
<TootList <Show when={session()!.account}>
threads={descendants.list} <TootComposer
onUnknownThread={descendants.getPath} mentions={defaultMentions()}
onChangeToot={descendants.set} profile={session().account!}
/> replyToDisplayName={toot()?.account?.displayName || ""}
</TimeSourceProvider> client={session().client}
<div style={{ height: "var(--safe-area-inset-bottom, 0)" }}></div> onSent={() => refetchContext()}
inReplyToId={remoteToot()?.reblog?.id ?? remoteToot()?.id}
/>
</Show>
<Show when={tootContextErrorUncaught.loading}>
<div
class="progress-line"
>
<CircularProgress style="width: 1.5em; height: 1.5em;" />
</div>
</Show>
<TootList
threads={descendants.list}
onUnknownThread={descendants.getPath}
onChangeToot={descendants.set}
/>
</TimeSourceProvider>
</div>
</Scaffold> </Scaffold>
); );
}; };