@planningcenter/chat-react-native 3.44.1-qa-875.0 → 3.44.1-qa-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/build/components/conversation/attachments/giphy_attachment.js +2 -2
  2. package/build/components/conversation/attachments/giphy_attachment.js.map +1 -1
  3. package/build/components/conversation/attachments/image_attachment.d.ts.map +1 -1
  4. package/build/components/conversation/attachments/image_attachment.js +14 -15
  5. package/build/components/conversation/attachments/image_attachment.js.map +1 -1
  6. package/build/components/conversation/attachments/video_attachment.d.ts.map +1 -1
  7. package/build/components/conversation/attachments/video_attachment.js +1 -2
  8. package/build/components/conversation/attachments/video_attachment.js.map +1 -1
  9. package/build/components/conversation/message.d.ts.map +1 -1
  10. package/build/components/conversation/message.js +3 -1
  11. package/build/components/conversation/message.js.map +1 -1
  12. package/build/components/conversation/message_form.d.ts.map +1 -1
  13. package/build/components/conversation/message_form.js +5 -3
  14. package/build/components/conversation/message_form.js.map +1 -1
  15. package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
  16. package/build/components/conversation/reply_shadow_message.js +5 -3
  17. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  18. package/build/components/display/badge.js +1 -3
  19. package/build/components/display/badge.js.map +1 -1
  20. package/build/hooks/use_conversation_messages.d.ts +0 -1
  21. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  22. package/build/hooks/use_conversation_messages.js +2 -10
  23. package/build/hooks/use_conversation_messages.js.map +1 -1
  24. package/build/screens/conversation_screen.d.ts.map +1 -1
  25. package/build/screens/conversation_screen.js +3 -10
  26. package/build/screens/conversation_screen.js.map +1 -1
  27. package/build/screens/message_report_screen.d.ts.map +1 -1
  28. package/build/screens/message_report_screen.js +2 -4
  29. package/build/screens/message_report_screen.js.map +1 -1
  30. package/build/utils/index.d.ts +0 -1
  31. package/build/utils/index.d.ts.map +1 -1
  32. package/build/utils/index.js +0 -1
  33. package/build/utils/index.js.map +1 -1
  34. package/build/utils/message_text.d.ts +3 -0
  35. package/build/utils/message_text.d.ts.map +1 -0
  36. package/build/utils/message_text.js +8 -0
  37. package/build/utils/message_text.js.map +1 -0
  38. package/package.json +3 -3
  39. package/src/components/conversation/attachments/giphy_attachment.tsx +2 -2
  40. package/src/components/conversation/attachments/image_attachment.tsx +26 -30
  41. package/src/components/conversation/attachments/video_attachment.tsx +1 -2
  42. package/src/components/conversation/message.tsx +3 -1
  43. package/src/components/conversation/message_form.tsx +6 -3
  44. package/src/components/conversation/reply_shadow_message.tsx +5 -3
  45. package/src/components/display/badge.tsx +1 -3
  46. package/src/hooks/use_conversation_messages.ts +2 -10
  47. package/src/screens/conversation_screen.tsx +3 -10
  48. package/src/screens/message_report_screen.tsx +6 -4
  49. package/src/utils/__tests__/message_text.test.ts +45 -0
  50. package/src/utils/index.ts +0 -1
  51. package/src/utils/message_text.ts +9 -0
  52. package/build/utils/aspect_ratio.d.ts +0 -2
  53. package/build/utils/aspect_ratio.d.ts.map +0 -1
  54. package/build/utils/aspect_ratio.js +0 -10
  55. package/build/utils/aspect_ratio.js.map +0 -1
  56. package/src/utils/__tests__/aspect_ratio.test.ts +0 -50
  57. package/src/utils/aspect_ratio.ts +0 -13
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { PlatformPressable } from '@react-navigation/elements';
3
3
  import { Text, StyleSheet, Linking } from 'react-native';
4
4
  import { useTheme } from '../../../hooks';
5
- import { assertKeysAreNumbers, safeAspectRatio } from '../../../utils';
5
+ import { assertKeysAreNumbers } from '../../../utils';
6
6
  import { tokens } from '../../../vendor/tapestry/tokens';
7
7
  import { Image } from '../../display';
