@upstep/react-native 0.6.12 → 0.6.13
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 +4 -2
- package/src/api.ts +43 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstep/react-native",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.13",
|
|
4
4
|
"description": "Upstep feedback SDK for React Native",
|
|
5
5
|
"homepage": "https://upstep.dev",
|
|
6
6
|
"repository": {
|
|
@@ -19,14 +19,16 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@upstep/types": "0.5.
|
|
22
|
+
"@upstep/types": "0.5.5"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
+
"@react-native-async-storage/async-storage": ">=1.18",
|
|
25
26
|
"react": ">=18",
|
|
26
27
|
"react-native": ">=0.73",
|
|
27
28
|
"react-native-safe-area-context": ">=4"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
31
|
+
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
30
32
|
"@types/react": "^19.0.7",
|
|
31
33
|
"@types/react-native": "^0.73.0",
|
|
32
34
|
"react-native-safe-area-context": "^5.8.0",
|
package/src/api.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
1
2
|
import type {
|
|
2
3
|
Feedback,
|
|
3
4
|
FeedbackListResponse,
|
|
@@ -7,15 +8,42 @@ import type {
|
|
|
7
8
|
VoteValue,
|
|
8
9
|
} from "@upstep/types";
|
|
9
10
|
|
|
11
|
+
const ANON_ID_KEY = "upstep-anonymous-id";
|
|
12
|
+
|
|
13
|
+
function generateId(): string {
|
|
14
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export class UpstepApiClient {
|
|
11
18
|
private readonly baseUrl: string;
|
|
12
19
|
private readonly apiKey: string;
|
|
13
20
|
userId: string | undefined;
|
|
21
|
+
// Set synchronously to a throwaway id so there's always something to send;
|
|
22
|
+
// swapped for the persisted value (or backfilled into storage) once
|
|
23
|
+
// AsyncStorage resolves. Callers await anonymousIdReady first so requests
|
|
24
|
+
// always go out with the id that's actually persisted.
|
|
25
|
+
anonymousId: string;
|
|
26
|
+
private readonly anonymousIdReady: Promise<void>;
|
|
14
27
|
|
|
15
28
|
constructor(config: UpstepConfig) {
|
|
16
29
|
this.baseUrl = (config.baseUrl ?? "https://upstep.dev").replace(/\/$/, "");
|
|
17
30
|
this.apiKey = config.apiKey;
|
|
18
31
|
if (config.userId !== undefined) this.userId = config.userId;
|
|
32
|
+
this.anonymousId = generateId();
|
|
33
|
+
this.anonymousIdReady = this.loadAnonymousId();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async loadAnonymousId(): Promise<void> {
|
|
37
|
+
try {
|
|
38
|
+
const stored = await AsyncStorage.getItem(ANON_ID_KEY);
|
|
39
|
+
if (stored) {
|
|
40
|
+
this.anonymousId = stored;
|
|
41
|
+
} else {
|
|
42
|
+
await AsyncStorage.setItem(ANON_ID_KEY, this.anonymousId);
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// storage unavailable - keep the in-memory id for this session
|
|
46
|
+
}
|
|
19
47
|
}
|
|
20
48
|
|
|
21
49
|
/** Update the end-user id at runtime (e.g. after the user logs in). */
|
|
@@ -28,20 +56,25 @@ export class UpstepApiClient {
|
|
|
28
56
|
}
|
|
29
57
|
|
|
30
58
|
async listFeedback(params?: { cursor?: string; limit?: number; sort?: "date" | "votes" }): Promise<FeedbackListResponse> {
|
|
59
|
+
await this.anonymousIdReady;
|
|
31
60
|
const qs = new URLSearchParams();
|
|
32
61
|
if (params?.cursor) qs.set("cursor", params.cursor);
|
|
33
62
|
if (params?.limit) qs.set("limit", String(params.limit));
|
|
34
63
|
if (params?.sort) qs.set("sort", params.sort);
|
|
35
64
|
// Pass userId so the server can include the user's own pending items.
|
|
36
65
|
if (this.userId) qs.set("endUserId", this.userId);
|
|
66
|
+
qs.set("anonymousId", this.anonymousId);
|
|
37
67
|
const res = await fetch(`${this.baseUrl}/api/sdk/feedback?${qs}`, { headers: this.headers() });
|
|
38
68
|
if (!res.ok) throw new Error(`Upstep: ${res.status}`);
|
|
39
69
|
return res.json() as Promise<FeedbackListResponse>;
|
|
40
70
|
}
|
|
41
71
|
|
|
42
72
|
async getItem(feedbackId: string): Promise<FeedbackWithComments> {
|
|
43
|
-
|
|
44
|
-
const
|
|
73
|
+
await this.anonymousIdReady;
|
|
74
|
+
const qs = new URLSearchParams();
|
|
75
|
+
if (this.userId) qs.set("endUserId", this.userId);
|
|
76
|
+
qs.set("anonymousId", this.anonymousId);
|
|
77
|
+
const res = await fetch(`${this.baseUrl}/api/sdk/feedback/${feedbackId}?${qs}`, {
|
|
45
78
|
headers: this.headers(),
|
|
46
79
|
});
|
|
47
80
|
if (!res.ok) throw new Error(`Upstep: ${res.status}`);
|
|
@@ -49,20 +82,26 @@ export class UpstepApiClient {
|
|
|
49
82
|
}
|
|
50
83
|
|
|
51
84
|
async submitFeedback(payload: SubmitFeedbackPayload): Promise<Feedback> {
|
|
85
|
+
await this.anonymousIdReady;
|
|
52
86
|
const res = await fetch(`${this.baseUrl}/api/sdk/feedback`, {
|
|
53
87
|
method: "POST",
|
|
54
88
|
headers: this.headers(),
|
|
55
|
-
body: JSON.stringify({
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
...payload,
|
|
91
|
+
endUserId: payload.endUserId ?? this.userId,
|
|
92
|
+
anonymousId: payload.anonymousId ?? this.anonymousId,
|
|
93
|
+
}),
|
|
56
94
|
});
|
|
57
95
|
if (!res.ok) throw new Error(`Upstep: ${res.status}`);
|
|
58
96
|
return res.json() as Promise<Feedback>;
|
|
59
97
|
}
|
|
60
98
|
|
|
61
99
|
async vote(feedbackId: string, value: VoteValue): Promise<void> {
|
|
100
|
+
await this.anonymousIdReady;
|
|
62
101
|
const res = await fetch(`${this.baseUrl}/api/sdk/feedback/${feedbackId}/vote`, {
|
|
63
102
|
method: "POST",
|
|
64
103
|
headers: this.headers(),
|
|
65
|
-
body: JSON.stringify({ value, endUserId: this.userId }),
|
|
104
|
+
body: JSON.stringify({ value, endUserId: this.userId, anonymousId: this.anonymousId }),
|
|
66
105
|
});
|
|
67
106
|
if (!res.ok) throw new Error(`Upstep: ${res.status}`);
|
|
68
107
|
}
|