@planningcenter/chat-react-native 3.44.1-qa-875.0 → 3.44.1-rc.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 (42) 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/reply_shadow_message.js +2 -2
  10. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  11. package/build/components/display/badge.js +1 -3
  12. package/build/components/display/badge.js.map +1 -1
  13. package/build/hooks/use_conversation_messages.d.ts +0 -1
  14. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  15. package/build/hooks/use_conversation_messages.js +2 -10
  16. package/build/hooks/use_conversation_messages.js.map +1 -1
  17. package/build/screens/conversation_screen.d.ts.map +1 -1
  18. package/build/screens/conversation_screen.js +3 -10
  19. package/build/screens/conversation_screen.js.map +1 -1
  20. package/build/screens/message_report_screen.d.ts.map +1 -1
  21. package/build/screens/message_report_screen.js +2 -4
  22. package/build/screens/message_report_screen.js.map +1 -1
  23. package/build/utils/index.d.ts +0 -1
  24. package/build/utils/index.d.ts.map +1 -1
  25. package/build/utils/index.js +0 -1
  26. package/build/utils/index.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/components/conversation/attachments/giphy_attachment.tsx +2 -2
  29. package/src/components/conversation/attachments/image_attachment.tsx +26 -30
  30. package/src/components/conversation/attachments/video_attachment.tsx +1 -2
  31. package/src/components/conversation/reply_shadow_message.tsx +2 -2
  32. package/src/components/display/badge.tsx +1 -3
  33. package/src/hooks/use_conversation_messages.ts +2 -10
  34. package/src/screens/conversation_screen.tsx +3 -10
  35. package/src/screens/message_report_screen.tsx +6 -4
  36. package/src/utils/index.ts +0 -1
  37. package/build/utils/aspect_ratio.d.ts +0 -2
  38. package/build/utils/aspect_ratio.d.ts.map +0 -1
  39. package/build/utils/aspect_ratio.js +0 -10
  40. package/build/utils/aspect_ratio.js.map +0 -1
  41. package/src/utils/__tests__/aspect_ratio.test.ts +0 -50
  42. package/src/utils/aspect_ratio.ts +0 -13
@@ -3,7 +3,7 @@ import React from 'react'
3
3
  import { Text, StyleSheet, Linking } from 'react-native'
4
4
  import { useTheme } from '../../../hooks'
