@planningcenter/chat-react-native 2.1.1 → 2.2.0-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.
- package/build/components/conversation/message_form.d.ts.map +1 -1
- package/build/components/conversation/message_form.js +4 -4
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/components/display/badge.d.ts +2 -9
- package/build/components/display/badge.d.ts.map +1 -1
- package/build/components/display/badge.js +8 -40
- package/build/components/display/badge.js.map +1 -1
- package/build/components/display/banner.d.ts +29 -0
- package/build/components/display/banner.d.ts.map +1 -0
- package/build/components/display/banner.js +16 -0
- package/build/components/display/banner.js.map +1 -0
- package/build/components/display/button.d.ts +1 -1
- package/build/components/display/button.d.ts.map +1 -1
- package/build/components/display/button.js +1 -1
- package/build/components/display/button.js.map +1 -1
- package/build/components/display/icon_button.d.ts +1 -1
- package/build/components/display/icon_button.d.ts.map +1 -1
- package/build/components/display/icon_button.js +1 -1
- package/build/components/display/icon_button.js.map +1 -1
- package/build/components/display/index.d.ts +1 -0
- package/build/components/display/index.d.ts.map +1 -1
- package/build/components/display/index.js +1 -0
- package/build/components/display/index.js.map +1 -1
- package/build/components/display/text_button.d.ts +1 -1
- package/build/components/display/text_button.d.ts.map +1 -1
- package/build/components/display/text_button.js +1 -1
- package/build/components/display/text_button.js.map +1 -1
- package/build/components/display/text_inline_button.d.ts +2 -2
- package/build/components/display/text_inline_button.d.ts.map +1 -1
- package/build/components/display/text_inline_button.js +3 -2
- package/build/components/display/text_inline_button.js.map +1 -1
- package/build/components/display/{button_color_utils.d.ts → utils/button_colors.d.ts} +1 -1
- package/build/components/display/utils/button_colors.d.ts.map +1 -0
- package/build/components/display/{button_color_utils.js → utils/button_colors.js} +2 -2
- package/build/components/display/utils/button_colors.js.map +1 -0
- package/build/components/display/utils/status_colors.d.ts +17 -0
- package/build/components/display/utils/status_colors.d.ts.map +1 -0
- package/build/components/display/utils/status_colors.js +49 -0
- package/build/components/display/utils/status_colors.js.map +1 -0
- package/build/components/primitive/banner_primitive.d.ts +38 -0
- package/build/components/primitive/banner_primitive.d.ts.map +1 -0
- package/build/components/primitive/banner_primitive.js +112 -0
- package/build/components/primitive/banner_primitive.js.map +1 -0
- package/build/contexts/api_provider.js +4 -3
- package/build/contexts/api_provider.js.map +1 -1
- package/build/contexts/chat_context.d.ts +2 -2
- package/build/contexts/chat_context.d.ts.map +1 -1
- package/build/contexts/chat_context.js +3 -15
- package/build/contexts/chat_context.js.map +1 -1
- package/build/hooks/use_api_client.d.ts +6 -0
- package/build/hooks/use_api_client.d.ts.map +1 -0
- package/build/hooks/use_api_client.js +18 -0
- package/build/hooks/use_api_client.js.map +1 -0
- package/build/hooks/use_conversation.d.ts +122 -0
- package/build/hooks/use_conversation.d.ts.map +1 -0
- package/build/hooks/use_conversation.js +103 -0
- package/build/hooks/use_conversation.js.map +1 -0
- package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_jolt_events.js +3 -4
- package/build/hooks/use_conversation_jolt_events.js.map +1 -1
- package/build/hooks/use_font_scale.d.ts +1 -1
- package/build/hooks/use_font_scale.d.ts.map +1 -1
- package/build/hooks/use_font_scale.js +1 -1
- package/build/hooks/use_font_scale.js.map +1 -1
- package/build/hooks/use_jolt.d.ts +1 -1
- package/build/hooks/use_jolt.d.ts.map +1 -1
- package/build/hooks/use_jolt.js +6 -6
- package/build/hooks/use_jolt.js.map +1 -1
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +3 -4
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/navigation/header.d.ts +10 -0
- package/build/navigation/header.d.ts.map +1 -0
- package/build/navigation/header.js +16 -0
- package/build/navigation/header.js.map +1 -0
- package/build/navigation/index.d.ts +17 -4
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +18 -6
- package/build/navigation/index.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts +7 -0
- package/build/screens/conversation_details_screen.d.ts.map +1 -0
- package/build/screens/conversation_details_screen.js +155 -0
- package/build/screens/conversation_details_screen.js.map +1 -0
- package/build/screens/conversation_screen.d.ts +5 -3
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +43 -15
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/design_system_screen.d.ts.map +1 -1
- package/build/screens/design_system_screen.js +24 -1
- package/build/screens/design_system_screen.js.map +1 -1
- package/build/screens/message_actions_screen.d.ts.map +1 -1
- package/build/screens/message_actions_screen.js +7 -7
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/utils/client/request_helpers.d.ts +2 -1
- package/build/utils/client/request_helpers.d.ts.map +1 -1
- package/build/utils/client/request_helpers.js +17 -0
- package/build/utils/client/request_helpers.js.map +1 -1
- package/package.json +2 -2
- package/src/components/conversation/message_form.tsx +4 -4
- package/src/components/display/badge.tsx +9 -53
- package/src/components/display/banner.tsx +56 -0
- package/src/components/display/button.tsx +2 -2
- package/src/components/display/icon_button.tsx +2 -2
- package/src/components/display/index.ts +1 -0
- package/src/components/display/text_button.tsx +2 -2
- package/src/components/display/text_inline_button.tsx +4 -2
- package/src/components/display/{button_color_utils.ts → utils/button_colors.ts} +1 -1
- package/src/components/display/utils/status_colors.ts +85 -0
- package/src/components/primitive/banner_primitive.tsx +247 -0
- package/src/contexts/api_provider.tsx +5 -5
- package/src/contexts/chat_context.tsx +4 -21
- package/src/hooks/use_api_client.ts +29 -0
- package/src/hooks/use_conversation.ts +113 -0
- package/src/hooks/use_conversation_jolt_events.ts +3 -4
- package/src/hooks/use_font_scale.ts +3 -1
- package/src/hooks/use_jolt.ts +9 -9
- package/src/hooks/use_suspense_api.ts +3 -4
- package/src/navigation/header.tsx +24 -0
- package/src/navigation/index.tsx +20 -6
- package/src/screens/conversation_details_screen.tsx +205 -0
- package/src/screens/conversation_screen.tsx +65 -20
- package/src/screens/design_system_screen.tsx +56 -0
- package/src/screens/message_actions_screen.tsx +7 -7
- package/src/utils/client/request_helpers.ts +21 -1
- package/build/components/display/button_color_utils.d.ts.map +0 -1
- package/build/components/display/button_color_utils.js.map +0 -1
|
@@ -7,30 +7,12 @@ import { Icon } from './icon'
|
|
|
7
7
|
import { Text } from './text'
|
|
8
8
|
import { platformFontWeightMedium, space } from '../../utils'
|
|
9
9
|
import { tokens } from '../../vendor/tapestry/tokens'
|
|
10
|
+
import { useStatusColorAppearanceMap, type StatusAppearanceUnion } from './utils/status_colors'
|
|
10
11
|
|
|
11
12
|
// =================================
|
|
12
13
|
// ====== Constants ================
|
|
13
14
|
// =================================
|
|
14
15
|
|
|
15
|
-
const APPEARANCES = {
|
|
16
|
-
error: 'error',
|
|
17
|
-
info: 'info',
|
|
18
|
-
neutral: 'neutral',
|
|
19
|
-
success: 'success',
|
|
20
|
-
warning: 'warning',
|
|
21
|
-
} as const
|
|
22
|
-
|
|
23
|
-
type AppearanceUnion = (typeof APPEARANCES)[keyof typeof APPEARANCES]
|
|
24
|
-
|
|
25
|
-
type AppearanceColors = Record<
|
|
26
|
-
AppearanceUnion,
|
|
27
|
-
{
|
|
28
|
-
background: string
|
|
29
|
-
text: string
|
|
30
|
-
icon: string
|
|
31
|
-
}
|
|
32
|
-
>
|
|
33
|
-
|
|
34
16
|
const VARIANTS = {
|
|
35
17
|
default: 'default',
|
|
36
18
|
meta: 'meta',
|
|
@@ -77,7 +59,7 @@ interface BadgeProps {
|
|
|
77
59
|
/**
|
|
78
60
|
* Changes the status color for the badge and icon.
|
|
79
61
|
*/
|
|
80
|
-
appearance?:
|
|
62
|
+
appearance?: StatusAppearanceUnion
|
|
81
63
|
/**
|
|
82
64
|
* Changes form of the logo to support a meta label and badge
|
|
83
65
|
*/
|
|
@@ -151,33 +133,7 @@ const useStyles = ({
|
|
|
151
133
|
const { colors } = useTheme()
|
|
152
134
|
const fontScale = useFontScale({ maxFontSizeMultiplier })
|
|
153
135
|
|
|
154
|
-
const
|
|
155
|
-
error: {
|
|
156
|
-
background: colors.statusErrorBackground,
|
|
157
|
-
text: colors.statusErrorText,
|
|
158
|
-
icon: colors.statusErrorIcon,
|
|
159
|
-
},
|
|
160
|
-
info: {
|
|
161
|
-
background: colors.statusInfoBackground,
|
|
162
|
-
text: colors.statusInfoText,
|
|
163
|
-
icon: colors.statusInfoIcon,
|
|
164
|
-
},
|
|
165
|
-
neutral: {
|
|
166
|
-
background: colors.statusNeutralBackground,
|
|
167
|
-
text: colors.statusNeutralText,
|
|
168
|
-
icon: colors.statusNeutralIcon,
|
|
169
|
-
},
|
|
170
|
-
success: {
|
|
171
|
-
background: colors.statusSuccessBackground,
|
|
172
|
-
text: colors.statusSuccessText,
|
|
173
|
-
icon: colors.statusSuccessIcon,
|
|
174
|
-
},
|
|
175
|
-
warning: {
|
|
176
|
-
background: colors.statusWarningBackground,
|
|
177
|
-
text: colors.statusWarningText,
|
|
178
|
-
icon: colors.statusWarningIcon,
|
|
179
|
-
},
|
|
180
|
-
}
|
|
136
|
+
const statusColorMap = useStatusColorAppearanceMap()
|
|
181
137
|
|
|
182
138
|
const badgePaddingHorizontal = space(1) * fontScale
|
|
183
139
|
const badgePaddingVertical = space(0.5) * fontScale
|
|
@@ -187,24 +143,24 @@ const useStyles = ({
|
|
|
187
143
|
const variantStylesMap: VariantStyles = {
|
|
188
144
|
default: {
|
|
189
145
|
gap: badgeGap,
|
|
190
|
-
backgroundColor:
|
|
146
|
+
backgroundColor: statusColorMap[appearance].background,
|
|
191
147
|
metaLabelPaddingLeft: badgePaddingHorizontal,
|
|
192
148
|
paddingHorizontal: badgePaddingHorizontal,
|
|
193
149
|
paddingVertical: badgePaddingVertical,
|
|
194
150
|
borderWidth: tokens.borderSizeDefault * fontScale,
|
|
195
151
|
borderRadius: tokens.borderRadiusMd,
|
|
196
|
-
textColor:
|
|
152
|
+
textColor: statusColorMap[appearance].text,
|
|
197
153
|
fontWeight: 'normal',
|
|
198
154
|
},
|
|
199
155
|
meta: {
|
|
200
156
|
gap: badgeGap,
|
|
201
|
-
backgroundColor:
|
|
157
|
+
backgroundColor: statusColorMap[appearance].background,
|
|
202
158
|
metaLabelPaddingLeft: badgePaddingHorizontal,
|
|
203
159
|
paddingHorizontal: badgePaddingHorizontal,
|
|
204
160
|
paddingVertical: badgePaddingVertical,
|
|
205
161
|
borderWidth: tokens.borderSizeDefault * fontScale,
|
|
206
162
|
borderRadius: tokens.borderRadiusMd,
|
|
207
|
-
textColor:
|
|
163
|
+
textColor: statusColorMap[appearance].text,
|
|
208
164
|
fontWeight: platformFontWeightMedium,
|
|
209
165
|
},
|
|
210
166
|
metaSubtle: {
|
|
@@ -227,7 +183,7 @@ const useStyles = ({
|
|
|
227
183
|
justifyContent: 'center',
|
|
228
184
|
borderRadius: variantStylesMap[variant].borderRadius,
|
|
229
185
|
borderWidth: variantStylesMap[variant].borderWidth,
|
|
230
|
-
borderColor:
|
|
186
|
+
borderColor: statusColorMap[appearance].background,
|
|
231
187
|
},
|
|
232
188
|
badge: {
|
|
233
189
|
flexDirection: 'row',
|
|
@@ -239,7 +195,7 @@ const useStyles = ({
|
|
|
239
195
|
borderRadius: variantStylesMap[variant].borderRadius - 2,
|
|
240
196
|
},
|
|
241
197
|
icon: {
|
|
242
|
-
color:
|
|
198
|
+
color: statusColorMap[appearance].icon,
|
|
243
199
|
fontSize: badgeFontSize,
|
|
244
200
|
},
|
|
245
201
|
logo: {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { isValidElement, ReactElement } from 'react'
|
|
2
|
+
import BannerPrimitive, {
|
|
3
|
+
type BannerStatusIconProps,
|
|
4
|
+
type BannerRootProps,
|
|
5
|
+
} from '../primitive/banner_primitive'
|
|
6
|
+
|
|
7
|
+
interface BannerProps {
|
|
8
|
+
/**
|
|
9
|
+
* Changes the status color for the background, text, and icon.
|
|
10
|
+
* Sets a preset status icon.
|
|
11
|
+
*/
|
|
12
|
+
appearance?: BannerRootProps['appearance']
|
|
13
|
+
/**
|
|
14
|
+
* Renders the banner's main text.
|
|
15
|
+
* If some of the text is interactive it can also take `BannerPrimitive.Link` wrapped in `BannerPrimitive.Text`.
|
|
16
|
+
*/
|
|
17
|
+
description: string | ReactElement
|
|
18
|
+
/**
|
|
19
|
+
* Renders the banner's heading text.
|
|
20
|
+
*/
|
|
21
|
+
heading?: string
|
|
22
|
+
/**
|
|
23
|
+
* Overrides the preset status icon.
|
|
24
|
+
*/
|
|
25
|
+
iconName?: BannerStatusIconProps['iconName']
|
|
26
|
+
/**
|
|
27
|
+
* Hides the status icon. By default, the status icon is shown.
|
|
28
|
+
*/
|
|
29
|
+
showIcon?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function Banner({
|
|
33
|
+
appearance,
|
|
34
|
+
description,
|
|
35
|
+
heading,
|
|
36
|
+
iconName,
|
|
37
|
+
showIcon = true,
|
|
38
|
+
}: BannerProps) {
|
|
39
|
+
if (!description && !heading) return null
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<BannerPrimitive.Root appearance={appearance}>
|
|
43
|
+
<BannerPrimitive.StaticLayout>
|
|
44
|
+
{showIcon && <BannerPrimitive.StatusIcon iconName={iconName} />}
|
|
45
|
+
<BannerPrimitive.Content>
|
|
46
|
+
{Boolean(heading) && <BannerPrimitive.Heading>{heading}</BannerPrimitive.Heading>}
|
|
47
|
+
{isValidElement(description) ? (
|
|
48
|
+
description
|
|
49
|
+
) : (
|
|
50
|
+
<BannerPrimitive.Text>{description}</BannerPrimitive.Text>
|
|
51
|
+
)}
|
|
52
|
+
</BannerPrimitive.Content>
|
|
53
|
+
</BannerPrimitive.StaticLayout>
|
|
54
|
+
</BannerPrimitive.Root>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -7,8 +7,8 @@ import { useTheme, useFontScale, useCreateAndroidRippleColor } from '../../hooks
|
|
|
7
7
|
import { tokens } from '../../vendor/tapestry/tokens'
|
|
8
8
|
import { platformFontWeightBold, platformPressedOpacityStyle } from '../../utils'
|
|
9
9
|
import { Spinner } from './spinner'
|
|
10
|
-
import { getColorKey, useButtonColorOptionMap, useGradientColorMap } from './
|
|
11
|
-
import type { ButtonAppearanceUnion } from './
|
|
10
|
+
import { getColorKey, useButtonColorOptionMap, useGradientColorMap } from './utils/button_colors'
|
|
11
|
+
import type { ButtonAppearanceUnion } from './utils/button_colors'
|
|
12
12
|
|
|
13
13
|
// =================================
|
|
14
14
|
// ====== Constants ================
|
|
@@ -6,8 +6,8 @@ import type { IconProps } from './icon'
|
|
|
6
6
|
import { useTheme, useFontScale, useCreateAndroidRippleColor } from '../../hooks'
|
|
7
7
|
import { platformPressedOpacityStyle } from '../../utils'
|
|
8
8
|
import { Spinner } from './spinner'
|
|
9
|
-
import { getColorKey, useIconButtonColorOptionMap } from './
|
|
10
|
-
import type { IconButtonAppearanceUnion } from './
|
|
9
|
+
import { getColorKey, useIconButtonColorOptionMap } from './utils/button_colors'
|
|
10
|
+
import type { IconButtonAppearanceUnion } from './utils/button_colors'
|
|
11
11
|
|
|
12
12
|
// =================================
|
|
13
13
|
// ====== Constants ================
|
|
@@ -5,8 +5,8 @@ import { useTheme } from '../../hooks'
|
|
|
5
5
|
import { platformFontWeightMedium } from '../../utils'
|
|
6
6
|
import { Text } from './text'
|
|
7
7
|
import type { TextProps } from './text'
|
|
8
|
-
import { getColorKey, useButtonColorOptionMap } from './
|
|
9
|
-
import type { ButtonAppearanceUnion } from './
|
|
8
|
+
import { getColorKey, useButtonColorOptionMap } from './utils/button_colors'
|
|
9
|
+
import type { ButtonAppearanceUnion } from './utils/button_colors'
|
|
10
10
|
|
|
11
11
|
// =================================
|
|
12
12
|
// ====== Component ================
|
|
@@ -5,8 +5,8 @@ import { Text } from './text'
|
|
|
5
5
|
import type { TextProps } from './text'
|
|
6
6
|
import { platformFontWeightMedium } from '../../utils'
|
|
7
7
|
import colorFunction from 'color'
|
|
8
|
-
import { getColorKey, useButtonColorOptionMap } from './
|
|
9
|
-
import type { ButtonAppearanceUnion } from './
|
|
8
|
+
import { getColorKey, useButtonColorOptionMap } from './utils/button_colors'
|
|
9
|
+
import type { ButtonAppearanceUnion } from './utils/button_colors'
|
|
10
10
|
|
|
11
11
|
// =================================
|
|
12
12
|
// ====== Component ================
|
|
@@ -24,6 +24,7 @@ export function TextInlineButton({
|
|
|
24
24
|
children,
|
|
25
25
|
disabled = false,
|
|
26
26
|
onPress,
|
|
27
|
+
style,
|
|
27
28
|
...props
|
|
28
29
|
}: TextInlineButtonProps) {
|
|
29
30
|
const styles = useStyles({ appearance, disabled })
|
|
@@ -43,6 +44,7 @@ export function TextInlineButton({
|
|
|
43
44
|
styles.underline,
|
|
44
45
|
disabled && styles.disabled,
|
|
45
46
|
isPressed && platformPressedStyle,
|
|
47
|
+
style,
|
|
46
48
|
]}
|
|
47
49
|
onPress={!disabled ? onPress : undefined}
|
|
48
50
|
onPressIn={() => setIsPressed(true)}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useTheme } from '../../../hooks'
|
|
2
|
+
|
|
3
|
+
// =================================
|
|
4
|
+
// ====== Exports ==================
|
|
5
|
+
// =================================
|
|
6
|
+
|
|
7
|
+
export { useStatusColorAppearanceMap }
|
|
8
|
+
export type { StatusAppearanceUnion }
|
|
9
|
+
|
|
10
|
+
// =================================
|
|
11
|
+
// ====== Constants ================
|
|
12
|
+
// =================================
|
|
13
|
+
|
|
14
|
+
const STATUS_APPEARANCES = {
|
|
15
|
+
error: 'error',
|
|
16
|
+
info: 'info',
|
|
17
|
+
neutral: 'neutral',
|
|
18
|
+
success: 'success',
|
|
19
|
+
warning: 'warning',
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
type StatusAppearanceUnion = (typeof STATUS_APPEARANCES)[keyof typeof STATUS_APPEARANCES]
|
|
23
|
+
|
|
24
|
+
type StatusAppearanceColors = Record<
|
|
25
|
+
StatusAppearanceUnion,
|
|
26
|
+
{
|
|
27
|
+
background: string
|
|
28
|
+
text: string
|
|
29
|
+
icon: string
|
|
30
|
+
}
|
|
31
|
+
>
|
|
32
|
+
|
|
33
|
+
// =================================
|
|
34
|
+
// ====== Hooks ====================
|
|
35
|
+
// =================================
|
|
36
|
+
|
|
37
|
+
function useStatusColorAppearanceMap(): StatusAppearanceColors {
|
|
38
|
+
const {
|
|
39
|
+
colors: {
|
|
40
|
+
statusErrorBackground,
|
|
41
|
+
statusErrorText,
|
|
42
|
+
statusErrorIcon,
|
|
43
|
+
statusInfoBackground,
|
|
44
|
+
statusInfoText,
|
|
45
|
+
statusInfoIcon,
|
|
46
|
+
statusNeutralBackground,
|
|
47
|
+
statusNeutralText,
|
|
48
|
+
statusNeutralIcon,
|
|
49
|
+
statusSuccessBackground,
|
|
50
|
+
statusSuccessText,
|
|
51
|
+
statusSuccessIcon,
|
|
52
|
+
statusWarningBackground,
|
|
53
|
+
statusWarningText,
|
|
54
|
+
statusWarningIcon,
|
|
55
|
+
},
|
|
56
|
+
} = useTheme()
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
error: {
|
|
60
|
+
background: statusErrorBackground,
|
|
61
|
+
text: statusErrorText,
|
|
62
|
+
icon: statusErrorIcon,
|
|
63
|
+
},
|
|
64
|
+
info: {
|
|
65
|
+
background: statusInfoBackground,
|
|
66
|
+
text: statusInfoText,
|
|
67
|
+
icon: statusInfoIcon,
|
|
68
|
+
},
|
|
69
|
+
neutral: {
|
|
70
|
+
background: statusNeutralBackground,
|
|
71
|
+
text: statusNeutralText,
|
|
72
|
+
icon: statusNeutralIcon,
|
|
73
|
+
},
|
|
74
|
+
success: {
|
|
75
|
+
background: statusSuccessBackground,
|
|
76
|
+
text: statusSuccessText,
|
|
77
|
+
icon: statusSuccessIcon,
|
|
78
|
+
},
|
|
79
|
+
warning: {
|
|
80
|
+
background: statusWarningBackground,
|
|
81
|
+
text: statusWarningText,
|
|
82
|
+
icon: statusWarningIcon,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import React, { createContext, FC, ReactNode, useContext } from 'react'
|
|
2
|
+
import { StyleSheet, View } from 'react-native'
|
|
3
|
+
import {
|
|
4
|
+
useStatusColorAppearanceMap,
|
|
5
|
+
type StatusAppearanceUnion,
|
|
6
|
+
} from '../display/utils/status_colors'
|
|
7
|
+
import { MAX_FONT_SIZE_MULTIPLIER, platformFontWeightMedium, space } from '../../utils'
|
|
8
|
+
import { tokens } from '../../vendor/tapestry/tokens'
|
|
9
|
+
import { Heading, Icon, Text, TextInlineButton } from '../display'
|
|
10
|
+
import { useFontScale } from '../../hooks'
|
|
11
|
+
|
|
12
|
+
// ========================================
|
|
13
|
+
// ====== Exports =========================
|
|
14
|
+
// ========================================
|
|
15
|
+
|
|
16
|
+
const Banner = {
|
|
17
|
+
Root: BannerRoot,
|
|
18
|
+
StaticLayout: BannerStaticLayout,
|
|
19
|
+
Content: BannerContent,
|
|
20
|
+
StatusIcon: BannerStatusIcon,
|
|
21
|
+
Heading: BannerHeading,
|
|
22
|
+
Text: BannerText,
|
|
23
|
+
Link: BannerLink,
|
|
24
|
+
} as const
|
|
25
|
+
|
|
26
|
+
type BannerComponents = {
|
|
27
|
+
Root: FC<BannerRootProps>
|
|
28
|
+
StaticLayout: FC<BannerStaticLayoutProps>
|
|
29
|
+
Content: FC<BannerContentProps>
|
|
30
|
+
StatusIcon: FC<BannerStatusIconProps>
|
|
31
|
+
Heading: FC<BannerHeadingProps>
|
|
32
|
+
Text: FC<BannerTextProps>
|
|
33
|
+
Link: FC<BannerLinkProps>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default Banner as BannerComponents
|
|
37
|
+
export type {
|
|
38
|
+
BannerRootProps,
|
|
39
|
+
BannerStaticLayoutProps,
|
|
40
|
+
BannerContentProps,
|
|
41
|
+
BannerStatusIconProps,
|
|
42
|
+
BannerHeadingProps,
|
|
43
|
+
BannerTextProps,
|
|
44
|
+
BannerLinkProps,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ========================================
|
|
48
|
+
// ====== Context =========================
|
|
49
|
+
// ========================================
|
|
50
|
+
|
|
51
|
+
interface BannerContextType {
|
|
52
|
+
appearance?: StatusAppearanceUnion
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const BannerContext = createContext<BannerContextType | null>(null)
|
|
56
|
+
|
|
57
|
+
function useBannerContext() {
|
|
58
|
+
const context = useContext(BannerContext)
|
|
59
|
+
if (!context) {
|
|
60
|
+
throw new Error('Banner components must be used within Banner.Root')
|
|
61
|
+
}
|
|
62
|
+
return context
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ========================================
|
|
66
|
+
// ====== BannerRoot ======================
|
|
67
|
+
// ========================================
|
|
68
|
+
|
|
69
|
+
interface BannerRootProps {
|
|
70
|
+
children: ReactNode
|
|
71
|
+
appearance?: StatusAppearanceUnion
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function BannerRoot({ children, appearance = 'neutral' }: BannerRootProps) {
|
|
75
|
+
return <BannerContext.Provider value={{ appearance }}>{children}</BannerContext.Provider>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
BannerRoot.displayName = 'Banner.Root'
|
|
79
|
+
|
|
80
|
+
// ========================================
|
|
81
|
+
// ====== BannerStaticLayout ==============
|
|
82
|
+
// ========================================
|
|
83
|
+
|
|
84
|
+
interface BannerStaticLayoutProps {
|
|
85
|
+
children: ReactNode
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function BannerStaticLayout({ children }: BannerStaticLayoutProps) {
|
|
89
|
+
const { appearance } = useBannerContext()
|
|
90
|
+
const styles = useStyles({ appearance })
|
|
91
|
+
|
|
92
|
+
return <View style={styles.staticLayout}>{children}</View>
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
BannerStaticLayout.displayName = 'Banner.StaticLayout'
|
|
96
|
+
|
|
97
|
+
// ========================================
|
|
98
|
+
// ====== BannerContent ===================
|
|
99
|
+
// ========================================
|
|
100
|
+
|
|
101
|
+
interface BannerContentProps {
|
|
102
|
+
children: ReactNode
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function BannerContent({ children }: BannerContentProps) {
|
|
106
|
+
const styles = useStyles()
|
|
107
|
+
|
|
108
|
+
return <View style={styles.content}>{children}</View>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
BannerContent.displayName = 'Banner.Content'
|
|
112
|
+
|
|
113
|
+
// ========================================
|
|
114
|
+
// ====== BannerStatusIcon ================
|
|
115
|
+
// ========================================
|
|
116
|
+
|
|
117
|
+
interface BannerStatusIconProps {
|
|
118
|
+
iconName?: string
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function BannerStatusIcon({ iconName }: BannerStatusIconProps) {
|
|
122
|
+
const { appearance = 'neutral' } = useBannerContext()
|
|
123
|
+
const styles = useStyles({ appearance })
|
|
124
|
+
|
|
125
|
+
const iconNameMap = {
|
|
126
|
+
error: 'general.exclamationTriangle',
|
|
127
|
+
info: 'general.outlinedInfoCircle',
|
|
128
|
+
neutral: 'general.outlinedInfoCircle',
|
|
129
|
+
success: 'general.check',
|
|
130
|
+
warning: 'general.exclamationTriangle',
|
|
131
|
+
} as const
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<Icon
|
|
135
|
+
name={iconName || iconNameMap[appearance]}
|
|
136
|
+
style={styles.icon}
|
|
137
|
+
maxFontSizeMultiplier={MAX_FONT_SIZE_MULTIPLIER}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
BannerStatusIcon.displayName = 'Banner.StatusIcon'
|
|
143
|
+
|
|
144
|
+
// ========================================
|
|
145
|
+
// ====== BannerHeading ===================
|
|
146
|
+
// ========================================
|
|
147
|
+
|
|
148
|
+
interface BannerHeadingProps {
|
|
149
|
+
children: ReactNode
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function BannerHeading({ children }: BannerHeadingProps) {
|
|
153
|
+
const { appearance = 'neutral' } = useBannerContext()
|
|
154
|
+
const styles = useStyles({ appearance })
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<Heading variant="h3" style={styles.heading}>
|
|
158
|
+
{children}
|
|
159
|
+
</Heading>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
BannerHeading.displayName = 'Banner.Heading'
|
|
164
|
+
|
|
165
|
+
// ========================================
|
|
166
|
+
// ====== BannerText ======================
|
|
167
|
+
// ========================================
|
|
168
|
+
|
|
169
|
+
interface BannerTextProps {
|
|
170
|
+
children: ReactNode
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function BannerText({ children }: BannerTextProps) {
|
|
174
|
+
const { appearance = 'neutral' } = useBannerContext()
|
|
175
|
+
const styles = useStyles({ appearance })
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Text variant="tertiary" style={styles.text}>
|
|
179
|
+
{children}
|
|
180
|
+
</Text>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
BannerText.displayName = 'Banner.Text'
|
|
185
|
+
|
|
186
|
+
// ========================================
|
|
187
|
+
// ====== BannerLink ======================
|
|
188
|
+
// ========================================
|
|
189
|
+
|
|
190
|
+
interface BannerLinkProps {
|
|
191
|
+
children: ReactNode
|
|
192
|
+
onPress?: () => void
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function BannerLink({ children, onPress }: BannerLinkProps) {
|
|
196
|
+
const { appearance = 'neutral' } = useBannerContext()
|
|
197
|
+
const styles = useStyles({ appearance })
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<TextInlineButton variant="tertiary" style={styles.text} onPress={onPress}>
|
|
201
|
+
{children}
|
|
202
|
+
</TextInlineButton>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
BannerLink.displayName = 'Banner.Link'
|
|
207
|
+
|
|
208
|
+
// ========================================
|
|
209
|
+
// ====== Styles ==========================
|
|
210
|
+
// ========================================
|
|
211
|
+
|
|
212
|
+
interface Styles {
|
|
213
|
+
appearance?: StatusAppearanceUnion
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const useStyles = ({ appearance = 'neutral' }: Styles = {}) => {
|
|
217
|
+
const statusColorMap = useStatusColorAppearanceMap()
|
|
218
|
+
const fontScale = useFontScale()
|
|
219
|
+
|
|
220
|
+
return StyleSheet.create({
|
|
221
|
+
staticLayout: {
|
|
222
|
+
flexDirection: 'row',
|
|
223
|
+
backgroundColor: statusColorMap[appearance].background,
|
|
224
|
+
padding: space(1.5),
|
|
225
|
+
gap: space(1),
|
|
226
|
+
borderRadius: tokens.borderRadiusMd,
|
|
227
|
+
flex: 1,
|
|
228
|
+
},
|
|
229
|
+
content: {
|
|
230
|
+
gap: space(0.5),
|
|
231
|
+
flex: 1,
|
|
232
|
+
},
|
|
233
|
+
icon: {
|
|
234
|
+
color: statusColorMap[appearance].icon,
|
|
235
|
+
fontSize: tokens.fontSizeMd,
|
|
236
|
+
marginTop: space(0.5) * fontScale,
|
|
237
|
+
},
|
|
238
|
+
heading: {
|
|
239
|
+
color: statusColorMap[appearance].text,
|
|
240
|
+
fontWeight: platformFontWeightMedium,
|
|
241
|
+
fontSize: tokens.fontSizeMd,
|
|
242
|
+
},
|
|
243
|
+
text: {
|
|
244
|
+
color: statusColorMap[appearance].text,
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { QueryClient, QueryClientProvider, QueryKey } from '@tanstack/react-query'
|
|
2
2
|
import React, { useContext, useEffect, useRef } from 'react'
|
|
3
3
|
import { ViewProps } from 'react-native'
|
|
4
|
-
import { Client } from '../utils'
|
|
5
4
|
import { ChatContext, ChatContextValue } from './chat_context'
|
|
6
5
|
import { RequestQueryKey } from '../hooks'
|
|
6
|
+
import { ApiClient, useApiClient } from '../hooks/use_api_client'
|
|
7
7
|
|
|
8
|
-
let apiClient:
|
|
8
|
+
let apiClient: ApiClient | undefined
|
|
9
9
|
|
|
10
10
|
const defaultQueryFn = ({ queryKey }: { queryKey: QueryKey }) => {
|
|
11
11
|
if (!apiClient) {
|
|
@@ -14,7 +14,7 @@ const defaultQueryFn = ({ queryKey }: { queryKey: QueryKey }) => {
|
|
|
14
14
|
|
|
15
15
|
const [url, data, headers] = queryKey as RequestQueryKey
|
|
16
16
|
|
|
17
|
-
return apiClient.get({ url, data, headers })
|
|
17
|
+
return apiClient.chat.get({ url, data, headers })
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export const queryClient = new QueryClient({
|
|
@@ -27,10 +27,10 @@ export const queryClient = new QueryClient({
|
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
export function ApiProvider({ children }: ViewProps) {
|
|
30
|
-
const { token, env
|
|
30
|
+
const { token, env } = useContext(ChatContext)
|
|
31
31
|
const sessionChanged = useSessionChanged({ token, env })
|
|
32
32
|
|
|
33
|
-
apiClient =
|
|
33
|
+
apiClient = useApiClient()
|
|
34
34
|
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
if (!sessionChanged) return
|
|
@@ -2,7 +2,7 @@ import { merge } from 'lodash'
|
|
|
2
2
|
import React, { createContext, useMemo } from 'react'
|
|
3
3
|
import { ColorSchemeName, useColorScheme } from 'react-native'
|
|
4
4
|
import { DeepPartial, OAuthToken } from '../types'
|
|
5
|
-
import {
|
|
5
|
+
import { ENV, Session } from '../utils'
|
|
6
6
|
import { ChatTheme, defaultTheme, DefaultTheme } from '../utils/theme'
|
|
7
7
|
|
|
8
8
|
export type ChatContextValue = {
|
|
@@ -10,49 +10,32 @@ export type ChatContextValue = {
|
|
|
10
10
|
onTokenExpired: () => void
|
|
11
11
|
theme: ChatTheme
|
|
12
12
|
env?: ENV
|
|
13
|
-
|
|
13
|
+
session: Session
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface ChatProviderProps extends Omit<ChatContextValue, 'client' | 'theme'> {
|
|
17
17
|
theme: CreateChatThemeProps
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const defaultChatClient = new Client({
|
|
21
|
-
app: 'chat',
|
|
22
|
-
session: new Session(),
|
|
23
|
-
version: '2018-11-01',
|
|
24
|
-
onTokenExpired: () => null,
|
|
25
|
-
})
|
|
26
|
-
|
|
27
20
|
export const ChatContext = createContext<ChatContextValue>({
|
|
28
21
|
theme: defaultTheme('light'),
|
|
29
22
|
token: undefined,
|
|
30
23
|
env: undefined,
|
|
31
24
|
onTokenExpired: () => {},
|
|
32
|
-
|
|
25
|
+
session: new Session(),
|
|
33
26
|
})
|
|
34
27
|
|
|
35
28
|
export function ChatProvider({ children, value }: { children: any; value: ChatProviderProps }) {
|
|
36
29
|
const { env, token, onTokenExpired } = value
|
|
37
30
|
const theme = useCreateChatTheme(value.theme || {})
|
|
38
31
|
const session = useMemo(() => new Session({ token, env }), [env, token])
|
|
39
|
-
const client = useMemo(
|
|
40
|
-
() =>
|
|
41
|
-
new Client({
|
|
42
|
-
app: 'chat',
|
|
43
|
-
session,
|
|
44
|
-
version: '2018-11-01',
|
|
45
|
-
onTokenExpired,
|
|
46
|
-
}),
|
|
47
|
-
[onTokenExpired, session]
|
|
48
|
-
)
|
|
49
32
|
|
|
50
33
|
const contextValue: ChatContextValue = {
|
|
51
34
|
env,
|
|
52
35
|
token,
|
|
53
36
|
onTokenExpired,
|
|
37
|
+
session,
|
|
54
38
|
theme,
|
|
55
|
-
client,
|
|
56
39
|
}
|
|
57
40
|
|
|
58
41
|
return <ChatContext.Provider value={contextValue}>{children}</ChatContext.Provider>
|