1import { Platform, Pressable, View, type StyleProp, type ViewStyle } from "react-native";
2
3import { cn } from "../lib/cn";
4import type { DraftComposerFileAttachment } from "../lib/composerImages";
5import { useMediaActions } from "../state/mediaActions";
6import { type MediaActionsSource } from "../lib/mediaActionsSource";
7import { SymbolView } from "./AppSymbol";
8import { AppText } from "./AppText";
9import { MediaActionsMenu } from "./MediaActionsMenu";
10import { PresentationSource } from "./NativePresentation";
11import { VideoThumbnailImage } from "./VideoThumbnailImage";
12
13export function VideoAttachmentTile(props: {
14 readonly name: string;
15 readonly sourceIdentifier: string;
16 readonly thumbnailSource: string | DraftComposerFileAttachment | null;
17 readonly actionsSource?: MediaActionsSource;
18 readonly compact?: boolean;
19 readonly onPress: (sourceIdentifier: string) => void;
20 readonly disabled?: boolean;
21 readonly className?: string;
22 readonly style?: StyleProp<ViewStyle>;
23}) {
24 const mediaActions = useMediaActions(props.disabled ? undefined : props.actionsSource);
25 const hasActions = mediaActions.actions.length > 0;
26 return (
27 <PresentationSource
28 identifier={props.sourceIdentifier}
29 accessible={Platform.OS === "ios"}
30 accessibilityRole="button"
31 accessibilityLabel={`Play ${props.name}`}
32 accessibilityState={{ disabled: props.disabled ?? false }}
33 onAccessibilityTap={() => {
34 if (!props.disabled) props.onPress(props.sourceIdentifier);
35 }}
36 accessibilityActions={mediaActions.actions.map(({ id, title }) => ({
37 name: id,
38 label: title,
39 }))}
40 onAccessibilityAction={({ nativeEvent }) => {
41 if (props.disabled) return;
42 mediaActions.actions.find(({ id }) => id === nativeEvent.actionName)?.run();
43 }}
44 >
45 <MediaActionsMenu media={mediaActions}>
46 <Pressable
47 accessibilityRole="button"
48 accessibilityLabel={`Play ${props.name}`}
49 accessibilityHint={hasActions ? "Touch and hold for media actions" : undefined}
50 accessibilityState={{ disabled: props.disabled ?? false }}
51 disabled={props.disabled}
52 onPress={() => props.onPress(props.sourceIdentifier)}
53 className={cn("items-center justify-center overflow-hidden bg-black/80", props.className)}
54 style={props.style}
55 >
56 <VideoThumbnailImage cacheKey={props.sourceIdentifier} source={props.thumbnailSource} />
57 <View
58 className={cn(
59 "items-center justify-center rounded-full bg-black/45",
60 props.compact ? "size-6" : "size-12",
61 )}
62 >
63 <SymbolView
64 name="play"
65 size={props.compact ? 15 : 24}
66 tintColor="#ffffff"
67 type="monochrome"
68 />
69 </View>
70 {!props.compact ? (
71 <View className="absolute inset-x-0 bottom-0 bg-black/55 px-2 py-1.5">
72 <AppText className="text-center text-xs text-white" numberOfLines={1}>
73 {props.name}
74 </AppText>
75 </View>
76 ) : null}
77 </Pressable>
78 </MediaActionsMenu>
79 </PresentationSource>
80 );
81}