@appsupportkit/support-sdk 0.1.2 → 0.3.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 (224) hide show
  1. package/lib/commonjs/api.js +17 -2
  2. package/lib/commonjs/api.js.map +1 -1
  3. package/lib/commonjs/components/BottomSheet.js +67 -47
  4. package/lib/commonjs/components/BottomSheet.js.map +1 -1
  5. package/lib/commonjs/components/LauncherButton.js +177 -0
  6. package/lib/commonjs/components/LauncherButton.js.map +1 -0
  7. package/lib/commonjs/components/SupportButton.js +168 -190
  8. package/lib/commonjs/components/SupportButton.js.map +1 -1
  9. package/lib/commonjs/components/icons.js +110 -23
  10. package/lib/commonjs/components/icons.js.map +1 -1
  11. package/lib/commonjs/components/primitives.js +191 -44
  12. package/lib/commonjs/components/primitives.js.map +1 -1
  13. package/lib/commonjs/components/screens/ComposeScreen.js +108 -60
  14. package/lib/commonjs/components/screens/ComposeScreen.js.map +1 -1
  15. package/lib/commonjs/components/screens/HomeScreen.js +221 -118
  16. package/lib/commonjs/components/screens/HomeScreen.js.map +1 -1
  17. package/lib/commonjs/components/screens/LanguageScreen.js +150 -0
  18. package/lib/commonjs/components/screens/LanguageScreen.js.map +1 -0
  19. package/lib/commonjs/components/screens/ThreadScreen.js +237 -111
  20. package/lib/commonjs/components/screens/ThreadScreen.js.map +1 -1
  21. package/lib/commonjs/i18n/index.js +101 -0
  22. package/lib/commonjs/i18n/index.js.map +1 -0
  23. package/lib/commonjs/i18n/languages.js +95 -0
  24. package/lib/commonjs/i18n/languages.js.map +1 -0
  25. package/lib/commonjs/i18n/strings/de.js +72 -0
  26. package/lib/commonjs/i18n/strings/de.js.map +1 -0
  27. package/lib/commonjs/i18n/strings/en.js +88 -0
  28. package/lib/commonjs/i18n/strings/en.js.map +1 -0
  29. package/lib/commonjs/i18n/strings/es.js +72 -0
  30. package/lib/commonjs/i18n/strings/es.js.map +1 -0
  31. package/lib/commonjs/i18n/strings/fr.js +72 -0
  32. package/lib/commonjs/i18n/strings/fr.js.map +1 -0
  33. package/lib/commonjs/i18n/strings/hi.js +72 -0
  34. package/lib/commonjs/i18n/strings/hi.js.map +1 -0
  35. package/lib/commonjs/i18n/strings/it.js +72 -0
  36. package/lib/commonjs/i18n/strings/it.js.map +1 -0
  37. package/lib/commonjs/i18n/strings/ja.js +72 -0
  38. package/lib/commonjs/i18n/strings/ja.js.map +1 -0
  39. package/lib/commonjs/i18n/strings/ko.js +72 -0
  40. package/lib/commonjs/i18n/strings/ko.js.map +1 -0
  41. package/lib/commonjs/i18n/strings/nl.js +72 -0
  42. package/lib/commonjs/i18n/strings/nl.js.map +1 -0
  43. package/lib/commonjs/i18n/strings/pl.js +72 -0
  44. package/lib/commonjs/i18n/strings/pl.js.map +1 -0
  45. package/lib/commonjs/i18n/strings/pt.js +72 -0
  46. package/lib/commonjs/i18n/strings/pt.js.map +1 -0
  47. package/lib/commonjs/i18n/strings/ru.js +72 -0
  48. package/lib/commonjs/i18n/strings/ru.js.map +1 -0
  49. package/lib/commonjs/i18n/strings/tr.js +72 -0
  50. package/lib/commonjs/i18n/strings/tr.js.map +1 -0
  51. package/lib/commonjs/i18n/strings/zh.js +72 -0
  52. package/lib/commonjs/i18n/strings/zh.js.map +1 -0
  53. package/lib/commonjs/index.js +31 -0
  54. package/lib/commonjs/index.js.map +1 -1
  55. package/lib/commonjs/theme.js +136 -17
  56. package/lib/commonjs/theme.js.map +1 -1
  57. package/lib/commonjs/types.js +18 -0
  58. package/lib/commonjs/types.js.map +1 -1
  59. package/lib/commonjs/unread.js +92 -0
  60. package/lib/commonjs/unread.js.map +1 -0
  61. package/lib/commonjs/useSupportThreads.js +296 -0
  62. package/lib/commonjs/useSupportThreads.js.map +1 -0
  63. package/lib/commonjs/utils.js +58 -10
  64. package/lib/commonjs/utils.js.map +1 -1
  65. package/lib/module/api.js +16 -2
  66. package/lib/module/api.js.map +1 -1
  67. package/lib/module/components/BottomSheet.js +68 -48
  68. package/lib/module/components/BottomSheet.js.map +1 -1
  69. package/lib/module/components/LauncherButton.js +172 -0
  70. package/lib/module/components/LauncherButton.js.map +1 -0
  71. package/lib/module/components/SupportButton.js +172 -194
  72. package/lib/module/components/SupportButton.js.map +1 -1
  73. package/lib/module/components/icons.js +107 -22
  74. package/lib/module/components/icons.js.map +1 -1
  75. package/lib/module/components/primitives.js +187 -45
  76. package/lib/module/components/primitives.js.map +1 -1
  77. package/lib/module/components/screens/ComposeScreen.js +114 -64
  78. package/lib/module/components/screens/ComposeScreen.js.map +1 -1
  79. package/lib/module/components/screens/HomeScreen.js +224 -121
  80. package/lib/module/components/screens/HomeScreen.js.map +1 -1
  81. package/lib/module/components/screens/LanguageScreen.js +147 -0
  82. package/lib/module/components/screens/LanguageScreen.js.map +1 -0
  83. package/lib/module/components/screens/ThreadScreen.js +243 -115
  84. package/lib/module/components/screens/ThreadScreen.js.map +1 -1
  85. package/lib/module/i18n/index.js +56 -0
  86. package/lib/module/i18n/index.js.map +1 -0
  87. package/lib/module/i18n/languages.js +88 -0
  88. package/lib/module/i18n/languages.js.map +1 -0
  89. package/lib/module/i18n/strings/de.js +68 -0
  90. package/lib/module/i18n/strings/de.js.map +1 -0
  91. package/lib/module/i18n/strings/en.js +84 -0
  92. package/lib/module/i18n/strings/en.js.map +1 -0
  93. package/lib/module/i18n/strings/es.js +68 -0
  94. package/lib/module/i18n/strings/es.js.map +1 -0
  95. package/lib/module/i18n/strings/fr.js +68 -0
  96. package/lib/module/i18n/strings/fr.js.map +1 -0
  97. package/lib/module/i18n/strings/hi.js +68 -0
  98. package/lib/module/i18n/strings/hi.js.map +1 -0
  99. package/lib/module/i18n/strings/it.js +68 -0
  100. package/lib/module/i18n/strings/it.js.map +1 -0
  101. package/lib/module/i18n/strings/ja.js +68 -0
  102. package/lib/module/i18n/strings/ja.js.map +1 -0
  103. package/lib/module/i18n/strings/ko.js +68 -0
  104. package/lib/module/i18n/strings/ko.js.map +1 -0
  105. package/lib/module/i18n/strings/nl.js +68 -0
  106. package/lib/module/i18n/strings/nl.js.map +1 -0
  107. package/lib/module/i18n/strings/pl.js +68 -0
  108. package/lib/module/i18n/strings/pl.js.map +1 -0
  109. package/lib/module/i18n/strings/pt.js +68 -0
  110. package/lib/module/i18n/strings/pt.js.map +1 -0
  111. package/lib/module/i18n/strings/ru.js +68 -0
  112. package/lib/module/i18n/strings/ru.js.map +1 -0
  113. package/lib/module/i18n/strings/tr.js +68 -0
  114. package/lib/module/i18n/strings/tr.js.map +1 -0
  115. package/lib/module/i18n/strings/zh.js +68 -0
  116. package/lib/module/i18n/strings/zh.js.map +1 -0
  117. package/lib/module/index.js +1 -0
  118. package/lib/module/index.js.map +1 -1
  119. package/lib/module/theme.js +135 -16
  120. package/lib/module/theme.js.map +1 -1
  121. package/lib/module/types.js +18 -0
  122. package/lib/module/types.js.map +1 -1
  123. package/lib/module/unread.js +83 -0
  124. package/lib/module/unread.js.map +1 -0
  125. package/lib/module/useSupportThreads.js +291 -0
  126. package/lib/module/useSupportThreads.js.map +1 -0
  127. package/lib/module/utils.js +55 -10
  128. package/lib/module/utils.js.map +1 -1
  129. package/lib/typescript/api.d.ts +9 -0
  130. package/lib/typescript/api.d.ts.map +1 -1
  131. package/lib/typescript/components/BottomSheet.d.ts.map +1 -1
  132. package/lib/typescript/components/LauncherButton.d.ts +14 -0
  133. package/lib/typescript/components/LauncherButton.d.ts.map +1 -0
  134. package/lib/typescript/components/SupportButton.d.ts +3 -3
  135. package/lib/typescript/components/SupportButton.d.ts.map +1 -1
  136. package/lib/typescript/components/icons.d.ts +18 -2
  137. package/lib/typescript/components/icons.d.ts.map +1 -1
  138. package/lib/typescript/components/primitives.d.ts +42 -7
  139. package/lib/typescript/components/primitives.d.ts.map +1 -1
  140. package/lib/typescript/components/screens/ComposeScreen.d.ts +3 -1
  141. package/lib/typescript/components/screens/ComposeScreen.d.ts.map +1 -1
  142. package/lib/typescript/components/screens/HomeScreen.d.ts +8 -2
  143. package/lib/typescript/components/screens/HomeScreen.d.ts.map +1 -1
  144. package/lib/typescript/components/screens/LanguageScreen.d.ts +30 -0
  145. package/lib/typescript/components/screens/LanguageScreen.d.ts.map +1 -0
  146. package/lib/typescript/components/screens/ThreadScreen.d.ts +3 -1
  147. package/lib/typescript/components/screens/ThreadScreen.d.ts.map +1 -1
  148. package/lib/typescript/i18n/index.d.ts +15 -0
  149. package/lib/typescript/i18n/index.d.ts.map +1 -0
  150. package/lib/typescript/i18n/languages.d.ts +36 -0
  151. package/lib/typescript/i18n/languages.d.ts.map +1 -0
  152. package/lib/typescript/i18n/strings/de.d.ts +3 -0
  153. package/lib/typescript/i18n/strings/de.d.ts.map +1 -0
  154. package/lib/typescript/i18n/strings/en.d.ts +83 -0
  155. package/lib/typescript/i18n/strings/en.d.ts.map +1 -0
  156. package/lib/typescript/i18n/strings/es.d.ts +3 -0
  157. package/lib/typescript/i18n/strings/es.d.ts.map +1 -0
  158. package/lib/typescript/i18n/strings/fr.d.ts +3 -0
  159. package/lib/typescript/i18n/strings/fr.d.ts.map +1 -0
  160. package/lib/typescript/i18n/strings/hi.d.ts +3 -0
  161. package/lib/typescript/i18n/strings/hi.d.ts.map +1 -0
  162. package/lib/typescript/i18n/strings/it.d.ts +3 -0
  163. package/lib/typescript/i18n/strings/it.d.ts.map +1 -0
  164. package/lib/typescript/i18n/strings/ja.d.ts +3 -0
  165. package/lib/typescript/i18n/strings/ja.d.ts.map +1 -0
  166. package/lib/typescript/i18n/strings/ko.d.ts +3 -0
  167. package/lib/typescript/i18n/strings/ko.d.ts.map +1 -0
  168. package/lib/typescript/i18n/strings/nl.d.ts +3 -0
  169. package/lib/typescript/i18n/strings/nl.d.ts.map +1 -0
  170. package/lib/typescript/i18n/strings/pl.d.ts +3 -0
  171. package/lib/typescript/i18n/strings/pl.d.ts.map +1 -0
  172. package/lib/typescript/i18n/strings/pt.d.ts +3 -0
  173. package/lib/typescript/i18n/strings/pt.d.ts.map +1 -0
  174. package/lib/typescript/i18n/strings/ru.d.ts +3 -0
  175. package/lib/typescript/i18n/strings/ru.d.ts.map +1 -0
  176. package/lib/typescript/i18n/strings/tr.d.ts +3 -0
  177. package/lib/typescript/i18n/strings/tr.d.ts.map +1 -0
  178. package/lib/typescript/i18n/strings/zh.d.ts +3 -0
  179. package/lib/typescript/i18n/strings/zh.d.ts.map +1 -0
  180. package/lib/typescript/index.d.ts +2 -1
  181. package/lib/typescript/index.d.ts.map +1 -1
  182. package/lib/typescript/theme.d.ts +44 -3
  183. package/lib/typescript/theme.d.ts.map +1 -1
  184. package/lib/typescript/types.d.ts +54 -2
  185. package/lib/typescript/types.d.ts.map +1 -1
  186. package/lib/typescript/unread.d.ts +22 -0
  187. package/lib/typescript/unread.d.ts.map +1 -0
  188. package/lib/typescript/useSupportThreads.d.ts +38 -0
  189. package/lib/typescript/useSupportThreads.d.ts.map +1 -0
  190. package/lib/typescript/utils.d.ts +8 -2
  191. package/lib/typescript/utils.d.ts.map +1 -1
  192. package/package.json +3 -3
  193. package/src/api.ts +24 -1
  194. package/src/components/BottomSheet.tsx +61 -43
  195. package/src/components/LauncherButton.tsx +217 -0
  196. package/src/components/SupportButton.tsx +195 -210
  197. package/src/components/icons.tsx +113 -19
  198. package/src/components/primitives.tsx +230 -39
  199. package/src/components/screens/ComposeScreen.tsx +129 -63
  200. package/src/components/screens/HomeScreen.tsx +230 -120
  201. package/src/components/screens/LanguageScreen.tsx +172 -0
  202. package/src/components/screens/ThreadScreen.tsx +319 -122
  203. package/src/i18n/index.ts +81 -0
  204. package/src/i18n/languages.ts +106 -0
  205. package/src/i18n/strings/de.ts +78 -0
  206. package/src/i18n/strings/en.ts +94 -0
  207. package/src/i18n/strings/es.ts +77 -0
  208. package/src/i18n/strings/fr.ts +78 -0
  209. package/src/i18n/strings/hi.ts +77 -0
  210. package/src/i18n/strings/it.ts +77 -0
  211. package/src/i18n/strings/ja.ts +77 -0
  212. package/src/i18n/strings/ko.ts +76 -0
  213. package/src/i18n/strings/nl.ts +77 -0
  214. package/src/i18n/strings/pl.ts +77 -0
  215. package/src/i18n/strings/pt.ts +77 -0
  216. package/src/i18n/strings/ru.ts +77 -0
  217. package/src/i18n/strings/tr.ts +77 -0
  218. package/src/i18n/strings/zh.ts +75 -0
  219. package/src/index.tsx +9 -0
  220. package/src/theme.ts +165 -18
  221. package/src/types.ts +56 -2
  222. package/src/unread.ts +91 -0
  223. package/src/useSupportThreads.ts +392 -0
  224. package/src/utils.ts +48 -10
