@planningcenter/chat-react-native 3.44.3 → 3.44.5-qa-900.0

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 (68) hide show
  1. package/build/components/conversation/message.d.ts.map +1 -1
  2. package/build/components/conversation/message.js +3 -3
  3. package/build/components/conversation/message.js.map +1 -1
  4. package/build/components/conversation/message_form.d.ts.map +1 -1
  5. package/build/components/conversation/message_form.js +3 -3
  6. package/build/components/conversation/message_form.js.map +1 -1
  7. package/build/components/conversation/message_reaction.d.ts.map +1 -1
  8. package/build/components/conversation/message_reaction.js +5 -5
  9. package/build/components/conversation/message_reaction.js.map +1 -1
  10. package/build/components/conversations/conversation_actions.d.ts.map +1 -1
  11. package/build/components/conversations/conversation_actions.js +1 -1
  12. package/build/components/conversations/conversation_actions.js.map +1 -1
  13. package/build/components/display/toggle_button.js +9 -5
  14. package/build/components/display/toggle_button.js.map +1 -1
  15. package/build/components/primitive/form_sheet.d.ts +1 -0
  16. package/build/components/primitive/form_sheet.d.ts.map +1 -1
  17. package/build/components/primitive/form_sheet.js +2 -2
  18. package/build/components/primitive/form_sheet.js.map +1 -1
  19. package/build/hooks/index.d.ts +2 -1
  20. package/build/hooks/index.d.ts.map +1 -1
  21. package/build/hooks/index.js +2 -1
  22. package/build/hooks/index.js.map +1 -1
  23. package/build/hooks/use_interaction_tinted_background_color.d.ts +2 -0
  24. package/build/hooks/use_interaction_tinted_background_color.d.ts.map +1 -0
  25. package/build/hooks/use_interaction_tinted_background_color.js +11 -0
  26. package/build/hooks/use_interaction_tinted_background_color.js.map +1 -0
  27. package/build/hooks/use_selected_chip_background_color.d.ts +2 -0
  28. package/build/hooks/use_selected_chip_background_color.d.ts.map +1 -0
  29. package/build/hooks/use_selected_chip_background_color.js +11 -0
  30. package/build/hooks/use_selected_chip_background_color.js.map +1 -0
  31. package/build/screens/conversation_screen.d.ts.map +1 -1
  32. package/build/screens/conversation_screen.js +1 -1
  33. package/build/screens/conversation_screen.js.map +1 -1
  34. package/build/screens/conversation_select_type_screen.d.ts.map +1 -1
  35. package/build/screens/conversation_select_type_screen.js +1 -1
  36. package/build/screens/conversation_select_type_screen.js.map +1 -1
  37. package/build/screens/conversations/components/list_header_component.d.ts.map +1 -1
  38. package/build/screens/conversations/components/list_header_component.js +1 -1
  39. package/build/screens/conversations/components/list_header_component.js.map +1 -1
  40. package/build/screens/design_system_screen.js +3 -3
  41. package/build/screens/design_system_screen.js.map +1 -1
  42. package/build/utils/theme.d.ts +16 -0
  43. package/build/utils/theme.d.ts.map +1 -1
  44. package/build/utils/theme.js +9 -0
  45. package/build/utils/theme.js.map +1 -1
  46. package/package.json +3 -3
  47. package/src/__tests__/hooks/use_selected_chip_background_color.test.tsx +78 -0
  48. package/src/__tests__/utils/theme.ts +23 -0
  49. package/src/components/conversation/message.tsx +3 -2
  50. package/src/components/conversation/message_form.tsx +3 -2
  51. package/src/components/conversation/message_reaction.tsx +5 -4
  52. package/src/components/conversations/conversation_actions.tsx +1 -0
  53. package/src/components/display/__tests__/toggle_button.test.tsx +135 -0
  54. package/src/components/display/toggle_button.tsx +9 -5
  55. package/src/components/primitive/form_sheet.tsx +3 -0
  56. package/src/hooks/index.ts +2 -1
  57. package/src/hooks/use_interaction_tinted_background_color.ts +16 -0
  58. package/src/hooks/use_selected_chip_background_color.ts +16 -0
  59. package/src/screens/conversation_screen.tsx +1 -0
  60. package/src/screens/conversation_select_type_screen.tsx +3 -0
  61. package/src/screens/conversations/components/list_header_component.tsx +1 -0
  62. package/src/screens/design_system_screen.tsx +3 -3
  63. package/src/utils/theme.ts +25 -0
  64. package/build/hooks/use_interaction_ghost_color.d.ts +0 -2
  65. package/build/hooks/use_interaction_ghost_color.d.ts.map +0 -1
  66. package/build/hooks/use_interaction_ghost_color.js +0 -14
  67. package/build/hooks/use_interaction_ghost_color.js.map +0 -1
  68. package/src/hooks/use_interaction_ghost_color.ts +0 -15
