add native browser sharing (#1255)

This commit is contained in:
Athou
2024-04-01 18:36:12 +02:00
parent 3964977a0a
commit 16199c5b54
2 changed files with 84 additions and 38 deletions

View File

@@ -14,14 +14,11 @@ interface FeedEntryFooterProps {
} }
export function FeedEntryFooter(props: FeedEntryFooterProps) { export function FeedEntryFooter(props: FeedEntryFooterProps) {
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
const tags = useAppSelector(state => state.user.tags) const tags = useAppSelector(state => state.user.tags)
const mobile = useMobile() const mobile = useMobile()
const { spacing } = useActionButton() const { spacing } = useActionButton()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const showSharingButtons = sharingSettings && Object.values(sharingSettings).some(v => v)
const readStatusButtonClicked = async () => const readStatusButtonClicked = async () =>
await dispatch( await dispatch(
markEntry({ markEntry({
@@ -60,7 +57,6 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
} }
/> />
{showSharingButtons && (
<Popover withArrow withinPortal shadow="md" closeOnClickOutside={!mobile}> <Popover withArrow withinPortal shadow="md" closeOnClickOutside={!mobile}>
<Popover.Target> <Popover.Target>
<ActionButton icon={<TbShare size={18} />} label={<Trans>Share</Trans>} /> <ActionButton icon={<TbShare size={18} />} label={<Trans>Share</Trans>} />
@@ -69,7 +65,6 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
<ShareButtons url={props.entry.url} description={props.entry.title} /> <ShareButtons url={props.entry.url} description={props.entry.title} />
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover>
)}
{tags && ( {tags && (
<Popover withArrow shadow="md" closeOnClickOutside={!mobile}> <Popover withArrow shadow="md" closeOnClickOutside={!mobile}>

View File

@@ -1,8 +1,12 @@
import { ActionIcon, Box, SimpleGrid } from "@mantine/core" import { Trans } from "@lingui/macro"
import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "app/constants"
import { useAppSelector } from "app/store" import { useAppSelector } from "app/store"
import { type SharingSettings } from "app/types" import { type SharingSettings } from "app/types"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import { type IconType } from "react-icons" import { type IconType } from "react-icons"
import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb"
import { tss } from "tss" import { tss } from "tss"
type Color = `#${string}` type Color = `#${string}`
@@ -12,45 +16,88 @@ const useStyles = tss
color: Color color: Color
}>() }>()
.create(({ theme, colorScheme, color }) => ({ .create(({ theme, colorScheme, color }) => ({
socialIcon: { icon: {
color, color,
backgroundColor: colorScheme === "dark" ? theme.colors.gray[2] : "white", backgroundColor: colorScheme === "dark" ? theme.colors.gray[2] : "white",
borderRadius: "50%",
}, },
})) }))
function ShareButton({ url, icon, color }: { url: string; icon: IconType; color: Color }) { function ShareButton({ icon, color, onClick }: { icon: IconType; color: Color; onClick: () => void }) {
const { classes } = useStyles({ const { classes } = useStyles({
color, color,
}) })
const onClick = (e: React.MouseEvent) => { return (
e.preventDefault() <ActionIcon variant="transparent" radius="xl" size={32}>
<Box p={6} className={classes.icon} onClick={onClick}>
{icon({ size: 18 })}
</Box>
</ActionIcon>
)
}
function SiteShareButton({ url, icon, color }: { icon: IconType; color: Color; url: string }) {
const onClick = () => {
window.open(url, "", "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=800,height=600") window.open(url, "", "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=800,height=600")
} }
return <ShareButton icon={icon} color={color} onClick={onClick} />
}
function CopyUrlButton({ url }: { url: string }) {
return ( return (
<ActionIcon variant="transparent"> <CopyButton value={url}>
<a href={url} target="_blank" rel="noreferrer" onClick={onClick}> {({ copied, copy }) => <ShareButton icon={copied ? TbCheck : TbCopy} color="#000" onClick={copy} />}
<Box p={6} className={classes.socialIcon}> </CopyButton>
{icon({ size: 18 })} )
</Box> }
</a>
</ActionIcon> function BrowserNativeShareButton({ url, description }: { url: string; description: string }) {
const mobile = useMobile()
const { isBrowserExtensionPopup } = useBrowserExtension()
const onClick = () => {
navigator.share({
title: description,
url,
})
}
return (
<ShareButton
icon={mobile && !isBrowserExtensionPopup ? TbDeviceMobileShare : TbDeviceDesktopShare}
color="#000"
onClick={onClick}
/>
) )
} }
export function ShareButtons(props: { url: string; description: string }) { export function ShareButtons(props: { url: string; description: string }) {
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
const enabledSharingSites = (Object.keys(Constants.sharing) as (keyof SharingSettings)[]).filter(site => sharingSettings?.[site])
const url = encodeURIComponent(props.url) const url = encodeURIComponent(props.url)
const desc = encodeURIComponent(props.description) const desc = encodeURIComponent(props.description)
const clipboardAvailable = typeof navigator.clipboard !== "undefined"
const nativeSharingAvailable = typeof navigator.share !== "undefined"
const showNativeSection = clipboardAvailable || nativeSharingAvailable
const showSharingSites = enabledSharingSites.length > 0
const showDivider = showNativeSection && showSharingSites
const showNoSharingOptionsAvailable = !showNativeSection && !showSharingSites
return ( return (
<>
{showNativeSection && (
<SimpleGrid cols={4}> <SimpleGrid cols={4}>
{(Object.keys(Constants.sharing) as (keyof SharingSettings)[]) {clipboardAvailable && <CopyUrlButton url={props.url} />}
.filter(site => sharingSettings?.[site]) {nativeSharingAvailable && <BrowserNativeShareButton url={props.url} description={props.description} />}
.map(site => ( </SimpleGrid>
<ShareButton )}
{showDivider && <Divider my="xs" />}
{showSharingSites && (
<SimpleGrid cols={4}>
{enabledSharingSites.map(site => (
<SiteShareButton
key={site} key={site}
icon={Constants.sharing[site].icon} icon={Constants.sharing[site].icon}
color={Constants.sharing[site].color} color={Constants.sharing[site].color}
@@ -58,5 +105,9 @@ export function ShareButtons(props: { url: string; description: string }) {
/> />
))} ))}
</SimpleGrid> </SimpleGrid>
)}
{showNoSharingOptionsAvailable && <Trans>No sharing options available.</Trans>}
</>
) )
} }