@planningcenter/chat-react-native 3.1.0-rc.2 → 3.1.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 (89) hide show
  1. package/build/components/conversation/message.d.ts +1 -1
  2. package/build/components/conversation/message.js.map +1 -1
  3. package/build/components/conversations.d.ts.map +1 -1
  4. package/build/components/conversations.js +3 -6
  5. package/build/components/conversations.js.map +1 -1
  6. package/build/hooks/use_conversation.d.ts +3 -3
  7. package/build/hooks/use_conversation.js.map +1 -1
  8. package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -1
  9. package/build/hooks/use_conversation_jolt_events.js +4 -3
  10. package/build/hooks/use_conversation_jolt_events.js.map +1 -1
  11. package/build/hooks/use_conversation_messages.d.ts +3 -3
  12. package/build/hooks/use_conversation_messages.js.map +1 -1
  13. package/build/hooks/use_conversation_messages_jolt_events.d.ts +1 -1
  14. package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
  15. package/build/hooks/use_current_person.js +1 -1
  16. package/build/hooks/use_current_person.js.map +1 -1
  17. package/build/screens/conversation_details_screen.d.ts +1 -1
  18. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  19. package/build/screens/conversation_details_screen.js +47 -17
  20. package/build/screens/conversation_details_screen.js.map +1 -1
  21. package/build/screens/conversation_screen.d.ts +1 -1
  22. package/build/screens/conversation_screen.js.map +1 -1
  23. package/build/screens/create/conversation_create_screen.js.map +1 -1
  24. package/build/screens/design_system_screen.js +2 -2
  25. package/build/screens/design_system_screen.js.map +1 -1
  26. package/build/screens/message_actions_screen.d.ts +1 -1
  27. package/build/screens/message_actions_screen.js.map +1 -1
  28. package/build/screens/reactions_screen.d.ts +1 -1
  29. package/build/screens/reactions_screen.js +2 -2
  30. package/build/screens/reactions_screen.js.map +1 -1
  31. package/build/types/api_primitives.d.ts +1 -1
  32. package/build/types/api_primitives.d.ts.map +1 -1
  33. package/build/types/api_primitives.js.map +1 -1
  34. package/build/types/resources/app_grant.d.ts +2 -0
  35. package/build/types/resources/app_grant.d.ts.map +1 -1
  36. package/build/types/resources/app_grant.js.map +1 -1
  37. package/build/types/resources/conversation.d.ts +1 -1
  38. package/build/types/resources/conversation.js.map +1 -1
  39. package/build/types/resources/groups/groups_group_resource.d.ts +2 -0
  40. package/build/types/resources/groups/groups_group_resource.d.ts.map +1 -1
  41. package/build/types/resources/groups/groups_group_resource.js.map +1 -1
  42. package/build/types/resources/member.d.ts +1 -1
  43. package/build/types/resources/member.js.map +1 -1
  44. package/build/types/resources/person.d.ts +2 -0
  45. package/build/types/resources/person.d.ts.map +1 -1
  46. package/build/types/resources/person.js.map +1 -1
  47. package/build/types/resources/reaction.js.map +1 -1
  48. package/build/utils/cache/page_mutations.d.ts +3 -1
  49. package/build/utils/cache/page_mutations.d.ts.map +1 -1
  50. package/build/utils/cache/page_mutations.js.map +1 -1
  51. package/build/utils/jolt/transform_message_event_data_to_message_resource.d.ts +1 -1
  52. package/build/utils/jolt/transform_message_event_data_to_message_resource.js +2 -2
  53. package/build/utils/jolt/transform_message_event_data_to_message_resource.js.map +1 -1
  54. package/build/utils/jolt/transform_reaction_event_data_to_reaction_count_resource.d.ts +1 -1
  55. package/build/utils/jolt/transform_reaction_event_data_to_reaction_count_resource.js.map +1 -1
  56. package/build/utils/theme.d.ts +1 -0
  57. package/build/utils/theme.d.ts.map +1 -1
  58. package/build/utils/theme.js +2 -0
  59. package/build/utils/theme.js.map +1 -1
  60. package/build/vendor/tapestry/tokens.d.ts +2 -0
  61. package/build/vendor/tapestry/tokens.d.ts.map +1 -1
  62. package/build/vendor/tapestry/tokens.js +2 -0
  63. package/build/vendor/tapestry/tokens.js.map +1 -1
  64. package/package.json +2 -2
  65. package/src/components/conversation/message.tsx +1 -1
  66. package/src/components/conversations.tsx +6 -8
  67. package/src/hooks/use_conversation.ts +3 -3
  68. package/src/hooks/use_conversation_jolt_events.ts +8 -11
  69. package/src/hooks/use_conversation_messages.ts +3 -3
  70. package/src/hooks/use_conversation_messages_jolt_events.ts +1 -1
  71. package/src/hooks/use_current_person.ts +1 -1
  72. package/src/screens/conversation_details_screen.tsx +65 -15
  73. package/src/screens/conversation_screen.tsx +1 -1
  74. package/src/screens/create/conversation_create_screen.tsx +1 -1
  75. package/src/screens/design_system_screen.tsx +2 -2
  76. package/src/screens/message_actions_screen.tsx +1 -1
  77. package/src/screens/reactions_screen.tsx +3 -3
  78. package/src/types/api_primitives.ts +1 -1
  79. package/src/types/resources/app_grant.ts +2 -0
  80. package/src/types/resources/conversation.ts +1 -1
  81. package/src/types/resources/groups/groups_group_resource.ts +2 -0
  82. package/src/types/resources/member.ts +1 -1
  83. package/src/types/resources/person.ts +2 -0
  84. package/src/types/resources/reaction.ts +1 -1
  85. package/src/utils/cache/page_mutations.ts +1 -1
  86. package/src/utils/jolt/transform_message_event_data_to_message_resource.ts +3 -3
  87. package/src/utils/jolt/transform_reaction_event_data_to_reaction_count_resource.ts +1 -1
  88. package/src/utils/theme.ts +3 -0
  89. package/src/vendor/tapestry/tokens.ts +2 -0
