@upstep/react-native 0.6.8 → 0.6.11
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/package.json +2 -2
- package/src/FeedbackButton.tsx +35 -4
- package/src/FeedbackContext.tsx +4 -0
- package/src/FeedbackSheet.tsx +20 -65
- package/src/api.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstep/react-native",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"description": "Upstep feedback SDK for React Native",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@upstep/types": "0.5.
|
|
15
|
+
"@upstep/types": "0.5.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"react": ">=18",
|
package/src/FeedbackButton.tsx
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { TouchableOpacity, Text, StyleSheet } from "react-native";
|
|
2
|
+
import { TouchableOpacity, Text, View, StyleSheet } from "react-native";
|
|
3
3
|
import { useUpstep } from "./FeedbackContext";
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
position?: "bottom-right" | "bottom-left";
|
|
7
7
|
label?: string;
|
|
8
|
+
icon?: React.ReactNode;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function FeedbackButton({ position = "bottom-right", label = "Feedback" }: Props) {
|
|
11
|
+
export function FeedbackButton({ position = "bottom-right", label = "Feedback", icon }: Props) {
|
|
11
12
|
const { openSheet, accentColor } = useUpstep();
|
|
12
13
|
|
|
13
14
|
return (
|
|
@@ -20,12 +21,21 @@ export function FeedbackButton({ position = "bottom-right", label = "Feedback" }
|
|
|
20
21
|
]}
|
|
21
22
|
activeOpacity={0.85}
|
|
22
23
|
>
|
|
23
|
-
<
|
|
24
|
+
{icon ?? <ChatIcon />}
|
|
24
25
|
<Text style={styles.label}>{label}</Text>
|
|
25
26
|
</TouchableOpacity>
|
|
26
27
|
);
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
function ChatIcon() {
|
|
31
|
+
return (
|
|
32
|
+
<View style={styles.iconWrap}>
|
|
33
|
+
<View style={styles.iconBubble} />
|
|
34
|
+
<View style={styles.iconTail} />
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
const styles = StyleSheet.create({
|
|
30
40
|
btn: {
|
|
31
41
|
position: "absolute",
|
|
@@ -45,6 +55,27 @@ const styles = StyleSheet.create({
|
|
|
45
55
|
},
|
|
46
56
|
right: { right: 20 },
|
|
47
57
|
left: { left: 20 },
|
|
48
|
-
icon: { fontSize: 14 },
|
|
49
58
|
label: { color: "#fff", fontSize: 14, fontWeight: "600" },
|
|
59
|
+
iconWrap: { width: 16, height: 16, position: "relative" },
|
|
60
|
+
iconBubble: {
|
|
61
|
+
position: "absolute",
|
|
62
|
+
top: 0,
|
|
63
|
+
left: 0,
|
|
64
|
+
right: 2,
|
|
65
|
+
bottom: 3,
|
|
66
|
+
borderRadius: 4,
|
|
67
|
+
borderWidth: 1.5,
|
|
68
|
+
borderColor: "#fff",
|
|
69
|
+
},
|
|
70
|
+
iconTail: {
|
|
71
|
+
position: "absolute",
|
|
72
|
+
bottom: 0,
|
|
73
|
+
left: 3,
|
|
74
|
+
width: 5,
|
|
75
|
+
height: 5,
|
|
76
|
+
borderRightWidth: 1.5,
|
|
77
|
+
borderBottomWidth: 1.5,
|
|
78
|
+
borderColor: "#fff",
|
|
79
|
+
transform: [{ rotate: "45deg" }],
|
|
80
|
+
},
|
|
50
81
|
});
|
package/src/FeedbackContext.tsx
CHANGED
|
@@ -38,6 +38,7 @@ interface UpstepContextValue {
|
|
|
38
38
|
identify: (userId: string | undefined) => void;
|
|
39
39
|
accentColor: string;
|
|
40
40
|
theme: "light" | "dark" | "auto";
|
|
41
|
+
showBranding: boolean;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const UpstepContext = createContext<UpstepContextValue | null>(null);
|
|
@@ -50,6 +51,7 @@ export function FeedbackProvider({
|
|
|
50
51
|
const [feedItems, setFeedItems] = useState<Feedback[]>([]);
|
|
51
52
|
const [feedLoading, setFeedLoading] = useState(true);
|
|
52
53
|
const [isOpen, setIsOpen] = useState(false);
|
|
54
|
+
const [showBranding, setShowBranding] = useState(true);
|
|
53
55
|
|
|
54
56
|
useEffect(() => {
|
|
55
57
|
client.setUserId(config.userId);
|
|
@@ -60,6 +62,7 @@ export function FeedbackProvider({
|
|
|
60
62
|
try {
|
|
61
63
|
const data = await client.listFeedback({ sort: "votes", limit: 30 });
|
|
62
64
|
setFeedItems(data.items);
|
|
65
|
+
setShowBranding(data.showBranding ?? true);
|
|
63
66
|
} catch {
|
|
64
67
|
// non-critical
|
|
65
68
|
} finally {
|
|
@@ -132,6 +135,7 @@ export function FeedbackProvider({
|
|
|
132
135
|
isOpen, openSheet, closeSheet, identify,
|
|
133
136
|
accentColor: config.accentColor ?? DEFAULT_ACCENT,
|
|
134
137
|
theme: config.theme ?? "auto",
|
|
138
|
+
showBranding,
|
|
135
139
|
}}
|
|
136
140
|
>
|
|
137
141
|
{children}
|
package/src/FeedbackSheet.tsx
CHANGED
|
@@ -60,13 +60,12 @@ type Nav =
|
|
|
60
60
|
// ─── FeedbackSheet ────────────────────────────────────────────────────────────
|
|
61
61
|
|
|
62
62
|
export function FeedbackSheet() {
|
|
63
|
-
const { isOpen, closeSheet, accentColor, theme, loadFeed } = useUpstep();
|
|
63
|
+
const { isOpen, closeSheet, accentColor, theme, loadFeed, showBranding } = useUpstep();
|
|
64
64
|
const scheme = useColorScheme();
|
|
65
65
|
const isDark = theme === "dark" || (theme === "auto" && scheme === "dark");
|
|
66
66
|
const p = isDark ? DARK : LIGHT;
|
|
67
67
|
|
|
68
68
|
const [nav, setNav] = useState<Nav>({ screen: "list" });
|
|
69
|
-
const [listTab, setListTab] = useState<"open" | "done">("open");
|
|
70
69
|
const insets = useSafeAreaInsets();
|
|
71
70
|
|
|
72
71
|
// Refresh list every time the modal opens
|
|
@@ -136,30 +135,6 @@ export function FeedbackSheet() {
|
|
|
136
135
|
)}
|
|
137
136
|
</View>
|
|
138
137
|
|
|
139
|
-
{/* Open / Completed tabs — only on list screen */}
|
|
140
|
-
{nav.screen === "list" && (
|
|
141
|
-
<View style={[styles.listTabBar, { borderBottomColor: p.border }]}>
|
|
142
|
-
<TouchableOpacity
|
|
143
|
-
onPress={() => setListTab("open")}
|
|
144
|
-
style={[styles.listTabBtn, listTab === "open" && { borderBottomColor: accentColor }]}
|
|
145
|
-
activeOpacity={0.7}
|
|
146
|
-
>
|
|
147
|
-
<Text style={[styles.listTabText, { color: listTab === "open" ? accentColor : p.textFaint }]}>
|
|
148
|
-
Open
|
|
149
|
-
</Text>
|
|
150
|
-
</TouchableOpacity>
|
|
151
|
-
<TouchableOpacity
|
|
152
|
-
onPress={() => setListTab("done")}
|
|
153
|
-
style={[styles.listTabBtn, listTab === "done" && { borderBottomColor: accentColor }]}
|
|
154
|
-
activeOpacity={0.7}
|
|
155
|
-
>
|
|
156
|
-
<Text style={[styles.listTabText, { color: listTab === "done" ? accentColor : p.textFaint }]}>
|
|
157
|
-
Completed
|
|
158
|
-
</Text>
|
|
159
|
-
</TouchableOpacity>
|
|
160
|
-
</View>
|
|
161
|
-
)}
|
|
162
|
-
|
|
163
138
|
{/* Scrollable body */}
|
|
164
139
|
<ScrollView
|
|
165
140
|
style={styles.scroll}
|
|
@@ -171,7 +146,6 @@ export function FeedbackSheet() {
|
|
|
171
146
|
<FeedListBody
|
|
172
147
|
p={p}
|
|
173
148
|
accent={accentColor}
|
|
174
|
-
tab={listTab}
|
|
175
149
|
onSelect={(id) => setNav({ screen: "detail", feedbackId: id })}
|
|
176
150
|
/>
|
|
177
151
|
)}
|
|
@@ -193,19 +167,21 @@ export function FeedbackSheet() {
|
|
|
193
167
|
)}
|
|
194
168
|
</ScrollView>
|
|
195
169
|
|
|
196
|
-
{/* Branding
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
170
|
+
{/* Branding — hidden for Business plan */}
|
|
171
|
+
{showBranding && (
|
|
172
|
+
<TouchableOpacity
|
|
173
|
+
onPress={() => Linking.openURL("https://upstep.dev")}
|
|
174
|
+
activeOpacity={0.6}
|
|
175
|
+
style={[
|
|
176
|
+
styles.poweredBy,
|
|
177
|
+
{ borderTopColor: p.border, paddingBottom: insets.bottom + 10, backgroundColor: p.bg },
|
|
178
|
+
]}
|
|
179
|
+
>
|
|
180
|
+
<Text style={[styles.poweredByText, { color: p.textFaint }]}>
|
|
181
|
+
Powered by <Text style={{ fontWeight: "700" }}>Upstep.dev</Text>
|
|
182
|
+
</Text>
|
|
183
|
+
</TouchableOpacity>
|
|
184
|
+
)}
|
|
209
185
|
</View>
|
|
210
186
|
</KeyboardAvoidingView>
|
|
211
187
|
</Modal>
|
|
@@ -215,15 +191,13 @@ export function FeedbackSheet() {
|
|
|
215
191
|
// ─── FeedListBody ─────────────────────────────────────────────────────────────
|
|
216
192
|
|
|
217
193
|
function FeedListBody({
|
|
218
|
-
p, accent,
|
|
194
|
+
p, accent, onSelect,
|
|
219
195
|
}: {
|
|
220
|
-
p: Palette; accent: string;
|
|
196
|
+
p: Palette; accent: string; onSelect: (id: string) => void;
|
|
221
197
|
}) {
|
|
222
198
|
const { feedItems, feedLoading, vote } = useUpstep();
|
|
223
199
|
|
|
224
|
-
const displayItems =
|
|
225
|
-
? feedItems.filter((f) => f.status === "DONE")
|
|
226
|
-
: feedItems.filter((f) => f.status === "OPEN" || f.status === "IN_PROGRESS" || f.status === "PENDING");
|
|
200
|
+
const displayItems = feedItems.filter((f) => f.status !== "CLOSED");
|
|
227
201
|
|
|
228
202
|
if (feedLoading) {
|
|
229
203
|
return (
|
|
@@ -237,9 +211,7 @@ function FeedListBody({
|
|
|
237
211
|
return (
|
|
238
212
|
<View style={styles.centered}>
|
|
239
213
|
<Text style={[styles.emptyText, { color: p.textFaint }]}>
|
|
240
|
-
|
|
241
|
-
? "No completed items yet."
|
|
242
|
-
: "No open feedback yet. Be the first to add one."}
|
|
214
|
+
No open feedback yet. Be the first to add one.
|
|
243
215
|
</Text>
|
|
244
216
|
</View>
|
|
245
217
|
);
|
|
@@ -638,23 +610,6 @@ const styles = StyleSheet.create({
|
|
|
638
610
|
newBtn: { borderRadius: 20, paddingHorizontal: 14, paddingVertical: 7 },
|
|
639
611
|
newBtnText: { color: "#fff", fontSize: 13, fontWeight: "700" },
|
|
640
612
|
|
|
641
|
-
// List tab bar
|
|
642
|
-
listTabBar: {
|
|
643
|
-
flexDirection: "row",
|
|
644
|
-
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
645
|
-
},
|
|
646
|
-
listTabBtn: {
|
|
647
|
-
flex: 1,
|
|
648
|
-
alignItems: "center",
|
|
649
|
-
paddingVertical: 11,
|
|
650
|
-
borderBottomWidth: 2,
|
|
651
|
-
borderBottomColor: "transparent",
|
|
652
|
-
},
|
|
653
|
-
listTabText: {
|
|
654
|
-
fontSize: 13,
|
|
655
|
-
fontWeight: "600",
|
|
656
|
-
},
|
|
657
|
-
|
|
658
613
|
// Scroll — flex: 1 fills all remaining space after the fixed header
|
|
659
614
|
scroll: { flex: 1 },
|
|
660
615
|
scrollContent: { flexGrow: 1, padding: 16, paddingBottom: 32 },
|
package/src/api.ts
CHANGED
|
@@ -13,7 +13,7 @@ export class UpstepApiClient {
|
|
|
13
13
|
userId: string | undefined;
|
|
14
14
|
|
|
15
15
|
constructor(config: UpstepConfig) {
|
|
16
|
-
this.baseUrl = (config.baseUrl ?? "https://upstep.
|
|
16
|
+
this.baseUrl = (config.baseUrl ?? "https://upstep.dev").replace(/\/$/, "");
|
|
17
17
|
this.apiKey = config.apiKey;
|
|
18
18
|
if (config.userId !== undefined) this.userId = config.userId;
|
|
19
19
|
}
|