@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstep/react-native",
3
- "version": "0.6.8",
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.0"
15
+ "@upstep/types": "0.5.3"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "react": ">=18",
@@ -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
- <Text style={styles.icon}>💬</Text>
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
  });
@@ -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}
@@ -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 + safe-area bottom padding in one strip */}
197
- <TouchableOpacity
198
- onPress={() => Linking.openURL("https://upstep.dev")}
199
- activeOpacity={0.6}
200
- style={[
201
- styles.poweredBy,
202
- { borderTopColor: p.border, paddingBottom: insets.bottom + 10, backgroundColor: p.bg },
203
- ]}
204
- >
205
- <Text style={[styles.poweredByText, { color: p.textFaint }]}>
206
- Powered by <Text style={{ fontWeight: "700" }}>Upstep.dev</Text>
207
- </Text>
208
- </TouchableOpacity>
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, tab, onSelect,
194
+ p, accent, onSelect,
219
195
  }: {
220
- p: Palette; accent: string; tab: "open" | "done"; onSelect: (id: string) => void;
196
+ p: Palette; accent: string; onSelect: (id: string) => void;
221
197
  }) {
222
198
  const { feedItems, feedLoading, vote } = useUpstep();
223
199
 
224
- const displayItems = tab === "done"
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
- {tab === "done"
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.io").replace(/\/$/, "");
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
  }