260 lines
8.7 KiB
TypeScript
260 lines
8.7 KiB
TypeScript
import React from 'react';
|
|
import { Sheet } from '@tamagui/sheet';
|
|
import { YStack, XStack } from '@tamagui/stacks';
|
|
import { SizableText as Text } from '@tamagui/text';
|
|
import { Button } from '@tamagui/button';
|
|
import { Share2, MessageSquare, Copy, X } from 'lucide-react';
|
|
import { useTranslation } from '@/guest/i18n/useTranslation';
|
|
import { useGuestThemeVariant } from '../lib/guestTheme';
|
|
|
|
type ShareSheetProps = {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
photoId?: number | null;
|
|
eventName?: string | null;
|
|
url?: string | null;
|
|
loading?: boolean;
|
|
variant?: 'modal' | 'inline';
|
|
onShareNative: () => void;
|
|
onShareWhatsApp: () => void;
|
|
onShareMessages: () => void;
|
|
onCopyLink: () => void;
|
|
};
|
|
|
|
const WhatsAppIcon = (props: React.SVGProps<SVGSVGElement>) => (
|
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden focusable="false" {...props}>
|
|
<path
|
|
fill="currentColor"
|
|
d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z"
|
|
/>
|
|
</svg>
|
|
);
|
|
|
|
export default function ShareSheet({
|
|
open,
|
|
onOpenChange,
|
|
photoId,
|
|
eventName,
|
|
url,
|
|
loading = false,
|
|
variant = 'modal',
|
|
onShareNative,
|
|
onShareWhatsApp,
|
|
onShareMessages,
|
|
onCopyLink,
|
|
}: ShareSheetProps) {
|
|
const { t } = useTranslation();
|
|
const { isDark } = useGuestThemeVariant();
|
|
const mutedSurface = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.06)';
|
|
const mutedBorder = isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(15, 23, 42, 0.12)';
|
|
const [inlineMounted, setInlineMounted] = React.useState(false);
|
|
const [inlineVisible, setInlineVisible] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
if (variant !== 'inline') return;
|
|
if (open) {
|
|
setInlineMounted(true);
|
|
const raf = window.requestAnimationFrame(() => {
|
|
setInlineVisible(true);
|
|
});
|
|
return () => window.cancelAnimationFrame(raf);
|
|
}
|
|
setInlineVisible(false);
|
|
const timeout = window.setTimeout(() => {
|
|
setInlineMounted(false);
|
|
}, 220);
|
|
return () => window.clearTimeout(timeout);
|
|
}, [open, variant]);
|
|
|
|
const content = (
|
|
<YStack gap="$3">
|
|
<XStack alignItems="center" justifyContent="space-between">
|
|
<YStack gap="$1">
|
|
<Text fontSize="$2" color="$color" opacity={0.7} textTransform="uppercase" letterSpacing={1.2}>
|
|
{t('share.title', 'Shared photo')}
|
|
</Text>
|
|
{photoId ? (
|
|
<Text fontSize="$5" fontWeight="$8">
|
|
#{photoId}
|
|
</Text>
|
|
) : null}
|
|
{eventName ? (
|
|
<Text fontSize="$2" color="$color" opacity={0.7}>
|
|
{eventName}
|
|
</Text>
|
|
) : null}
|
|
</YStack>
|
|
<Button
|
|
size="$3"
|
|
circular
|
|
backgroundColor={mutedSurface}
|
|
borderWidth={1}
|
|
borderColor={mutedBorder}
|
|
onPress={() => onOpenChange(false)}
|
|
aria-label={t('common.actions.close', 'Close')}
|
|
>
|
|
<X size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
|
|
</Button>
|
|
</XStack>
|
|
|
|
<XStack gap="$2" flexWrap="wrap">
|
|
<Button
|
|
flex={1}
|
|
minWidth="45%"
|
|
borderRadius="$card"
|
|
backgroundColor={mutedSurface}
|
|
borderWidth={1}
|
|
borderColor={mutedBorder}
|
|
onPress={onShareNative}
|
|
disabled={loading}
|
|
>
|
|
<XStack alignItems="center" gap="$2">
|
|
<Share2 size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
|
|
<YStack>
|
|
<Text fontSize="$2" fontWeight="$7">
|
|
{t('share.button', 'Share')}
|
|
</Text>
|
|
<Text fontSize="$1" color="$color" opacity={0.7}>
|
|
{t('share.title', 'Shared photo')}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
</Button>
|
|
<Button
|
|
flex={1}
|
|
minWidth="45%"
|
|
borderRadius="$card"
|
|
backgroundColor="#22C55E"
|
|
onPress={onShareWhatsApp}
|
|
disabled={loading}
|
|
>
|
|
<XStack alignItems="center" gap="$2">
|
|
<WhatsAppIcon width={18} height={18} />
|
|
<YStack>
|
|
<Text fontSize="$2" fontWeight="$7" color="#FFFFFF">
|
|
{t('share.whatsapp', 'WhatsApp')}
|
|
</Text>
|
|
<Text fontSize="$1" color="rgba(255,255,255,0.8)">
|
|
{loading ? '...' : ''}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
</Button>
|
|
<Button
|
|
flex={1}
|
|
minWidth="45%"
|
|
borderRadius="$card"
|
|
backgroundColor="#38BDF8"
|
|
onPress={onShareMessages}
|
|
disabled={loading}
|
|
>
|
|
<XStack alignItems="center" gap="$2">
|
|
<MessageSquare size={16} color="#FFFFFF" />
|
|
<YStack>
|
|
<Text fontSize="$2" fontWeight="$7" color="#FFFFFF">
|
|
{t('share.imessage', 'Messages')}
|
|
</Text>
|
|
<Text fontSize="$1" color="rgba(255,255,255,0.8)">
|
|
{loading ? '...' : ''}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
</Button>
|
|
<Button
|
|
flex={1}
|
|
minWidth="45%"
|
|
borderRadius="$card"
|
|
backgroundColor={mutedSurface}
|
|
borderWidth={1}
|
|
borderColor={mutedBorder}
|
|
onPress={onCopyLink}
|
|
disabled={loading}
|
|
>
|
|
<XStack alignItems="center" gap="$2">
|
|
<Copy size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
|
|
<YStack>
|
|
<Text fontSize="$2" fontWeight="$7">
|
|
{t('share.copyLink', 'Copy link')}
|
|
</Text>
|
|
<Text fontSize="$1" color="$color" opacity={0.7}>
|
|
{loading ? t('share.loading', 'Loading...') : ''}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
</Button>
|
|
</XStack>
|
|
|
|
{url ? (
|
|
<Text fontSize="$1" color="$color" opacity={0.7} numberOfLines={1}>
|
|
{url}
|
|
</Text>
|
|
) : null}
|
|
</YStack>
|
|
);
|
|
|
|
if (variant === 'inline') {
|
|
if (!inlineMounted) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<YStack
|
|
position="absolute"
|
|
inset={0}
|
|
zIndex={20}
|
|
justifyContent="flex-end"
|
|
pointerEvents={open ? 'auto' : 'none'}
|
|
>
|
|
<Button
|
|
unstyled
|
|
onPress={() => onOpenChange(false)}
|
|
style={{
|
|
position: 'absolute',
|
|
inset: 0,
|
|
backgroundColor: isDark ? 'rgba(15, 23, 42, 0.5)' : 'rgba(15, 23, 42, 0.35)',
|
|
opacity: inlineVisible ? 1 : 0,
|
|
transition: 'opacity 200ms ease',
|
|
}}
|
|
aria-label={t('common.actions.close', 'Close')}
|
|
/>
|
|
<YStack
|
|
padding="$4"
|
|
backgroundColor="$surface"
|
|
borderTopLeftRadius="$6"
|
|
borderTopRightRadius="$6"
|
|
style={{
|
|
transform: inlineVisible ? 'translateY(0)' : 'translateY(100%)',
|
|
transition: 'transform 220ms cubic-bezier(0.22, 1, 0.36, 1)',
|
|
}}
|
|
>
|
|
<YStack
|
|
height={5}
|
|
width={52}
|
|
backgroundColor="#CBD5E1"
|
|
borderRadius={999}
|
|
alignSelf="center"
|
|
marginBottom="$3"
|
|
/>
|
|
{content}
|
|
</YStack>
|
|
</YStack>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Sheet
|
|
open={open}
|
|
onOpenChange={onOpenChange}
|
|
snapPoints={[60]}
|
|
position={open ? 0 : -1}
|
|
modal
|
|
>
|
|
<Sheet.Overlay {...({ backgroundColor: isDark ? 'rgba(15, 23, 42, 0.6)' : 'rgba(15, 23, 42, 0.3)' } as any)} />
|
|
<Sheet.Frame padding="$4" backgroundColor="$surface" borderTopLeftRadius="$6" borderTopRightRadius="$6">
|
|
<Sheet.Handle height={5} width={52} backgroundColor="#CBD5E1" borderRadius={999} marginBottom="$3" />
|
|
{content}
|
|
</Sheet.Frame>
|
|
</Sheet>
|
|
);
|
|
}
|