@planningcenter/chat-react-native 3.14.0-rc.2 → 3.14.0-rc.4

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 (46) hide show
  1. package/build/components/conversation/attachments/image_attachment.js +18 -2
  2. package/build/components/conversation/attachments/image_attachment.js.map +1 -1
  3. package/build/components/conversation/message_reaction.d.ts.map +1 -1
  4. package/build/components/conversation/message_reaction.js +2 -0
  5. package/build/components/conversation/message_reaction.js.map +1 -1
  6. package/build/components/conversations/conversation_actions.js +3 -3
  7. package/build/components/conversations/conversation_actions.js.map +1 -1
  8. package/build/components/conversations/{action_toggle_button.d.ts → swipeable_toggle_button.d.ts} +3 -3
  9. package/build/components/conversations/swipeable_toggle_button.d.ts.map +1 -0
  10. package/build/components/conversations/{action_toggle_button.js → swipeable_toggle_button.js} +7 -3
  11. package/build/components/conversations/swipeable_toggle_button.js.map +1 -0
  12. package/build/hooks/use_conversation.d.ts.map +1 -1
  13. package/build/hooks/use_conversation.js +2 -0
  14. package/build/hooks/use_conversation.js.map +1 -1
  15. package/build/hooks/use_message_create_or_update.d.ts.map +1 -1
  16. package/build/hooks/use_message_create_or_update.js +2 -1
  17. package/build/hooks/use_message_create_or_update.js.map +1 -1
  18. package/build/screens/attachment_actions/hooks/useDeleteAttachment.d.ts.map +1 -1
  19. package/build/screens/attachment_actions/hooks/useDeleteAttachment.js +2 -0
  20. package/build/screens/attachment_actions/hooks/useDeleteAttachment.js.map +1 -1
  21. package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
  22. package/build/screens/conversation_new/components/groups_form.js +2 -0
  23. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  24. package/build/screens/conversation_new/components/services_form.d.ts.map +1 -1
  25. package/build/screens/conversation_new/components/services_form.js +3 -0
  26. package/build/screens/conversation_new/components/services_form.js.map +1 -1
  27. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  28. package/build/screens/conversations/components/list_header_component.js +2 -0
  29. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  30. package/build/screens/message_actions_screen.d.ts.map +1 -1
  31. package/build/screens/message_actions_screen.js +8 -4
  32. package/build/screens/message_actions_screen.js.map +1 -1
  33. package/package.json +2 -2
  34. package/src/components/conversation/attachments/image_attachment.tsx +25 -2
  35. package/src/components/conversation/message_reaction.tsx +2 -0
  36. package/src/components/conversations/conversation_actions.tsx +3 -3
  37. package/src/components/conversations/{action_toggle_button.tsx → swipeable_toggle_button.tsx} +9 -5
  38. package/src/hooks/use_conversation.ts +2 -0
  39. package/src/hooks/use_message_create_or_update.ts +3 -0
  40. package/src/screens/attachment_actions/hooks/useDeleteAttachment.tsx +2 -0
  41. package/src/screens/conversation_new/components/groups_form.tsx +2 -0
  42. package/src/screens/conversation_new/components/services_form.tsx +2 -0
  43. package/src/screens/conversations/components/list_header_component.tsx +2 -0
  44. package/src/screens/message_actions_screen.tsx +4 -2
  45. package/build/components/conversations/action_toggle_button.d.ts.map +0 -1
  46. package/build/components/conversations/action_toggle_button.js.map +0 -1
@@ -33,10 +33,10 @@ import { formatDatePreview } from '../../../utils/date'
33
33
  import { DenormalizedMessageAttachmentResource } from '../../../types/resources/denormalized_attachment_resource'
34
34
  import { PlatformPressable } from '@react-navigation/elements'
35
35
  import { useTheme } from '../../../hooks'
36
- import { platformFontWeightMedium } from '../../../utils'
36
+ import { Haptic, platformFontWeightMedium } from '../../../utils'
37
37
 
38
38
  const { width: WINDOW_WIDTH, height: WINDOW_HEIGHT } = Dimensions.get('window')
