Compare commits

..

No commits in common. "99bbd3eeabccd9fb7481f200fd13dcc600e4e790" and "70649e97e61e8a40c982239febe724bcc5132415" have entirely different histories.

13 changed files with 25 additions and 166 deletions

View file

@ -25,17 +25,13 @@ const AccountMastodonOAuth2Callback = lazy(
const TimelineHome = lazy(() => import("./timelines/Home.js")); const TimelineHome = lazy(() => import("./timelines/Home.js"));
const Settings = lazy(() => import("./settings/Settings.js")); const Settings = lazy(() => import("./settings/Settings.js"));
const TootBottomSheet = lazy(() => import("./timelines/TootBottomSheet.js")); const TootBottomSheet = lazy(() => import("./timelines/TootBottomSheet.js"));
const MotionSettings = lazy(() => import("./settings/Motions.js"));
const Routing: Component = () => { const Routing: Component = () => {
return ( return (
<Router> <Router>
<Route path="/" component={TimelineHome}> <Route path="/" component={TimelineHome}>
<Route path=""></Route> <Route path=""></Route>
<Route path="/settings" component={Settings}> <Route path="/settings" component={Settings}></Route>
<Route path=""></Route>
<Route path="/motions" component={MotionSettings}></Route>
</Route>
<Route path="/:acct/:id" component={TootBottomSheet}></Route> <Route path="/:acct/:id" component={TootBottomSheet}></Route>
</Route> </Route>
<Route path={"/accounts"}> <Route path={"/accounts"}>

View file

@ -62,14 +62,12 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
}); });
const onClose = () => { const onClose = () => {
hero()!.style.visibility = 'unset'
element.close(); element.close();
setHero(); setHero();
}; };
const animatedClose = () => { const animatedClose = () => {
const srcElement = hero() const endRect = hero();
const endRect = srcElement?.getBoundingClientRect();
if (endRect) { if (endRect) {
const startRect = element.getBoundingClientRect(); const startRect = element.getBoundingClientRect();
const animation = animateHero(startRect, endRect, element, true); const animation = animateHero(startRect, endRect, element, true);
@ -83,10 +81,8 @@ const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
const animatedOpen = () => { const animatedOpen = () => {
element.showModal(); element.showModal();
const srcElement = hero() const startRect = hero();
const startRect = srcElement?.getBoundingClientRect();
if (!startRect) return; if (!startRect) return;
srcElement!.style.visibility = 'hidden'
const endRect = element.getBoundingClientRect(); const endRect = element.getBoundingClientRect();
animateHero(startRect, endRect, element); animateHero(startRect, endRect, element);
}; };

View file

@ -44,7 +44,7 @@ const Img: Component<ImgProps> = (props) => {
object-position: center; object-position: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
visibility: ${isBlurEnabled() ? "hidden" : "initial"}; visibility: ${isBlurEnabled() ? "hidden" : "visible"};
} }
} }

View file

@ -9,7 +9,7 @@ import {
} from "solid-js"; } from "solid-js";
export type HeroSource = { export type HeroSource = {
[key: string | symbol | number]: HTMLElement | undefined; [key: string | symbol | number]: DOMRect | undefined;
}; };
const HeroSourceContext = createContext<Signal<HeroSource>>( const HeroSourceContext = createContext<Signal<HeroSource>>(
@ -27,10 +27,10 @@ function useHeroSource() {
*/ */
export function useHeroSignal( export function useHeroSignal(
key: string | symbol | number, key: string | symbol | number,
): Signal<HTMLElement | undefined> { ): Signal<DOMRect | undefined> {
const source = useHeroSource(); const source = useHeroSource();
if (source) { if (source) {
const [get, set] = createSignal<HTMLElement>(); const [get, set] = createSignal<DOMRect>();
createRenderEffect(() => { createRenderEffect(() => {
const value = source[0](); const value = source[0]();

View file

@ -1,89 +0,0 @@
import type { Component } from "solid-js";
import Scaffold from "../material/Scaffold";
import {
AppBar,
Divider,
IconButton,
List,
ListItemButton,
ListItemSecondaryAction,
ListItemText,
ListSubheader,
Switch,
Toolbar,
} from "@suid/material";
import { Title } from "../material/typography";
import { useNavigate } from "@solidjs/router";
import { Close as CloseIcon } from "@suid/icons-material";
import { createTranslator } from "../platform/i18n";
import { useStore } from "@nanostores/solid";
import { $settings } from "./stores";
const Motions: Component = () => {
const navigate = useNavigate();
const [t] = createTranslator(
(code) =>
import(`./i18n/${code}.json`) as Promise<{
default: Record<string, string | undefined>;
}>,
);
const settings = useStore($settings);
return (
<Scaffold
topbar={
<AppBar position="static">
<Toolbar
variant="dense"
sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
>
<IconButton color="inherit" onClick={[navigate, -1]} disableRipple>
<CloseIcon />
</IconButton>
<Title>{t("motions")}</Title>
</Toolbar>
</AppBar>
}
>
<List
sx={{
paddingBottom: "calc(var(--safe-area-inset-bottom, 0px) + 16px)",
}}
>
<li>
<ul style={{ "padding-left": 0 }}>
<ListSubheader>{t("motions.gifs")}</ListSubheader>
<ListItemButton
onClick={() =>
$settings.setKey("autoPlayGIFs", !settings().autoPlayGIFs)
}
>
<ListItemText>{t("motions.gifs.autoplay")}</ListItemText>
<ListItemSecondaryAction>
<Switch checked={settings().autoPlayGIFs}></Switch>
</ListItemSecondaryAction>
</ListItemButton>
<Divider />
</ul>
</li>
<li>
<ul style={{ "padding-left": 0 }}>
<ListSubheader>{t("motions.vids")}</ListSubheader>
<ListItemButton
onClick={() =>
$settings.setKey("autoPlayVideos", !settings().autoPlayVideos)
}
>
<ListItemText>{t("motions.vids.autoplay")}</ListItemText>
<ListItemSecondaryAction>
<Switch checked={settings().autoPlayVideos}></Switch>
</ListItemSecondaryAction>
</ListItemButton>
<Divider />
</ul>
</li>
</List>
</Scaffold>
);
};
export default Motions;

View file

@ -1,10 +1,4 @@
import { import { createSignal, For, Show, type ParentComponent } from "solid-js";
children,
createSignal,
For,
Show,
type ParentComponent,
} from "solid-js";
import Scaffold from "../material/Scaffold.js"; import Scaffold from "../material/Scaffold.js";
import { import {
AppBar, AppBar,
@ -17,18 +11,18 @@ import {
ListItemSecondaryAction, ListItemSecondaryAction,
ListItemText, ListItemText,
ListSubheader, ListSubheader,
NativeSelect,
Switch, Switch,
Toolbar, Toolbar,
} from "@suid/material"; } from "@suid/material";
import { import {
Animation as AnimationIcon,
Close as CloseIcon, Close as CloseIcon,
Logout, Logout,
Public as PublicIcon, Public as PublicIcon,
Refresh as RefreshIcon, Refresh as RefreshIcon,
Translate as TranslateIcon, Translate as TranslateIcon,
} from "@suid/icons-material"; } from "@suid/icons-material";
import { A, useNavigate } from "@solidjs/router"; import { useNavigate } from "@solidjs/router";
import { Title } from "../material/typography.jsx"; import { Title } from "../material/typography.jsx";
import { css } from "solid-styled"; import { css } from "solid-styled";
import { useSignedInProfiles } from "../masto/acct.js"; import { useSignedInProfiles } from "../masto/acct.js";
@ -49,13 +43,12 @@ import { type Template } from "@solid-primitives/i18n";
import BottomSheet from "../material/BottomSheet.jsx"; import BottomSheet from "../material/BottomSheet.jsx";
import ChooseLang from "./ChooseLang.jsx"; import ChooseLang from "./ChooseLang.jsx";
import ChooseRegion from "./ChooseRegion.jsx"; import ChooseRegion from "./ChooseRegion.jsx";
import { Portal } from "solid-js/web";
type Strings = { type Strings = {
["lang.auto"]: Template<{ detected: string }>; ["lang.auto"]: Template<{ detected: string }>;
} & Record<string, string | undefined>; } & Record<string, string | undefined>;
const Settings: ParentComponent = (props) => { const Settings: ParentComponent = () => {
const [t] = createTranslator( const [t] = createTranslator(
(code) => (code) =>
import(`./i18n/${code}.json`) as Promise<{ import(`./i18n/${code}.json`) as Promise<{
@ -78,8 +71,6 @@ const Settings: ParentComponent = (props) => {
signOut((a) => a.site === acct.site && a.accessToken === acct.accessToken); signOut((a) => a.site === acct.site && a.accessToken === acct.accessToken);
}; };
const subpage = children(() => props.children);
css` css`
ul { ul {
padding: 0; padding: 0;
@ -105,10 +96,6 @@ const Settings: ParentComponent = (props) => {
</AppBar> </AppBar>
} }
> >
<BottomSheet open={!!subpage()} onClose={() => navigate(-1)}>
{subpage()}
</BottomSheet>
<List class="setting-list" use:solid-styled> <List class="setting-list" use:solid-styled>
<li> <li>
<ul> <ul>
@ -148,7 +135,7 @@ const Settings: ParentComponent = (props) => {
</For> </For>
</li> </li>
<li> <li>
<ListSubheader>{t("timelines")}</ListSubheader> <ListSubheader>{t("Reading")}</ListSubheader>
<ListItemButton <ListItemButton
onClick={(e) => onClick={(e) =>
$settings.setKey( $settings.setKey(
@ -165,13 +152,6 @@ const Settings: ParentComponent = (props) => {
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItemButton> </ListItemButton>
<Divider /> <Divider />
<ListItemButton component={A} href="./motions">
<ListItemIcon>
<AnimationIcon></AnimationIcon>
</ListItemIcon>
<ListItemText>{t("motions")}</ListItemText>
</ListItemButton>
<Divider />
</li> </li>
<li> <li>
<ListSubheader>{t("This Application")}</ListSubheader> <ListSubheader>{t("This Application")}</ListSubheader>

View file

@ -3,7 +3,7 @@
"Accounts": "Accounts", "Accounts": "Accounts",
"All Notifications": "All Notifications", "All Notifications": "All Notifications",
"Sign in...": "Sign in...", "Sign in...": "Sign in...",
"timelines": "Timelines and Toot Lists", "Reading": "Reading",
"Prefetch Toots": "Prefetch Toots", "Prefetch Toots": "Prefetch Toots",
"Prefetch Toots.2nd": "Tutu will download the toots before you need.", "Prefetch Toots.2nd": "Tutu will download the toots before you need.",
"This Application": "This Application", "This Application": "This Application",
@ -26,11 +26,5 @@
"Choose Language": "Choose Language", "Choose Language": "Choose Language",
"Supported": "Supported", "Supported": "Supported",
"Unsupported": "Unsupported", "Unsupported": "Unsupported",
"Choose Region": "Choose Region", "Choose Region": "Choose Region"
"motions": "GIFs and Videos",
"motions.gifs": "GIFs",
"motions.gifs.autoplay": "Auto-play GIFs",
"motions.vids": "Videos",
"motions.vids.autoplay": "Auto-play Videos"
} }

View file

@ -3,7 +3,7 @@
"Accounts": "所有账户", "Accounts": "所有账户",
"All Notifications": "所有通知", "All Notifications": "所有通知",
"Sign in...": "登录新账户...", "Sign in...": "登录新账户...",
"timelines": "时间线和嘟文列表", "Reading": "阅读",
"Prefetch Toots": "提前下载嘟文", "Prefetch Toots": "提前下载嘟文",
"Prefetch Toots.2nd": "图图会在你可能需要的时候提前下载嘟文。", "Prefetch Toots.2nd": "图图会在你可能需要的时候提前下载嘟文。",
"This Application": "本应用", "This Application": "本应用",
@ -26,11 +26,5 @@
"Choose Language": "选择语言", "Choose Language": "选择语言",
"Supported": "已支持", "Supported": "已支持",
"Unsupported": "尚未支持", "Unsupported": "尚未支持",
"Choose Region": "选择区域", "Choose Region": "选择区域"
"motions": "动图和视频",
"motions.gifs": "动图",
"motions.gifs.autoplay": "自动播放动图",
"motions.vids": "视频",
"motions.vids.autoplay": "自动播放视频"
} }

View file

@ -5,10 +5,6 @@ type Settings = {
prefetchTootsDisabled?: boolean; prefetchTootsDisabled?: boolean;
language?: string; language?: string;
region?: string; region?: string;
// GIFs and Videos
autoPlayGIFs?: boolean;
autoPlayVideos?: boolean;
}; };
export const $settings = persistentMap<Settings>( export const $settings = persistentMap<Settings>(

View file

@ -340,7 +340,8 @@ const Home: ParentComponent = (props) => {
console.warn("no account info?"); console.warn("no account info?");
return; return;
} }
setHeroSrc((x) => Object.assign({}, x, { [BOTTOM_SHEET_HERO]: srcElement })); const rect = srcElement?.getBoundingClientRect();
setHeroSrc((x) => Object.assign({}, x, { [BOTTOM_SHEET_HERO]: rect }));
const acct = `${inf.username}@${p.account.site}`; const acct = `${inf.username}@${p.account.site}`;
setTootBottomSheetCache(acct, toot); setTootBottomSheetCache(acct, toot);
navigate(`/${encodeURIComponent(acct)}/${toot.id}`, { navigate(`/${encodeURIComponent(acct)}/${toot.id}`, {

View file

@ -13,8 +13,6 @@ import tootStyle from "./toot.module.css";
import MediaViewer from "./MediaViewer"; import MediaViewer from "./MediaViewer";
import { render } from "solid-js/web"; import { render } from "solid-js/web";
import { useWindowSize } from "@solid-primitives/resize-observer"; import { useWindowSize } from "@solid-primitives/resize-observer";
import { useStore } from "@nanostores/solid";
import { $settings } from "../settings/stores";
const MediaAttachmentGrid: Component<{ const MediaAttachmentGrid: Component<{
attachments: mastodon.v1.MediaAttachment[]; attachments: mastodon.v1.MediaAttachment[];
@ -24,7 +22,6 @@ const MediaAttachmentGrid: Component<{
const viewerOpened = () => typeof viewerIndex() !== "undefined"; const viewerOpened = () => typeof viewerIndex() !== "undefined";
const windowSize = useWindowSize(); const windowSize = useWindowSize();
const vh35 = () => Math.floor(windowSize.height * 0.35); const vh35 = () => Math.floor(windowSize.height * 0.35);
const settings = useStore($settings);
createRenderEffect((lastDispose?: () => void) => { createRenderEffect((lastDispose?: () => void) => {
lastDispose?.(); lastDispose?.();
@ -73,11 +70,7 @@ const MediaAttachmentGrid: Component<{
<section <section
ref={rootRef!} ref={rootRef!}
class={[tootStyle.tootAttachmentGrp, "attachments"].join(" ")} class={[tootStyle.tootAttachmentGrp, "attachments"].join(" ")}
onClick={(e) => { onClick={(e) => e.stopImmediatePropagation()}
if (e.target !== e.currentTarget) {
e.stopImmediatePropagation();
}
}}
> >
<For each={props.attachments}> <For each={props.attachments}>
{(item, index) => { {(item, index) => {
@ -116,23 +109,20 @@ const MediaAttachmentGrid: Component<{
src={item.url || undefined} src={item.url || undefined}
style={style()} style={style()}
onLoadedMetadata={[setLoaded, true]} onLoadedMetadata={[setLoaded, true]}
autoplay={settings().autoPlayVideos} autoplay={false}
playsinline={settings().autoPlayVideos ? true : undefined}
controls controls
poster={item.previewUrl}
/> />
); );
case "gifv": case "gifv": // Later we can handle the preview
return ( return (
<video <video
src={item.url || undefined} src={item.url || undefined}
style={style()} style={style()}
onLoadedMetadata={[setLoaded, true]} onLoadedMetadata={[setLoaded, true]}
autoplay={settings().autoPlayGIFs} autoplay={false}
controls controls
playsinline /* or safari on iOS will play in full-screen */ playsinline /* or safari on iOS will play in full-screen */
loop loop
poster={item.previewUrl}
/> />
); );

View file

@ -95,7 +95,7 @@ const PullDownToRefresh: Component<{
const scrollTop = (event.target as HTMLElement).scrollTop; const scrollTop = (event.target as HTMLElement).scrollTop;
if (scrollTop >= 0 && scrollTop < 1) { if (scrollTop >= 0 && scrollTop < 1) {
const d = untrack(pullDown); const d = untrack(pullDown);
if (d > 1) event.preventDefault(); if (event.deltaY <= 0 || d > 0) event.preventDefault();
ds = -(event.deltaY / window.devicePixelRatio / 2); ds = -(event.deltaY / window.devicePixelRatio / 2);
holding = d < stopPos(); holding = d < stopPos();
if (wheelTimeout) { if (wheelTimeout) {
@ -129,6 +129,7 @@ const PullDownToRefresh: Component<{
return; return;
} }
const item = event.targetTouches.item(0)!; const item = event.targetTouches.item(0)!;
if (untrack(pullDown) > 0) event.preventDefault();
if (lastTouchId && item.identifier !== lastTouchId) { if (lastTouchId && item.identifier !== lastTouchId) {
lastTouchId = undefined; lastTouchId = undefined;
lastTouchScreenY = 0; lastTouchScreenY = 0;
@ -139,7 +140,6 @@ const PullDownToRefresh: Component<{
if (lastTouchScreenY !== 0) { if (lastTouchScreenY !== 0) {
ds = item.screenY - lastTouchScreenY; ds = item.screenY - lastTouchScreenY;
} }
if (Math.abs(ds) > 1 && untrack(pullDown) > 1) event.preventDefault();
lastTouchScreenY = item.screenY; lastTouchScreenY = item.screenY;
if (released) { if (released) {
released = false; released = false;

View file

@ -185,6 +185,7 @@ function randomChoose<T extends any[]>(
K: T, K: T,
): T extends Array<infer E> ? E : never { ): T extends Array<infer E> ? E : never {
const idx = Math.floor(rn * K.length); const idx = Math.floor(rn * K.length);
console.log(idx, K.length);
return K[idx]; return K[idx];
} }