@@ -15,7 +15,7 @@ import { JoltReactionEvent } from '../types/jolt_events'
15
15
  import { transformReactionEventDataToReactionCountResource } from '../utils/jolt/transform_reaction_event_data_to_reaction_count_resource'
16
16
 
17
17
  interface Props {
18
- conversationId: string
18
+ conversationId: number
19
19
  }
20
20
 
21
21
  export function useConversationMessagesJoltEvents({ conversationId }: Props) {
@@ -6,7 +6,7 @@ export const useCurrentPerson = () => {
6
6
  url: '/me',
7
7
  data: {
8
8
  fields: {
9
- Person: ['id', 'name', 'avatar', 'unread_count', 'pco_chat_enabled'],
9
+ Person: ['id', 'name', 'avatar', 'can_chat', 'unread_count', 'pco_chat_enabled'],
10
10
  },
11
11
  },
12
12
  })
@@ -6,6 +6,7 @@ import React, {
6
6
  type SetStateAction,
7
7
  type Dispatch,
8
8
  type ReactNode,
9
+ useRef,
9
10
  } from 'react'
10
11
  import {
11
12
  StyleSheet,
@@ -14,6 +15,7 @@ import {
14
15
  type TextStyle,
15
16
  type ViewStyle,
16
17
  type ViewProps,
18
+ Pressable,
17
19
  } from 'react-native'
18
20
  import { Heading, Icon, Person, Switch, Text } from '../components'
19
21
  import { useSuspenseGet, useTheme } from '../hooks'
@@ -26,6 +28,7 @@ import { MemberResource, isDefined } from '../types'
26
28
  import { HeaderRightButton } from '../navigation/header'
27
29
  import { FlashList } from '@shopify/flash-list'
28
30
  import { space } from '../utils'
31
+ import { tokens } from '../vendor/tapestry/tokens'
29
32
 
30
33
  // =========================================
31
34
  // ====== Factory Constants & Types ========
@@ -58,7 +61,7 @@ interface DataItem<T, TName extends SectionTypes> {
58
61
  // =================================
59
62
 
60
63
  export type ConversationDetailsScreenProps = StaticScreenProps<{
61
- conversation_id: string
64
+ conversation_id: number
62
65
  }>
63
66
 
64
67
  export function ConversationDetailsScreen({ route }: ConversationDetailsScreenProps) {
@@ -70,6 +73,9 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
70
73
  const { muted, setMuted } = useConversationMute(route.params)
71
74
  const { mutate: saveTitle } = useConversationUpdate(route.params)
72
75
 
76
+ const trimmedTitle = title.trim()
77
+ const emptyTitle = trimmedTitle === '' || title === null
78
+
73
79
  const canUpdate = conversation.memberAbility?.canUpdate || false
74
80
  const { data: members } = useSuspenseGet<MemberResource[]>({
75
81
  url: `/me/conversations/${route.params.conversation_id}/members`,
@@ -90,14 +96,14 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
90
96
  return (
91
97
  <HeaderRightButton
92
98
  onPress={() => {
93
- saveTitle({ title })
99
+ saveTitle({ title: trimmedTitle || conversation.title })
94
100
  navigation.goBack()
95
101
  }}
96
102
  >
97
103
  Done
98
104
  </HeaderRightButton>
99
105
  )
100
- }, [navigation, saveTitle, title])
106
+ }, [conversation.title, navigation, saveTitle, trimmedTitle])
101
107
 
102
108
  useEffect(() => {
103
109
  navigation.setOptions({
@@ -109,8 +115,14 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
109
115
  {
110
116
  type: SectionTypes.view,
111
117
  data: {
112
- children: <TitleInput canUpdate={canUpdate} title={title} setTitle={setTitle} />,
113
- style: styles.titleContainer,
118
+ children: (
119
+ <TitleInput
120
+ canUpdate={canUpdate}
121
+ title={title}
122
+ setTitle={setTitle}
123
+ isEmpty={emptyTitle}
124
+ />
125
+ ),
114
126
  },
115
127
  },
116
128
  {
@@ -215,23 +227,45 @@ interface InputProps {
215
227
  canUpdate: boolean
216
228
  title: string
217
229
  setTitle: Dispatch<SetStateAction<string>>
230
+ style?: ViewStyle
231
+ isEmpty: boolean
218
232
  }
219
233
 
220
- function TitleInput({ canUpdate, title, setTitle }: InputProps) {
234
+ function TitleInput({ canUpdate, title, setTitle, style, isEmpty }: InputProps) {
221
235
  const styles = useStyles()
236
+ const textRef = useRef<TextInput>(null)
237
+
238
+ const handleFocus = () => {
239
+ if (textRef.current) {
240
+ textRef.current.focus()
241
+ }
242
+ }
243
+
222
244
  return (
223
- <>
224
- <View style={[styles.titleLabelContainer, !canUpdate && styles.titleInputDisabled]}>
225
- <Text variant="tertiary">Title</Text>
226
- {!canUpdate && <Icon name="general.lock" />}
245
+ <Pressable style={[styles.titleContainer, style]} onPress={() => handleFocus()}>
246
+ <View style={styles.titleLabelContainer}>
247
+ <Text variant="tertiary" style={styles.titleLabel}>
248
+ Title
249
+ </Text>
250
+ {!canUpdate && <Icon name="general.lock" style={styles.titleDisabledIcon} />}
227
251
  </View>
228
252
  <TextInput
253
+ ref={textRef}
229
254
  editable={canUpdate}
230
255
  onChangeText={setTitle}
231
256
  style={[styles.titleInput, !canUpdate && styles.titleInputDisabled]}
232
257
  value={title}
258
+ multiline
259
+ maxLength={255} // Matches Chat Web
260
+ enterKeyHint="done"
261
+ submitBehavior="blurAndSubmit"
233
262
  />
234
- </>
263
+ {isEmpty && (
264
+ <Text variant="footnote" style={styles.inputValidationText}>
265
+ A title is required for your conversation.
266
+ </Text>
267
+ )}
268
+ </Pressable>
235
269
  )
236
270
  }
237
271
 
@@ -298,17 +332,33 @@ const useStyles = ({ isStart, isEnd }: { isStart?: boolean; isEnd?: boolean } =
298
332
  addBottomBorder: {
299
333
  borderBottomWidth: isEnd ? 0 : 1,
300
334
  },
301
- titleContainer: {},
302
- titleLabel: {},
335
+ titleContainer: {
336
+ gap: 4,
337
+ },
303
338
  titleLabelContainer: {
304
339
  flexDirection: 'row',
305
340
  alignItems: 'center',
306
- gap: 8,
341
+ gap: 4,
342
+ },
343
+ titleLabel: {
344
+ color: colors.textColorDefaultSecondary,
345
+ },
346
+ titleDisabledIcon: {
347
+ color: colors.iconColorDefaultSecondary,
307
348
  },
308
349
  titleInput: {
309
350
  color: colors.textColorDefaultPrimary,
351
+ fontSize: tokens.fontSizeMd,
352
+ },
353
+ titleInputDisabled: {
354
+ color: colors.textColorDefaultSecondary,
355
+ },
356
+ inputValidationText: {
357
+ color: colors.statusErrorText,
358
+ paddingTop: 8,
359
+ borderTopWidth: 1,
360
+ borderColor: colors.borderColorStatusError,
310
361
  },
311
- titleInputDisabled: { opacity: 0.7 },
312
362
  header: {
313
363
  paddingVertical: space(1.5),
314
364
  },
@@ -16,7 +16,7 @@ import { useConversation } from '../hooks/use_conversation'
16
16
  import { useConversationMessages } from '../hooks/use_conversation_messages'
17
17
 
18
18
  type ConversationRouteProps = {
19
- conversation_id: string
19
+ conversation_id: number
20
20
  chat_group_graph_id?: string
21
21
  }
22
22
 
@@ -64,7 +64,7 @@ export const ConversationCreateScreen = ({ route }: ConversationCreateScreenProp
64
64
  })
65
65
 
66
66
  const handleRedirectToConversation = useCallback(
67
- ({ conversation_id }: { conversation_id: string }) => {
67
+ ({ conversation_id }: { conversation_id: number }) => {
68
68
  // exit from the create stack
69
69
  navigation.getParent()?.goBack()
70
70
  // navigate to the conversation screen
@@ -52,7 +52,7 @@ const URL = {
52
52
  }
53
53
 
54
54
  const personAdult = {
55
- id: '1',
55
+ id: 1,
56
56
  type: 'Member' as const,
57
57
  name: 'John Doe',
58
58
  avatar: URL.avatar,
@@ -61,7 +61,7 @@ const personAdult = {
61
61
  }
62
62
 
63
63
  const personChild = {
64
- id: '2',
64
+ id: 2,
65
65
  type: 'Member' as const,
66
66
  name: 'Kid McChild',
67
67
  avatar: URL.two_avatars[1],
@@ -24,7 +24,7 @@ export const MessageActionsScreenOptions: NativeStackNavigationOptions = {
24
24
 
25
25
  export type ReactionScreenProps = StaticScreenProps<{
26
26
  message_id: string
27
- conversation_id: string
27
+ conversation_id: number
28
28
  }>
29
29
 
30
30
  export function MessageActionsScreen({ route }: ReactionScreenProps) {
@@ -20,7 +20,7 @@ export const ReactionsScreenOptions: NativeStackNavigationOptions = {
20
20
 
21
21
  export type ReactionScreenProps = StaticScreenProps<{
22
22
  message_id: string
23
- conversation_id: string
23
+ conversation_id: number
24
24
  reaction_value?: string
25
25
  }>
26
26
 
@@ -45,7 +45,7 @@ export function ReactionsScreen({ route }: ReactionScreenProps) {
45
45
  React.useState<ReactionCountResource>(initialReactionCount)
46
46
 
47
47
  const authorIds = reactionCount.authorIds
48
- const authors = members.filter(member => authorIds.includes(member.id))
48
+ const authors = members.filter(member => authorIds.includes(member.id.toString()))
49
49
 
50
50
  return (
51
51
  <View style={styles.container}>
@@ -64,7 +64,7 @@ export function ReactionsScreen({ route }: ReactionScreenProps) {
64
64
  />
65
65
  <FlatList
66
66
  data={authors}
67
- keyExtractor={item => item.id}
67
+ keyExtractor={item => item.id.toString()}
68
68
  renderItem={({ item: author }) => (
69
69
  <View style={styles.authorList}>
70
70
  <Avatar size={'md'} sourceUri={author.avatar} />
@@ -1,5 +1,5 @@
1
1
  export interface ResourceObject {
2
- id: string
2
+ id: string | number
3
3
  type: string
4
4
  }
5
5
 
@@ -1,6 +1,8 @@
1
1
  import { ResourceObject } from '../api_primitives'
2
2
 
3
3
  export interface AppGrantsResource extends ResourceObject {
4
+ type: 'AppGrant'
5
+ id: number
4
6
  createConversations: boolean
5
7
  appName: string
6
8
  }
@@ -4,7 +4,7 @@ import { MemberAbilityResource } from './member_ability'
4
4
 
5
5
  export interface ConversationResource {
6
6
  type: 'Conversation'
7
- id: string
7
+ id: number
8
8
  badges?: ConversationBadgeResource[]
9
9
  createdAt: string
10
10
  deleted?: boolean
@@ -1,6 +1,8 @@
1
1
  import { ResourceObject } from '../../api_primitives'
2
2
 
3
3
  export interface GroupsGroupResource extends ResourceObject {
4
+ id: string
5
+ type: 'Group'
4
6
  headerImage: {
5
7
  thumbnail: string
6
8
  medium: string
@@ -1,6 +1,6 @@
1
1
  export interface MemberResource {
2
2
  type: 'Member'
3
- id: string
3
+ id: number
4
4
  name: string
5
5
  avatar: string
6
6
  badges: MemberBadge[]
@@ -1,6 +1,8 @@
1
1
  import { ResourceObject } from '../api_primitives'
2
2
 
3
3
  export interface PersonResource extends ResourceObject {
4
+ id: number
5
+ type: 'Person'
4
6
  name: string
5
7
  avatar: string
6
8
  }
@@ -5,5 +5,5 @@ export interface ReactionCountResource {
5
5
  count: number
6
6
  mine: number
7
7
  messageId: string
8
- authorIds: string[]
8
+ authorIds: string[] // These is in fact a string array
9
9
  }
@@ -101,7 +101,7 @@ export function deleteRecordInPagesData<T extends ResourceObject>({
101
101
  record,
102
102
  }: {
103
103
  data?: { pages: ApiCollection<T>[]; pageParams: any }
104
- record: T
104
+ record: { id: string | number }
105
105
  }) {
106
106
  if (!data) return data
107
107
 
@@ -8,7 +8,7 @@ export function transformMessageEventDataToMessageResource({
8
8
  currentPersonId,
9
9
  }: {
10
10
  data: MessageCreatedEvent['data']['data']
11
- currentPersonId: string
11
+ currentPersonId: number
12
12
  }): MessageResource {
13
13
  return {
14
14
  type: 'Message',
@@ -18,11 +18,11 @@ export function transformMessageEventDataToMessageResource({
18
18
  createdAt: data.created_at,
19
19
  deletedAt: data.deleted_at,
20
20
  textEditedAt: data.text_edited_at,
21
- mine: data.author_id.toString() === currentPersonId.toString(),
21
+ mine: data.author_id === currentPersonId,
22
22
  attachments: deepCamelCaseKeys<DenormalizedAttachmentResource[]>(data.attachments) || [],
23
23
  author: {
24
24
  type: 'Person',
25
- id: data.author_id.toString(),
25
+ id: data.author_id,
26
26
  name: data.author_name,
27
27
  avatar: data.author_avatar,
28
28
  },
@@ -6,7 +6,7 @@ interface Props {
6
6
  data: ReactionCreatedEvent['data']['data']
7
7
  oldData?: ReactionCountResource
8
8
  event: JoltReactionEvent['event']
9
- currentPersonId: string
9
+ currentPersonId: number
10
10
  }
11
11
 
12
12
  export function transformReactionEventDataToReactionCountResource({
@@ -73,6 +73,7 @@ interface ChatColors {
73
73
  statusNeutralIcon: string
74
74
  statusNeutralText: string
75
75
  statusNeutralBackground: string
76
+ borderColorStatusError: string
76
77
  }
77
78
 
78
79
  const colorsChatLight: ChatColors = {
@@ -99,6 +100,7 @@ const colorsChatLight: ChatColors = {
99
100
  statusNeutralIcon: tokens.iconColorStatusNeutralPrimary,
100
101
  statusNeutralText: tokens.textColorStatusNeutral,
101
102
  statusNeutralBackground: tokens.fillColorStatusNeutralGhost,
103
+ borderColorStatusError: tokens.borderColorStatusError,
102
104
  }
103
105
 
104
106
  const colorsChatDark: ChatColors = {
@@ -125,6 +127,7 @@ const colorsChatDark: ChatColors = {
125
127
  statusNeutralIcon: tokens.iconColorStatusNeutralPrimaryDark,
126
128
  statusNeutralText: tokens.textColorStatusNeutralDark,
127
129
  statusNeutralBackground: tokens.fillColorStatusNeutralGhostDark,
130
+ borderColorStatusError: tokens.borderColorStatusErrorDark,
128
131
  }
129
132
 
130
133
  const chatThemeColorMap = {
@@ -20,6 +20,8 @@ const colorPrimitives = {
20
20
  colorNeutral95: 'hsl(0, 0%, 95%)',
21
21
  colorNeutral97: 'hsl(0, 0%, 97%)',
22
22
  colorNeutral98: 'hsl(0, 0%, 98%)',
23
+ borderColorStatusError: 'hsl(8, 60%, 47%)',
24
+ borderColorStatusErrorDark: 'hsl(8, 60%, 55%)',
23
25
  colorNeutral100White: 'hsl(0, 0%, 100%)',
24
26
  fillColorInteractionDefault: 'hsl(204, 100%, 40%)',
25
27
  fillColorStatusNeutralGhost: 'hsl(0, 0%, 93%)',