@@ -0,0 +1,78 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2
+ import { renderHook } from '@testing-library/react-native'
3
+ import React from 'react'
4
+ import { buildTestQueryClient } from '../../__utils__/query_client'
5
+ import { ChatProvider, CreateChatThemeProps } from '../../contexts/chat_context'
6
+ import { useSelectedChipBackgroundColor } from '../../hooks'
7
+
8
+ let themeProps: CreateChatThemeProps
9
+ let queryClient: QueryClient
10
+
11
+ beforeEach(() => {
12
+ queryClient = buildTestQueryClient({
13
+ defaultOptions: {
14
+ queries: {
15
+ refetchInterval: false,
16
+ refetchOnWindowFocus: false,
17
+ refetchOnReconnect: false,
18
+ },
19
+ },
20
+ })
21
+ })
22
+
23
+ afterEach(() => {
24
+ queryClient.clear()
25
+ queryClient.cancelQueries()
26
+ queryClient.removeQueries()
27
+ })
28
+
29
+ const Wrapper = ({ children }: { children: React.ReactNode }) => {
30
+ return (
31
+ <QueryClientProvider client={queryClient}>
32
+ <ChatProvider value={{ onUnauthorizedResponse: () => {}, theme: themeProps }}>
33
+ {children}
34
+ </ChatProvider>
35
+ </QueryClientProvider>
36
+ )
37
+ }
38
+
39
+ describe('useSelectedChipBackgroundColor', () => {
40
+ test('falls back to colors.interaction when fillColorChipSelected is absent', () => {
41
+ themeProps = { theme: { colors: { interaction: 'rgb(0, 255, 0)' } } }
42
+
43
+ const { result } = renderHook(() => useSelectedChipBackgroundColor(), {
44
+ wrapper: Wrapper,
45
+ })
46
+
47
+ expect(result.current).toBe('hsl(120, 100%, 92%)')
48
+ })
49
+
50
+ test('prefers fillColorChipSelected over colors.interaction when supplied', () => {
51
+ themeProps = {
52
+ theme: {
53
+ colors: { interaction: 'rgb(0, 255, 0)', fillColorChipSelected: 'rgb(255, 0, 0)' },
54
+ },
55
+ }
56
+
57
+ const { result } = renderHook(() => useSelectedChipBackgroundColor(), {
58
+ wrapper: Wrapper,
59
+ })
60
+
61
+ expect(result.current).toBe('hsl(0, 100%, 92%)')
62
+ })
63
+
64
+ test('respects a custom hslLightnessChipSelected', () => {
65
+ themeProps = {
66
+ theme: {
67
+ colors: { interaction: 'rgb(0, 255, 0)' },
68
+ chip: { hslLightnessChipSelected: 50 },
69
+ },
70
+ }
71
+
72
+ const { result } = renderHook(() => useSelectedChipBackgroundColor(), {
73
+ wrapper: Wrapper,
74
+ })
75
+
76
+ expect(result.current).toBe('hsl(120, 100%, 50%)')
77
+ })
78
+ })
@@ -20,4 +20,27 @@ describe('defaultTheme', () => {
20
20
  // render an unstyled or misthemed UI.
21
21
  expect(defaultTheme('unspecified').colors).toMatchObject(aliasTokensColorMap.light)
22
22
  })