39
- const DISMISS_PAN_THRESHOLD = 250
39
+ const DISMISS_PAN_THRESHOLD = 175
40
40
  const MIN_DISTANCE_FOR_PAN = 10 // Higher threshold gives pinching priority
41
41
  const SINGLE_FINGER_POINTER = 1 // Single-finger panning / tapping helps to avoid conflicts with pinching
42
42
  const DEFAULT_OPACITY = 1
@@ -161,6 +161,7 @@ const LightboxGallery = ({
161
161
  const styles = useStyles()
162
162
  const insets = useSafeAreaInsets()
163
163
  const [currentImageIndex, setCurrentImageIndex] = useState(initialImageIndex)
164
+ const { soft: triggerMaxZoomHaptic, rigid: triggerDismissHaptic } = Haptic
164
165
 
165
166
  // Get current image data
166
167
  const currentImage = imageAttachments[currentImageIndex]
@@ -510,6 +511,15 @@ const LightboxGallery = ({
510
511
  MAX_SCALE + MAX_SCALE_OVERSHOOT,
511
512
  Math.max(MIN_SCALE, newScale)
512
513
  )
514
+
515
+ // Check if we just reached the minimum scale threshold
516
+ const previousScale = scale.value
517
+ const reacjedMinScale = previousScale > MIN_SCALE && newScaleClamped <= MIN_SCALE
518
+
519
+ if (reacjedMinScale) {
520
+ runOnJS(triggerDismissHaptic)()
521
+ }
522
+
513
523
  scale.value = newScaleClamped
514
524
 
515
525
  // Calculate new translation to keep focal point under fingers
@@ -545,6 +555,10 @@ const LightboxGallery = ({
545
555
  // Check if overshooting the max scale and clamp it
546
556
  const finalScale = currentScale > MAX_SCALE ? MAX_SCALE : currentScale
547
557
 
558
+ if (finalScale === MAX_SCALE) {
559
+ runOnJS(triggerMaxZoomHaptic)()
560
+ }
561
+
548
562
  // Recalculate translation using focal point to always return to the same position when image is overshooting the max scale
549
563
  const newTranslation = zoomToFocalPoint({
550
564
  focalPointX: focalX.value,
@@ -655,6 +669,15 @@ const LightboxGallery = ({
655
669
  const fadeDistance = Math.max(0, panDistance - halfThreshold)
656
670
  const fadeProgress = fadeDistance / halfThreshold
657
671
 
672
+ // Check if we just crossed the dismiss threshold
673
+ const previousDistance = Math.abs(dismissY.value)
674
+ const crossedDismissThreshold =
675
+ previousDistance < DISMISS_PAN_THRESHOLD && panDistance >= DISMISS_PAN_THRESHOLD
676
+
677
+ if (crossedDismissThreshold) {
678
+ runOnJS(triggerDismissHaptic)()
679
+ }
680
+
658
681
  opacity.value = Math.max(0, DEFAULT_OPACITY - fadeProgress)
659
682
  dismissY.value = e.translationY
660
683
  })
@@ -8,6 +8,7 @@ import { useMessageReactionToggle } from '../../hooks/use_message_reaction_toggl
8
8
  import { ReactionCountResource } from '../../types/resources/reaction'
9
9
  import { MessageResource } from '../../types'
10
10
  import { useCreateAndroidRippleColor } from '../../hooks/use_create_android_ripple_color'
11
+ import { Haptic } from '../../utils/native_adapters'
11
12
 
12
13
  export const REACTION_EMOJIS: Record<ReactionCountResource['value'], string> = {
13
14
  thumbs_up: '👍',
@@ -42,6 +43,7 @@ export function MessageReaction({
42
43
  if (!REACTION_EMOJIS[reaction.value]) return null
43
44
 
44
45
  const handlePress = () => {
46
+ Haptic.impactLight()
45
47
  handleReactionToggle({ value: reaction.value, mine: !!reaction.mine })
46
48
  }
47
49
 
@@ -20,7 +20,7 @@ import {
20
20
  import { ConversationResource } from '../../types'
21
21
  import { platformPressedOpacityStyle } from '../../utils'
22
22
  import { tokens } from '../../vendor/tapestry/tokens'
23
- import { ActionToggleButton } from './action_toggle_button'
23
+ import { SwipeableToggleButton } from './swipeable_toggle_button'
24
24
 
25
25
  export function ConversationActions({
26
26
  children,
@@ -193,7 +193,7 @@ function LeftActions({
193
193
 
194
194
  return (
195
195
  <View style={styles.actionButtonContainer}>
196
- <ActionToggleButton
196
+ <SwipeableToggleButton
197
197
  loading={isMarkReadPending}
198
198
  disabled={isConversationEmpty}
199
199
  toggled={!read}
@@ -201,7 +201,7 @@ function LeftActions({
201
201
  toggleContent={latestMessageUnreadToggleContent}
202
202
  backgroundColor={tokens.fillColorInteractionSwipeDefault}
203
203
  />
204
- <ActionToggleButton
204
+ <SwipeableToggleButton
205
205
  loading={isMutedPending}
206
206
  toggled={muted}
207
207
  onPress={handleMute}
@@ -5,8 +5,9 @@ import { Icon, IconString, Spinner, Text } from '../display'
5
5
  import { useCreateAndroidRippleColor, useTheme } from '../../hooks'
6
6
  import { platformFontWeightMedium, platformPressedOpacityStyle } from '../../utils/styles'
7
7
  import { tokens } from '../../vendor/tapestry/tokens'
8
+ import { Haptic } from '../../utils/native_adapters/configuration'
8
9
 
9
- interface ActionToggleButtonProps extends PressableProps {
10
+ interface SwipeableToggleButtonProps extends PressableProps {
10
11
  backgroundColor: string
11
12
  toggled: boolean
12
13
  toggleContent: {
@@ -16,7 +17,7 @@ interface ActionToggleButtonProps extends PressableProps {
16
17
  loading?: boolean
17
18
  }
18
19
 
19
- export function ActionToggleButton({
20
+ export function SwipeableToggleButton({
20
21
  backgroundColor,
21
22
  onPress,
22
23
  toggleContent,
@@ -24,7 +25,7 @@ export function ActionToggleButton({
24
25
  toggled,
25
26
  loading = false,
26
27
  ...props
27
- }: ActionToggleButtonProps) {
28
+ }: SwipeableToggleButtonProps) {
28
29
  const styles = useStyles({ backgroundColor, disabled })
29
30
  const androidRippleColor = useCreateAndroidRippleColor({
30
31
  color: backgroundColor,
@@ -34,7 +35,10 @@ export function ActionToggleButton({
34
35
 
35
36
  return (
36
37
  <Pressable
37
- onPress={onPress}
38
+ onPress={event => {
39
+ Haptic.impactLight()
40
+ onPress?.(event)
41
+ }}
38
42
  style={({ pressed }) => [styles.button, pressed && platformPressedOpacityStyle]}
39
43
  android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}
40
44
  disabled={disabled || loading}
@@ -56,7 +60,7 @@ export function ActionToggleButton({
56
60
  )
57
61
  }
58
62
 
59
- const useStyles = ({ backgroundColor, disabled }: Partial<ActionToggleButtonProps>) => {
63
+ const useStyles = ({ backgroundColor, disabled }: Partial<SwipeableToggleButtonProps>) => {
60
64
  const { colors } = useTheme()
61
65
  const fillColor = disabled ? colors.textColorDefaultDisabled : tokens.colorNeutral100White
62
66
  const buttonBackgroundColor = disabled
@@ -5,6 +5,7 @@ import { transformGetToPost } from '../utils/client/request_helpers'
5
5
  import { useApiClient } from './use_api_client'
6
6
  import { getRequestQueryKey, useSuspenseGet } from './use_suspense_api'
7
7
  import { getConversationsRequestArgs } from '../utils/request/conversation'
8
+ import { Haptic } from '../utils/native_adapters'
8
9
 
9
10
  export const getConversationRequestArgs = ({ conversation_id }: { conversation_id: number }) => ({
10
11
  url: `/me/conversations/${conversation_id}`,
@@ -139,6 +140,7 @@ export const useConversationDelete = ({ conversation_id }: { conversation_id: nu
139
140
  },
140
141
  onSuccess: () => {
141
142
  queryClient.invalidateQueries({ queryKey: [url] })
143
+ Haptic.notificationSuccess()
142
144
  },
143
145
  })
144
146
  }
@@ -5,6 +5,7 @@ import { ApiCollection, ApiResource, MessageResource } from '../types'
5
5
  import { chatQueryClient } from '../contexts/api_provider'
6
6
  import {
7
7
  deleteRecordInPagesData,
8
+ Haptic,
8
9
  updateOrCreateRecordInPagesData,
9
10
  updateRecordInPagesData,
10
11
  } from '../utils'
@@ -93,6 +94,8 @@ export function useMessageCreateOrUpdate({ conversationId, message }: Props) {
93
94
  },
94
95
  onError: (error, variables, context) => {
95
96
  const { message: optimisticMessage } = context || {}
97
+ Haptic.notificationError()
98
+
96
99
  // Add error to the optimistic message from the cache on error
97
100
  if (optimisticMessage) {
98
101
  const queryKey = getMessagesQueryKey({ conversation_id: conversationId })
@@ -4,6 +4,7 @@ import { useMutation } from '@tanstack/react-query'
4
4
  import { useApiClient } from '../../../hooks/use_api_client'
5
5
  import { useCallback } from 'react'
6
6
  import { useConversationMessages } from '../../../hooks/use_conversation_messages'
7
+ import { Haptic } from '../../../utils/native_adapters'
7
8
 
8
9
  type UseDeleteAttachmentProps = {
9
10
  conversation_id: number
@@ -30,6 +31,7 @@ export function useDeleteAttachment({
30
31
  mutationFn: deleteAttachment,
31
32
  onSuccess: () => {
32
33
  refetch()
34
+ Haptic.notificationSuccess()
33
35
  navigation.goBack()
34
36
  },
35
37
  onError: () => {
@@ -14,6 +14,7 @@ import { GroupsGroupResource } from '../../../types'
14
14
  import { GraphId } from '../../../types/resources/group_resource'
15
15
  import { pluralize } from '../../../utils'
16
16
  import { Divider, FormList } from './form_list'
17
+ import { Haptic } from '../../../utils/native_adapters'
17
18
 
18
19
  type GroupsFormProps = {
19
20
  groupId: number
@@ -44,6 +45,7 @@ export const GroupsForm = ({ groupId, chat_group_graph_id }: GroupsFormProps) =>
44
45
  chat_group_graph_id,
45
46
  })
46
47
  )
48
+ Haptic.notificationSuccess()
47
49
  },
48
50
  [chat_group_graph_id, navigation]
49
51
  )
@@ -70,6 +70,8 @@ export const ServicesForm = ({
70
70
  conversation_id: conversation.id,
71
71
  })
72
72
  )
73
+ // Only trigger success haptic if creating a new conversation
74
+ if (!selectionHasConversation) Haptic.notificationSuccess()
73
75
  },
74
76
  })
75
77
 
@@ -7,6 +7,7 @@ import { useMarkAllRead } from '../../../hooks/use_conversations_actions'
7
7
  import { useCanDisplayGroups } from '../../../hooks/use_groups'
8
8
  import { ConversationsScreenProps } from '../conversations_screen'
9
9
  import { ChatGroupBadge } from './chat_group_badge'
10
+ import { Haptic } from '../../../utils/native_adapters'
10
11
 
11
12
  const ROW_PADDING_HORIZONTAL = 16
12
13
 
@@ -41,6 +42,7 @@ export const ListHeaderComponent = () => {
41
42
 
42
43
  const handleMarkAllRead = useCallback(() => {
43
44
  currentPersonCache.update({ unreadCount: 0 })
45
+ Haptic.notificationSuccess()
44
46
  markAllRead()
45
47
  }, [currentPersonCache, markAllRead])
46
48
 
@@ -82,6 +82,7 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
82
82
  mutationFn: deleteMessage,
83
83
  onSuccess: () => {
84
84
  refetch()
85
+ Haptic.notificationSuccess()
85
86
  navigation.goBack()
86
87
  },
87
88
  onError: () => {
@@ -129,12 +130,13 @@ export function MessageActionsScreen({ route }: MessageActionsScreenProps) {
129
130
  <Reaction
130
131
  key={index}
131
132
  reaction={reaction}
132
- onPress={() =>
133
+ onPress={() => {
134
+ Haptic.impactLight()
133
135
  handleReactionToggle({
134
136
  value: reaction.value,
135
137
  mine: reaction.mine,
136
138
  })
137
- }
139
+ }}
138
140
  />
139
141
  ))}
140
142
  </View>
@@ -1 +0,0 @@
1
- {"version":3,"file":"action_toggle_button.d.ts","sourceRoot":"","sources":["../../../src/components/conversations/action_toggle_button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7E,OAAO,EAAQ,UAAU,EAAiB,MAAM,YAAY,CAAA;AAK5D,UAAU,uBAAwB,SAAQ,cAAc;IACtD,eAAe,EAAE,MAAM,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE;QACb,IAAI,EAAE;YAAE,QAAQ,EAAE,UAAU,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;QAC7C,KAAK,EAAE;YAAE,QAAQ,EAAE,UAAU,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAC/C,CAAA;IACD,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,eAAe,EACf,OAAO,EACP,aAAa,EACb,QAAgB,EAChB,OAAO,EACP,OAAe,EACf,GAAG,KAAK,EACT,EAAE,uBAAuB,qBA8BzB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"action_toggle_button.js","sourceRoot":"","sources":["../../../src/components/conversations/action_toggle_button.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react'\nimport { StyleSheet } from 'react-native'\nimport { Pressable, type PressableProps } from 'react-native-gesture-handler'\nimport { Icon, IconString, Spinner, Text } from '../display'\nimport { useCreateAndroidRippleColor, useTheme } from '../../hooks'\nimport { platformFontWeightMedium, platformPressedOpacityStyle } from '../../utils/styles'\nimport { tokens } from '../../vendor/tapestry/tokens'\n\ninterface ActionToggleButtonProps extends PressableProps {\n backgroundColor: string\n toggled: boolean\n toggleContent: {\n true: { iconName: IconString; label: string }\n false: { iconName: IconString; label: string }\n }\n loading?: boolean\n}\n\nexport function ActionToggleButton({\n backgroundColor,\n onPress,\n toggleContent,\n disabled = false,\n toggled,\n loading = false,\n ...props\n}: ActionToggleButtonProps) {\n const styles = useStyles({ backgroundColor, disabled })\n const androidRippleColor = useCreateAndroidRippleColor({\n color: backgroundColor,\n })\n\n const { iconName, label } = toggleContent[toggled ? 'true' : 'false']\n\n return (\n <Pressable\n onPress={onPress}\n style={({ pressed }) => [styles.button, pressed && platformPressedOpacityStyle]}\n android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}\n disabled={disabled || loading}\n accessibilityRole=\"togglebutton\"\n accessibilityState={{ checked: toggled }}\n {...props}\n >\n {loading ? (\n <Spinner size={20} />\n ) : (\n <>\n <Icon name={iconName} style={styles.icon} size={20} />\n <Text variant=\"footnote\" style={styles.text}>\n {label}\n </Text>\n </>\n )}\n </Pressable>\n )\n}\n\nconst useStyles = ({ backgroundColor, disabled }: Partial<ActionToggleButtonProps>) => {\n const { colors } = useTheme()\n const fillColor = disabled ? colors.textColorDefaultDisabled : tokens.colorNeutral100White\n const buttonBackgroundColor = disabled\n ? colors.fillColorButtonNeutralSolidDisabled\n : backgroundColor\n\n return StyleSheet.create({\n button: {\n flex: 1,\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 4,\n height: '100%',\n backgroundColor: buttonBackgroundColor,\n },\n icon: {\n color: fillColor,\n marginTop: 4,\n },\n text: {\n color: fillColor,\n fontWeight: platformFontWeightMedium,\n },\n })\n}\n"]}