@@ -0,0 +1,217 @@
1
+ // The launcher: the one piece of the SDK that lives inside the host app's
2
+ // own UI, so it has to look intentional next to anything. Three variants
3
+ // cover the placements that actually occur — a floating action button, a
4
+ // bordered button in a header, and a bare icon in a toolbar.
5
+ import { useEffect, useRef } from 'react';
6
+ import {
7
+ Animated,
8
+ Easing,
9
+ Pressable,
10
+ Text,
11
+ View,
12
+ type StyleProp,
13
+ type ViewStyle,
14
+ } from 'react-native';
15
+ import type { SupportTheme } from '../theme';
16
+ import type { LauncherVariant } from '../types';
17
+ import { ChatBubbleMark } from './icons';
18
+
19
+ const PRESS_SCALE = 0.93;
20
+
21
+ /** Counts stay legible: three digits in a pill is not a design. */
22
+ function badgeLabel(unread: number): string {
23
+ return unread > 99 ? '99+' : String(unread);
24
+ }
25
+
26
+ export function LauncherButton({
27
+ theme,
28
+ unread,
29
+ variant = 'solid',
30
+ size = 52,
31
+ label,
32
+ style,
33
+ onPress,
34
+ }: {
35
+ theme: SupportTheme;
36
+ unread: number;
37
+ variant?: LauncherVariant;
38
+ size?: number;
39
+ /** Renders a pill with text next to the mark instead of a circle. */
40
+ label?: string;
41
+ style?: StyleProp<ViewStyle>;
42
+ onPress: () => void;
43
+ }) {
44
+ const scale = useRef(new Animated.Value(1)).current;
45
+ const pulse = useRef(new Animated.Value(0)).current;
46
+ const previousUnread = useRef(unread);
47
+
48
+ // A new reply pulses the halo twice, then stops. Enough to catch the eye
49
+ // on the way past; not a permanently animating element in someone's app.
50
+ useEffect(() => {
51
+ if (unread > previousUnread.current) {
52
+ pulse.setValue(0);
53
+ const animation = Animated.loop(
54
+ Animated.timing(pulse, {
55
+ toValue: 1,
56
+ duration: 1600,
57
+ easing: Easing.out(Easing.ease),
58
+ useNativeDriver: true,
59
+ }),
60
+ { iterations: 2 },
61
+ );
62
+ animation.start(() => pulse.setValue(0));
63
+ previousUnread.current = unread;
64
+ return () => animation.stop();
65
+ }
66
+ previousUnread.current = unread;
67
+ return undefined;
68
+ }, [unread, pulse]);
69
+
70
+ const press = (to: number) =>
71
+ Animated.timing(scale, {
72
+ toValue: to,
73
+ duration: to === 1 ? theme.motion.base : theme.motion.fast,
74
+ easing: Easing.out(Easing.cubic),
75
+ useNativeDriver: true,
76
+ }).start();
77
+
78
+ const solid = variant === 'solid';
79
+ const soft = variant === 'soft';
80
+ const fill = solid
81
+ ? theme.colors.accent
82
+ : soft
83
+ ? theme.colors.surface
84
+ : 'transparent';
85
+ const markColor = solid
86
+ ? theme.colors.accentForeground
87
+ : soft
88
+ ? theme.colors.accent
89
+ : theme.launcher.minimalMark;
90
+ // Dots are punched out of the bubble in whatever sits behind the mark —
91
+ // the button's own fill, or the app background when there isn't one.
92
+ const markHoleColor = solid
93
+ ? theme.colors.accent
94
+ : soft
95
+ ? theme.colors.surface
96
+ : theme.colors.bg;
97
+ // Dark: a hairline separates the fill from a near-black app background.
98
+ // Light: the accent-tinted shadow already does that, so the ring only has
99
+ // to keep the edge from looking soft.
100
+ const border = solid
101
+ ? { borderWidth: 1, borderColor: theme.launcher.solidRing }
102
+ : soft
103
+ ? { borderWidth: 1, borderColor: theme.colors.border }
104
+ : { borderWidth: 0, borderColor: 'transparent' };
105
+ const badgeBg = solid ? theme.colors.accentForeground : theme.colors.accent;
106
+ const badgeFg = solid ? theme.colors.accent : theme.colors.accentForeground;
107
+
108
+ const height = label ? Math.max(44, size * 0.84) : size;
109
+ const markSize = Math.round(size * (label ? 0.36 : 0.42));
110
+
111
+ return (
112
+ <Animated.View
113
+ style={[{ transform: [{ scale }] }, style]}
114
+ // The halo and badge sit outside the button's bounds.
115
+ pointerEvents="box-none"
116
+ >
117
+ {unread > 0 && (
118
+ <Animated.View
119
+ pointerEvents="none"
120
+ style={{
121
+ position: 'absolute',
122
+ top: 0,
123
+ left: 0,
124
+ width: label ? undefined : size,
125
+ right: label ? 0 : undefined,
126
+ height,
127
+ borderRadius: theme.radii.pill,
128
+ backgroundColor: theme.colors.accent,
129
+ opacity: pulse.interpolate({
130
+ inputRange: [0, 1],
131
+ outputRange: [0.28, 0],
132
+ }),
133
+ transform: [
134
+ {
135
+ scale: pulse.interpolate({
136
+ inputRange: [0, 1],
137
+ outputRange: [1, 1.5],
138
+ }),
139
+ },
140
+ ],
141
+ }}
142
+ />
143
+ )}
144
+
145
+ <Pressable
146
+ onPress={onPress}
147
+ onPressIn={() => press(PRESS_SCALE)}
148
+ onPressOut={() => press(1)}
149
+ hitSlop={8}
150
+ accessibilityRole="button"
151
+ accessibilityLabel={
152
+ unread > 0
153
+ ? `Support, ${unread} unread ${unread === 1 ? 'reply' : 'replies'}`
154
+ : 'Support'
155
+ }
156
+ style={{
157
+ height,
158
+ width: label ? undefined : size,
159
+ paddingHorizontal: label ? 18 : 0,
160
+ flexDirection: 'row',
161
+ alignItems: 'center',
162
+ justifyContent: 'center',
163
+ gap: 9,
164
+ borderRadius: theme.radii.pill,
165
+ backgroundColor: fill,
166
+ ...border,
167
+ ...(variant === 'minimal' ? null : theme.shadows.launcher),
168
+ }}
169
+ >
170
+ <ChatBubbleMark color={markColor} dotColor={markHoleColor} size={markSize} />
171
+ {label !== undefined && (
172
+ <Text
173
+ style={{
174
+ fontSize: 15,
175
+ fontWeight: '600',
176
+ letterSpacing: -0.1,
177
+ color: solid ? theme.colors.accentForeground : theme.colors.textPrimary,
178
+ }}
179
+ >
180
+ {label}
181
+ </Text>
182
+ )}
183
+ </Pressable>
184
+
185
+ {unread > 0 && (
186
+ <View
187
+ pointerEvents="none"
188
+ style={{
189
+ position: 'absolute',
190
+ top: -5,
191
+ right: -5,
192
+ minWidth: 19,
193
+ height: 19,
194
+ borderRadius: theme.radii.pill,
195
+ paddingHorizontal: 5,
196
+ backgroundColor: badgeBg,
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ ...theme.shadows.badge,
200
+ }}
201
+ >
202
+ <Text
203
+ style={{
204
+ fontSize: 11,
205
+ lineHeight: 14,
206
+ fontWeight: '700',
207
+ color: badgeFg,
208
+ fontVariant: ['tabular-nums'],
209
+ }}
210
+ >
211
+ {badgeLabel(unread)}
212
+ </Text>
213
+ </View>
214
+ )}
215
+ </Animated.View>
216
+ );
217
+ }