23
+
24
+ describe('chip tokens', () => {
25
+ test('seeds static defaults for the non-selected chip trio in both schemes', () => {
26
+ const light = defaultTheme('light').colors
27
+ const dark = defaultTheme('dark').colors
28
+
29
+ expect(light.borderColorChip).toBe('hsl(0, 0%, 88%)')
30
+ expect(light.fillColorChip).toBe('transparent')
31
+ expect(light.textColorChip).toBe('hsl(0, 0%, 45%)')
32
+
33
+ expect(dark.borderColorChip).toBe('hsl(0, 0%, 24%)')
34
+ expect(dark.fillColorChip).toBe('transparent')
35
+ expect(dark.textColorChip).toBe('hsl(0, 0%, 68%)')
36
+ })
37
+
38
+ test('never seeds a static default for the selected chip trio', () => {
39
+ const { colors } = defaultTheme('light')
40
+
41
+ expect(colors.borderColorChipSelected).toBeUndefined()
42
+ expect(colors.fillColorChipSelected).toBeUndefined()
43
+ expect(colors.textColorChipSelected).toBeUndefined()
44
+ })
45
+ })
23
46
  })
@@ -13,7 +13,7 @@ import { MessageReaction } from '../../components/conversation/message_reaction'
13
13
  import { Avatar, Icon, Spinner, Text, TextButton, TextInlineButton } from '../../components/display'
14
14
  import {
15
15
  useAnimatedMessageBackgroundColor,
16
- useInteractionGhostBackgroundColor,
16
+ useInteractionTintedBackgroundColor,
17
17
  useTheme,
18
18
  } from '../../hooks'
19
19
  import { useDeletingIds } from '../../hooks'
