@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
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef, type ReactNode } from 'react';
2
2
  import {
3
3
  Animated,
4
+ Easing,
4
5
  Pressable,
5
6
  Text,
6
7
  View,
@@ -8,9 +9,9 @@ import {
8
9
  type TextStyle,
9
10
  type ViewStyle,
10
11
  } from 'react-native';
12
+ import type { Translator } from '../i18n';
11
13
  import type { SupportTheme } from '../theme';
12
14
  import type { QueryStatus, QueryType } from '../types';
13
- import { QUERY_TYPE_LABEL } from '../types';
14
15
  import { AlertCircleIcon, ChatIcon, PlusCircleIcon } from './icons';
15
16
 
16
17
  export function typeIcon(type: QueryType, color: string, size = 15): ReactNode {
@@ -25,13 +26,47 @@ export function typeColor(type: QueryType, theme: SupportTheme): string {
25
26
  return theme.colors.statusOpen;
26
27
  }
27
28
 
29
+ export function typeBackground(type: QueryType, theme: SupportTheme): string {
30
+ if (type === 'bug_report') return theme.colors.statusDangerBg;
31
+ if (type === 'feature_request') return theme.colors.statusProgressBg;
32
+ return theme.colors.statusOpenBg;
33
+ }
34
+
35
+ /** Tinted round chip holding a type icon — the anchor of every list row. */
36
+ export function TypeGlyph({
37
+ type,
38
+ theme,
39
+ size = 36,
40
+ }: {
41
+ type: QueryType;
42
+ theme: SupportTheme;
43
+ size?: number;
44
+ }) {
45
+ return (
46
+ <View
47
+ style={{
48
+ width: size,
49
+ height: size,
50
+ borderRadius: size / 2,
51
+ backgroundColor: typeBackground(type, theme),
52
+ alignItems: 'center',
53
+ justifyContent: 'center',
54
+ }}
55
+ >
56
+ {typeIcon(type, typeColor(type, theme), size * 0.44)}
57
+ </View>
58
+ );
59
+ }
60
+
28
61
  /** Status dot + label — same visual language as the dashboard. */
29
62
  export function StatusIndicator({
30
63
  status,
31
64
  theme,
65
+ strings,
32
66
  }: {
33
67
  status: QueryStatus;
34
68
  theme: SupportTheme;
69
+ strings: Translator;
35
70
  }) {
36
71
  const color =
37
72
  status === 'open' ? theme.colors.statusOpen : theme.colors.statusResolved;
@@ -41,13 +76,13 @@ export function StatusIndicator({
41
76
  style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: color }}
42
77
  />
43
78
  <Text style={{ fontSize: 12, color }}>
44
- {status === 'open' ? 'Open' : 'Resolved'}
79
+ {strings.t(status === 'open' ? 'status.open' : 'status.resolved')}
45
80
  </Text>
46
81
  </View>
47
82
  );
48
83
  }
49
84
 
50
- /** Pressable row with the standard hover/press wash. */
85
+ /** Pressable row with the standard press wash. */
51
86
  export function PressableRow({
52
87
  onPress,
53
88
  children,
@@ -76,30 +111,83 @@ export function PressableRow({
76
111
  );
77
112
  }
78
113
 
114
+ /** Circular icon button — back, close, send. */
115
+ export function IconButton({
116
+ onPress,
117
+ children,
118
+ theme,
119
+ accessibilityLabel,
120
+ size = 34,
121
+ disabled,
122
+ tone = 'surface',
123
+ }: {
124
+ onPress: () => void;
125
+ children: ReactNode;
126
+ theme: SupportTheme;
127
+ accessibilityLabel: string;
128
+ size?: number;
129
+ disabled?: boolean;
130
+ tone?: 'surface' | 'accent';
131
+ }) {
132
+ const accent = tone === 'accent';
133
+ return (
134
+ <Pressable
135
+ onPress={onPress}
136
+ disabled={disabled}
137
+ hitSlop={6}
138
+ accessibilityRole="button"
139
+ accessibilityLabel={accessibilityLabel}
140
+ style={({ pressed }) => ({
141
+ width: size,
142
+ height: size,
143
+ borderRadius: size / 2,
144
+ alignItems: 'center',
145
+ justifyContent: 'center',
146
+ opacity: disabled ? 0.45 : 1,
147
+ backgroundColor: accent
148
+ ? pressed
149
+ ? theme.colors.accentHover
150
+ : theme.colors.accent
151
+ : pressed
152
+ ? theme.colors.surfaceActive
153
+ : theme.colors.surface,
154
+ borderWidth: accent ? 0 : 1,
155
+ borderColor: theme.colors.border,
156
+ })}
157
+ >
158
+ {children}
159
+ </Pressable>
160
+ );
161
+ }
162
+
79
163
  export function PrimaryButton({
80
164
  label,
81
165
  onPress,
82
166
  disabled,
83
167
  theme,
168
+ fullWidth,
84
169
  }: {
85
170
  label: string;
86
171
  onPress: () => void;
87
172
  disabled?: boolean;
88
173
  theme: SupportTheme;
174
+ fullWidth?: boolean;
89
175
  }) {
90
176
  return (
91
177
  <Pressable
92
178
  onPress={onPress}
93
179
  disabled={disabled}
94
180
  accessibilityRole="button"
181
+ accessibilityState={{ disabled: Boolean(disabled) }}
95
182
  style={({ pressed }) => ({
96
- height: 36,
97
- paddingHorizontal: 14,
183
+ height: 44,
184
+ alignSelf: fullWidth ? 'stretch' : 'flex-start',
185
+ paddingHorizontal: 18,
98
186
  borderRadius: theme.radii.control,
99
187
  backgroundColor: pressed
100
188
  ? theme.colors.accentHover
101
189
  : theme.colors.accent,
102
- opacity: disabled ? 0.5 : 1,
190
+ opacity: disabled ? 0.45 : 1,
103
191
  alignItems: 'center',
104
192
  justifyContent: 'center',
105
193
  })}
@@ -107,8 +195,9 @@ export function PrimaryButton({
107
195
  <Text
108
196
  style={{
109
197
  color: theme.colors.accentForeground,
110
- fontSize: 13,
111
- fontWeight: '500',
198
+ fontSize: 14,
199
+ fontWeight: '600',
200
+ letterSpacing: -0.1,
112
201
  }}
113
202
  >
114
203
  {label}
@@ -131,8 +220,8 @@ export function SecondaryButton({
131
220
  onPress={onPress}
132
221
  accessibilityRole="button"
133
222
  style={({ pressed }) => ({
134
- height: 32,
135
- paddingHorizontal: 12,
223
+ height: 38,
224
+ paddingHorizontal: 16,
136
225
  borderRadius: theme.radii.control,
137
226
  borderWidth: 1,
138
227
  borderColor: theme.colors.border,
@@ -143,29 +232,118 @@ export function SecondaryButton({
143
232
  justifyContent: 'center',
144
233
  })}
145
234
  >
146
- <Text style={{ color: theme.colors.textPrimary, fontSize: 12, fontWeight: '500' }}>
235
+ <Text style={{ color: theme.colors.textPrimary, fontSize: 13, fontWeight: '500' }}>
147
236
  {label}
148
237
  </Text>
149
238
  </Pressable>
150
239
  );
151
240
  }
152
241
 
242
+ /** The count pill on a thread row. */
243
+ export function UnreadPill({
244
+ count,
245
+ theme,
246
+ }: {
247
+ count: number;
248
+ theme: SupportTheme;
249
+ }) {
250
+ return (
251
+ <View
252
+ style={{
253
+ minWidth: 20,
254
+ height: 20,
255
+ paddingHorizontal: 6,
256
+ borderRadius: theme.radii.pill,
257
+ backgroundColor: theme.colors.accent,
258
+ alignItems: 'center',
259
+ justifyContent: 'center',
260
+ }}
261
+ >
262
+ <Text
263
+ style={{
264
+ fontSize: 11,
265
+ lineHeight: 14,
266
+ fontWeight: '700',
267
+ color: theme.colors.accentForeground,
268
+ fontVariant: ['tabular-nums'],
269
+ }}
270
+ >
271
+ {count > 99 ? '99+' : count}
272
+ </Text>
273
+ </View>
274
+ );
275
+ }
276
+
277
+ export function SectionLabel({
278
+ children,
279
+ theme,
280
+ style,
281
+ }: {
282
+ children: ReactNode;
283
+ theme: SupportTheme;
284
+ style?: StyleProp<TextStyle>;
285
+ }) {
286
+ return (
287
+ <Text
288
+ style={[
289
+ {
290
+ fontSize: 11,
291
+ fontWeight: '600',
292
+ textTransform: 'uppercase',
293
+ letterSpacing: 0.6,
294
+ color: theme.colors.textTertiary,
295
+ },
296
+ style,
297
+ ]}
298
+ >
299
+ {children}
300
+ </Text>
301
+ );
302
+ }
303
+
153
304
  /** Small muted icon + one line — no illustrations. */
154
305
  export function EmptyState({
155
306
  message,
307
+ hint,
156
308
  theme,
157
309
  icon,
158
310
  }: {
159
311
  message: string;
312
+ hint?: string;
160
313
  theme: SupportTheme;
161
314
  icon?: ReactNode;
162
315
  }) {
163
316
  return (
164
- <View style={{ alignItems: 'center', paddingVertical: 40, gap: 8 }}>
165
- {icon ?? <ChatIcon color={theme.colors.textTertiary} size={16} />}
166
- <Text style={{ fontSize: 13, color: theme.colors.textSecondary }}>
317
+ <View style={{ alignItems: 'center', paddingVertical: 44, paddingHorizontal: 32, gap: 10 }}>
318
+ <View
319
+ style={{
320
+ width: 44,
321
+ height: 44,
322
+ borderRadius: 22,
323
+ alignItems: 'center',
324
+ justifyContent: 'center',
325
+ backgroundColor: theme.colors.surface,
326
+ borderWidth: 1,
327
+ borderColor: theme.colors.border,
328
+ }}
329
+ >
330
+ {icon ?? <ChatIcon color={theme.colors.textTertiary} size={18} />}
331
+ </View>
332
+ <Text style={{ fontSize: 15, fontWeight: '600', color: theme.colors.textPrimary }}>
167
333
  {message}
168
334
  </Text>
335
+ {hint !== undefined && (
336
+ <Text
337
+ style={{
338
+ fontSize: 13,
339
+ lineHeight: 19,
340
+ color: theme.colors.textSecondary,
341
+ textAlign: 'center',
342
+ }}
343
+ >
344
+ {hint}
345
+ </Text>
346
+ )}
169
347
  </View>
170
348
  );
171
349
  }
@@ -175,17 +353,23 @@ export function ErrorState({
175
353
  message,
176
354
  onRetry,
177
355
  theme,
356
+ strings,
178
357
  }: {
179
358
  message: string;
180
359
  onRetry: () => void;
181
360
  theme: SupportTheme;
361
+ strings: Translator;
182
362
  }) {
183
363
  return (
184
- <View style={{ alignItems: 'center', paddingVertical: 32, gap: 10 }}>
185
- <Text style={{ fontSize: 13, color: theme.colors.textSecondary }}>
364
+ <View style={{ alignItems: 'center', paddingVertical: 40, gap: 12 }}>
365
+ <Text style={{ fontSize: 14, color: theme.colors.textSecondary }}>
186
366
  {message}
187
367
  </Text>
188
- <SecondaryButton label="Retry" onPress={onRetry} theme={theme} />
368
+ <SecondaryButton
369
+ label={strings.t('error.tryAgain')}
370
+ onPress={onRetry}
371
+ theme={theme}
372
+ />
189
373
  </View>
190
374
  );
191
375
  }
@@ -195,26 +379,30 @@ export function SkeletonBlock({
195
379
  width,
196
380
  height,
197
381
  theme,
382
+ radius,
198
383
  style,
199
384
  }: {
200
385
  width: number | `${number}%`;
201
386
  height: number;
202
387
  theme: SupportTheme;
388
+ radius?: number;
203
389
  style?: StyleProp<ViewStyle>;
204
390
  }) {
205
- const opacity = useRef(new Animated.Value(0.5)).current;
391
+ const opacity = useRef(new Animated.Value(0.4)).current;
206
392
 
207
393
  useEffect(() => {
208
394
  const loop = Animated.loop(
209
395
  Animated.sequence([
210
396
  Animated.timing(opacity, {
211
- toValue: 1,
212
- duration: 600,
397
+ toValue: 0.9,
398
+ duration: 700,
399
+ easing: Easing.inOut(Easing.quad),
213
400
  useNativeDriver: true,
214
401
  }),
215
402
  Animated.timing(opacity, {
216
- toValue: 0.5,
217
- duration: 600,
403
+ toValue: 0.4,
404
+ duration: 700,
405
+ easing: Easing.inOut(Easing.quad),
218
406
  useNativeDriver: true,
219
407
  }),
220
408
  ]),
@@ -229,7 +417,7 @@ export function SkeletonBlock({
229
417
  {
230
418
  width,
231
419
  height,
232
- borderRadius: theme.radii.control,
420
+ borderRadius: radius ?? theme.radii.control,
233
421
  backgroundColor: theme.colors.surfaceActive,
234
422
  opacity,
235
423
  },
@@ -241,22 +429,29 @@ export function SkeletonBlock({
241
429
 
242
430
  export const textStyles = {
243
431
  title: (theme: SupportTheme): TextStyle => ({
244
- fontSize: 16,
432
+ fontSize: 19,
433
+ fontWeight: '700',
434
+ letterSpacing: -0.4,
435
+ color: theme.colors.textPrimary,
436
+ }),
437
+ heading: (theme: SupportTheme): TextStyle => ({
438
+ fontSize: 15,
245
439
  fontWeight: '600',
246
440
  letterSpacing: -0.2,
247
441
  color: theme.colors.textPrimary,
248
442
  }),
249
443
  body: (theme: SupportTheme): TextStyle => ({
250
- fontSize: 14,
444
+ fontSize: 15,
251
445
  color: theme.colors.textPrimary,
252
- lineHeight: 20,
446
+ lineHeight: 21,
253
447
  }),
254
448
  secondary: (theme: SupportTheme): TextStyle => ({
255
449
  fontSize: 13,
450
+ lineHeight: 19,
256
451
  color: theme.colors.textSecondary,
257
452
  }),
258
453
  meta: (theme: SupportTheme): TextStyle => ({
259
- fontSize: 11,
454
+ fontSize: 11.5,
260
455
  color: theme.colors.textTertiary,
261
456
  fontVariant: ['tabular-nums'],
262
457
  }),
@@ -265,33 +460,29 @@ export const textStyles = {
265
460
  export function TypeChipView({
266
461
  type,
267
462
  theme,
463
+ strings,
268
464
  }: {
269
465
  type: QueryType;
270
466
  theme: SupportTheme;
467
+ strings: Translator;
271
468
  }) {
272
469
  const color = typeColor(type, theme);
273
- const bg =
274
- type === 'bug_report'
275
- ? theme.colors.statusDangerBg
276
- : type === 'feature_request'
277
- ? theme.colors.statusProgressBg
278
- : theme.colors.statusOpenBg;
279
470
  return (
280
471
  <View
281
472
  style={{
282
473
  flexDirection: 'row',
283
474
  alignItems: 'center',
284
475
  gap: 5,
285
- paddingHorizontal: 6,
286
- paddingVertical: 2,
287
- borderRadius: theme.radii.control,
288
- backgroundColor: bg,
476
+ paddingHorizontal: 8,
477
+ paddingVertical: 4,
478
+ borderRadius: theme.radii.pill,
479
+ backgroundColor: typeBackground(type, theme),
289
480
  alignSelf: 'flex-start',
290
481
  }}
291
482
  >
292
483
  {typeIcon(type, color, 11)}
293
- <Text style={{ fontSize: 11, fontWeight: '500', color }}>
294
- {QUERY_TYPE_LABEL[type]}
484
+ <Text style={{ fontSize: 11.5, fontWeight: '600', color }}>
485
+ {strings.t(`type.${type}`)}
295
486
  </Text>
296
487
  </View>
297
488
  );