TootActionGroup: added share button (closes #18)
All checks were successful
/ depoly (push) Successful in 1m2s
All checks were successful
/ depoly (push) Successful in 1m2s
This commit is contained in:
parent
5eda17d958
commit
591a930bb6
3 changed files with 129 additions and 6 deletions
|
@ -1,13 +1,15 @@
|
||||||
//! This module has side effect.
|
//! This module has side effect.
|
||||||
//! It recommended to include the module by <script> tag.
|
//! It recommended to include the module by <script> tag.
|
||||||
if (!document.body.animate) {
|
if (typeof document.body.animate === "undefined") {
|
||||||
// @ts-ignore: this file is polyfill, no exposed decls
|
// @ts-ignore: this file is polyfill, no exposed decls
|
||||||
import("web-animations-js").then(() => { // all target platforms supported, prepared to remove
|
import("web-animations-js").then(() => {
|
||||||
|
// all target platforms supported, prepared to remove
|
||||||
console.warn("web animation polyfill is included");
|
console.warn("web animation polyfill is included");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window.crypto.randomUUID) { // Chrome/Edge 92+
|
if (typeof window.crypto.randomUUID === "undefined") {
|
||||||
|
// Chrome/Edge 92+
|
||||||
// https://stackoverflow.com/a/2117523/2800218
|
// https://stackoverflow.com/a/2117523/2800218
|
||||||
// LICENSE: https://creativecommons.org/licenses/by-sa/4.0/legalcode
|
// LICENSE: https://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||||
window.crypto.randomUUID =
|
window.crypto.randomUUID =
|
||||||
|
|
104
src/platform/share.tsx
Normal file
104
src/platform/share.tsx
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import {
|
||||||
|
createEffect,
|
||||||
|
createSignal,
|
||||||
|
onCleanup,
|
||||||
|
type Component,
|
||||||
|
} from "solid-js";
|
||||||
|
import Scaffold from "../material/Scaffold";
|
||||||
|
import BottomSheet from "../material/BottomSheet";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
ThemeProvider,
|
||||||
|
Toolbar,
|
||||||
|
} from "@suid/material";
|
||||||
|
import { Close as CloseIcon, ContentCopy } from "@suid/icons-material";
|
||||||
|
import { Title } from "../material/typography";
|
||||||
|
import { render } from "solid-js/web";
|
||||||
|
import { useRootTheme } from "../material/mui";
|
||||||
|
|
||||||
|
const ShareBottomSheet: Component<{
|
||||||
|
data?: ShareData;
|
||||||
|
open?: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}> = (props) => {
|
||||||
|
return (
|
||||||
|
<BottomSheet open={props.open} onClose={props.onClose} bottomUp>
|
||||||
|
<Scaffold
|
||||||
|
topbar={
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton onClick={props.onClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Title>Share...</Title>
|
||||||
|
</Toolbar>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
"padding": "8px 8px calc(var(--safe-area-inset-bottom, 0px) + 40px)",
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={props.data?.url}
|
||||||
|
onChange={() => undefined}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(props.data?.url ?? "");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ContentCopy sx={{ marginTop: "-.25em", marginRight: ".25em" }} />
|
||||||
|
Copy
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Scaffold>
|
||||||
|
</BottomSheet>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function canShare(data?: ShareData) {
|
||||||
|
if (navigator.canShare) {
|
||||||
|
return navigator.canShare(data);
|
||||||
|
}
|
||||||
|
return !!data?.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function share(data?: ShareData): Promise<void> {
|
||||||
|
if (navigator.share) {
|
||||||
|
return await navigator.share(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const element = document.createElement("div");
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
const dispose = render(() => {
|
||||||
|
const [open, setOpen] = createSignal(true);
|
||||||
|
const theme = useRootTheme();
|
||||||
|
onCleanup(() => {
|
||||||
|
element.remove();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!open()) {
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={theme()}>
|
||||||
|
<ShareBottomSheet
|
||||||
|
data={data}
|
||||||
|
open={open()}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
/>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}, element);
|
||||||
|
});
|
||||||
|
}
|
|
@ -27,16 +27,18 @@ import {
|
||||||
StarOutline,
|
StarOutline,
|
||||||
Bookmark,
|
Bookmark,
|
||||||
Reply,
|
Reply,
|
||||||
|
Share,
|
||||||
} from "@suid/icons-material";
|
} from "@suid/icons-material";
|
||||||
import { useTimeSource } from "../platform/timesrc.js";
|
import { useTimeSource } from "../platform/timesrc.js";
|
||||||
import { resolveCustomEmoji } from "../masto/toot.js";
|
import { resolveCustomEmoji } from "../masto/toot.js";
|
||||||
import { Divider } from "@suid/material";
|
import { Divider, IconButton } 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 { FastAverageColor } from "fast-average-color";
|
||||||
import Color from "colorjs.io";
|
import Color from "colorjs.io";
|
||||||
import { useDateFnLocale } from "../platform/i18n";
|
import { useDateFnLocale } from "../platform/i18n";
|
||||||
|
import { canShare, share } from "../platform/share";
|
||||||
|
|
||||||
type TootContentViewProps = {
|
type TootContentViewProps = {
|
||||||
source?: string;
|
source?: string;
|
||||||
|
@ -165,13 +167,26 @@ function TootActionGroup<T extends mastodon.v1.Status>(
|
||||||
>
|
>
|
||||||
{toot().bookmarked ? <Bookmark /> : <BookmarkAddOutlined />}
|
{toot().bookmarked ? <Bookmark /> : <BookmarkAddOutlined />}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Show when={canShare({ url: toot().url ?? undefined })}>
|
||||||
|
<Button
|
||||||
|
class={tootStyle.tootAction}
|
||||||
|
aria-label="Share"
|
||||||
|
onClick={async () => {
|
||||||
|
await share({
|
||||||
|
url: toot().url ?? undefined,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Share />
|
||||||
|
</Button>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
||||||
const toot = () => props.status;
|
const toot = () => props.status;
|
||||||
const dateFnLocale = useDateFnLocale()
|
const dateFnLocale = useDateFnLocale();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={tootStyle.tootAuthorGrp}>
|
<div class={tootStyle.tootAuthorGrp}>
|
||||||
|
@ -189,7 +204,9 @@ function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<time datetime={toot().createdAt}>
|
<time datetime={toot().createdAt}>
|
||||||
{formatRelative(toot().createdAt, props.now, {locale: dateFnLocale()})}
|
{formatRelative(toot().createdAt, props.now, {
|
||||||
|
locale: dateFnLocale(),
|
||||||
|
})}
|
||||||
</time>
|
</time>
|
||||||
<span>
|
<span>
|
||||||
@{toot().account.username}@{new URL(toot().account.url).hostname}
|
@{toot().account.username}@{new URL(toot().account.url).hostname}
|
||||||
|
|
Loading…
Reference in a new issue