@@ -194,6 +194,7 @@ export function Message({
194
194
 
195
195
  return (
196
196
  <Pressable
197
+ testID={`message-${message.id}`}
197
198
  onLongPress={isDeleting ? undefined : handleMessageLongPress}
198
199
  onPress={isDeleting ? undefined : handleMessagePress}
199
200
  onPressIn={isDeleting ? undefined : handleMessagePressIn}
@@ -341,7 +342,7 @@ export function Message({
341
342
 
342
343
  const useMessageStyles = (mine: boolean, isDeleting: boolean) => {
343
344
  const { colors } = useTheme()
344
- const myMessageBackgroundColor = useInteractionGhostBackgroundColor()
345
+ const myMessageBackgroundColor = useInteractionTintedBackgroundColor()
345
346
  const { width } = useWindowDimensions()
346
347
  const tabletWidth = width >= 744 // Smallest iPad Mini's width
347
348
 
@@ -17,7 +17,7 @@ import { ChatContext } from '../../contexts/chat_context'
17
17
  import {
18
18
  useCreateAndroidRippleColor,
19
19
  useTheme,
20
- useInteractionGhostBackgroundColor,
20
+ useInteractionTintedBackgroundColor,
21
21
  useScalableNumberOfLines,
22
22
  useFontScale,
23
23
  } from '../../hooks'
@@ -465,6 +465,7 @@ function MessageFormInput() {
465
465
  ) : null}
466
466
 
467
467
  <TextInput
468
+ testID="message-composer-input"
468
469
  style={[styles.textInput, usingGiphy && styles.textInputWithGiphy]}
469
470
  multiline={true}
470
471
  placeholder={placeholder}
@@ -783,7 +784,7 @@ const useMessageFormStyles = () => {
783
784
  const navigationTheme = useNavigationTheme()
784
785
  const buttonSize = 38
785
786
 
786
- const giphyBadgeBackgroundColor = useInteractionGhostBackgroundColor()
787
+ const giphyBadgeBackgroundColor = useInteractionTintedBackgroundColor()
787
788
 
788
789
  return StyleSheet.create({
789
790
  container: {
@@ -3,7 +3,7 @@ import colorFunction from 'color'
3
3
  import React, { useMemo } from 'react'
4
4
  import { StyleSheet } from 'react-native'
5
5
  import { Text } from '../../components/display'
6
- import { useInteractionGhostBackgroundColor, useTheme } from '../../hooks'
6
+ import { useSelectedChipBackgroundColor, useTheme } from '../../hooks'
7
7
  import { useCreateAndroidRippleColor } from '../../hooks/use_create_android_ripple_color'
8
8
  import { useMessageReactionToggle } from '../../hooks/use_message_reaction_toggle'
9
9
  import { MessageResource } from '../../types'
@@ -43,6 +43,7 @@ export function MessageReaction({
43
43
  return (
44
44
  <PlatformPressable
45
45
  key={reaction.value}
46
+ testID={`reaction-${message.id}-${reaction.value}`}
46
47
  style={styles.reaction}
47
48
  onLongPress={() => onLongPress(reaction)}
48
49
  onPress={handlePress}
@@ -57,12 +58,12 @@ export function MessageReaction({
57
58
  export const useReactionStyles = ({ mine }: { mine: number }) => {
58
59
  const { colors } = useTheme()
59
60
  const activeBorderColor = useMemo(() => {
60
- return colorFunction(colors.interaction)
61
+ return colorFunction(colors.borderColorChipSelected ?? colors.interaction)
61
62
  .hsl()
62
63
  .lightness(colors.name === 'dark' ? 20 : 80)
63
64
  .string()
64
- }, [colors.interaction, colors.name])
65
- const activeBackgroundColor = useInteractionGhostBackgroundColor()
65
+ }, [colors.borderColorChipSelected, colors.interaction, colors.name])
66
+ const activeBackgroundColor = useSelectedChipBackgroundColor()
66
67
 
67
68
  return StyleSheet.create({
68
69
  reaction: {
@@ -146,6 +146,7 @@ export function ConversationActions({
146
146
  )}
147
147
  >
148
148
  <Pressable
149
+ testID={`conversation-${conversation.id}`}
149
150
  onPress={handlePress}
150
151
  style={({ pressed }) => [style, pressed && platformPressedOpacityStyle]}
151
152
  android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }}
@@ -0,0 +1,135 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2
+ import { render } from '@testing-library/react-native'
3
+ import React from 'react'
4
+ import { buildTestQueryClient } from '../../../__utils__/query_client'
5
+ import { ChatProvider, CreateChatThemeProps } from '../../../contexts/chat_context'
6
+ import { ToggleButton } from '../toggle_button'
7
+
8
+ let themeProps: CreateChatThemeProps
9
+ let queryClient: QueryClient
10
+
11
+ beforeEach(() => {
12
+ queryClient = buildTestQueryClient({
13
+ defaultOptions: {
14
+ queries: {
15
+ refetchInterval: false,
16
+ refetchOnWindowFocus: false,
17
+ refetchOnReconnect: false,
18
+ },
19
+ },
20
+ })
21
+ })
22
+
23
+ afterEach(() => {
24
+ queryClient.clear()
25
+ queryClient.cancelQueries()
26
+ queryClient.removeQueries()
27
+ })
28
+
29
+ const Wrapper = ({ children }: { children: React.ReactNode }) => {
30
+ return (
31
+ <QueryClientProvider client={queryClient}>
32
+ <ChatProvider
33
+ value={{
34
+ token: {
35
+ access_token: '',
36
+ created_at: 0,
37
+ expires_in: 0,
38
+ scope: '',
39
+ refresh_token: '',
40
+ token_type: '',
41
+ },
42
+ onUnauthorizedResponse: () => {},
43
+ theme: themeProps,
44
+ }}
45
+ >
46
+ {children}
47
+ </ChatProvider>
48
+ </QueryClientProvider>
49
+ )
50
+ }
51
+
52
+ const renderToggleButton = (active: boolean) => {
53
+ const view = render(<ToggleButton active={active} accessibilityLabel="Filter" title="Filter" />, {
54
+ wrapper: Wrapper,
55
+ })
56
+
57
+ return {
58
+ button: view.getByLabelText('Filter'),
59
+ text: view.getByText('Filter'),
60
+ }
61
+ }
62
+
63
+ describe('ToggleButton chip colors', () => {
64
+ describe('backward compatibility (Services / sandbox pass only colors.interaction)', () => {
65
+ const distinctiveGreen = 'rgb(0, 255, 0)'
66
+
67
+ beforeEach(() => {
68
+ themeProps = {
69
+ theme: {
70
+ colors: {
71
+ interaction: distinctiveGreen,
72
+ },
73
+ },
74
+ }
75
+ })
76
+
77
+ test('active state derives border, fill, and text color from colors.interaction', () => {
78
+ const { button, text } = renderToggleButton(true)
79
+
80
+ expect(button).toHaveStyle({ borderColor: distinctiveGreen })
81
+ // The active fill is the green interaction color lightened via the
82
+ // chip-background hook (hsl lightness 92 in the light scheme), not
83
+ // the raw interaction color — pins the hook's transform in place.
84
+ expect(button).toHaveStyle({ backgroundColor: 'hsl(120, 100%, 92%)' })
85
+ expect(text).toHaveStyle({ color: distinctiveGreen })
86
+ })
87
+
88
+ test.each([
89
+ ['light', undefined, 'hsl(0, 0%, 88%)', 'hsl(0, 0%, 45%)'],
90
+ ['dark', 'dark' as const, 'hsl(0, 0%, 24%)', 'hsl(0, 0%, 68%)'],
91
+ ])(
92
+ 'inactive state uses the static chip defaults in the %s scheme',
93
+ (_label, colorScheme, borderColor, color) => {
94
+ themeProps = {
95
+ theme: { colors: { interaction: distinctiveGreen } },
96
+ colorScheme,
97
+ }
98
+
99
+ const { button, text } = renderToggleButton(false)
100
+
101
+ expect(button).toHaveStyle({ borderColor })
102
+ expect(text).toHaveStyle({ color })
103
+ }
104
+ )
105
+ })
106
+
107
+ describe('explicit chip token overrides', () => {
108
+ const explicitBorder = 'rgb(1, 2, 3)'
109
+ const explicitFill = 'rgb(4, 5, 6)'
110
+ const explicitText = 'rgb(7, 8, 9)'
111
+
112
+ beforeEach(() => {
113
+ themeProps = {
114
+ theme: {
115
+ colors: {
116
+ interaction: 'rgb(0, 255, 0)',
117
+ borderColorChipSelected: explicitBorder,
118
+ fillColorChipSelected: explicitFill,
119
+ textColorChipSelected: explicitText,
120
+ },
121
+ },
122
+ }
123
+ })
124
+
125
+ test('active state prefers explicit chip tokens over colors.interaction', () => {
126
+ const { button, text } = renderToggleButton(true)
127
+
128
+ expect(button).toHaveStyle({ borderColor: explicitBorder })
129
+ // fillColorChipSelected still passes through the chip-background hook's
130
+ // lightness transform, so the rendered fill isn't the raw override.
131
+ expect(button).toHaveStyle({ backgroundColor: 'hsl(210, 20%, 92%)' })
132
+ expect(text).toHaveStyle({ color: explicitText })
133
+ })
134
+ })
135
+ })
@@ -5,7 +5,7 @@ import {
5
5
  useTheme,
6
6
  useFontScale,
7
7
  useCreateAndroidRippleColor,
8
- useInteractionGhostBackgroundColor,
8
+ useSelectedChipBackgroundColor,
9
9
  } from '../../hooks'
10
10
  import {
11
11
  MAX_FONT_SIZE_MULTIPLIER_LANDMARK,
@@ -164,16 +164,20 @@ const useStyles = ({ active, maxFontSizeMultiplier }: Partial<ToggleButtonProps>
164
164
  const { colors, button } = useTheme()
165
165
  const fontScale = useFontScale({ maxFontSizeMultiplier })
166
166
 
167
- const activeBackgroundColor = useInteractionGhostBackgroundColor()
167
+ const activeBackgroundColor = useSelectedChipBackgroundColor()
168
168
 
169
- const color = active ? colors.interaction : colors.textColorDefaultSecondary
169
+ const borderColor = active
170
+ ? (colors.borderColorChipSelected ?? colors.interaction)
171
+ : colors.borderColorChip
172
+ const fillColor = active ? activeBackgroundColor : colors.fillColorChip
173
+ const color = active ? (colors.textColorChipSelected ?? colors.interaction) : colors.textColorChip
170
174
 
171
175
  return StyleSheet.create({
172
176
  pressable: {
173
177
  borderRadius: button.borderRadius * fontScale,
174
178
  borderWidth: 1,
175
- borderColor: active ? colors.interaction : colors.borderColorDefaultBase,
176
- backgroundColor: active ? activeBackgroundColor : 'transparent',
179
+ borderColor,
180
+ backgroundColor: fillColor,
177
181
  paddingHorizontal: 16 * fontScale,
178
182
  flexDirection: 'row',
179
183
  alignItems: 'center',
@@ -247,6 +247,7 @@ interface FormSheetActionProps {
247
247
  appearance?: FormSheetActionAppearanceUnion
248
248
  disabled?: boolean
249
249
  disabledReason?: string
250
+ testID?: string
250
251
  }
251
252
 
252
253
  function FormSheetAction({
@@ -259,11 +260,13 @@ function FormSheetAction({
259
260
  appearance = 'neutral',
260
261
  disabled = false,
261
262
  disabledReason,
263
+ testID,
262
264
  }: FormSheetActionProps) {
263
265
  const styles = useStyles({ appearance, disabled })
264
266
 
265
267
  return (
266
268
  <PlatformPressable
269
+ testID={testID}
267
270
  onPress={onPress}
268
271
  accessibilityLabel={accessibilityLabel}
269
272
  accessibilityHint={
@@ -13,7 +13,8 @@ export * from './use_font_scale'
13
13
  export * from './use_has_direct_messages'
14
14
  export * from './use_groups_groups'
15
15
  export * from './use_groups'
16
- export * from './use_interaction_ghost_color'
16
+ export * from './use_selected_chip_background_color'
17
+ export * from './use_interaction_tinted_background_color'
17
18
  export * from './use_message_reaction_toggle'
18
19
  export * from './use_new_conversation_entry'
19
20
  export * from './use_organization'
@@ -0,0 +1,16 @@
1
+ import colorFunction from 'color'
2
+ import { useMemo } from 'react'
3
+ import { useTheme } from './use_theme'
4
+
5
+ export function useInteractionTintedBackgroundColor() {
6
+ const { colors } = useTheme()
7
+
8
+ return useMemo(
9
+ () =>
10
+ colorFunction(colors.interaction)
11
+ .hsl()
12
+ .lightness(colors.name === 'dark' ? 12 : 92)
13
+ .string(),
14
+ [colors.interaction, colors.name]
15
+ )
16
+ }
@@ -0,0 +1,16 @@
1
+ import colorFunction from 'color'
2
+ import { useMemo } from 'react'
3
+ import { useTheme } from './use_theme'
4
+
5
+ export function useSelectedChipBackgroundColor() {
6
+ const { colors, chip } = useTheme()
7
+
8
+ return useMemo(
9
+ () =>
10
+ colorFunction(colors.fillColorChipSelected ?? colors.interaction)
11
+ .hsl()
12
+ .lightness(chip.hslLightnessChipSelected)
13
+ .string(),
14
+ [colors.fillColorChipSelected, colors.interaction, chip.hslLightnessChipSelected]
15
+ )
16
+ }
@@ -355,6 +355,7 @@ export const ConversationScreenTitle = ({
355
355
 
356
356
  return (
357
357
  <PlatformPressable
358
+ testID="conversation-details-button"
358
359
  accessibilityHint="Opens details about members and settings"
359
360
  style={styles.container}
360
361
  onPress={() => {
@@ -95,6 +95,7 @@ export function ConversationSelectTypeScreen({ route }: ConversationSelectTypeSc
95
95
  )}
96
96
  {canCreateGroupsConversations && (
97
97
  <FormSheet.Action
98
+ testID="conversation-select-type-group"
98
99
  title="Group"
99
100
  onPress={handleGroupPress}
100
101
  accessibilityHint="Opens group selection screen"
@@ -102,6 +103,7 @@ export function ConversationSelectTypeScreen({ route }: ConversationSelectTypeSc
102
103
  )}
103
104
  {canCreateServicesConversations && (
104
105
  <FormSheet.Action
106
+ testID="conversation-select-type-team"
105
107
  title="Team"
106
108
  onPress={handleTeamPress}
107
109
  accessibilityHint="Opens team selection screen"
@@ -109,6 +111,7 @@ export function ConversationSelectTypeScreen({ route }: ConversationSelectTypeSc
109
111
  )}
110
112
  {showDirectMessageOption && (
111
113
  <FormSheet.Action
114
+ testID="conversation-select-type-direct-message"
112
115
  title="Direct message"
113
116
  onPress={handleDirectMessagePress}
114
117
  disabled={directMessagesDisabled}
@@ -162,6 +162,7 @@ export const ListHeaderComponent = () => {
162
162
  name="general.cog"
163
163
  onPress={handleChatSettings}
164
164
  accessibilityLabel="Chat settings"
165
+ testID="conversations-chat-settings-button"
165
166
  maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER_LANDMARK}
166
167
  />
167
168
  {canCreateConversations && (
@@ -29,7 +29,7 @@ import {
29
29
  import { VideoAttachmentPreview } from '../components/display/video_attachment_preview'
30
30
  import BannerPrimitive from '../components/primitive/banner_primitive'
31
31
  import BlankState from '../components/primitive/blank_state_primitive'
32
- import { useInteractionGhostBackgroundColor, useTheme } from '../hooks'
32
+ import { useSelectedChipBackgroundColor, useTheme } from '../hooks'
33
33
  import {
34
34
  MAX_FONT_SIZE_MULTIPLIER,
35
35
  platformPressedOpacityStyle,
@@ -1325,7 +1325,7 @@ function HapticButton({ title, onPress }: { title: string; onPress: () => void }
1325
1325
 
1326
1326
  const useStyles = () => {
1327
1327
  const { colors } = useTheme()
1328
- const interactionGhostBackgroundColor = useInteractionGhostBackgroundColor()
1328
+ const selectedChipBackgroundColor = useSelectedChipBackgroundColor()
1329
1329
 
1330
1330
  return StyleSheet.create({
1331
1331
  hapticButton: {
@@ -1334,7 +1334,7 @@ const useStyles = () => {
1334
1334
  padding: 16,
1335
1335
  alignItems: 'center',
1336
1336
  justifyContent: 'center',
1337
- backgroundColor: interactionGhostBackgroundColor,
1337
+ backgroundColor: selectedChipBackgroundColor,
1338
1338
  borderWidth: 1,
1339
1339
  borderColor: colors.interaction,
1340
1340
  borderRadius: 8,
@@ -28,6 +28,9 @@ export interface DefaultTheme {
28
28
  button: {
29
29
  borderRadius: number
30
30
  }
31
+ chip: {
32
+ hslLightnessChipSelected: number
33
+ }
31
34
  showBadgeProductLogo: boolean
32
35
  }
33
36
 
@@ -44,6 +47,9 @@ export const defaultTheme = (colorScheme: ColorSchemeName | null): ChatTheme =>
44
47
  button: {
45
48
  borderRadius: 40,
46
49
  },
50
+ chip: {
51
+ hslLightnessChipSelected: scheme === 'dark' ? 12 : 92,
52
+ },
47
53
  showBadgeProductLogo: true, // Overrides visibility of the product logo in `src/display/badge.tsx`
48
54
  }
49
55
  }
@@ -91,6 +97,19 @@ interface ChatColors {
91
97
  statusNeutralComposedIcon: string
92
98
  statusNeutralComposedText: string
93
99
  statusNeutralComposedBackground: string
100
+ borderColorChip: string
101
+ fillColorChip: string
102
+ textColorChip: string
103
+ /**
104
+ * Selected-chip colors (border/fill/text). Never assign a static default
105
+ * directly — these stay optional so they always fall back to
106
+ * `colors.interaction` via `??` at the read site. Every current consumer
107
+ * overrides `interaction` at runtime, and a seeded default here could win
108
+ * over that override depending on merge order.
109
+ */
110
+ borderColorChipSelected?: string
111
+ fillColorChipSelected?: string
112
+ textColorChipSelected?: string
94
113
  }
95
114
 
96
115
  // 'link' is an iOS-only semantic color name — constructing it on Android is
@@ -138,6 +157,9 @@ const colorsChatLight: ChatColors = {
138
157
  statusNeutralComposedIcon: tokens.iconColorStatusNeutralPrimary,
139
158
  statusNeutralComposedText: tokens.textColorStatusNeutral,
140
159
  statusNeutralComposedBackground: tokens.fillColorStatusNeutralGhost,
160
+ borderColorChip: tokens.colorNeutral88,
161
+ fillColorChip: 'transparent',
162
+ textColorChip: tokens.colorNeutral45,
141
163
  }
142
164
 
143
165
  const colorsChatDark: ChatColors = {
@@ -183,6 +205,9 @@ const colorsChatDark: ChatColors = {
183
205
  statusNeutralComposedIcon: tokens.iconColorStatusNeutralPrimaryDark,
184
206
  statusNeutralComposedText: tokens.textColorStatusNeutralDark,
185
207
  statusNeutralComposedBackground: tokens.fillColorStatusNeutralGhostDark,
208
+ borderColorChip: tokens.colorNeutral24,
209
+ fillColorChip: 'transparent',
210
+ textColorChip: tokens.colorNeutral68,
186
211
  }
187
212
 
188
213
  const chatThemeColorMap = {
@@ -1,2 +0,0 @@
1
- export declare function useInteractionGhostBackgroundColor(): string;
2
- //# sourceMappingURL=use_interaction_ghost_color.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use_interaction_ghost_color.d.ts","sourceRoot":"","sources":["../../src/hooks/use_interaction_ghost_color.ts"],"names":[],"mappings":"AAIA,wBAAgB,kCAAkC,WAUjD"}
@@ -1,14 +0,0 @@
1
- import colorFunction from 'color';
2
- import { useMemo } from 'react';
3
- import { useTheme } from './use_theme';
4
- export function useInteractionGhostBackgroundColor() {
5
- const { colors } = useTheme();
6
- const interactionGhostColor = useMemo(() => {
7
- return colorFunction(colors.interaction)
8
- .hsl()
9
- .lightness(colors.name === 'dark' ? 12 : 92)
10
- .string();
11
- }, [colors.interaction, colors.name]);
12
- return interactionGhostColor;
13
- }
14
- //# sourceMappingURL=use_interaction_ghost_color.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use_interaction_ghost_color.js","sourceRoot":"","sources":["../../src/hooks/use_interaction_ghost_color.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,MAAM,UAAU,kCAAkC;IAChD,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC7B,MAAM,qBAAqB,GAAG,OAAO,CAAC,GAAG,EAAE;QACzC,OAAO,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC;aACrC,GAAG,EAAE;aACL,SAAS,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3C,MAAM,EAAE,CAAA;IACb,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAErC,OAAO,qBAAqB,CAAA;AAC9B,CAAC","sourcesContent":["import colorFunction from 'color'\nimport { useMemo } from 'react'\nimport { useTheme } from './use_theme'\n\nexport function useInteractionGhostBackgroundColor() {\n const { colors } = useTheme()\n const interactionGhostColor = useMemo(() => {\n return colorFunction(colors.interaction)\n .hsl()\n .lightness(colors.name === 'dark' ? 12 : 92)\n .string()\n }, [colors.interaction, colors.name])\n\n return interactionGhostColor\n}\n"]}
@@ -1,15 +0,0 @@
1
- import colorFunction from 'color'
2
- import { useMemo } from 'react'
3
- import { useTheme } from './use_theme'
4
-
5
- export function useInteractionGhostBackgroundColor() {
6
- const { colors } = useTheme()
7
- const interactionGhostColor = useMemo(() => {
8
- return colorFunction(colors.interaction)
9
- .hsl()
10
- .lightness(colors.name === 'dark' ? 12 : 92)
11
- .string()
12
- }, [colors.interaction, colors.name])
13
-
14
- return interactionGhostColor
15
- }