8
8
  export function GiphyAttachment({ attachment, onMessageLongPress, }) {
@@ -27,7 +27,7 @@ const useStyles = ({ imageWidth, imageHeight }) => {
27
27
  },
28
28
  imageWrapper: {
29
29
  width: '100%',
30
- aspectRatio: safeAspectRatio(imageWidth, imageHeight),
30
+ aspectRatio: imageWidth / imageHeight,
31
31
  },
32
32
  image: {
33
33
  borderRadius: 8,
@@ -1 +1 @@
1
- {"version":3,"file":"giphy_attachment.js","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/giphy_attachment.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAE9D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAErC,MAAM,UAAU,eAAe,CAAC,EAC9B,UAAU,EACV,kBAAkB,GAInB;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC7B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,UAAU,CAAA;IAC9C,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,UAAU,CAAA;IAChC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAA;IAEpE,SAAS,WAAW;QAClB,IAAI,SAAS,EAAE,CAAC;YACd,6CAA6C;YAC7C,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,CACL,MAAC,iBAAiB,IAChB,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EACvB,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,oBAAoB,EAAE,UAAU,EAAE,IAAI,EAAE,aAExE,KAAC,KAAK,IACJ,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EACpB,YAAY,EAAE,MAAM,CAAC,YAAY,EACjC,KAAK,EAAE,MAAM,CAAC,KAAK,EACnB,GAAG,EAAE,KAAK,GACV,EACF,MAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,IAAI,aACtB,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,GAAG,uBAAe,OAAE,KAAK,IACxC,IACW,CACrB,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,EAAE,UAAU,EAAE,WAAW,EAA+C,EAAE,EAAE;IAC7F,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC7B,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE;YACT,GAAG,EAAE,CAAC;SACP;QACD,YAAY,EAAE;YACZ,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,eAAe,CAAC,UAAU,EAAE,WAAW,CAAC;SACtD;QACD,KAAK,EAAE;YACL,YAAY,EAAE,CAAC;SAChB;QACD,IAAI,EAAE;YACJ,QAAQ,EAAE,MAAM,CAAC,UAAU;YAC3B,KAAK,EAAE,MAAM,CAAC,WAAW;YACzB,iBAAiB,EAAE,CAAC;YACpB,eAAe,EAAE,CAAC;SACnB;QACD,GAAG,EAAE;YACH,UAAU,EAAE,MAAM;SACnB;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport React from 'react'\nimport { Text, StyleSheet, Linking } from 'react-native'\nimport { useTheme } from '../../../hooks'\nimport { DenormalizedGiphyAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'\nimport { assertKeysAreNumbers, safeAspectRatio } from '../../../utils'\nimport { tokens } from '../../../vendor/tapestry/tokens'\nimport { Image } from '../../display'\n\nexport function GiphyAttachment({\n attachment,\n onMessageLongPress,\n}: {\n attachment: DenormalizedGiphyAttachmentResource\n onMessageLongPress: () => void\n}) {\n const { colors } = useTheme()\n const { title, titleLink, giphy } = attachment\n const { url } = giphy.fixedWidth\n const { width, height } = assertKeysAreNumbers(giphy.fixedWidth)\n const styles = useStyles({ imageWidth: width, imageHeight: height })\n\n function handlePress() {\n if (titleLink) {\n // Open Giphy link in the default web browser\n Linking.openURL(titleLink)\n }\n }\n\n return (\n <PlatformPressable\n onPress={handlePress}\n onLongPress={onMessageLongPress}\n style={styles.container}\n android_ripple={{ color: colors.androidRippleNeutral, foreground: true }}\n >\n <Image\n source={{ uri: url }}\n wrapperStyle={styles.imageWrapper}\n style={styles.image}\n alt={title}\n />\n <Text style={styles.link}>\n <Text style={styles.tag}>/giphy</Text> {title}\n </Text>\n </PlatformPressable>\n )\n}\n\nconst useStyles = ({ imageWidth, imageHeight }: { imageWidth: number; imageHeight: number }) => {\n const { colors } = useTheme()\n return StyleSheet.create({\n container: {\n gap: 4,\n },\n imageWrapper: {\n width: '100%',\n aspectRatio: safeAspectRatio(imageWidth, imageHeight),\n },\n image: {\n borderRadius: 8,\n },\n link: {\n fontSize: tokens.fontSizeSm,\n color: colors.interaction,\n paddingHorizontal: 8,\n paddingVertical: 6,\n },\n tag: {\n fontWeight: 'bold',\n },\n })\n}\n"]}
1
+ {"version":3,"file":"giphy_attachment.js","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/giphy_attachment.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAE9D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAA;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAErC,MAAM,UAAU,eAAe,CAAC,EAC9B,UAAU,EACV,kBAAkB,GAInB;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC7B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,UAAU,CAAA;IAC9C,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,UAAU,CAAA;IAChC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAA;IAEpE,SAAS,WAAW;QAClB,IAAI,SAAS,EAAE,CAAC;YACd,6CAA6C;YAC7C,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,CACL,MAAC,iBAAiB,IAChB,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EACvB,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,oBAAoB,EAAE,UAAU,EAAE,IAAI,EAAE,aAExE,KAAC,KAAK,IACJ,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EACpB,YAAY,EAAE,MAAM,CAAC,YAAY,EACjC,KAAK,EAAE,MAAM,CAAC,KAAK,EACnB,GAAG,EAAE,KAAK,GACV,EACF,MAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,IAAI,aACtB,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,GAAG,uBAAe,OAAE,KAAK,IACxC,IACW,CACrB,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,EAAE,UAAU,EAAE,WAAW,EAA+C,EAAE,EAAE;IAC7F,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC7B,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE;YACT,GAAG,EAAE,CAAC;SACP;QACD,YAAY,EAAE;YACZ,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,UAAU,GAAG,WAAW;SACtC;QACD,KAAK,EAAE;YACL,YAAY,EAAE,CAAC;SAChB;QACD,IAAI,EAAE;YACJ,QAAQ,EAAE,MAAM,CAAC,UAAU;YAC3B,KAAK,EAAE,MAAM,CAAC,WAAW;YACzB,iBAAiB,EAAE,CAAC;YACpB,eAAe,EAAE,CAAC;SACnB;QACD,GAAG,EAAE;YACH,UAAU,EAAE,MAAM;SACnB;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport React from 'react'\nimport { Text, StyleSheet, Linking } from 'react-native'\nimport { useTheme } from '../../../hooks'\nimport { DenormalizedGiphyAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'\nimport { assertKeysAreNumbers } from '../../../utils'\nimport { tokens } from '../../../vendor/tapestry/tokens'\nimport { Image } from '../../display'\n\nexport function GiphyAttachment({\n attachment,\n onMessageLongPress,\n}: {\n attachment: DenormalizedGiphyAttachmentResource\n onMessageLongPress: () => void\n}) {\n const { colors } = useTheme()\n const { title, titleLink, giphy } = attachment\n const { url } = giphy.fixedWidth\n const { width, height } = assertKeysAreNumbers(giphy.fixedWidth)\n const styles = useStyles({ imageWidth: width, imageHeight: height })\n\n function handlePress() {\n if (titleLink) {\n // Open Giphy link in the default web browser\n Linking.openURL(titleLink)\n }\n }\n\n return (\n <PlatformPressable\n onPress={handlePress}\n onLongPress={onMessageLongPress}\n style={styles.container}\n android_ripple={{ color: colors.androidRippleNeutral, foreground: true }}\n >\n <Image\n source={{ uri: url }}\n wrapperStyle={styles.imageWrapper}\n style={styles.image}\n alt={title}\n />\n <Text style={styles.link}>\n <Text style={styles.tag}>/giphy</Text> {title}\n </Text>\n </PlatformPressable>\n )\n}\n\nconst useStyles = ({ imageWidth, imageHeight }: { imageWidth: number; imageHeight: number }) => {\n const { colors } = useTheme()\n return StyleSheet.create({\n container: {\n gap: 4,\n },\n imageWrapper: {\n width: '100%',\n aspectRatio: imageWidth / imageHeight,\n },\n image: {\n borderRadius: 8,\n },\n link: {\n fontSize: tokens.fontSizeSm,\n color: colors.interaction,\n paddingHorizontal: 8,\n paddingVertical: 6,\n },\n tag: {\n fontWeight: 'bold',\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"image_attachment.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/image_attachment.tsx"],"names":[],"mappings":"AA+BA,OAAO,EAAE,qCAAqC,EAAE,MAAM,2DAA2D,CAAA;AAwBjH,MAAM,MAAM,SAAS,GAAG;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,4BAA4B,EAC5B,UAAU,GACX,EAAE;IACD,UAAU,EAAE,qCAAqC,CAAA;IACjD,gBAAgB,EAAE,qCAAqC,EAAE,CAAA;IACzD,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,SAAS,CAAA;IACpB,4BAA4B,EAAE,CAAC,UAAU,EAAE,qCAAqC,KAAK,IAAI,CAAA;IACzF,UAAU,EAAE,OAAO,CAAA;CACpB,2CA6CA"}
1
+ {"version":3,"file":"image_attachment.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/image_attachment.tsx"],"names":[],"mappings":"AA+BA,OAAO,EAAE,qCAAqC,EAAE,MAAM,2DAA2D,CAAA;AAyBjH,MAAM,MAAM,SAAS,GAAG;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,4BAA4B,EAC5B,UAAU,GACX,EAAE;IACD,UAAU,EAAE,qCAAqC,CAAA;IACjD,gBAAgB,EAAE,qCAAqC,EAAE,CAAA;IACzD,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,EAAE,SAAS,CAAA;IACpB,4BAA4B,EAAE,CAAC,UAAU,EAAE,qCAAqC,KAAK,IAAI,CAAA;IACzF,UAAU,EAAE,OAAO,CAAA;CACpB,2CA6CA"}
@@ -2,16 +2,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { PlatformPressable } from '@react-navigation/elements';
3
3
  import colorFunction from 'color';
4
4
  import { useMemo, useState, useCallback, useRef } from 'react';
5
- import { StatusBar, StyleSheet, Modal, View, Linking, useWindowDimensions, } from 'react-native';
5
+ import { StatusBar, StyleSheet, Modal, View, Linking, Dimensions, } from 'react-native';
6
6
  import { FlatList, Gesture, GestureDetector, GestureHandlerRootView, } from 'react-native-gesture-handler';
7
7
  import Animated, { runOnJS, useAnimatedStyle, useAnimatedReaction, useSharedValue, withSpring, withDecay, } from 'react-native-reanimated';
8
8
  import { useSafeAreaInsets, SafeAreaProvider } from 'react-native-safe-area-context';
9
9
  import { useTheme } from '../../../hooks';
10
- import { Haptic, platformFontWeightMedium, safeAspectRatio } from '../../../utils';
10
+ import { Haptic, platformFontWeightMedium } from '../../../utils';
11
11
  import { formatDatePreview } from '../../../utils/date';
12
12
  import { tokens } from '../../../vendor/tapestry/tokens';
13
13
  import { IconButton, Image, Heading, Text } from '../../display';
14
14
  import { AttachmentDeletingOverlay } from './attachment_deleting_overlay';
15
+ const { width: WINDOW_WIDTH, height: WINDOW_HEIGHT } = Dimensions.get('window');
15
16
  const DISMISS_PAN_THRESHOLD = 175;
16
17
  const MIN_DISTANCE_FOR_PAN = 10; // Higher threshold gives pinching priority
17
18
  const SINGLE_FINGER_POINTER = 1; // Single-finger panning / tapping helps to avoid conflicts with pinching
@@ -53,7 +54,6 @@ const LightboxModal = ({ visible, setModalVisible, imageAttachments, initialImag
53
54
  const LightboxGallery = ({ imageAttachments, initialImageIndex, metaProps, onRequestClose, }) => {
54
55
  const styles = useStyles();
55
56
  const insets = useSafeAreaInsets();
56
- const { width: windowWidth, height: windowHeight } = useWindowDimensions();
57
57
  const [currentImageIndex, setCurrentImageIndex] = useState(initialImageIndex);
58
58
  const { soft: triggerMaxZoomHaptic, rigid: triggerDismissHaptic } = Haptic;
59
59
  // Get current image data
@@ -62,8 +62,8 @@ const LightboxGallery = ({ imageAttachments, initialImageIndex, metaProps, onReq
62
62
  const imageWidth = metadata.width;
63
63
  const imageHeight = metadata.height;
64
64
  // Calculate available space for image display
65
- const availableWindowWidth = windowWidth;
66
- const availableWindowHeight = windowHeight - insets.top - insets.bottom;
65
+ const availableWindowWidth = WINDOW_WIDTH;
66
+ const availableWindowHeight = WINDOW_HEIGHT - insets.top - insets.bottom;
67
67
  /* ============================
68
68
  ANIMATION VALUES
69
69
  ============================ */
@@ -152,8 +152,8 @@ const LightboxGallery = ({ imageAttachments, initialImageIndex, metaProps, onReq
152
152
  const zoomToFocalPoint = ({ focalPointX, focalPointY, targetScale, currentSavedScale, currentSavedTranslateX, currentSavedTranslateY, }) => {
153
153
  'worklet';
154
154
  // How far the focal point is from the center of the window
155
- const windowCenterX = windowWidth / 2;
156
- const windowCenterY = windowHeight / 2;
155
+ const windowCenterX = WINDOW_WIDTH / 2;
156
+ const windowCenterY = WINDOW_HEIGHT / 2;
157
157
  // Position of focal point relative to current image position
158
158
  const focalRelativeX = focalPointX - windowCenterX - currentSavedTranslateX;
159
159
  const focalRelativeY = focalPointY - windowCenterY - currentSavedTranslateY;
@@ -221,23 +221,23 @@ const LightboxGallery = ({ imageAttachments, initialImageIndex, metaProps, onReq
221
221
  stableImageAttachmentsLengthRef.current = imageAttachments.length;
222
222
  // Used in tandem with FlatList's initialScrollIndex to quickly calculate the position and size of each image before they load.
223
223
  const getItemLayout = useCallback((_, index) => ({
224
- length: windowWidth,
225
- offset: windowWidth * index,
224
+ length: WINDOW_WIDTH,
225
+ offset: WINDOW_WIDTH * index,
226
226
  index,
227
- }), [windowWidth]);
227
+ }), []);
228
228
  // Captures the current image's index after the FlatList finishes its scroll animation.
229
229
  // Used in tandem with onViewableItemsChanged to ensure the final value for currentImageIndex is set.
230
230
  const onMomentumScrollEnd = useCallback((event) => {
231
231
  // Calculate the index of the image that is currently visible
232
232
  const imageOffsetX = event.nativeEvent.contentOffset.x;
233
- const newImageIndex = Math.round(imageOffsetX / windowWidth);
233
+ const newImageIndex = Math.round(imageOffsetX / WINDOW_WIDTH);
234
234
  // Check if the image index has changed and the FlatList didn't scroll past the first or last image
235
235
  const didImageIndexChange = newImageIndex !== stableCurrentImageIndexRef.current;
236
236
  const isImageIndexWithinBounds = newImageIndex >= 0 && newImageIndex < stableImageAttachmentsLengthRef.current;
237
237
  if (didImageIndexChange && isImageIndexWithinBounds) {
238
238
  setCurrentImageIndex(newImageIndex);
239
239
  }
240
- }, [windowWidth]);
240
+ }, []);
241
241
  // Supplements onMomentumScrollEnd by capturing the current image's index while the FlatList is actively scrolling.
242
242
  // Used in tandem with viewabilityConfig to trigger when the image is 50% visible in the window.
243
243
  const onViewableItemsChanged = useCallback(({ viewableItems }) => {
@@ -516,7 +516,6 @@ const LightboxToolbarFooter = ({ lightboxToolbarVisible, totalImages, currentIma
516
516
  };
517
517
  const useStyles = ({ imageWidth = 100, imageHeight = 100 } = {}) => {
518
518
  const { top, bottom } = useSafeAreaInsets();
519
- const { width: windowWidth } = useWindowDimensions();
520
519
  const backgroundColor = tokens.colorNeutral7;
521
520
  const transparentBackgroundColor = useMemo(() => colorFunction(backgroundColor).alpha(0.8).toString(), [backgroundColor]);
522
521
  return StyleSheet.create({
@@ -530,7 +529,7 @@ const useStyles = ({ imageWidth = 100, imageHeight = 100 } = {}) => {
530
529
  attachmentImageWrapper: {
531
530
  width: '100%',
532
531
  minWidth: 200,
533
- aspectRatio: safeAspectRatio(imageWidth, imageHeight),
532
+ aspectRatio: imageWidth / imageHeight,
534
533
  },
535
534
  image: {
536
535
  borderRadius: 8,
@@ -544,7 +543,7 @@ const useStyles = ({ imageWidth = 100, imageHeight = 100 } = {}) => {
544
543
  },
545
544
  gestureImage: {
546
545
  height: '100%',
547
- width: windowWidth,
546
+ width: WINDOW_WIDTH,
548
547
  backgroundColor: 'transparent',
549
548
  },
550
549
  gestureImageLoading: {
@@ -1 +1 @@
1
- {"version":3,"file":"image_attachment.js","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/image_attachment.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport colorFunction from 'color'\nimport React, { useMemo, useState, Dispatch, SetStateAction, useCallback, useRef } from 'react'\nimport {\n StatusBar,\n StyleSheet,\n Modal,\n View,\n Linking,\n NativeSyntheticEvent,\n NativeScrollEvent,\n useWindowDimensions,\n ViewToken,\n} from 'react-native'\nimport {\n FlatList,\n Gesture,\n GestureDetector,\n GestureHandlerRootView,\n} from 'react-native-gesture-handler'\nimport Animated, {\n runOnJS,\n useAnimatedStyle,\n useAnimatedReaction,\n useSharedValue,\n withSpring,\n withDecay,\n SharedValue,\n} from 'react-native-reanimated'\nimport { useSafeAreaInsets, SafeAreaProvider } from 'react-native-safe-area-context'\nimport { useTheme } from '../../../hooks'\nimport { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'\nimport { Haptic, platformFontWeightMedium, safeAspectRatio } from '../../../utils'\nimport { formatDatePreview } from '../../../utils/date'\nimport { tokens } from '../../../vendor/tapestry/tokens'\nimport { IconButton, Image, Heading, Text } from '../../display'\nimport { AttachmentDeletingOverlay } from './attachment_deleting_overlay'\n\nconst DISMISS_PAN_THRESHOLD = 175\nconst MIN_DISTANCE_FOR_PAN = 10 // Higher threshold gives pinching priority\nconst SINGLE_FINGER_POINTER = 1 // Single-finger panning / tapping helps to avoid conflicts with pinching\nconst DEFAULT_OPACITY = 1\nconst DEFAULT_TRANSLATE_X = 0\nconst DEFAULT_TRANSLATE_Y = 0\nconst DEFAULT_SCALE = 1\nconst MIN_SCALE = 0.5\nconst MAX_SCALE = 5\nconst MAX_SCALE_OVERSHOOT = 5\nconst DOUBLE_TAP_ZOOM_SCALE = 2\nconst DECAY_VELOCITY_FACTOR = 0.4\nconst RESET_SPRING_CONFIG = {\n damping: 20,\n stiffness: 150,\n}\n\nexport type MetaProps = {\n authorName: string\n createdAt: string\n}\n\nexport function ImageAttachment({\n attachment,\n imageAttachments,\n currentImageIndex,\n metaProps,\n onMessageAttachmentLongPress,\n isDeleting,\n}: {\n attachment: DenormalizedMessageAttachmentResource\n imageAttachments: DenormalizedMessageAttachmentResource[]\n currentImageIndex: number\n metaProps: MetaProps\n onMessageAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void\n isDeleting: boolean\n}) {\n const { attributes } = attachment\n const { url, urlMedium, filename, metadata = {} } = attributes\n const { colors } = useTheme()\n\n const styles = useStyles({ imageWidth: metadata.width, imageHeight: metadata.height })\n const [visible, setVisible] = useState(false)\n\n // Force modal to remount with fresh state\n // Fixes a bug where dismissing the modal too quickly causes the Reanimated shared values (like toolbarVisible) to not reset.\n const [modalKey, setModalKey] = useState(0)\n\n return (\n <>\n <PlatformPressable\n style={styles.container}\n onPress={() => {\n if (!isDeleting) {\n setModalKey(prev => prev + 1)\n setVisible(true)\n }\n }}\n onLongPress={() => !isDeleting && onMessageAttachmentLongPress(attachment)}\n android_ripple={{ color: colors.androidRippleNeutral, foreground: true }}\n accessibilityHint=\"Long press for more options\"\n disabled={isDeleting}\n >\n <Image\n source={{ uri: urlMedium || url }}\n style={styles.image}\n wrapperStyle={styles.attachmentImageWrapper}\n alt={filename}\n />\n {isDeleting && <AttachmentDeletingOverlay />}\n </PlatformPressable>\n <LightboxModal\n key={modalKey}\n visible={visible}\n setModalVisible={setVisible}\n imageAttachments={imageAttachments}\n initialImageIndex={currentImageIndex}\n metaProps={metaProps}\n />\n </>\n )\n}\n\ninterface LightboxModalProps {\n visible: boolean\n setModalVisible: Dispatch<SetStateAction<boolean>>\n imageAttachments: DenormalizedMessageAttachmentResource[]\n initialImageIndex: number\n metaProps: MetaProps\n}\n\nconst LightboxModal = ({\n visible,\n setModalVisible,\n imageAttachments,\n initialImageIndex,\n metaProps,\n}: LightboxModalProps) => {\n const onRequestClose = useCallback(() => {\n setModalVisible(false)\n }, [setModalVisible])\n\n return (\n <Modal visible={visible} transparent animationType=\"fade\" onRequestClose={onRequestClose}>\n <SafeAreaProvider>\n <LightboxGallery\n imageAttachments={imageAttachments}\n initialImageIndex={initialImageIndex}\n metaProps={metaProps}\n onRequestClose={onRequestClose}\n />\n </SafeAreaProvider>\n </Modal>\n )\n}\n\ninterface LightboxGalleryProps {\n imageAttachments: DenormalizedMessageAttachmentResource[]\n initialImageIndex: number\n metaProps: MetaProps\n onRequestClose: () => void\n}\nconst LightboxGallery = ({\n imageAttachments,\n initialImageIndex,\n metaProps,\n onRequestClose,\n}: LightboxGalleryProps) => {\n const styles = useStyles()\n const insets = useSafeAreaInsets()\n const { width: windowWidth, height: windowHeight } = useWindowDimensions()\n const [currentImageIndex, setCurrentImageIndex] = useState(initialImageIndex)\n const { soft: triggerMaxZoomHaptic, rigid: triggerDismissHaptic } = Haptic\n\n // Get current image data\n const currentImage = imageAttachments[currentImageIndex]\n const { url, urlMedium, metadata = {} } = currentImage.attributes\n const imageWidth = metadata.width\n const imageHeight = metadata.height\n\n // Calculate available space for image display\n const availableWindowWidth = windowWidth\n const availableWindowHeight = windowHeight - insets.top - insets.bottom\n\n /* ============================\n ANIMATION VALUES\n ============================ */\n // Native State:\n const dismissY = useSharedValue(0) // vertical distance to dismiss modal\n const opacity = useSharedValue(DEFAULT_OPACITY) // opacity of modal\n const scale = useSharedValue(DEFAULT_SCALE) // zoom level of image\n const focalX = useSharedValue(0) // focal point of image between fingers\n const focalY = useSharedValue(0) // focal point of image between fingers\n const translateX = useSharedValue(DEFAULT_TRANSLATE_X) // horizontal distance to pan image\n const translateY = useSharedValue(DEFAULT_TRANSLATE_Y) // vertical distance to pan image\n const savedScale = useSharedValue(DEFAULT_SCALE) // previous zoom level\n const savedTranslateX = useSharedValue(DEFAULT_TRANSLATE_X) // previous horizontal position\n const savedTranslateY = useSharedValue(DEFAULT_TRANSLATE_Y) // previous vertical position\n const lightboxToolbarVisible = useSharedValue(1) // toolbar visibility state\n\n // React (JS) State:\n const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)\n const [flatListScrollEnabled, setFlatListScrollEnabled] = useState(true)\n\n // Syncs toolbar useSharedValue state with React's state so that the status bar can be hidden/shown based on the toolbar's animation\n useAnimatedReaction(\n () => lightboxToolbarVisible.value,\n value => {\n runOnJS(setIsStatusBarHidden)(value === 0)\n }\n )\n\n /* ============================\n HANDLERS\n ============================ */\n const handleOpenInBrowser = useCallback(() => {\n Linking.openURL(urlMedium || url)\n }, [urlMedium, url])\n\n // Helper to enable or disable FlatList scroll state after gestures have ended\n const enableFlatListScroll = useCallback((enableScroll: boolean) => {\n setFlatListScrollEnabled(enableScroll)\n }, [])\n\n const resetDismissGestures = useCallback(() => {\n dismissY.value = withSpring(0, RESET_SPRING_CONFIG)\n opacity.value = withSpring(DEFAULT_OPACITY, RESET_SPRING_CONFIG)\n }, [dismissY, opacity])\n\n const resetAllGestures = useCallback(() => {\n resetDismissGestures()\n scale.value = withSpring(DEFAULT_SCALE, RESET_SPRING_CONFIG)\n translateX.value = withSpring(DEFAULT_TRANSLATE_X, RESET_SPRING_CONFIG)\n translateY.value = withSpring(DEFAULT_TRANSLATE_Y, RESET_SPRING_CONFIG)\n savedScale.value = DEFAULT_SCALE\n savedTranslateX.value = DEFAULT_TRANSLATE_X\n savedTranslateY.value = DEFAULT_TRANSLATE_Y\n lightboxToolbarVisible.value = withSpring(1, RESET_SPRING_CONFIG)\n enableFlatListScroll(true)\n }, [\n resetDismissGestures,\n scale,\n translateX,\n translateY,\n savedScale,\n savedTranslateX,\n savedTranslateY,\n lightboxToolbarVisible,\n enableFlatListScroll,\n ])\n\n const handleCloseModal = useCallback(() => {\n onRequestClose()\n resetAllGestures()\n }, [onRequestClose, resetAllGestures])\n\n /* ============================\n UTILITY WORKLET FUNCTIONS\n 'worklet' runs functions on the UI thread, instead of the JS thread for better performance.\n ============================ */\n const getImageContainedToWindowDimensions = () => {\n 'worklet'\n\n if (!imageWidth || !imageHeight) {\n return { width: availableWindowWidth, height: availableWindowHeight }\n }\n\n const imageAspectRatio = imageWidth / imageHeight\n const windowAspectRatio = availableWindowWidth / availableWindowHeight\n\n // Constrain image width if its wider than window\n if (imageAspectRatio > windowAspectRatio) {\n return {\n width: availableWindowWidth,\n height: availableWindowWidth / imageAspectRatio,\n }\n }\n\n // Constrain image height if its taller than window\n return {\n width: availableWindowHeight * imageAspectRatio,\n height: availableWindowHeight,\n }\n }\n\n const zoomToFocalPoint = ({\n focalPointX,\n focalPointY,\n targetScale,\n currentSavedScale,\n currentSavedTranslateX,\n currentSavedTranslateY,\n }: {\n focalPointX: number\n focalPointY: number\n targetScale: number\n currentSavedScale: number\n currentSavedTranslateX: number\n currentSavedTranslateY: number\n }) => {\n 'worklet'\n\n // How far the focal point is from the center of the window\n const windowCenterX = windowWidth / 2\n const windowCenterY = windowHeight / 2\n\n // Position of focal point relative to current image position\n const focalRelativeX = focalPointX - windowCenterX - currentSavedTranslateX\n const focalRelativeY = focalPointY - windowCenterY - currentSavedTranslateY\n\n // Calculate new translation to keep focal point under fingers\n const scaleRatio = targetScale / currentSavedScale\n const newTranslateX = currentSavedTranslateX + focalRelativeX * (1 - scaleRatio)\n const newTranslateY = currentSavedTranslateY + focalRelativeY * (1 - scaleRatio)\n\n return {\n translateX: newTranslateX,\n translateY: newTranslateY,\n }\n }\n\n const getMaxTranslationLimits = (currentScale: number) => {\n 'worklet'\n\n const { width: containedImageWidth, height: containedImageHeight } =\n getImageContainedToWindowDimensions()\n\n // Image dimensions after scaling/zooming\n const scaledWidth = containedImageWidth * currentScale\n const scaledHeight = containedImageHeight * currentScale\n\n // How much the scaled image exceeds the window container\n const excessWidth = scaledWidth - availableWindowWidth\n const excessHeight = scaledHeight - availableWindowHeight\n\n // How far the image can move in each direction before hitting window edges\n const maxTranslateX = Math.max(DEFAULT_TRANSLATE_X, excessWidth / 2)\n const maxTranslateY = Math.max(DEFAULT_TRANSLATE_Y, excessHeight / 2)\n\n return { maxTranslateX, maxTranslateY }\n }\n\n const clampDecay = (currentScale: number): { x: [number, number]; y: [number, number] } => {\n 'worklet'\n\n const { maxTranslateX, maxTranslateY } = getMaxTranslationLimits(currentScale)\n\n return {\n x: [-maxTranslateX, maxTranslateX],\n y: [-maxTranslateY, maxTranslateY],\n }\n }\n\n const clampTranslation = ({\n x,\n y,\n currentScale,\n }: {\n x: number\n y: number\n currentScale: number\n }) => {\n 'worklet'\n\n const { maxTranslateX, maxTranslateY } = getMaxTranslationLimits(currentScale)\n\n return {\n x: Math.min(maxTranslateX, Math.max(-maxTranslateX, x)),\n y: Math.min(maxTranslateY, Math.max(-maxTranslateY, y)),\n }\n }\n\n const isImageAtVerticalBoundry = (currentScale: number) => {\n 'worklet'\n\n const { height: containedImageHeight } = getImageContainedToWindowDimensions()\n\n // Calculate how much the image can exceed the window container\n const scaledHeight = containedImageHeight * currentScale\n const excessHeight = scaledHeight - availableWindowHeight\n const maxTranslateY = Math.max(DEFAULT_TRANSLATE_Y, excessHeight / 2)\n\n const currentTranslateY = translateY.value\n const panPositionTolerance = 1 // buffer to account for translateY being at a subpixel position\n\n const atTopBoundry = currentTranslateY >= maxTranslateY - panPositionTolerance\n const atBottomBoundry = currentTranslateY <= -maxTranslateY + panPositionTolerance\n\n return atTopBoundry || atBottomBoundry\n }\n\n /* ============================\n UTILITY FLATLIST FUNCTIONS\n Supports the image gallery layout and swipe functionality.\n ============================ */\n\n // Use refs to avoid re-rendering FlatList callbacks...\n const stableCurrentImageIndexRef = useRef(currentImageIndex)\n const stableImageAttachmentsLengthRef = useRef(imageAttachments.length)\n // ...while still ensuring they are updated with the latest values\n stableCurrentImageIndexRef.current = currentImageIndex\n stableImageAttachmentsLengthRef.current = imageAttachments.length\n\n // Used in tandem with FlatList's initialScrollIndex to quickly calculate the position and size of each image before they load.\n const getItemLayout = useCallback(\n (_: unknown, index: number) => ({\n length: windowWidth,\n offset: windowWidth * index,\n index,\n }),\n [windowWidth]\n )\n\n // Captures the current image's index after the FlatList finishes its scroll animation.\n // Used in tandem with onViewableItemsChanged to ensure the final value for currentImageIndex is set.\n const onMomentumScrollEnd = useCallback(\n (event: NativeSyntheticEvent<NativeScrollEvent>) => {\n // Calculate the index of the image that is currently visible\n const imageOffsetX = event.nativeEvent.contentOffset.x\n const newImageIndex = Math.round(imageOffsetX / windowWidth)\n\n // Check if the image index has changed and the FlatList didn't scroll past the first or last image\n const didImageIndexChange = newImageIndex !== stableCurrentImageIndexRef.current\n const isImageIndexWithinBounds =\n newImageIndex >= 0 && newImageIndex < stableImageAttachmentsLengthRef.current\n\n if (didImageIndexChange && isImageIndexWithinBounds) {\n setCurrentImageIndex(newImageIndex)\n }\n },\n [windowWidth]\n )\n\n // Supplements onMomentumScrollEnd by capturing the current image's index while the FlatList is actively scrolling.\n // Used in tandem with viewabilityConfig to trigger when the image is 50% visible in the window.\n const onViewableItemsChanged = useCallback(\n ({ viewableItems }: { viewableItems: ViewToken<DenormalizedMessageAttachmentResource>[] }) => {\n if (viewableItems.length === 0) return\n\n // Use the first viewable item which is enforced by the FlatList's pagingEnabled prop that allows only two images to be visible at a time when scrolling.\n const firstViewableItem = viewableItems[0]\n const newIndex = firstViewableItem.index\n\n if (newIndex !== null && newIndex !== stableCurrentImageIndexRef.current) {\n setCurrentImageIndex(newIndex)\n }\n },\n []\n )\n\n /* ============================\n GESTURES\n ============================ */\n const singleTapGesture = Gesture.Tap()\n .minPointers(SINGLE_FINGER_POINTER)\n .numberOfTaps(1)\n .onStart(() => {\n lightboxToolbarVisible.value = withSpring(\n lightboxToolbarVisible.value > 0.5 ? 0 : 1,\n RESET_SPRING_CONFIG\n )\n })\n\n const doubleTapGesture = Gesture.Tap()\n .minPointers(SINGLE_FINGER_POINTER)\n .numberOfTaps(2)\n .onStart(e => {\n const isZoomedIn = scale.value > DEFAULT_SCALE\n if (isZoomedIn) {\n runOnJS(resetAllGestures)()\n } else {\n // Hide toolbar when starting to zoom\n lightboxToolbarVisible.value = withSpring(0, RESET_SPRING_CONFIG)\n\n // Zoom to 2x at tap location\n const newTranslation = zoomToFocalPoint({\n focalPointX: e.x,\n focalPointY: e.y,\n targetScale: DOUBLE_TAP_ZOOM_SCALE,\n currentSavedScale: savedScale.value,\n currentSavedTranslateX: savedTranslateX.value,\n currentSavedTranslateY: savedTranslateY.value,\n })\n\n // Apply clamping to ensure image edges remain within window boundaries\n const clampedTranslate = clampTranslation({\n x: newTranslation.translateX,\n y: newTranslation.translateY,\n currentScale: DOUBLE_TAP_ZOOM_SCALE,\n })\n\n // Animate to new scale and translation\n scale.value = withSpring(DOUBLE_TAP_ZOOM_SCALE, RESET_SPRING_CONFIG)\n translateX.value = withSpring(clampedTranslate.x, RESET_SPRING_CONFIG)\n translateY.value = withSpring(clampedTranslate.y, RESET_SPRING_CONFIG)\n\n // Update saved state for next gesture\n savedScale.value = DOUBLE_TAP_ZOOM_SCALE\n savedTranslateX.value = clampedTranslate.x\n savedTranslateY.value = clampedTranslate.y\n\n // Disable FlatList scroll since image is now zoomed\n runOnJS(enableFlatListScroll)(false)\n }\n })\n\n const pinchGesture = Gesture.Pinch()\n .onStart(e => {\n focalX.value = e.focalX\n focalY.value = e.focalY\n\n // Hide toolbar when starting to zoom\n lightboxToolbarVisible.value = withSpring(0, RESET_SPRING_CONFIG)\n\n // Ensure that pinch accounts for the decay animation and starts from the true current position.\n savedTranslateX.value = translateX.value\n savedTranslateY.value = translateY.value\n })\n .onUpdate(e => {\n // Zoom image in/out within scale limits\n const newScale = savedScale.value * e.scale\n const newScaleClamped = Math.min(\n MAX_SCALE + MAX_SCALE_OVERSHOOT,\n Math.max(MIN_SCALE, newScale)\n )\n\n // Check if we just reached the minimum scale threshold\n const previousScale = scale.value\n const reacjedMinScale = previousScale > MIN_SCALE && newScaleClamped <= MIN_SCALE\n\n if (reacjedMinScale) {\n runOnJS(triggerDismissHaptic)()\n }\n\n scale.value = newScaleClamped\n\n // Calculate new translation to keep focal point under fingers\n const newTranslation = zoomToFocalPoint({\n focalPointX: focalX.value,\n focalPointY: focalY.value,\n targetScale: newScaleClamped,\n currentSavedScale: savedScale.value,\n currentSavedTranslateX: savedTranslateX.value,\n currentSavedTranslateY: savedTranslateY.value,\n })\n\n // Apply translation without clamping to ensure focal point doesn't jump during gesture\n translateX.value = newTranslation.translateX\n translateY.value = newTranslation.translateY\n })\n .onEnd(() => {\n const currentScale = scale.value\n\n // Dismiss modal if fully zoomed out\n if (currentScale <= MIN_SCALE) {\n runOnJS(handleCloseModal)()\n return\n }\n\n // Reset all gestures if image is near default scale\n const isNearDefaultScale = currentScale <= DEFAULT_SCALE + 0.1\n if (isNearDefaultScale) {\n runOnJS(resetAllGestures)()\n return\n }\n\n // Check if overshooting the max scale and clamp it\n const finalScale = currentScale > MAX_SCALE ? MAX_SCALE : currentScale\n\n if (finalScale === MAX_SCALE) {\n runOnJS(triggerMaxZoomHaptic)()\n }\n\n // Recalculate translation using focal point to always return to the same position when image is overshooting the max scale\n const newTranslation = zoomToFocalPoint({\n focalPointX: focalX.value,\n focalPointY: focalY.value,\n targetScale: finalScale,\n currentSavedScale: savedScale.value,\n currentSavedTranslateX: savedTranslateX.value,\n currentSavedTranslateY: savedTranslateY.value,\n })\n\n // Ensure image edges remain within window boundaries\n const clampedTranslate = clampTranslation({\n x: newTranslation.translateX,\n y: newTranslation.translateY,\n currentScale: finalScale,\n })\n\n // Animate to position and scale limits with a spring\n scale.value = withSpring(finalScale, RESET_SPRING_CONFIG)\n translateX.value = withSpring(clampedTranslate.x, RESET_SPRING_CONFIG)\n translateY.value = withSpring(clampedTranslate.y, RESET_SPRING_CONFIG)\n\n // Save state for next gesture\n savedScale.value = finalScale\n savedTranslateX.value = clampedTranslate.x\n savedTranslateY.value = clampedTranslate.y\n\n // Disable FlatList scroll since image is still zoomed\n runOnJS(enableFlatListScroll)(false)\n })\n\n const panGesture = Gesture.Pan()\n .minDistance(MIN_DISTANCE_FOR_PAN)\n .minPointers(SINGLE_FINGER_POINTER)\n .maxPointers(SINGLE_FINGER_POINTER)\n .onStart(() => {\n // Only start pan if image is zoomed in - prevents conflicts with FlatList\n if (scale.value <= DEFAULT_SCALE) return\n\n // Update saved position to current animated position\n // This ensures smooth continuation if decay is running\n savedTranslateX.value = translateX.value\n savedTranslateY.value = translateY.value\n })\n .onUpdate(e => {\n // Only pan if image is zoomed in - prevents conflicts with FlatList\n if (scale.value <= DEFAULT_SCALE) return\n\n const newTranslateX = savedTranslateX.value + e.translationX\n const newTranslateY = savedTranslateY.value + e.translationY\n\n // Ensure image edges remain within window boundaries\n const clampedTranslate = clampTranslation({\n x: newTranslateX,\n y: newTranslateY,\n currentScale: scale.value,\n })\n translateX.value = clampedTranslate.x\n translateY.value = clampedTranslate.y\n })\n .onEnd(e => {\n // Prevents saving pan position if image is zoomed out while panning\n if (scale.value <= DEFAULT_SCALE) return\n\n const clampDecayBounds = clampDecay(scale.value)\n\n translateX.value = withDecay(\n {\n velocity: e.velocityX * DECAY_VELOCITY_FACTOR,\n clamp: clampDecayBounds.x,\n deceleration: 0.996,\n },\n finished => {\n if (finished) {\n savedTranslateX.value = translateX.value\n }\n }\n )\n\n translateY.value = withDecay(\n {\n velocity: e.velocityY * DECAY_VELOCITY_FACTOR,\n clamp: clampDecayBounds.y,\n deceleration: 0.996,\n },\n finished => {\n if (finished) {\n savedTranslateY.value = translateY.value\n }\n }\n )\n })\n\n const panToDismissModalGesture = Gesture.Pan()\n .onUpdate(e => {\n const atDefaultScale = scale.value === DEFAULT_SCALE\n\n // Calculate zoom conditions for dismissing modal\n const atVerticalBoundry = isImageAtVerticalBoundry(scale.value)\n const panDirectionIsPrimarilyVertical = Math.abs(e.translationY) > Math.abs(e.translationX)\n const canDismissWhileZoomed = atVerticalBoundry && panDirectionIsPrimarilyVertical\n\n if (!(atDefaultScale || canDismissWhileZoomed)) return\n\n // Fade image if its been panned past 50% of the dismiss threshold\n const panDistance = Math.abs(e.translationY)\n const halfThreshold = DISMISS_PAN_THRESHOLD / 2\n const fadeDistance = Math.max(0, panDistance - halfThreshold)\n const fadeProgress = fadeDistance / halfThreshold\n\n // Check if we just crossed the dismiss threshold\n const previousDistance = Math.abs(dismissY.value)\n const crossedDismissThreshold =\n previousDistance < DISMISS_PAN_THRESHOLD && panDistance >= DISMISS_PAN_THRESHOLD\n\n if (crossedDismissThreshold) {\n runOnJS(triggerDismissHaptic)()\n }\n\n opacity.value = Math.max(0, DEFAULT_OPACITY - fadeProgress)\n dismissY.value = e.translationY\n })\n .onEnd(() => {\n const exceededDismissThreshold = Math.abs(dismissY.value) > DISMISS_PAN_THRESHOLD\n\n if (exceededDismissThreshold) {\n runOnJS(handleCloseModal)()\n } else {\n runOnJS(resetDismissGestures)()\n }\n })\n\n /* ==============================\n COMPOSE GESTURES\n ================================= */\n // Race between pinch and pan ensures only one is active at a time, preserving focal point logic\n const pinchOrPanGestures = Gesture.Race(pinchGesture, panGesture)\n\n // Exclusive race ensures single tap doesn't interfere with double tap\n const tapGestures = Gesture.Exclusive(doubleTapGesture, singleTapGesture)\n\n // Race between tap gestures and pinch/pan\n const transformImageGestures = Gesture.Race(tapGestures, pinchOrPanGestures)\n\n // Dismiss can work simultaneously with all gestures\n const composedGesture = Gesture.Simultaneous(transformImageGestures, panToDismissModalGesture)\n\n return (\n <>\n <StatusBar\n barStyle=\"light-content\"\n hidden={isStatusBarHidden}\n animated\n showHideTransition=\"slide\"\n />\n <GestureHandlerRootView>\n <GestureDetector gesture={composedGesture}>\n <FlatList\n data={imageAttachments}\n renderItem={({ item, index }) => (\n <GestureImage\n item={item}\n gesturesEnabled={index === currentImageIndex}\n scale={scale}\n translateX={translateX}\n translateY={translateY}\n dismissY={dismissY}\n opacity={opacity}\n />\n )}\n keyExtractor={(item, index) => `${item.id}-${index}`}\n horizontal\n pagingEnabled\n scrollEnabled={flatListScrollEnabled}\n showsHorizontalScrollIndicator={false}\n initialScrollIndex={initialImageIndex}\n getItemLayout={getItemLayout}\n onMomentumScrollEnd={onMomentumScrollEnd}\n onViewableItemsChanged={onViewableItemsChanged}\n viewabilityConfig={{\n itemVisiblePercentThreshold: 50, // 50% of the image must be visible in the window to be considered viewable\n }}\n style={styles.gallery}\n contentContainerStyle={styles.galleryContentContainer}\n />\n </GestureDetector>\n </GestureHandlerRootView>\n <LightboxToolbarHeader\n metaProps={metaProps}\n lightboxToolbarVisible={lightboxToolbarVisible}\n isStatusBarHidden={isStatusBarHidden}\n handleOpenInBrowser={handleOpenInBrowser}\n handleCloseModal={handleCloseModal}\n />\n {imageAttachments.length > 1 && (\n <LightboxToolbarFooter\n lightboxToolbarVisible={lightboxToolbarVisible}\n totalImages={imageAttachments.length}\n currentImageIndex={currentImageIndex}\n />\n )}\n </>\n )\n}\n\ninterface GestureImageProps {\n item: DenormalizedMessageAttachmentResource\n gesturesEnabled: boolean\n scale: SharedValue<number>\n translateX: SharedValue<number>\n translateY: SharedValue<number>\n dismissY: SharedValue<number>\n opacity: SharedValue<number>\n}\n\nconst GestureImage = ({\n item,\n gesturesEnabled,\n scale,\n translateX,\n translateY,\n dismissY,\n opacity,\n}: GestureImageProps) => {\n const styles = useStyles()\n const { url: itemUrl, urlMedium: itemUrlMedium } = item.attributes\n\n const animatedImageStyles = useAnimatedStyle(() => {\n return {\n transform: [\n { translateX: gesturesEnabled ? translateX.value : DEFAULT_TRANSLATE_X },\n { translateY: gesturesEnabled ? translateY.value + dismissY.value : DEFAULT_TRANSLATE_Y },\n { scale: gesturesEnabled ? scale.value : DEFAULT_SCALE },\n ],\n opacity: opacity.value,\n }\n })\n\n return (\n <TouchEventIsolator>\n <Image\n source={{ uri: itemUrlMedium || itemUrl }}\n style={styles.gestureImage}\n animatedImageStyle={animatedImageStyles}\n loadingBackgroundStyles={styles.gestureImageLoading}\n resizeMode=\"contain\"\n alt=\"\"\n />\n </TouchEventIsolator>\n )\n}\n\nconst TouchEventIsolator = ({ children }: { children: React.ReactNode }) => {\n const styles = useStyles()\n\n return (\n <View style={styles.touchEventIsolator} onStartShouldSetResponder={() => true}>\n {children}\n </View>\n )\n}\n\ninterface LightboxToolbarHeaderProps {\n metaProps: MetaProps\n lightboxToolbarVisible: SharedValue<number>\n isStatusBarHidden: boolean\n handleOpenInBrowser: () => void\n handleCloseModal: () => void\n}\n\nconst LightboxToolbarHeader = ({\n metaProps,\n lightboxToolbarVisible,\n isStatusBarHidden,\n handleOpenInBrowser,\n handleCloseModal,\n}: LightboxToolbarHeaderProps) => {\n const styles = useStyles()\n const { authorName, createdAt } = metaProps\n\n const animatedHeaderStyles = useAnimatedStyle(() => ({\n opacity: lightboxToolbarVisible.value,\n transform: [\n { translateY: (1 - lightboxToolbarVisible.value) * -20 }, // slide up when hiding\n ],\n }))\n\n return (\n <Animated.View\n style={[styles.lightboxToolbar, styles.lightboxToolbarHeader, animatedHeaderStyles]}\n accessibilityRole=\"toolbar\"\n >\n <View style={styles.lightboxToolbarHeaderMetaContainer}>\n <Heading variant=\"h3\" style={styles.lightboxToolbarHeaderTitle} numberOfLines={1}>\n {authorName}\n </Heading>\n <Text variant=\"tertiary\" style={styles.lightboxToolbarHeaderSubtitle}>\n {formatDatePreview(createdAt)}\n </Text>\n </View>\n <IconButton\n onPress={handleOpenInBrowser}\n disabled={isStatusBarHidden}\n name=\"general.newWindow\"\n accessibilityRole=\"link\"\n accessibilityLabel=\"Open image in browser\"\n accessibilityHint=\"Image can be downloaded and shared through the browser.\"\n style={styles.lightboxToolbarButton}\n iconStyle={styles.lightboxToolbarButtonIcon}\n size=\"lg\"\n />\n <IconButton\n onPress={handleCloseModal}\n disabled={isStatusBarHidden}\n name=\"general.x\"\n accessibilityLabel=\"Close image\"\n style={styles.lightboxToolbarButton}\n iconStyle={styles.lightboxToolbarButtonIcon}\n size=\"lg\"\n />\n </Animated.View>\n )\n}\n\ninterface LightboxToolbarFooterProps {\n lightboxToolbarVisible: SharedValue<number>\n totalImages: number\n currentImageIndex: number\n}\n\nconst LightboxToolbarFooter = ({\n lightboxToolbarVisible,\n totalImages,\n currentImageIndex,\n}: LightboxToolbarFooterProps) => {\n const styles = useStyles()\n\n const animatedFooterStyles = useAnimatedStyle(() => ({\n opacity: lightboxToolbarVisible.value,\n transform: [\n { translateY: (1 - lightboxToolbarVisible.value) * 20 }, // slide down when showing\n ],\n }))\n\n return (\n <Animated.View\n style={[styles.lightboxToolbar, styles.lightboxToolbarFooter, animatedFooterStyles]}\n accessibilityRole=\"toolbar\"\n >\n <Text style={styles.lightboxToolbarFooterText}>\n {currentImageIndex + 1} of {totalImages}\n </Text>\n </Animated.View>\n )\n}\n\ninterface UseStylesProps {\n imageWidth?: number\n imageHeight?: number\n}\n\nconst useStyles = ({ imageWidth = 100, imageHeight = 100 }: UseStylesProps = {}) => {\n const { top, bottom } = useSafeAreaInsets()\n const { width: windowWidth } = useWindowDimensions()\n const backgroundColor = tokens.colorNeutral7\n const transparentBackgroundColor = useMemo(\n () => colorFunction(backgroundColor).alpha(0.8).toString(),\n [backgroundColor]\n )\n\n return StyleSheet.create({\n container: {\n maxWidth: '100%',\n position: 'relative',\n },\n touchEventIsolator: {\n flex: 1,\n },\n attachmentImageWrapper: {\n width: '100%',\n minWidth: 200,\n aspectRatio: safeAspectRatio(imageWidth, imageHeight),\n },\n image: {\n borderRadius: 8,\n },\n gallery: {\n backgroundColor,\n },\n galleryContentContainer: {\n paddingTop: top,\n paddingBottom: bottom,\n },\n gestureImage: {\n height: '100%',\n width: windowWidth,\n backgroundColor: 'transparent',\n },\n gestureImageLoading: {\n backgroundColor,\n },\n lightboxToolbar: {\n width: '100%',\n position: 'absolute',\n flexDirection: 'row',\n alignItems: 'center',\n gap: 20,\n paddingHorizontal: 16,\n backgroundColor: transparentBackgroundColor,\n },\n lightboxToolbarButton: {\n backgroundColor,\n height: 40,\n width: 40,\n borderRadius: 50,\n borderWidth: 1,\n borderColor: tokens.colorNeutral24,\n },\n lightboxToolbarButtonIcon: {\n color: tokens.colorNeutral88,\n },\n lightboxToolbarHeader: {\n top: 0,\n paddingTop: top + 16,\n paddingBottom: 8,\n },\n lightboxToolbarHeaderMetaContainer: {\n flex: 1,\n },\n lightboxToolbarHeaderTitle: {\n marginRight: 'auto',\n flexShrink: 1,\n color: tokens.colorNeutral88,\n },\n lightboxToolbarHeaderSubtitle: {\n color: tokens.colorNeutral68,\n },\n lightboxToolbarFooter: {\n justifyContent: 'center',\n bottom: 0,\n paddingTop: 8,\n paddingBottom: bottom + 16,\n },\n lightboxToolbarFooterText: {\n color: tokens.colorNeutral88,\n fontWeight: platformFontWeightMedium,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"image_attachment.js","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/image_attachment.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport colorFunction from 'color'\nimport React, { useMemo, useState, Dispatch, SetStateAction, useCallback, useRef } from 'react'\nimport {\n StatusBar,\n StyleSheet,\n Modal,\n View,\n Linking,\n Dimensions,\n NativeSyntheticEvent,\n NativeScrollEvent,\n ViewToken,\n} from 'react-native'\nimport {\n FlatList,\n Gesture,\n GestureDetector,\n GestureHandlerRootView,\n} from 'react-native-gesture-handler'\nimport Animated, {\n runOnJS,\n useAnimatedStyle,\n useAnimatedReaction,\n useSharedValue,\n withSpring,\n withDecay,\n SharedValue,\n} from 'react-native-reanimated'\nimport { useSafeAreaInsets, SafeAreaProvider } from 'react-native-safe-area-context'\nimport { useTheme } from '../../../hooks'\nimport { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'\nimport { Haptic, platformFontWeightMedium } from '../../../utils'\nimport { formatDatePreview } from '../../../utils/date'\nimport { tokens } from '../../../vendor/tapestry/tokens'\nimport { IconButton, Image, Heading, Text } from '../../display'\nimport { AttachmentDeletingOverlay } from './attachment_deleting_overlay'\n\nconst { width: WINDOW_WIDTH, height: WINDOW_HEIGHT } = Dimensions.get('window')\nconst DISMISS_PAN_THRESHOLD = 175\nconst MIN_DISTANCE_FOR_PAN = 10 // Higher threshold gives pinching priority\nconst SINGLE_FINGER_POINTER = 1 // Single-finger panning / tapping helps to avoid conflicts with pinching\nconst DEFAULT_OPACITY = 1\nconst DEFAULT_TRANSLATE_X = 0\nconst DEFAULT_TRANSLATE_Y = 0\nconst DEFAULT_SCALE = 1\nconst MIN_SCALE = 0.5\nconst MAX_SCALE = 5\nconst MAX_SCALE_OVERSHOOT = 5\nconst DOUBLE_TAP_ZOOM_SCALE = 2\nconst DECAY_VELOCITY_FACTOR = 0.4\nconst RESET_SPRING_CONFIG = {\n damping: 20,\n stiffness: 150,\n}\n\nexport type MetaProps = {\n authorName: string\n createdAt: string\n}\n\nexport function ImageAttachment({\n attachment,\n imageAttachments,\n currentImageIndex,\n metaProps,\n onMessageAttachmentLongPress,\n isDeleting,\n}: {\n attachment: DenormalizedMessageAttachmentResource\n imageAttachments: DenormalizedMessageAttachmentResource[]\n currentImageIndex: number\n metaProps: MetaProps\n onMessageAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void\n isDeleting: boolean\n}) {\n const { attributes } = attachment\n const { url, urlMedium, filename, metadata = {} } = attributes\n const { colors } = useTheme()\n\n const styles = useStyles({ imageWidth: metadata.width, imageHeight: metadata.height })\n const [visible, setVisible] = useState(false)\n\n // Force modal to remount with fresh state\n // Fixes a bug where dismissing the modal too quickly causes the Reanimated shared values (like toolbarVisible) to not reset.\n const [modalKey, setModalKey] = useState(0)\n\n return (\n <>\n <PlatformPressable\n style={styles.container}\n onPress={() => {\n if (!isDeleting) {\n setModalKey(prev => prev + 1)\n setVisible(true)\n }\n }}\n onLongPress={() => !isDeleting && onMessageAttachmentLongPress(attachment)}\n android_ripple={{ color: colors.androidRippleNeutral, foreground: true }}\n accessibilityHint=\"Long press for more options\"\n disabled={isDeleting}\n >\n <Image\n source={{ uri: urlMedium || url }}\n style={styles.image}\n wrapperStyle={styles.attachmentImageWrapper}\n alt={filename}\n />\n {isDeleting && <AttachmentDeletingOverlay />}\n </PlatformPressable>\n <LightboxModal\n key={modalKey}\n visible={visible}\n setModalVisible={setVisible}\n imageAttachments={imageAttachments}\n initialImageIndex={currentImageIndex}\n metaProps={metaProps}\n />\n </>\n )\n}\n\ninterface LightboxModalProps {\n visible: boolean\n setModalVisible: Dispatch<SetStateAction<boolean>>\n imageAttachments: DenormalizedMessageAttachmentResource[]\n initialImageIndex: number\n metaProps: MetaProps\n}\n\nconst LightboxModal = ({\n visible,\n setModalVisible,\n imageAttachments,\n initialImageIndex,\n metaProps,\n}: LightboxModalProps) => {\n const onRequestClose = useCallback(() => {\n setModalVisible(false)\n }, [setModalVisible])\n\n return (\n <Modal visible={visible} transparent animationType=\"fade\" onRequestClose={onRequestClose}>\n <SafeAreaProvider>\n <LightboxGallery\n imageAttachments={imageAttachments}\n initialImageIndex={initialImageIndex}\n metaProps={metaProps}\n onRequestClose={onRequestClose}\n />\n </SafeAreaProvider>\n </Modal>\n )\n}\n\ninterface LightboxGalleryProps {\n imageAttachments: DenormalizedMessageAttachmentResource[]\n initialImageIndex: number\n metaProps: MetaProps\n onRequestClose: () => void\n}\nconst LightboxGallery = ({\n imageAttachments,\n initialImageIndex,\n metaProps,\n onRequestClose,\n}: LightboxGalleryProps) => {\n const styles = useStyles()\n const insets = useSafeAreaInsets()\n const [currentImageIndex, setCurrentImageIndex] = useState(initialImageIndex)\n const { soft: triggerMaxZoomHaptic, rigid: triggerDismissHaptic } = Haptic\n\n // Get current image data\n const currentImage = imageAttachments[currentImageIndex]\n const { url, urlMedium, metadata = {} } = currentImage.attributes\n const imageWidth = metadata.width\n const imageHeight = metadata.height\n\n // Calculate available space for image display\n const availableWindowWidth = WINDOW_WIDTH\n const availableWindowHeight = WINDOW_HEIGHT - insets.top - insets.bottom\n\n /* ============================\n ANIMATION VALUES\n ============================ */\n // Native State:\n const dismissY = useSharedValue(0) // vertical distance to dismiss modal\n const opacity = useSharedValue(DEFAULT_OPACITY) // opacity of modal\n const scale = useSharedValue(DEFAULT_SCALE) // zoom level of image\n const focalX = useSharedValue(0) // focal point of image between fingers\n const focalY = useSharedValue(0) // focal point of image between fingers\n const translateX = useSharedValue(DEFAULT_TRANSLATE_X) // horizontal distance to pan image\n const translateY = useSharedValue(DEFAULT_TRANSLATE_Y) // vertical distance to pan image\n const savedScale = useSharedValue(DEFAULT_SCALE) // previous zoom level\n const savedTranslateX = useSharedValue(DEFAULT_TRANSLATE_X) // previous horizontal position\n const savedTranslateY = useSharedValue(DEFAULT_TRANSLATE_Y) // previous vertical position\n const lightboxToolbarVisible = useSharedValue(1) // toolbar visibility state\n\n // React (JS) State:\n const [isStatusBarHidden, setIsStatusBarHidden] = useState(false)\n const [flatListScrollEnabled, setFlatListScrollEnabled] = useState(true)\n\n // Syncs toolbar useSharedValue state with React's state so that the status bar can be hidden/shown based on the toolbar's animation\n useAnimatedReaction(\n () => lightboxToolbarVisible.value,\n value => {\n runOnJS(setIsStatusBarHidden)(value === 0)\n }\n )\n\n /* ============================\n HANDLERS\n ============================ */\n const handleOpenInBrowser = useCallback(() => {\n Linking.openURL(urlMedium || url)\n }, [urlMedium, url])\n\n // Helper to enable or disable FlatList scroll state after gestures have ended\n const enableFlatListScroll = useCallback((enableScroll: boolean) => {\n setFlatListScrollEnabled(enableScroll)\n }, [])\n\n const resetDismissGestures = useCallback(() => {\n dismissY.value = withSpring(0, RESET_SPRING_CONFIG)\n opacity.value = withSpring(DEFAULT_OPACITY, RESET_SPRING_CONFIG)\n }, [dismissY, opacity])\n\n const resetAllGestures = useCallback(() => {\n resetDismissGestures()\n scale.value = withSpring(DEFAULT_SCALE, RESET_SPRING_CONFIG)\n translateX.value = withSpring(DEFAULT_TRANSLATE_X, RESET_SPRING_CONFIG)\n translateY.value = withSpring(DEFAULT_TRANSLATE_Y, RESET_SPRING_CONFIG)\n savedScale.value = DEFAULT_SCALE\n savedTranslateX.value = DEFAULT_TRANSLATE_X\n savedTranslateY.value = DEFAULT_TRANSLATE_Y\n lightboxToolbarVisible.value = withSpring(1, RESET_SPRING_CONFIG)\n enableFlatListScroll(true)\n }, [\n resetDismissGestures,\n scale,\n translateX,\n translateY,\n savedScale,\n savedTranslateX,\n savedTranslateY,\n lightboxToolbarVisible,\n enableFlatListScroll,\n ])\n\n const handleCloseModal = useCallback(() => {\n onRequestClose()\n resetAllGestures()\n }, [onRequestClose, resetAllGestures])\n\n /* ============================\n UTILITY WORKLET FUNCTIONS\n 'worklet' runs functions on the UI thread, instead of the JS thread for better performance.\n ============================ */\n const getImageContainedToWindowDimensions = () => {\n 'worklet'\n\n if (!imageWidth || !imageHeight) {\n return { width: availableWindowWidth, height: availableWindowHeight }\n }\n\n const imageAspectRatio = imageWidth / imageHeight\n const windowAspectRatio = availableWindowWidth / availableWindowHeight\n\n // Constrain image width if its wider than window\n if (imageAspectRatio > windowAspectRatio) {\n return {\n width: availableWindowWidth,\n height: availableWindowWidth / imageAspectRatio,\n }\n }\n\n // Constrain image height if its taller than window\n return {\n width: availableWindowHeight * imageAspectRatio,\n height: availableWindowHeight,\n }\n }\n\n const zoomToFocalPoint = ({\n focalPointX,\n focalPointY,\n targetScale,\n currentSavedScale,\n currentSavedTranslateX,\n currentSavedTranslateY,\n }: {\n focalPointX: number\n focalPointY: number\n targetScale: number\n currentSavedScale: number\n currentSavedTranslateX: number\n currentSavedTranslateY: number\n }) => {\n 'worklet'\n\n // How far the focal point is from the center of the window\n const windowCenterX = WINDOW_WIDTH / 2\n const windowCenterY = WINDOW_HEIGHT / 2\n\n // Position of focal point relative to current image position\n const focalRelativeX = focalPointX - windowCenterX - currentSavedTranslateX\n const focalRelativeY = focalPointY - windowCenterY - currentSavedTranslateY\n\n // Calculate new translation to keep focal point under fingers\n const scaleRatio = targetScale / currentSavedScale\n const newTranslateX = currentSavedTranslateX + focalRelativeX * (1 - scaleRatio)\n const newTranslateY = currentSavedTranslateY + focalRelativeY * (1 - scaleRatio)\n\n return {\n translateX: newTranslateX,\n translateY: newTranslateY,\n }\n }\n\n const getMaxTranslationLimits = (currentScale: number) => {\n 'worklet'\n\n const { width: containedImageWidth, height: containedImageHeight } =\n getImageContainedToWindowDimensions()\n\n // Image dimensions after scaling/zooming\n const scaledWidth = containedImageWidth * currentScale\n const scaledHeight = containedImageHeight * currentScale\n\n // How much the scaled image exceeds the window container\n const excessWidth = scaledWidth - availableWindowWidth\n const excessHeight = scaledHeight - availableWindowHeight\n\n // How far the image can move in each direction before hitting window edges\n const maxTranslateX = Math.max(DEFAULT_TRANSLATE_X, excessWidth / 2)\n const maxTranslateY = Math.max(DEFAULT_TRANSLATE_Y, excessHeight / 2)\n\n return { maxTranslateX, maxTranslateY }\n }\n\n const clampDecay = (currentScale: number): { x: [number, number]; y: [number, number] } => {\n 'worklet'\n\n const { maxTranslateX, maxTranslateY } = getMaxTranslationLimits(currentScale)\n\n return {\n x: [-maxTranslateX, maxTranslateX],\n y: [-maxTranslateY, maxTranslateY],\n }\n }\n\n const clampTranslation = ({\n x,\n y,\n currentScale,\n }: {\n x: number\n y: number\n currentScale: number\n }) => {\n 'worklet'\n\n const { maxTranslateX, maxTranslateY } = getMaxTranslationLimits(currentScale)\n\n return {\n x: Math.min(maxTranslateX, Math.max(-maxTranslateX, x)),\n y: Math.min(maxTranslateY, Math.max(-maxTranslateY, y)),\n }\n }\n\n const isImageAtVerticalBoundry = (currentScale: number) => {\n 'worklet'\n\n const { height: containedImageHeight } = getImageContainedToWindowDimensions()\n\n // Calculate how much the image can exceed the window container\n const scaledHeight = containedImageHeight * currentScale\n const excessHeight = scaledHeight - availableWindowHeight\n const maxTranslateY = Math.max(DEFAULT_TRANSLATE_Y, excessHeight / 2)\n\n const currentTranslateY = translateY.value\n const panPositionTolerance = 1 // buffer to account for translateY being at a subpixel position\n\n const atTopBoundry = currentTranslateY >= maxTranslateY - panPositionTolerance\n const atBottomBoundry = currentTranslateY <= -maxTranslateY + panPositionTolerance\n\n return atTopBoundry || atBottomBoundry\n }\n\n /* ============================\n UTILITY FLATLIST FUNCTIONS\n Supports the image gallery layout and swipe functionality.\n ============================ */\n\n // Use refs to avoid re-rendering FlatList callbacks...\n const stableCurrentImageIndexRef = useRef(currentImageIndex)\n const stableImageAttachmentsLengthRef = useRef(imageAttachments.length)\n // ...while still ensuring they are updated with the latest values\n stableCurrentImageIndexRef.current = currentImageIndex\n stableImageAttachmentsLengthRef.current = imageAttachments.length\n\n // Used in tandem with FlatList's initialScrollIndex to quickly calculate the position and size of each image before they load.\n const getItemLayout = useCallback(\n (_: unknown, index: number) => ({\n length: WINDOW_WIDTH,\n offset: WINDOW_WIDTH * index,\n index,\n }),\n []\n )\n\n // Captures the current image's index after the FlatList finishes its scroll animation.\n // Used in tandem with onViewableItemsChanged to ensure the final value for currentImageIndex is set.\n const onMomentumScrollEnd = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {\n // Calculate the index of the image that is currently visible\n const imageOffsetX = event.nativeEvent.contentOffset.x\n const newImageIndex = Math.round(imageOffsetX / WINDOW_WIDTH)\n\n // Check if the image index has changed and the FlatList didn't scroll past the first or last image\n const didImageIndexChange = newImageIndex !== stableCurrentImageIndexRef.current\n const isImageIndexWithinBounds =\n newImageIndex >= 0 && newImageIndex < stableImageAttachmentsLengthRef.current\n\n if (didImageIndexChange && isImageIndexWithinBounds) {\n setCurrentImageIndex(newImageIndex)\n }\n }, [])\n\n // Supplements onMomentumScrollEnd by capturing the current image's index while the FlatList is actively scrolling.\n // Used in tandem with viewabilityConfig to trigger when the image is 50% visible in the window.\n const onViewableItemsChanged = useCallback(\n ({ viewableItems }: { viewableItems: ViewToken<DenormalizedMessageAttachmentResource>[] }) => {\n if (viewableItems.length === 0) return\n\n // Use the first viewable item which is enforced by the FlatList's pagingEnabled prop that allows only two images to be visible at a time when scrolling.\n const firstViewableItem = viewableItems[0]\n const newIndex = firstViewableItem.index\n\n if (newIndex !== null && newIndex !== stableCurrentImageIndexRef.current) {\n setCurrentImageIndex(newIndex)\n }\n },\n []\n )\n\n /* ============================\n GESTURES\n ============================ */\n const singleTapGesture = Gesture.Tap()\n .minPointers(SINGLE_FINGER_POINTER)\n .numberOfTaps(1)\n .onStart(() => {\n lightboxToolbarVisible.value = withSpring(\n lightboxToolbarVisible.value > 0.5 ? 0 : 1,\n RESET_SPRING_CONFIG\n )\n })\n\n const doubleTapGesture = Gesture.Tap()\n .minPointers(SINGLE_FINGER_POINTER)\n .numberOfTaps(2)\n .onStart(e => {\n const isZoomedIn = scale.value > DEFAULT_SCALE\n if (isZoomedIn) {\n runOnJS(resetAllGestures)()\n } else {\n // Hide toolbar when starting to zoom\n lightboxToolbarVisible.value = withSpring(0, RESET_SPRING_CONFIG)\n\n // Zoom to 2x at tap location\n const newTranslation = zoomToFocalPoint({\n focalPointX: e.x,\n focalPointY: e.y,\n targetScale: DOUBLE_TAP_ZOOM_SCALE,\n currentSavedScale: savedScale.value,\n currentSavedTranslateX: savedTranslateX.value,\n currentSavedTranslateY: savedTranslateY.value,\n })\n\n // Apply clamping to ensure image edges remain within window boundaries\n const clampedTranslate = clampTranslation({\n x: newTranslation.translateX,\n y: newTranslation.translateY,\n currentScale: DOUBLE_TAP_ZOOM_SCALE,\n })\n\n // Animate to new scale and translation\n scale.value = withSpring(DOUBLE_TAP_ZOOM_SCALE, RESET_SPRING_CONFIG)\n translateX.value = withSpring(clampedTranslate.x, RESET_SPRING_CONFIG)\n translateY.value = withSpring(clampedTranslate.y, RESET_SPRING_CONFIG)\n\n // Update saved state for next gesture\n savedScale.value = DOUBLE_TAP_ZOOM_SCALE\n savedTranslateX.value = clampedTranslate.x\n savedTranslateY.value = clampedTranslate.y\n\n // Disable FlatList scroll since image is now zoomed\n runOnJS(enableFlatListScroll)(false)\n }\n })\n\n const pinchGesture = Gesture.Pinch()\n .onStart(e => {\n focalX.value = e.focalX\n focalY.value = e.focalY\n\n // Hide toolbar when starting to zoom\n lightboxToolbarVisible.value = withSpring(0, RESET_SPRING_CONFIG)\n\n // Ensure that pinch accounts for the decay animation and starts from the true current position.\n savedTranslateX.value = translateX.value\n savedTranslateY.value = translateY.value\n })\n .onUpdate(e => {\n // Zoom image in/out within scale limits\n const newScale = savedScale.value * e.scale\n const newScaleClamped = Math.min(\n MAX_SCALE + MAX_SCALE_OVERSHOOT,\n Math.max(MIN_SCALE, newScale)\n )\n\n // Check if we just reached the minimum scale threshold\n const previousScale = scale.value\n const reacjedMinScale = previousScale > MIN_SCALE && newScaleClamped <= MIN_SCALE\n\n if (reacjedMinScale) {\n runOnJS(triggerDismissHaptic)()\n }\n\n scale.value = newScaleClamped\n\n // Calculate new translation to keep focal point under fingers\n const newTranslation = zoomToFocalPoint({\n focalPointX: focalX.value,\n focalPointY: focalY.value,\n targetScale: newScaleClamped,\n currentSavedScale: savedScale.value,\n currentSavedTranslateX: savedTranslateX.value,\n currentSavedTranslateY: savedTranslateY.value,\n })\n\n // Apply translation without clamping to ensure focal point doesn't jump during gesture\n translateX.value = newTranslation.translateX\n translateY.value = newTranslation.translateY\n })\n .onEnd(() => {\n const currentScale = scale.value\n\n // Dismiss modal if fully zoomed out\n if (currentScale <= MIN_SCALE) {\n runOnJS(handleCloseModal)()\n return\n }\n\n // Reset all gestures if image is near default scale\n const isNearDefaultScale = currentScale <= DEFAULT_SCALE + 0.1\n if (isNearDefaultScale) {\n runOnJS(resetAllGestures)()\n return\n }\n\n // Check if overshooting the max scale and clamp it\n const finalScale = currentScale > MAX_SCALE ? MAX_SCALE : currentScale\n\n if (finalScale === MAX_SCALE) {\n runOnJS(triggerMaxZoomHaptic)()\n }\n\n // Recalculate translation using focal point to always return to the same position when image is overshooting the max scale\n const newTranslation = zoomToFocalPoint({\n focalPointX: focalX.value,\n focalPointY: focalY.value,\n targetScale: finalScale,\n currentSavedScale: savedScale.value,\n currentSavedTranslateX: savedTranslateX.value,\n currentSavedTranslateY: savedTranslateY.value,\n })\n\n // Ensure image edges remain within window boundaries\n const clampedTranslate = clampTranslation({\n x: newTranslation.translateX,\n y: newTranslation.translateY,\n currentScale: finalScale,\n })\n\n // Animate to position and scale limits with a spring\n scale.value = withSpring(finalScale, RESET_SPRING_CONFIG)\n translateX.value = withSpring(clampedTranslate.x, RESET_SPRING_CONFIG)\n translateY.value = withSpring(clampedTranslate.y, RESET_SPRING_CONFIG)\n\n // Save state for next gesture\n savedScale.value = finalScale\n savedTranslateX.value = clampedTranslate.x\n savedTranslateY.value = clampedTranslate.y\n\n // Disable FlatList scroll since image is still zoomed\n runOnJS(enableFlatListScroll)(false)\n })\n\n const panGesture = Gesture.Pan()\n .minDistance(MIN_DISTANCE_FOR_PAN)\n .minPointers(SINGLE_FINGER_POINTER)\n .maxPointers(SINGLE_FINGER_POINTER)\n .onStart(() => {\n // Only start pan if image is zoomed in - prevents conflicts with FlatList\n if (scale.value <= DEFAULT_SCALE) return\n\n // Update saved position to current animated position\n // This ensures smooth continuation if decay is running\n savedTranslateX.value = translateX.value\n savedTranslateY.value = translateY.value\n })\n .onUpdate(e => {\n // Only pan if image is zoomed in - prevents conflicts with FlatList\n if (scale.value <= DEFAULT_SCALE) return\n\n const newTranslateX = savedTranslateX.value + e.translationX\n const newTranslateY = savedTranslateY.value + e.translationY\n\n // Ensure image edges remain within window boundaries\n const clampedTranslate = clampTranslation({\n x: newTranslateX,\n y: newTranslateY,\n currentScale: scale.value,\n })\n translateX.value = clampedTranslate.x\n translateY.value = clampedTranslate.y\n })\n .onEnd(e => {\n // Prevents saving pan position if image is zoomed out while panning\n if (scale.value <= DEFAULT_SCALE) return\n\n const clampDecayBounds = clampDecay(scale.value)\n\n translateX.value = withDecay(\n {\n velocity: e.velocityX * DECAY_VELOCITY_FACTOR,\n clamp: clampDecayBounds.x,\n deceleration: 0.996,\n },\n finished => {\n if (finished) {\n savedTranslateX.value = translateX.value\n }\n }\n )\n\n translateY.value = withDecay(\n {\n velocity: e.velocityY * DECAY_VELOCITY_FACTOR,\n clamp: clampDecayBounds.y,\n deceleration: 0.996,\n },\n finished => {\n if (finished) {\n savedTranslateY.value = translateY.value\n }\n }\n )\n })\n\n const panToDismissModalGesture = Gesture.Pan()\n .onUpdate(e => {\n const atDefaultScale = scale.value === DEFAULT_SCALE\n\n // Calculate zoom conditions for dismissing modal\n const atVerticalBoundry = isImageAtVerticalBoundry(scale.value)\n const panDirectionIsPrimarilyVertical = Math.abs(e.translationY) > Math.abs(e.translationX)\n const canDismissWhileZoomed = atVerticalBoundry && panDirectionIsPrimarilyVertical\n\n if (!(atDefaultScale || canDismissWhileZoomed)) return\n\n // Fade image if its been panned past 50% of the dismiss threshold\n const panDistance = Math.abs(e.translationY)\n const halfThreshold = DISMISS_PAN_THRESHOLD / 2\n const fadeDistance = Math.max(0, panDistance - halfThreshold)\n const fadeProgress = fadeDistance / halfThreshold\n\n // Check if we just crossed the dismiss threshold\n const previousDistance = Math.abs(dismissY.value)\n const crossedDismissThreshold =\n previousDistance < DISMISS_PAN_THRESHOLD && panDistance >= DISMISS_PAN_THRESHOLD\n\n if (crossedDismissThreshold) {\n runOnJS(triggerDismissHaptic)()\n }\n\n opacity.value = Math.max(0, DEFAULT_OPACITY - fadeProgress)\n dismissY.value = e.translationY\n })\n .onEnd(() => {\n const exceededDismissThreshold = Math.abs(dismissY.value) > DISMISS_PAN_THRESHOLD\n\n if (exceededDismissThreshold) {\n runOnJS(handleCloseModal)()\n } else {\n runOnJS(resetDismissGestures)()\n }\n })\n\n /* ==============================\n COMPOSE GESTURES\n ================================= */\n // Race between pinch and pan ensures only one is active at a time, preserving focal point logic\n const pinchOrPanGestures = Gesture.Race(pinchGesture, panGesture)\n\n // Exclusive race ensures single tap doesn't interfere with double tap\n const tapGestures = Gesture.Exclusive(doubleTapGesture, singleTapGesture)\n\n // Race between tap gestures and pinch/pan\n const transformImageGestures = Gesture.Race(tapGestures, pinchOrPanGestures)\n\n // Dismiss can work simultaneously with all gestures\n const composedGesture = Gesture.Simultaneous(transformImageGestures, panToDismissModalGesture)\n\n return (\n <>\n <StatusBar\n barStyle=\"light-content\"\n hidden={isStatusBarHidden}\n animated\n showHideTransition=\"slide\"\n />\n <GestureHandlerRootView>\n <GestureDetector gesture={composedGesture}>\n <FlatList\n data={imageAttachments}\n renderItem={({ item, index }) => (\n <GestureImage\n item={item}\n gesturesEnabled={index === currentImageIndex}\n scale={scale}\n translateX={translateX}\n translateY={translateY}\n dismissY={dismissY}\n opacity={opacity}\n />\n )}\n keyExtractor={(item, index) => `${item.id}-${index}`}\n horizontal\n pagingEnabled\n scrollEnabled={flatListScrollEnabled}\n showsHorizontalScrollIndicator={false}\n initialScrollIndex={initialImageIndex}\n getItemLayout={getItemLayout}\n onMomentumScrollEnd={onMomentumScrollEnd}\n onViewableItemsChanged={onViewableItemsChanged}\n viewabilityConfig={{\n itemVisiblePercentThreshold: 50, // 50% of the image must be visible in the window to be considered viewable\n }}\n style={styles.gallery}\n contentContainerStyle={styles.galleryContentContainer}\n />\n </GestureDetector>\n </GestureHandlerRootView>\n <LightboxToolbarHeader\n metaProps={metaProps}\n lightboxToolbarVisible={lightboxToolbarVisible}\n isStatusBarHidden={isStatusBarHidden}\n handleOpenInBrowser={handleOpenInBrowser}\n handleCloseModal={handleCloseModal}\n />\n {imageAttachments.length > 1 && (\n <LightboxToolbarFooter\n lightboxToolbarVisible={lightboxToolbarVisible}\n totalImages={imageAttachments.length}\n currentImageIndex={currentImageIndex}\n />\n )}\n </>\n )\n}\n\ninterface GestureImageProps {\n item: DenormalizedMessageAttachmentResource\n gesturesEnabled: boolean\n scale: SharedValue<number>\n translateX: SharedValue<number>\n translateY: SharedValue<number>\n dismissY: SharedValue<number>\n opacity: SharedValue<number>\n}\n\nconst GestureImage = ({\n item,\n gesturesEnabled,\n scale,\n translateX,\n translateY,\n dismissY,\n opacity,\n}: GestureImageProps) => {\n const styles = useStyles()\n const { url: itemUrl, urlMedium: itemUrlMedium } = item.attributes\n\n const animatedImageStyles = useAnimatedStyle(() => {\n return {\n transform: [\n { translateX: gesturesEnabled ? translateX.value : DEFAULT_TRANSLATE_X },\n { translateY: gesturesEnabled ? translateY.value + dismissY.value : DEFAULT_TRANSLATE_Y },\n { scale: gesturesEnabled ? scale.value : DEFAULT_SCALE },\n ],\n opacity: opacity.value,\n }\n })\n\n return (\n <TouchEventIsolator>\n <Image\n source={{ uri: itemUrlMedium || itemUrl }}\n style={styles.gestureImage}\n animatedImageStyle={animatedImageStyles}\n loadingBackgroundStyles={styles.gestureImageLoading}\n resizeMode=\"contain\"\n alt=\"\"\n />\n </TouchEventIsolator>\n )\n}\n\nconst TouchEventIsolator = ({ children }: { children: React.ReactNode }) => {\n const styles = useStyles()\n\n return (\n <View style={styles.touchEventIsolator} onStartShouldSetResponder={() => true}>\n {children}\n </View>\n )\n}\n\ninterface LightboxToolbarHeaderProps {\n metaProps: MetaProps\n lightboxToolbarVisible: SharedValue<number>\n isStatusBarHidden: boolean\n handleOpenInBrowser: () => void\n handleCloseModal: () => void\n}\n\nconst LightboxToolbarHeader = ({\n metaProps,\n lightboxToolbarVisible,\n isStatusBarHidden,\n handleOpenInBrowser,\n handleCloseModal,\n}: LightboxToolbarHeaderProps) => {\n const styles = useStyles()\n const { authorName, createdAt } = metaProps\n\n const animatedHeaderStyles = useAnimatedStyle(() => ({\n opacity: lightboxToolbarVisible.value,\n transform: [\n { translateY: (1 - lightboxToolbarVisible.value) * -20 }, // slide up when hiding\n ],\n }))\n\n return (\n <Animated.View\n style={[styles.lightboxToolbar, styles.lightboxToolbarHeader, animatedHeaderStyles]}\n accessibilityRole=\"toolbar\"\n >\n <View style={styles.lightboxToolbarHeaderMetaContainer}>\n <Heading variant=\"h3\" style={styles.lightboxToolbarHeaderTitle} numberOfLines={1}>\n {authorName}\n </Heading>\n <Text variant=\"tertiary\" style={styles.lightboxToolbarHeaderSubtitle}>\n {formatDatePreview(createdAt)}\n </Text>\n </View>\n <IconButton\n onPress={handleOpenInBrowser}\n disabled={isStatusBarHidden}\n name=\"general.newWindow\"\n accessibilityRole=\"link\"\n accessibilityLabel=\"Open image in browser\"\n accessibilityHint=\"Image can be downloaded and shared through the browser.\"\n style={styles.lightboxToolbarButton}\n iconStyle={styles.lightboxToolbarButtonIcon}\n size=\"lg\"\n />\n <IconButton\n onPress={handleCloseModal}\n disabled={isStatusBarHidden}\n name=\"general.x\"\n accessibilityLabel=\"Close image\"\n style={styles.lightboxToolbarButton}\n iconStyle={styles.lightboxToolbarButtonIcon}\n size=\"lg\"\n />\n </Animated.View>\n )\n}\n\ninterface LightboxToolbarFooterProps {\n lightboxToolbarVisible: SharedValue<number>\n totalImages: number\n currentImageIndex: number\n}\n\nconst LightboxToolbarFooter = ({\n lightboxToolbarVisible,\n totalImages,\n currentImageIndex,\n}: LightboxToolbarFooterProps) => {\n const styles = useStyles()\n\n const animatedFooterStyles = useAnimatedStyle(() => ({\n opacity: lightboxToolbarVisible.value,\n transform: [\n { translateY: (1 - lightboxToolbarVisible.value) * 20 }, // slide down when showing\n ],\n }))\n\n return (\n <Animated.View\n style={[styles.lightboxToolbar, styles.lightboxToolbarFooter, animatedFooterStyles]}\n accessibilityRole=\"toolbar\"\n >\n <Text style={styles.lightboxToolbarFooterText}>\n {currentImageIndex + 1} of {totalImages}\n </Text>\n </Animated.View>\n )\n}\n\ninterface UseStylesProps {\n imageWidth?: number\n imageHeight?: number\n}\n\nconst useStyles = ({ imageWidth = 100, imageHeight = 100 }: UseStylesProps = {}) => {\n const { top, bottom } = useSafeAreaInsets()\n const backgroundColor = tokens.colorNeutral7\n const transparentBackgroundColor = useMemo(\n () => colorFunction(backgroundColor).alpha(0.8).toString(),\n [backgroundColor]\n )\n\n return StyleSheet.create({\n container: {\n maxWidth: '100%',\n position: 'relative',\n },\n touchEventIsolator: {\n flex: 1,\n },\n attachmentImageWrapper: {\n width: '100%',\n minWidth: 200,\n aspectRatio: imageWidth / imageHeight,\n },\n image: {\n borderRadius: 8,\n },\n gallery: {\n backgroundColor,\n },\n galleryContentContainer: {\n paddingTop: top,\n paddingBottom: bottom,\n },\n gestureImage: {\n height: '100%',\n width: WINDOW_WIDTH,\n backgroundColor: 'transparent',\n },\n gestureImageLoading: {\n backgroundColor,\n },\n lightboxToolbar: {\n width: '100%',\n position: 'absolute',\n flexDirection: 'row',\n alignItems: 'center',\n gap: 20,\n paddingHorizontal: 16,\n backgroundColor: transparentBackgroundColor,\n },\n lightboxToolbarButton: {\n backgroundColor,\n height: 40,\n width: 40,\n borderRadius: 50,\n borderWidth: 1,\n borderColor: tokens.colorNeutral24,\n },\n lightboxToolbarButtonIcon: {\n color: tokens.colorNeutral88,\n },\n lightboxToolbarHeader: {\n top: 0,\n paddingTop: top + 16,\n paddingBottom: 8,\n },\n lightboxToolbarHeaderMetaContainer: {\n flex: 1,\n },\n lightboxToolbarHeaderTitle: {\n marginRight: 'auto',\n flexShrink: 1,\n color: tokens.colorNeutral88,\n },\n lightboxToolbarHeaderSubtitle: {\n color: tokens.colorNeutral68,\n },\n lightboxToolbarFooter: {\n justifyContent: 'center',\n bottom: 0,\n paddingTop: 8,\n paddingBottom: bottom + 16,\n },\n lightboxToolbarFooterText: {\n color: tokens.colorNeutral88,\n fontWeight: platformFontWeightMedium,\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"video_attachment.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/video_attachment.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,qCAAqC,EAAE,MAAM,2DAA2D,CAAA;AAQjH,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,4BAA4B,EAC5B,UAAU,GACX,EAAE;IACD,UAAU,EAAE,qCAAqC,CAAA;IACjD,4BAA4B,EAAE,CAAC,UAAU,EAAE,qCAAqC,KAAK,IAAI,CAAA;IACzF,UAAU,EAAE,OAAO,CAAA;CACpB,2CA4DA"}
1
+ {"version":3,"file":"video_attachment.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/video_attachment.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,qCAAqC,EAAE,MAAM,2DAA2D,CAAA;AAOjH,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,4BAA4B,EAC5B,UAAU,GACX,EAAE;IACD,UAAU,EAAE,qCAAqC,CAAA;IACjD,4BAA4B,EAAE,CAAC,UAAU,EAAE,qCAAqC,KAAK,IAAI,CAAA;IACzF,UAAU,EAAE,OAAO,CAAA;CACpB,2CA4DA"}
@@ -3,7 +3,6 @@ import { PlatformPressable } from '@react-navigation/elements';
3
3
  import { useRef, useState } from 'react';
4
4
  import { View, StyleSheet } from 'react-native';
5
5
  import { useTheme } from '../../../hooks';
6
- import { safeAspectRatio } from '../../../utils';
7
6
  import { Video } from '../../../utils/native_adapters';
8
7
  import { tokens } from '../../../vendor/tapestry/tokens';
9
8
  import { Icon } from '../../display';
@@ -29,7 +28,7 @@ export function VideoAttachment({ attachment, onMessageAttachmentLongPress, isDe
29
28
  setIsOpen(false);
30
29
  }
31
30
  const viewRef = useRef(null);
32
- return (_jsx(View, { style: styles.container, ref: viewRef, children: _jsxs(PlatformPressable, { onPress: openVideo, onLongPress: () => !isDeleting && onMessageAttachmentLongPress(attachment), android_ripple: { color: colors.androidRippleNeutral, foreground: true }, accessibilityLabel: "Play Video", accessibilityHint: "Press to play video. Long press for more options", disabled: isDeleting, children: [_jsx(View, { style: styles.thumbnailOverlay }), _jsx(Video.Player, { ref: videoRef, source: { uri: url }, aspectRatio: safeAspectRatio(width, height), style: styles.video, onFullscreenPlayerWillDismiss: onFullscreenPlayerWillDismiss }), !isOpen && !isDeleting && (_jsx(View, { style: styles.playContainer, children: _jsx(View, { style: styles.playCircle, children: _jsx(Icon, { name: "services.play", style: styles.playIcon, accessibilityElementsHidden: true }) }) })), isDeleting && _jsx(AttachmentDeletingOverlay, {})] }) }));
31
+ return (_jsx(View, { style: styles.container, ref: viewRef, children: _jsxs(PlatformPressable, { onPress: openVideo, onLongPress: () => !isDeleting && onMessageAttachmentLongPress(attachment), android_ripple: { color: colors.androidRippleNeutral, foreground: true }, accessibilityLabel: "Play Video", accessibilityHint: "Press to play video. Long press for more options", disabled: isDeleting, children: [_jsx(View, { style: styles.thumbnailOverlay }), _jsx(Video.Player, { ref: videoRef, source: { uri: url }, aspectRatio: width / height, style: styles.video, onFullscreenPlayerWillDismiss: onFullscreenPlayerWillDismiss }), !isOpen && !isDeleting && (_jsx(View, { style: styles.playContainer, children: _jsx(View, { style: styles.playCircle, children: _jsx(Icon, { name: "services.play", style: styles.playIcon, accessibilityElementsHidden: true }) }) })), isDeleting && _jsx(AttachmentDeletingOverlay, {})] }) }));
33
32
  }
34
33
  const OVERLAY_Z_INDEX = 1;
35
34
  const useStyles = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"video_attachment.js","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/video_attachment.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport React, { useRef, useState } from 'react'\nimport { View, StyleSheet } from 'react-native'\nimport { useTheme } from '../../../hooks'\nimport { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'\nimport { safeAspectRatio } from '../../../utils'\nimport { Video, VideoPlayerHandle } from '../../../utils/native_adapters'\nimport { tokens } from '../../../vendor/tapestry/tokens'\nimport { Icon } from '../../display'\nimport { AttachmentDeletingOverlay } from './attachment_deleting_overlay'\nimport { MESSAGE_ATTACHMENT_WIDTH_SINGLE } from './constants'\n\nexport function VideoAttachment({\n attachment,\n onMessageAttachmentLongPress,\n isDeleting,\n}: {\n attachment: DenormalizedMessageAttachmentResource\n onMessageAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void\n isDeleting: boolean\n}) {\n const { colors } = useTheme()\n const styles = useStyles()\n\n const { attributes } = attachment\n const { width = MESSAGE_ATTACHMENT_WIDTH_SINGLE, height = MESSAGE_ATTACHMENT_WIDTH_SINGLE } =\n attributes.metadata || {}\n const { url } = attributes\n\n const videoRef = useRef<VideoPlayerHandle>(null)\n const [isOpen, setIsOpen] = useState(false)\n\n function openVideo() {\n if (!isOpen && videoRef.current) {\n videoRef.current.presentFullscreenPlayer()\n videoRef.current.play()\n setIsOpen(true)\n }\n }\n\n function onFullscreenPlayerWillDismiss() {\n videoRef.current?.pause()\n setIsOpen(false)\n }\n\n const viewRef = useRef<View>(null)\n\n return (\n <View style={styles.container} ref={viewRef}>\n <PlatformPressable\n onPress={openVideo}\n onLongPress={() => !isDeleting && onMessageAttachmentLongPress(attachment)}\n android_ripple={{ color: colors.androidRippleNeutral, foreground: true }}\n accessibilityLabel=\"Play Video\"\n accessibilityHint=\"Press to play video. Long press for more options\"\n disabled={isDeleting}\n >\n <View style={styles.thumbnailOverlay} />\n <Video.Player\n ref={videoRef}\n source={{ uri: url }}\n aspectRatio={safeAspectRatio(width, height)}\n style={styles.video}\n onFullscreenPlayerWillDismiss={onFullscreenPlayerWillDismiss}\n />\n {!isOpen && !isDeleting && (\n <View style={styles.playContainer}>\n <View style={styles.playCircle}>\n <Icon\n name=\"services.play\"\n style={styles.playIcon}\n accessibilityElementsHidden={true}\n />\n </View>\n </View>\n )}\n {isDeleting && <AttachmentDeletingOverlay />}\n </PlatformPressable>\n </View>\n )\n}\nconst OVERLAY_Z_INDEX = 1\n\nconst useStyles = () => {\n const { colors } = useTheme()\n const overlayBackgroundColor =\n colors.name === 'light'\n ? tokens.fillColorTransparencyDark010\n : tokens.fillColorTransparencyDark030\n\n return StyleSheet.create({\n container: {\n borderRadius: 8,\n overflow: 'hidden',\n },\n video: {\n width: '100%',\n backgroundColor: colors.fillColorNeutral070,\n },\n thumbnailOverlay: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: overlayBackgroundColor,\n zIndex: OVERLAY_Z_INDEX,\n },\n playContainer: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: OVERLAY_Z_INDEX + 1,\n },\n playCircle: {\n justifyContent: 'center',\n alignItems: 'center',\n width: 42,\n height: 42,\n borderRadius: 25,\n backgroundColor: colors.fillColorNeutral100Inverted,\n borderColor: colors.borderColorDefaultBase,\n borderWidth: 1,\n },\n playIcon: {\n fontSize: 20,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"video_attachment.js","sourceRoot":"","sources":["../../../../src/components/conversation/attachments/video_attachment.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport React, { useRef, useState } from 'react'\nimport { View, StyleSheet } from 'react-native'\nimport { useTheme } from '../../../hooks'\nimport { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'\nimport { Video, VideoPlayerHandle } from '../../../utils/native_adapters'\nimport { tokens } from '../../../vendor/tapestry/tokens'\nimport { Icon } from '../../display'\nimport { AttachmentDeletingOverlay } from './attachment_deleting_overlay'\nimport { MESSAGE_ATTACHMENT_WIDTH_SINGLE } from './constants'\n\nexport function VideoAttachment({\n attachment,\n onMessageAttachmentLongPress,\n isDeleting,\n}: {\n attachment: DenormalizedMessageAttachmentResource\n onMessageAttachmentLongPress: (attachment: DenormalizedMessageAttachmentResource) => void\n isDeleting: boolean\n}) {\n const { colors } = useTheme()\n const styles = useStyles()\n\n const { attributes } = attachment\n const { width = MESSAGE_ATTACHMENT_WIDTH_SINGLE, height = MESSAGE_ATTACHMENT_WIDTH_SINGLE } =\n attributes.metadata || {}\n const { url } = attributes\n\n const videoRef = useRef<VideoPlayerHandle>(null)\n const [isOpen, setIsOpen] = useState(false)\n\n function openVideo() {\n if (!isOpen && videoRef.current) {\n videoRef.current.presentFullscreenPlayer()\n videoRef.current.play()\n setIsOpen(true)\n }\n }\n\n function onFullscreenPlayerWillDismiss() {\n videoRef.current?.pause()\n setIsOpen(false)\n }\n\n const viewRef = useRef<View>(null)\n\n return (\n <View style={styles.container} ref={viewRef}>\n <PlatformPressable\n onPress={openVideo}\n onLongPress={() => !isDeleting && onMessageAttachmentLongPress(attachment)}\n android_ripple={{ color: colors.androidRippleNeutral, foreground: true }}\n accessibilityLabel=\"Play Video\"\n accessibilityHint=\"Press to play video. Long press for more options\"\n disabled={isDeleting}\n >\n <View style={styles.thumbnailOverlay} />\n <Video.Player\n ref={videoRef}\n source={{ uri: url }}\n aspectRatio={width / height}\n style={styles.video}\n onFullscreenPlayerWillDismiss={onFullscreenPlayerWillDismiss}\n />\n {!isOpen && !isDeleting && (\n <View style={styles.playContainer}>\n <View style={styles.playCircle}>\n <Icon\n name=\"services.play\"\n style={styles.playIcon}\n accessibilityElementsHidden={true}\n />\n </View>\n </View>\n )}\n {isDeleting && <AttachmentDeletingOverlay />}\n </PlatformPressable>\n </View>\n )\n}\nconst OVERLAY_Z_INDEX = 1\n\nconst useStyles = () => {\n const { colors } = useTheme()\n const overlayBackgroundColor =\n colors.name === 'light'\n ? tokens.fillColorTransparencyDark010\n : tokens.fillColorTransparencyDark030\n\n return StyleSheet.create({\n container: {\n borderRadius: 8,\n overflow: 'hidden',\n },\n video: {\n width: '100%',\n backgroundColor: colors.fillColorNeutral070,\n },\n thumbnailOverlay: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: overlayBackgroundColor,\n zIndex: OVERLAY_Z_INDEX,\n },\n playContainer: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: OVERLAY_Z_INDEX + 1,\n },\n playCircle: {\n justifyContent: 'center',\n alignItems: 'center',\n width: 42,\n height: 42,\n borderRadius: 25,\n backgroundColor: colors.fillColorNeutral100Inverted,\n borderColor: colors.borderColorDefaultBase,\n borderWidth: 1,\n },\n playIcon: {\n fontSize: 20,\n },\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/message.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAiB7C;;GAEG;AACH,UAAU,YAAa,SAAQ,eAAe;IAC5C,4BAA4B,EAAE,OAAO,CAAA;IACrC,eAAe,EAAE,MAAM,CAAA;IACvB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAgB,OAAO,CAAC,EACtB,4BAA4B,EAC5B,eAAe,EACf,wBAAwB,EACxB,aAAa,EACb,GAAG,OAAO,EACX,EAAE,YAAY,2CA0Rd"}
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/message.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAkB7C;;GAEG;AACH,UAAU,YAAa,SAAQ,eAAe;IAC5C,4BAA4B,EAAE,OAAO,CAAA;IACrC,eAAe,EAAE,MAAM,CAAA;IACvB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAgB,OAAO,CAAC,EACtB,4BAA4B,EAC5B,eAAe,EACf,wBAAwB,EACxB,aAAa,EACb,GAAG,OAAO,EACX,EAAE,YAAY,2CA2Rd"}
@@ -13,6 +13,7 @@ import { useLiveRelativeTime } from '../../hooks/use_live_relative_time';
13
13
  import { useMessageCreateOrUpdate } from '../../hooks/use_message_create_or_update';
14
14
  import { pluralize } from '../../utils';
15
15
  import { isNewMessage } from '../../utils/cache/messages_cache';
16
+ import { stripEmptyTextCharacters } from '../../utils/message_text';
16
17
  import { Haptic } from '../../utils/native_adapters';
17
18
  import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL, MESSAGE_AUTHOR_AVATAR_COLUMN_WIDTH, platformFontWeightMedium, } from '../../utils/styles';
18
19
  import { ComponentErrorBoundary } from '../page/component_error_boundary';
@@ -21,7 +22,8 @@ import { MessageMarkdown } from './message_markdown';
21
22
  import { MessageReadReceipts } from './message_read_receipts';
22
23
  import { TheirReplyConnector, MyReplyConnector, AVATAR_CONNECTOR_SPACING } from './reply_connectors';
23
24
  export function Message({ canDeleteNonAuthoredMessages, conversation_id, latestReadMessageSortKey, inReplyScreen, ...message }) {
24
- const { text, reactionCounts, pending, error, attachments, author } = message;
25
+ const { reactionCounts, pending, error, attachments, author } = message;
26
+ const text = stripEmptyTextCharacters(message.text ?? '');
25
27
  const deletingMessageIds = useDeletingIds('deleteMessage');
26
28
  const isDeleting = deletingMessageIds.has(message.id);
27
29
  const styles = useMessageStyles(message.mine, isDeleting);