@appsupportkit/support-sdk 0.1.2 → 0.2.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.
- package/lib/commonjs/components/BottomSheet.js +67 -47
- package/lib/commonjs/components/BottomSheet.js.map +1 -1
- package/lib/commonjs/components/LauncherButton.js +177 -0
- package/lib/commonjs/components/LauncherButton.js.map +1 -0
- package/lib/commonjs/components/SupportButton.js +54 -176
- package/lib/commonjs/components/SupportButton.js.map +1 -1
- package/lib/commonjs/components/icons.js +74 -23
- package/lib/commonjs/components/icons.js.map +1 -1
- package/lib/commonjs/components/primitives.js +183 -38
- package/lib/commonjs/components/primitives.js.map +1 -1
- package/lib/commonjs/components/screens/ComposeScreen.js +115 -58
- package/lib/commonjs/components/screens/ComposeScreen.js.map +1 -1
- package/lib/commonjs/components/screens/HomeScreen.js +170 -115
- package/lib/commonjs/components/screens/HomeScreen.js.map +1 -1
- package/lib/commonjs/components/screens/ThreadScreen.js +222 -108
- package/lib/commonjs/components/screens/ThreadScreen.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/theme.js +136 -17
- package/lib/commonjs/theme.js.map +1 -1
- package/lib/commonjs/types.js +7 -0
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/unread.js +92 -0
- package/lib/commonjs/unread.js.map +1 -0
- package/lib/commonjs/useSupportThreads.js +286 -0
- package/lib/commonjs/useSupportThreads.js.map +1 -0
- package/lib/commonjs/utils.js +33 -0
- package/lib/commonjs/utils.js.map +1 -1
- package/lib/module/components/BottomSheet.js +68 -48
- package/lib/module/components/BottomSheet.js.map +1 -1
- package/lib/module/components/LauncherButton.js +172 -0
- package/lib/module/components/LauncherButton.js.map +1 -0
- package/lib/module/components/SupportButton.js +57 -179
- package/lib/module/components/SupportButton.js.map +1 -1
- package/lib/module/components/icons.js +72 -22
- package/lib/module/components/icons.js.map +1 -1
- package/lib/module/components/primitives.js +179 -39
- package/lib/module/components/primitives.js.map +1 -1
- package/lib/module/components/screens/ComposeScreen.js +118 -61
- package/lib/module/components/screens/ComposeScreen.js.map +1 -1
- package/lib/module/components/screens/HomeScreen.js +172 -117
- package/lib/module/components/screens/HomeScreen.js.map +1 -1
- package/lib/module/components/screens/ThreadScreen.js +228 -112
- package/lib/module/components/screens/ThreadScreen.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/theme.js +135 -16
- package/lib/module/theme.js.map +1 -1
- package/lib/module/types.js +7 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/unread.js +83 -0
- package/lib/module/unread.js.map +1 -0
- package/lib/module/useSupportThreads.js +281 -0
- package/lib/module/useSupportThreads.js.map +1 -0
- package/lib/module/utils.js +30 -0
- package/lib/module/utils.js.map +1 -1
- package/lib/typescript/components/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/components/LauncherButton.d.ts +14 -0
- package/lib/typescript/components/LauncherButton.d.ts.map +1 -0
- package/lib/typescript/components/SupportButton.d.ts +3 -3
- package/lib/typescript/components/SupportButton.d.ts.map +1 -1
- package/lib/typescript/components/icons.d.ts +13 -2
- package/lib/typescript/components/icons.d.ts.map +1 -1
- package/lib/typescript/components/primitives.d.ts +35 -4
- package/lib/typescript/components/primitives.d.ts.map +1 -1
- package/lib/typescript/components/screens/ComposeScreen.d.ts.map +1 -1
- package/lib/typescript/components/screens/HomeScreen.d.ts +3 -2
- package/lib/typescript/components/screens/HomeScreen.d.ts.map +1 -1
- package/lib/typescript/components/screens/ThreadScreen.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/theme.d.ts +44 -3
- package/lib/typescript/theme.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +22 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/unread.d.ts +22 -0
- package/lib/typescript/unread.d.ts.map +1 -0
- package/lib/typescript/useSupportThreads.d.ts +28 -0
- package/lib/typescript/useSupportThreads.d.ts.map +1 -0
- package/lib/typescript/utils.d.ts +5 -0
- package/lib/typescript/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/BottomSheet.tsx +61 -43
- package/src/components/LauncherButton.tsx +217 -0
- package/src/components/SupportButton.tsx +52 -196
- package/src/components/icons.tsx +77 -19
- package/src/components/primitives.tsx +217 -36
- package/src/components/screens/ComposeScreen.tsx +131 -61
- package/src/components/screens/HomeScreen.tsx +168 -117
- package/src/components/screens/ThreadScreen.tsx +289 -119
- package/src/index.tsx +1 -0
- package/src/theme.ts +165 -18
- package/src/types.ts +24 -0
- package/src/unread.ts +91 -0
- package/src/useSupportThreads.ts +372 -0
- package/src/utils.ts +33 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScrollView, Text, View } from 'react-native';
|
|
1
|
+
import { RefreshControl, ScrollView, Text, View } from 'react-native';
|
|
2
2
|
import type { SupportTheme } from '../../theme';
|
|
3
3
|
import type { QueryType, Thread } from '../../types';
|
|
4
4
|
import { QUERY_TYPE_LABEL } from '../../types';
|
|
@@ -7,84 +7,149 @@ import { Chevron, CloseIcon } from '../icons';
|
|
|
7
7
|
import {
|
|
8
8
|
EmptyState,
|
|
9
9
|
ErrorState,
|
|
10
|
+
IconButton,
|
|
10
11
|
PressableRow,
|
|
12
|
+
SectionLabel,
|
|
11
13
|
SkeletonBlock,
|
|
12
14
|
textStyles,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
TypeGlyph,
|
|
16
|
+
UnreadPill,
|
|
15
17
|
} from '../primitives';
|
|
16
18
|
|
|
17
19
|
const COMPOSE_TYPES: QueryType[] = ['feedback', 'feature_request', 'bug_report'];
|
|
18
20
|
|
|
21
|
+
const COMPOSE_HINT: Record<QueryType, string> = {
|
|
22
|
+
feedback: 'Tell us what you think',
|
|
23
|
+
feature_request: 'Suggest an improvement',
|
|
24
|
+
bug_report: "Something isn't working",
|
|
25
|
+
};
|
|
26
|
+
|
|
19
27
|
function ThreadRow({
|
|
20
28
|
thread,
|
|
21
29
|
theme,
|
|
22
30
|
onPress,
|
|
31
|
+
last,
|
|
23
32
|
}: {
|
|
24
33
|
thread: Thread;
|
|
25
34
|
theme: SupportTheme;
|
|
26
35
|
onPress: () => void;
|
|
36
|
+
last: boolean;
|
|
27
37
|
}) {
|
|
28
|
-
const
|
|
38
|
+
const unread = thread.unread_count > 0;
|
|
39
|
+
const preview = thread.last_message?.body ?? 'No messages yet';
|
|
40
|
+
const at = thread.last_message?.created_at ?? thread.created_at;
|
|
41
|
+
|
|
29
42
|
return (
|
|
30
43
|
<PressableRow
|
|
31
44
|
onPress={onPress}
|
|
32
45
|
theme={theme}
|
|
33
|
-
accessibilityLabel={
|
|
46
|
+
accessibilityLabel={
|
|
47
|
+
unread
|
|
48
|
+
? `${QUERY_TYPE_LABEL[thread.type]} thread, ${thread.unread_count} unread`
|
|
49
|
+
: `${QUERY_TYPE_LABEL[thread.type]} thread`
|
|
50
|
+
}
|
|
34
51
|
style={{
|
|
52
|
+
flexDirection: 'row',
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
gap: 12,
|
|
35
55
|
paddingHorizontal: 16,
|
|
36
|
-
paddingVertical:
|
|
37
|
-
borderBottomWidth: 1,
|
|
56
|
+
paddingVertical: 13,
|
|
57
|
+
borderBottomWidth: last ? 0 : 1,
|
|
38
58
|
borderBottomColor: theme.colors.border,
|
|
59
|
+
opacity: thread.status === 'resolved' ? 0.75 : 1,
|
|
39
60
|
}}
|
|
40
61
|
>
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
<TypeGlyph type={thread.type} theme={theme} size={38} />
|
|
63
|
+
|
|
64
|
+
<View style={{ flex: 1, gap: 3 }}>
|
|
65
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
66
|
+
<Text
|
|
67
|
+
numberOfLines={1}
|
|
44
68
|
style={{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
69
|
+
flex: 1,
|
|
70
|
+
fontSize: 14,
|
|
71
|
+
fontWeight: unread ? '700' : '600',
|
|
72
|
+
letterSpacing: -0.1,
|
|
73
|
+
color: theme.colors.textPrimary,
|
|
49
74
|
}}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
</
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</Text>
|
|
76
|
-
<Chevron color={theme.colors.textTertiary} />
|
|
75
|
+
>
|
|
76
|
+
{QUERY_TYPE_LABEL[thread.type]}
|
|
77
|
+
</Text>
|
|
78
|
+
<Text style={textStyles.meta(theme)}>{relativeTime(at)}</Text>
|
|
79
|
+
</View>
|
|
80
|
+
|
|
81
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
82
|
+
<Text
|
|
83
|
+
numberOfLines={1}
|
|
84
|
+
style={{
|
|
85
|
+
flex: 1,
|
|
86
|
+
fontSize: 13,
|
|
87
|
+
lineHeight: 18,
|
|
88
|
+
color: unread ? theme.colors.textPrimary : theme.colors.textSecondary,
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{thread.last_message?.sender === 'user' ? 'You: ' : ''}
|
|
92
|
+
{preview}
|
|
93
|
+
</Text>
|
|
94
|
+
{unread ? (
|
|
95
|
+
<UnreadPill count={thread.unread_count} theme={theme} />
|
|
96
|
+
) : (
|
|
97
|
+
<Chevron color={theme.colors.textTertiary} />
|
|
98
|
+
)}
|
|
99
|
+
</View>
|
|
77
100
|
</View>
|
|
78
101
|
</PressableRow>
|
|
79
102
|
);
|
|
80
103
|
}
|
|
81
104
|
|
|
105
|
+
function ThreadGroup({
|
|
106
|
+
label,
|
|
107
|
+
threads,
|
|
108
|
+
theme,
|
|
109
|
+
onOpenThread,
|
|
110
|
+
}: {
|
|
111
|
+
label: string;
|
|
112
|
+
threads: Thread[];
|
|
113
|
+
theme: SupportTheme;
|
|
114
|
+
onOpenThread: (thread: Thread) => void;
|
|
115
|
+
}) {
|
|
116
|
+
if (threads.length === 0) return null;
|
|
117
|
+
return (
|
|
118
|
+
<View style={{ marginTop: 24 }}>
|
|
119
|
+
<SectionLabel theme={theme} style={{ paddingHorizontal: 16, paddingBottom: 8 }}>
|
|
120
|
+
{label}
|
|
121
|
+
</SectionLabel>
|
|
122
|
+
<View
|
|
123
|
+
style={{
|
|
124
|
+
marginHorizontal: 16,
|
|
125
|
+
borderWidth: 1,
|
|
126
|
+
borderColor: theme.colors.border,
|
|
127
|
+
borderRadius: theme.radii.card,
|
|
128
|
+
backgroundColor: theme.colors.surface,
|
|
129
|
+
overflow: 'hidden',
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
{threads.map((thread, i) => (
|
|
133
|
+
<ThreadRow
|
|
134
|
+
key={thread.id}
|
|
135
|
+
thread={thread}
|
|
136
|
+
theme={theme}
|
|
137
|
+
onPress={() => onOpenThread(thread)}
|
|
138
|
+
last={i === threads.length - 1}
|
|
139
|
+
/>
|
|
140
|
+
))}
|
|
141
|
+
</View>
|
|
142
|
+
</View>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
82
146
|
export function HomeScreen({
|
|
83
147
|
theme,
|
|
84
148
|
threads,
|
|
85
149
|
loading,
|
|
150
|
+
refreshing,
|
|
86
151
|
error,
|
|
87
|
-
|
|
152
|
+
onRefresh,
|
|
88
153
|
onCompose,
|
|
89
154
|
onOpenThread,
|
|
90
155
|
onClose,
|
|
@@ -92,8 +157,9 @@ export function HomeScreen({
|
|
|
92
157
|
theme: SupportTheme;
|
|
93
158
|
threads: Thread[] | null;
|
|
94
159
|
loading: boolean;
|
|
160
|
+
refreshing: boolean;
|
|
95
161
|
error: string | null;
|
|
96
|
-
|
|
162
|
+
onRefresh: () => void;
|
|
97
163
|
onCompose: (type: QueryType) => void;
|
|
98
164
|
onOpenThread: (thread: Thread) => void;
|
|
99
165
|
onClose: () => void;
|
|
@@ -107,23 +173,42 @@ export function HomeScreen({
|
|
|
107
173
|
<View
|
|
108
174
|
style={{
|
|
109
175
|
flexDirection: 'row',
|
|
110
|
-
alignItems: '
|
|
176
|
+
alignItems: 'flex-start',
|
|
111
177
|
paddingHorizontal: 16,
|
|
112
|
-
|
|
178
|
+
paddingTop: 4,
|
|
179
|
+
paddingBottom: 16,
|
|
113
180
|
}}
|
|
114
181
|
>
|
|
115
|
-
<
|
|
116
|
-
|
|
182
|
+
<View style={{ flex: 1, gap: 2 }}>
|
|
183
|
+
<Text style={textStyles.title(theme)}>Support</Text>
|
|
184
|
+
<Text style={textStyles.secondary(theme)}>
|
|
185
|
+
Start a conversation — we reply right here.
|
|
186
|
+
</Text>
|
|
187
|
+
</View>
|
|
188
|
+
<IconButton
|
|
117
189
|
onPress={onClose}
|
|
118
190
|
theme={theme}
|
|
119
|
-
accessibilityLabel="Close"
|
|
120
|
-
|
|
191
|
+
accessibilityLabel="Close support"
|
|
192
|
+
size={32}
|
|
121
193
|
>
|
|
122
|
-
<CloseIcon color={theme.colors.textSecondary} size={
|
|
123
|
-
</
|
|
194
|
+
<CloseIcon color={theme.colors.textSecondary} size={12} />
|
|
195
|
+
</IconButton>
|
|
124
196
|
</View>
|
|
125
197
|
|
|
126
|
-
<ScrollView
|
|
198
|
+
<ScrollView
|
|
199
|
+
style={{ flex: 1 }}
|
|
200
|
+
contentContainerStyle={{ paddingBottom: 24 }}
|
|
201
|
+
keyboardShouldPersistTaps="handled"
|
|
202
|
+
refreshControl={
|
|
203
|
+
<RefreshControl
|
|
204
|
+
refreshing={refreshing}
|
|
205
|
+
onRefresh={onRefresh}
|
|
206
|
+
tintColor={theme.colors.textTertiary}
|
|
207
|
+
colors={[theme.colors.accent]}
|
|
208
|
+
progressBackgroundColor={theme.colors.surface}
|
|
209
|
+
/>
|
|
210
|
+
}
|
|
211
|
+
>
|
|
127
212
|
{/* Compose actions */}
|
|
128
213
|
<View
|
|
129
214
|
style={{
|
|
@@ -144,90 +229,56 @@ export function HomeScreen({
|
|
|
144
229
|
style={{
|
|
145
230
|
flexDirection: 'row',
|
|
146
231
|
alignItems: 'center',
|
|
147
|
-
gap:
|
|
232
|
+
gap: 12,
|
|
148
233
|
paddingHorizontal: 14,
|
|
149
234
|
paddingVertical: 12,
|
|
150
235
|
borderTopWidth: i === 0 ? 0 : 1,
|
|
151
236
|
borderTopColor: theme.colors.border,
|
|
152
237
|
}}
|
|
153
238
|
>
|
|
154
|
-
{
|
|
155
|
-
<
|
|
156
|
-
{
|
|
157
|
-
|
|
239
|
+
<TypeGlyph type={type} theme={theme} size={34} />
|
|
240
|
+
<View style={{ flex: 1, gap: 1 }}>
|
|
241
|
+
<Text style={textStyles.heading(theme)}>
|
|
242
|
+
{QUERY_TYPE_LABEL[type]}
|
|
243
|
+
</Text>
|
|
244
|
+
<Text style={textStyles.meta(theme)}>{COMPOSE_HINT[type]}</Text>
|
|
245
|
+
</View>
|
|
158
246
|
<Chevron color={theme.colors.textTertiary} />
|
|
159
247
|
</PressableRow>
|
|
160
248
|
))}
|
|
161
249
|
</View>
|
|
162
250
|
|
|
163
251
|
{/* Threads */}
|
|
164
|
-
{error && threads === null ? (
|
|
252
|
+
{error !== null && threads === null ? (
|
|
165
253
|
// Only take over the screen when there's nothing cached to show —
|
|
166
254
|
// a failed background poll shouldn't hide an already-loaded list.
|
|
167
|
-
<ErrorState message={error} onRetry={
|
|
255
|
+
<ErrorState message={error} onRetry={onRefresh} theme={theme} />
|
|
168
256
|
) : loading && threads === null ? (
|
|
169
|
-
<View style={{ paddingHorizontal: 16, paddingTop:
|
|
170
|
-
<SkeletonBlock width={
|
|
171
|
-
<SkeletonBlock width="100%" height={
|
|
172
|
-
<SkeletonBlock width="100%" height={
|
|
257
|
+
<View style={{ paddingHorizontal: 16, paddingTop: 28, gap: 12 }}>
|
|
258
|
+
<SkeletonBlock width={110} height={11} theme={theme} radius={4} />
|
|
259
|
+
<SkeletonBlock width="100%" height={64} theme={theme} radius={theme.radii.card} />
|
|
260
|
+
<SkeletonBlock width="100%" height={64} theme={theme} radius={theme.radii.card} />
|
|
173
261
|
</View>
|
|
174
262
|
) : (threads ?? []).length === 0 ? (
|
|
175
|
-
<EmptyState
|
|
263
|
+
<EmptyState
|
|
264
|
+
message="No conversations yet"
|
|
265
|
+
hint="Anything you send lands with the team, and their replies come back to this sheet."
|
|
266
|
+
theme={theme}
|
|
267
|
+
/>
|
|
176
268
|
) : (
|
|
177
269
|
<>
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}}
|
|
191
|
-
>
|
|
192
|
-
Open
|
|
193
|
-
</Text>
|
|
194
|
-
{open.map((t) => (
|
|
195
|
-
<ThreadRow
|
|
196
|
-
key={t.id}
|
|
197
|
-
thread={t}
|
|
198
|
-
theme={theme}
|
|
199
|
-
onPress={() => onOpenThread(t)}
|
|
200
|
-
/>
|
|
201
|
-
))}
|
|
202
|
-
</>
|
|
203
|
-
)}
|
|
204
|
-
{resolved.length > 0 && (
|
|
205
|
-
<>
|
|
206
|
-
<Text
|
|
207
|
-
style={{
|
|
208
|
-
fontSize: 11,
|
|
209
|
-
fontWeight: '500',
|
|
210
|
-
textTransform: 'uppercase',
|
|
211
|
-
letterSpacing: 0.4,
|
|
212
|
-
color: theme.colors.textTertiary,
|
|
213
|
-
paddingHorizontal: 16,
|
|
214
|
-
paddingTop: 20,
|
|
215
|
-
paddingBottom: 6,
|
|
216
|
-
}}
|
|
217
|
-
>
|
|
218
|
-
Resolved
|
|
219
|
-
</Text>
|
|
220
|
-
{resolved.map((t) => (
|
|
221
|
-
<ThreadRow
|
|
222
|
-
key={t.id}
|
|
223
|
-
thread={t}
|
|
224
|
-
theme={theme}
|
|
225
|
-
onPress={() => onOpenThread(t)}
|
|
226
|
-
/>
|
|
227
|
-
))}
|
|
228
|
-
</>
|
|
229
|
-
)}
|
|
230
|
-
<View style={{ height: 16 }} />
|
|
270
|
+
<ThreadGroup
|
|
271
|
+
label={`Open · ${open.length}`}
|
|
272
|
+
threads={open}
|
|
273
|
+
theme={theme}
|
|
274
|
+
onOpenThread={onOpenThread}
|
|
275
|
+
/>
|
|
276
|
+
<ThreadGroup
|
|
277
|
+
label="Resolved"
|
|
278
|
+
threads={resolved}
|
|
279
|
+
theme={theme}
|
|
280
|
+
onOpenThread={onOpenThread}
|
|
281
|
+
/>
|
|
231
282
|
</>
|
|
232
283
|
)}
|
|
233
284
|
</ScrollView>
|