TootActionGroup: added share button (closes #18)
This commit is contained in:
parent
6506a60022
commit
161c72fea5
3 changed files with 129 additions and 6 deletions
|
@ -1,13 +1,15 @@
|
|||
//! This module has side effect.
|
||||
//! 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
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
if (!window.crypto.randomUUID) { // Chrome/Edge 92+
|
||||
if (typeof window.crypto.randomUUID === "undefined") {
|
||||
// Chrome/Edge 92+
|
||||
// https://stackoverflow.com/a/2117523/2800218
|
||||
// LICENSE: https://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
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,
|
||||
Bookmark,
|
||||
Reply,
|
||||
Share,
|
||||
} from "@suid/icons-material";
|
||||
import { useTimeSource } from "../platform/timesrc.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 Button from "../material/Button.js";
|
||||
import MediaAttachmentGrid from "./MediaAttachmentGrid.js";
|
||||
import { FastAverageColor } from "fast-average-color";
|
||||
import Color from "colorjs.io";
|
||||
import { useDateFnLocale } from "../platform/i18n";
|
||||
import { canShare, share } from "../platform/share";
|
||||
|
||||
type TootContentViewProps = {
|
||||
source?: string;
|
||||
|
@ -165,13 +167,26 @@ function TootActionGroup<T extends mastodon.v1.Status>(
|
|||
>
|
||||
{toot().bookmarked ? <Bookmark /> : <BookmarkAddOutlined />}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
||||
const toot = () => props.status;
|
||||
const dateFnLocale = useDateFnLocale()
|
||||
const dateFnLocale = useDateFnLocale();
|
||||
|
||||
return (
|
||||
<div class={tootStyle.tootAuthorGrp}>
|
||||
|
@ -189,7 +204,9 @@ function TootAuthorGroup(props: { status: mastodon.v1.Status; now: Date }) {
|
|||
}}
|
||||
/>
|
||||
<time datetime={toot().createdAt}>
|
||||
{formatRelative(toot().createdAt, props.now, {locale: dateFnLocale()})}
|
||||
{formatRelative(toot().createdAt, props.now, {
|
||||
locale: dateFnLocale(),
|
||||
})}
|
||||
</time>
|
||||
<span>
|
||||
@{toot().account.username}@{new URL(toot().account.url).hostname}
|
||||
|
|
Loading…
Reference in a new issue