5
5
  import { DenormalizedGiphyAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
6
- import { assertKeysAreNumbers, safeAspectRatio } from '../../../utils'
6
+ import { assertKeysAreNumbers } from '../../../utils'
7
7
  import { tokens } from '../../../vendor/tapestry/tokens'
8
8
  import { Image } from '../../display'
9
9
 
@@ -55,7 +55,7 @@ const useStyles = ({ imageWidth, imageHeight }: { imageWidth: number; imageHeigh
55
55
  },
56
56
  imageWrapper: {
57
57
  width: '100%',
58
- aspectRatio: safeAspectRatio(imageWidth, imageHeight),
58
+ aspectRatio: imageWidth / imageHeight,
59
59
  },
60
60
  image: {
61
61
  borderRadius: 8,
@@ -7,9 +7,9 @@ import {
7
7
  Modal,
8
8
  View,
9
9
  Linking,
10
+ Dimensions,
10
11
  NativeSyntheticEvent,
11
12
  NativeScrollEvent,
12
- useWindowDimensions,
13
13
  ViewToken,
14
14
  } from 'react-native'
15
15
  import {
@@ -30,12 +30,13 @@ import Animated, {
30
30
  import { useSafeAreaInsets, SafeAreaProvider } from 'react-native-safe-area-context'
31
31
  import { useTheme } from '../../../hooks'
32
32
  import { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
33
- import { Haptic, platformFontWeightMedium, safeAspectRatio } from '../../../utils'
33
+ import { Haptic, platformFontWeightMedium } from '../../../utils'
34
34
  import { formatDatePreview } from '../../../utils/date'
35
35
  import { tokens } from '../../../vendor/tapestry/tokens'
36
36
  import { IconButton, Image, Heading, Text } from '../../display'
37
37
  import { AttachmentDeletingOverlay } from './attachment_deleting_overlay'
38
38
 
39
+ const { width: WINDOW_WIDTH, height: WINDOW_HEIGHT } = Dimensions.get('window')
39
40
  const DISMISS_PAN_THRESHOLD = 175
40
41
  const MIN_DISTANCE_FOR_PAN = 10 // Higher threshold gives pinching priority
41
42
  const SINGLE_FINGER_POINTER = 1 // Single-finger panning / tapping helps to avoid conflicts with pinching
@@ -166,7 +167,6 @@ const LightboxGallery = ({
166
167
  }: LightboxGalleryProps) => {
167
168
  const styles = useStyles()
168
169
  const insets = useSafeAreaInsets()
169
- const { width: windowWidth, height: windowHeight } = useWindowDimensions()
170
170
  const [currentImageIndex, setCurrentImageIndex] = useState(initialImageIndex)
171
171
  const { soft: triggerMaxZoomHaptic, rigid: triggerDismissHaptic } = Haptic
172
172
 
@@ -177,8 +177,8 @@ const LightboxGallery = ({
177
177
  const imageHeight = metadata.height
178
178
 
179
179
  // Calculate available space for image display
180
- const availableWindowWidth = windowWidth
181
- const availableWindowHeight = windowHeight - insets.top - insets.bottom
180
+ const availableWindowWidth = WINDOW_WIDTH
181
+ const availableWindowHeight = WINDOW_HEIGHT - insets.top - insets.bottom
182
182
 
183
183
  /* ============================
184
184
  ANIMATION VALUES
@@ -299,8 +299,8 @@ const LightboxGallery = ({
299
299
  'worklet'
300
300
 
301
301
  // How far the focal point is from the center of the window
302
- const windowCenterX = windowWidth / 2
303
- const windowCenterY = windowHeight / 2
302
+ const windowCenterX = WINDOW_WIDTH / 2
303
+ const windowCenterY = WINDOW_HEIGHT / 2
304
304
 
305
305
  // Position of focal point relative to current image position
306
306
  const focalRelativeX = focalPointX - windowCenterX - currentSavedTranslateX
@@ -402,32 +402,29 @@ const LightboxGallery = ({
402
402
  // Used in tandem with FlatList's initialScrollIndex to quickly calculate the position and size of each image before they load.
403
403
  const getItemLayout = useCallback(
404
404
  (_: unknown, index: number) => ({
405
- length: windowWidth,
406
- offset: windowWidth * index,
405
+ length: WINDOW_WIDTH,
406
+ offset: WINDOW_WIDTH * index,
407
407
  index,
408
408
  }),
409
- [windowWidth]
409
+ []
410
410
  )
411
411
 
412
412
  // Captures the current image's index after the FlatList finishes its scroll animation.
413
413
  // Used in tandem with onViewableItemsChanged to ensure the final value for currentImageIndex is set.
414
- const onMomentumScrollEnd = useCallback(
415
- (event: NativeSyntheticEvent<NativeScrollEvent>) => {
416
- // Calculate the index of the image that is currently visible
417
- const imageOffsetX = event.nativeEvent.contentOffset.x
418
- const newImageIndex = Math.round(imageOffsetX / windowWidth)
419
-
420
- // Check if the image index has changed and the FlatList didn't scroll past the first or last image
421
- const didImageIndexChange = newImageIndex !== stableCurrentImageIndexRef.current
422
- const isImageIndexWithinBounds =
423
- newImageIndex >= 0 && newImageIndex < stableImageAttachmentsLengthRef.current
424
-
425
- if (didImageIndexChange && isImageIndexWithinBounds) {
426
- setCurrentImageIndex(newImageIndex)
427
- }
428
- },
429
- [windowWidth]
430
- )
414
+ const onMomentumScrollEnd = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
415
+ // Calculate the index of the image that is currently visible
416
+ const imageOffsetX = event.nativeEvent.contentOffset.x
417
+ const newImageIndex = Math.round(imageOffsetX / WINDOW_WIDTH)
418
+
419
+ // Check if the image index has changed and the FlatList didn't scroll past the first or last image
420
+ const didImageIndexChange = newImageIndex !== stableCurrentImageIndexRef.current
421
+ const isImageIndexWithinBounds =
422
+ newImageIndex >= 0 && newImageIndex < stableImageAttachmentsLengthRef.current
423
+
424
+ if (didImageIndexChange && isImageIndexWithinBounds) {
425
+ setCurrentImageIndex(newImageIndex)
426
+ }
427
+ }, [])
431
428
 
432
429
  // Supplements onMomentumScrollEnd by capturing the current image's index while the FlatList is actively scrolling.
433
430
  // Used in tandem with viewabilityConfig to trigger when the image is 50% visible in the window.
@@ -932,7 +929,6 @@ interface UseStylesProps {
932
929
 
933
930
  const useStyles = ({ imageWidth = 100, imageHeight = 100 }: UseStylesProps = {}) => {
934
931
  const { top, bottom } = useSafeAreaInsets()
935
- const { width: windowWidth } = useWindowDimensions()
936
932
  const backgroundColor = tokens.colorNeutral7
937
933
  const transparentBackgroundColor = useMemo(
938
934
  () => colorFunction(backgroundColor).alpha(0.8).toString(),
@@ -950,7 +946,7 @@ const useStyles = ({ imageWidth = 100, imageHeight = 100 }: UseStylesProps = {})
950
946
  attachmentImageWrapper: {
951
947
  width: '100%',
952
948
  minWidth: 200,
953
- aspectRatio: safeAspectRatio(imageWidth, imageHeight),
949
+ aspectRatio: imageWidth / imageHeight,
954
950
  },
955
951
  image: {
956
952
  borderRadius: 8,
@@ -964,7 +960,7 @@ const useStyles = ({ imageWidth = 100, imageHeight = 100 }: UseStylesProps = {})
964
960
  },
965
961
  gestureImage: {
966
962
  height: '100%',
967
- width: windowWidth,
963
+ width: WINDOW_WIDTH,
968
964
  backgroundColor: 'transparent',
969
965
  },
970
966
  gestureImageLoading: {
@@ -3,7 +3,6 @@ import React, { useRef, useState } from 'react'
3
3
  import { View, StyleSheet } from 'react-native'
4
4
  import { useTheme } from '../../../hooks'
5
5
  import { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
6
- import { safeAspectRatio } from '../../../utils'
7
6
  import { Video, VideoPlayerHandle } from '../../../utils/native_adapters'
8
7
  import { tokens } from '../../../vendor/tapestry/tokens'
9
8
  import { Icon } from '../../display'
@@ -59,7 +58,7 @@ export function VideoAttachment({
59
58
  <Video.Player
60
59
  ref={videoRef}
61
60
  source={{ uri: url }}
62
- aspectRatio={safeAspectRatio(width, height)}
61
+ aspectRatio={width / height}
63
62
  style={styles.video}
64
63
  onFullscreenPlayerWillDismiss={onFullscreenPlayerWillDismiss}
65
64
  />
@@ -18,7 +18,7 @@ import {
18
18
  DenormalizedExpandedLinkAttachmentResource,
19
19
  DenormalizedMessageAttachmentResource,
20
20
  } from '../../types/resources/denormalized_attachment_resource'
21
- import { assertKeysAreNumbers, pluralize, safeAspectRatio } from '../../utils'
21
+ import { assertKeysAreNumbers, pluralize } from '../../utils'
22
22
  import {
23
23
  CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
24
24
  MAX_FONT_SIZE_MULTIPLIER,
@@ -324,7 +324,7 @@ const useStyles = ({ mine, imageWidth = 32, imageHeight = 32 }: StylesProps = {}
324
324
  },
325
325
  imageWrapper: {
326
326
  width: 32 * fontScale,
327
- aspectRatio: safeAspectRatio(imageWidth, imageHeight),
327
+ aspectRatio: imageWidth / imageHeight,
328
328
  opacity: 0.5,
329
329
  },
330
330
  image: {
@@ -234,9 +234,7 @@ const useStyles = ({
234
234
  paddingVertical: variantStylesMap[variant].paddingVertical,
235
235
  paddingHorizontal: variantStylesMap[variant].paddingHorizontal,
236
236
  backgroundColor: variantStylesMap[variant].backgroundColor,
237
- // metaSubtle's borderRadius is 0, so this can't go below 0 - Android
238
- // hard-throws on a negative borderRadius during layout.
239
- borderRadius: Math.max(0, variantStylesMap[variant].borderRadius - 2),
237
+ borderRadius: variantStylesMap[variant].borderRadius - 2,
240
238
  },
241
239
  icon: {
242
240
  color: statusColorMap[appearance].icon,
@@ -7,7 +7,7 @@ export const useConversationMessages = (
7
7
  { conversation_id, reply_root_id }: { conversation_id: number; reply_root_id?: string | null },
8
8
  opts?: SuspensePaginatorOptions
9
9
  ) => {
10
- const { data, refetch, isRefetching, fetchNextPage, hasNextPage, isFetchingNextPage } =
10
+ const { data, refetch, isRefetching, fetchNextPage, hasNextPage } =
11
11
  useSuspensePaginator<MessageResource>(
12
12
  getMessagesRequestArgs({ conversation_id, reply_root_id }),
13
13
  opts
@@ -25,13 +25,5 @@ export const useConversationMessages = (
25
25
  [data]
26
26
  )
27
27
 
28
- return {
29
- messages,
30
- refetch,
31
- isRefetching,
32
- fetchNextPage,
33
- hasNextPage,
34
- isFetchingNextPage,
35
- queryKey,
36
- }
28
+ return { messages, refetch, isRefetching, fetchNextPage, hasNextPage, queryKey }
37
29
  }
@@ -91,7 +91,7 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
91
91
  const { conversation_id, editing_message_id, reply_root_id, reply_root_author_name } =
92
92
  route.params
93
93
  const { data: conversation } = useConversation(route.params)
94
- const { messages, fetchNextPage, hasNextPage, isFetchingNextPage } = useConversationMessages({
94
+ const { messages, fetchNextPage, hasNextPage } = useConversationMessages({
95
95
  conversation_id,
96
96
  reply_root_id,
97
97
  })
@@ -216,11 +216,7 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
216
216
  />
217
217
  )
218
218
  }}
219
- onEndReached={() => {
220
- // Android's inverted FlatList can refire onEndReached while a
221
- // page fetch is already in flight, queueing unbounded requests.
222
- if (hasNextPage && !isFetchingNextPage) fetchNextPage()
223
- }}
219
+ onEndReached={() => fetchNextPage()}
224
220
  ListHeaderComponent={<View style={styles.listHeader} />}
225
221
  />
226
222
  )}
@@ -381,10 +377,7 @@ export const ConversationScreenTitle = ({
381
377
  const usePressableHeaderStyle = () => {
382
378
  const { width } = useWindowDimensions()
383
379
  const slotReserve = useHeaderSlotReserve()
384
-
385
- // Guards split-screen/DeX widths where slotReserve * 2 exceeds the
386
- // available width, which would otherwise resolve to a negative maxWidth.
387
- const headerContainerStyle = { maxWidth: Math.max(0, width - slotReserve * 2) }
380
+ const headerContainerStyle = { maxWidth: width - slotReserve * 2 }
388
381
 
389
382
  return StyleSheet.create({
390
383
  container: {
@@ -30,9 +30,11 @@ export const MessageReportScreenOptions = ({
30
30
  }: NativeStackScreenProps<any>): NativeStackNavigationOptions => {
31
31
  const handleClose = () =>
32
32
  navigation.dispatch(
33
- StackActions.popTo('Conversation', {
34
- conversation_id: route.params?.conversation_id,
35
- })
33
+ StackActions.popTo(
34
+ 'Conversation',
35
+ { conversation_id: route.params?.conversation_id },
36
+ { merge: true }
37
+ )
36
38
  )
37
39
 
38
40
  return composeOptions(
@@ -100,7 +102,7 @@ function MessageReportScreenContent({
100
102
  }, [isFormValid, selectedReason, reasonDetail, submitReport])
101
103
 
102
104
  const handleClose = useCallback(() => {
103
- navigation.dispatch(StackActions.popTo('Conversation', { conversation_id }))
105
+ navigation.dispatch(StackActions.popTo('Conversation', { conversation_id }, { merge: true }))
104
106
  }, [navigation, conversation_id])
105
107
 
106
108
  const formVisible = status !== 'pending' && status !== 'success'
@@ -1,5 +1,4 @@
1
1
  export * from './assert_keys_are_numbers'
2
- export * from './aspect_ratio'
3
2
  export * from './attachment_kind'
4
3
  export * from './chat_type_label'
5
4
  export * from './cache'
@@ -1,2 +0,0 @@
1
- export declare const safeAspectRatio: (width: number | undefined, height: number | undefined, fallback?: number) => number;
2
- //# sourceMappingURL=aspect_ratio.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aspect_ratio.d.ts","sourceRoot":"","sources":["../../src/utils/aspect_ratio.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,GAAG,SAAS,EACzB,QAAQ,MAAM,GAAG,SAAS,EAC1B,iBAAY,KACX,MAKF,CAAA"}
@@ -1,10 +0,0 @@
1
- // Android hard-throws when a <Text>/<Image> is measured with a non-positive
2
- // resolved width (`Layout: -2 < 0`), so any aspectRatio derived from
3
- // attachment metadata must never resolve to 0, negative, or non-finite.
4
- export const safeAspectRatio = (width, height, fallback = 1) => {
5
- if (!width || !height)
6
- return fallback;
7
- const ratio = width / height;
8
- return Number.isFinite(ratio) && ratio > 0 ? ratio : fallback;
9
- };
10
- //# sourceMappingURL=aspect_ratio.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aspect_ratio.js","sourceRoot":"","sources":["../../src/utils/aspect_ratio.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,qEAAqE;AACrE,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,KAAyB,EACzB,MAA0B,EAC1B,QAAQ,GAAG,CAAC,EACJ,EAAE;IACV,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAA;IAEtC,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;IAC5B,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAA;AAC/D,CAAC,CAAA","sourcesContent":["// Android hard-throws when a <Text>/<Image> is measured with a non-positive\n// resolved width (`Layout: -2 < 0`), so any aspectRatio derived from\n// attachment metadata must never resolve to 0, negative, or non-finite.\nexport const safeAspectRatio = (\n width: number | undefined,\n height: number | undefined,\n fallback = 1\n): number => {\n if (!width || !height) return fallback\n\n const ratio = width / height\n return Number.isFinite(ratio) && ratio > 0 ? ratio : fallback\n}\n"]}
@@ -1,50 +0,0 @@
1
- import { safeAspectRatio } from '../aspect_ratio'
2
-
3
- describe('safeAspectRatio', () => {
4
- it.each([
5
- [200, 100, 2],
6
- [100, 200, 0.5],
7
- [100, 100, 1],
8
- ])('divides width %p by height %p to get %p', (width, height, expected) => {
9
- expect(safeAspectRatio(width, height)).toBe(expected)
10
- })
11
-
12
- describe('with values that would produce a non-positive or non-finite ratio', () => {
13
- it.each([
14
- [0, 100],
15
- [100, 0],
16
- [0, 0],
17
- [-100, 100],
18
- [100, -100],
19
- [undefined, 100],
20
- [100, undefined],
21
- [undefined, undefined],
22
- [NaN, 100],
23
- [100, NaN],
24
- [Infinity, 100],
25
- ])('falls back to 1 for width %p, height %p', (width, height) => {
26
- expect(safeAspectRatio(width, height)).toBe(1)
27
- })
28
- })
29
-
30
- it('falls back to a caller-supplied value instead of the default', () => {
31
- expect(safeAspectRatio(0, 100, 0.75)).toBe(0.75)
32
- })
33
-
34
- it('never returns 0, a negative number, or a non-finite number', () => {
35
- const invertedFallbackCases: Array<[number | undefined, number | undefined]> = [
36
- [0, 100],
37
- [100, 0],
38
- [-100, 100],
39
- [NaN, 100],
40
- [Infinity, 100],
41
- ]
42
-
43
- for (const [width, height] of invertedFallbackCases) {
44
- const ratio = safeAspectRatio(width, height)
45
- expect(ratio).not.toBe(0)
46
- expect(Number.isFinite(ratio)).toBe(true)
47
- expect(ratio).toBeGreaterThan(0)
48
- }
49
- })
50
- })
@@ -1,13 +0,0 @@
1
- // Android hard-throws when a <Text>/<Image> is measured with a non-positive
2
- // resolved width (`Layout: -2 < 0`), so any aspectRatio derived from
3
- // attachment metadata must never resolve to 0, negative, or non-finite.
4
- export const safeAspectRatio = (
5
- width: number | undefined,
6
- height: number | undefined,
7
- fallback = 1
8
- ): number => {
9
- if (!width || !height) return fallback
10
-
11
- const ratio = width / height
12
- return Number.isFinite(ratio) && ratio > 0 ? ratio : fallback
13
- }