@bbearai/react-native 0.1.1 → 0.1.3
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/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +202 -40
- package/dist/index.mjs +207 -45
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -27,6 +27,17 @@ interface BugBearContextValue {
|
|
|
27
27
|
sendMessage: (threadId: string, content: string) => Promise<boolean>;
|
|
28
28
|
/** Mark a thread as read */
|
|
29
29
|
markAsRead: (threadId: string) => Promise<void>;
|
|
30
|
+
/** Create a new thread */
|
|
31
|
+
createThread: (options: {
|
|
32
|
+
subject: string;
|
|
33
|
+
message: string;
|
|
34
|
+
}) => Promise<{
|
|
35
|
+
success: boolean;
|
|
36
|
+
threadId?: string;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Re-check tester status (call after auth state changes) */
|
|
40
|
+
refreshTesterStatus: () => Promise<void>;
|
|
30
41
|
}
|
|
31
42
|
declare function useBugBear(): BugBearContextValue;
|
|
32
43
|
interface BugBearProviderProps {
|
|
@@ -34,8 +45,14 @@ interface BugBearProviderProps {
|
|
|
34
45
|
children: ReactNode;
|
|
35
46
|
/** App version string */
|
|
36
47
|
appVersion?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Set to false to delay initialization until auth is ready.
|
|
50
|
+
* When enabled changes from false to true, BugBear will initialize.
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
enabled?: boolean;
|
|
37
54
|
}
|
|
38
|
-
declare function BugBearProvider({ config, children, appVersion }: BugBearProviderProps): React.JSX.Element;
|
|
55
|
+
declare function BugBearProvider({ config, children, appVersion, enabled }: BugBearProviderProps): React.JSX.Element;
|
|
39
56
|
|
|
40
57
|
interface BugBearButtonProps {
|
|
41
58
|
/** Get current app context */
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,17 @@ interface BugBearContextValue {
|
|
|
27
27
|
sendMessage: (threadId: string, content: string) => Promise<boolean>;
|
|
28
28
|
/** Mark a thread as read */
|
|
29
29
|
markAsRead: (threadId: string) => Promise<void>;
|
|
30
|
+
/** Create a new thread */
|
|
31
|
+
createThread: (options: {
|
|
32
|
+
subject: string;
|
|
33
|
+
message: string;
|
|
34
|
+
}) => Promise<{
|
|
35
|
+
success: boolean;
|
|
36
|
+
threadId?: string;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/** Re-check tester status (call after auth state changes) */
|
|
40
|
+
refreshTesterStatus: () => Promise<void>;
|
|
30
41
|
}
|
|
31
42
|
declare function useBugBear(): BugBearContextValue;
|
|
32
43
|
interface BugBearProviderProps {
|
|
@@ -34,8 +45,14 @@ interface BugBearProviderProps {
|
|
|
34
45
|
children: ReactNode;
|
|
35
46
|
/** App version string */
|
|
36
47
|
appVersion?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Set to false to delay initialization until auth is ready.
|
|
50
|
+
* When enabled changes from false to true, BugBear will initialize.
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
enabled?: boolean;
|
|
37
54
|
}
|
|
38
|
-
declare function BugBearProvider({ config, children, appVersion }: BugBearProviderProps): React.JSX.Element;
|
|
55
|
+
declare function BugBearProvider({ config, children, appVersion, enabled }: BugBearProviderProps): React.JSX.Element;
|
|
39
56
|
|
|
40
57
|
interface BugBearButtonProps {
|
|
41
58
|
/** Get current app context */
|
package/dist/index.js
CHANGED
|
@@ -60,12 +60,15 @@ var BugBearContext = (0, import_react.createContext)({
|
|
|
60
60
|
getThreadMessages: async () => [],
|
|
61
61
|
sendMessage: async () => false,
|
|
62
62
|
markAsRead: async () => {
|
|
63
|
+
},
|
|
64
|
+
createThread: async () => ({ success: false }),
|
|
65
|
+
refreshTesterStatus: async () => {
|
|
63
66
|
}
|
|
64
67
|
});
|
|
65
68
|
function useBugBear() {
|
|
66
69
|
return (0, import_react.useContext)(BugBearContext);
|
|
67
70
|
}
|
|
68
|
-
function BugBearProvider({ config, children, appVersion }) {
|
|
71
|
+
function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
69
72
|
const [client] = (0, import_react.useState)(() => (0, import_core.createBugBear)(config));
|
|
70
73
|
const [isTester, setIsTester] = (0, import_react.useState)(false);
|
|
71
74
|
const [isQAEnabled, setIsQAEnabled] = (0, import_react.useState)(false);
|
|
@@ -74,7 +77,9 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
74
77
|
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
75
78
|
const [threads, setThreads] = (0, import_react.useState)([]);
|
|
76
79
|
const [unreadCount, setUnreadCount] = (0, import_react.useState)(0);
|
|
77
|
-
const
|
|
80
|
+
const [initCount, setInitCount] = (0, import_react.useState)(0);
|
|
81
|
+
const hasInitialized = (0, import_react.useRef)(false);
|
|
82
|
+
const getDeviceInfo = (0, import_react.useCallback)(() => {
|
|
78
83
|
const { width, height } = import_react_native.Dimensions.get("window");
|
|
79
84
|
return {
|
|
80
85
|
platform: import_react_native.Platform.OS,
|
|
@@ -82,56 +87,74 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
82
87
|
appVersion,
|
|
83
88
|
screenSize: { width, height }
|
|
84
89
|
};
|
|
85
|
-
};
|
|
86
|
-
const refreshAssignments = async () => {
|
|
90
|
+
}, [appVersion]);
|
|
91
|
+
const refreshAssignments = (0, import_react.useCallback)(async () => {
|
|
87
92
|
const newAssignments = await client.getAssignedTests();
|
|
88
93
|
setAssignments(newAssignments);
|
|
89
|
-
};
|
|
90
|
-
const refreshThreads = async () => {
|
|
94
|
+
}, [client]);
|
|
95
|
+
const refreshThreads = (0, import_react.useCallback)(async () => {
|
|
91
96
|
const newThreads = await client.getThreadsForTester();
|
|
92
97
|
setThreads(newThreads);
|
|
93
98
|
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
94
99
|
setUnreadCount(totalUnread);
|
|
95
|
-
};
|
|
96
|
-
const getThreadMessages = async (threadId) => {
|
|
100
|
+
}, [client]);
|
|
101
|
+
const getThreadMessages = (0, import_react.useCallback)(async (threadId) => {
|
|
97
102
|
return client.getThreadMessages(threadId);
|
|
98
|
-
};
|
|
99
|
-
const sendMessage = async (threadId, content) => {
|
|
103
|
+
}, [client]);
|
|
104
|
+
const sendMessage = (0, import_react.useCallback)(async (threadId, content) => {
|
|
100
105
|
const success = await client.sendMessage(threadId, content);
|
|
101
106
|
if (success) {
|
|
102
107
|
await refreshThreads();
|
|
103
108
|
}
|
|
104
109
|
return success;
|
|
105
|
-
};
|
|
106
|
-
const markAsRead = async (threadId) => {
|
|
110
|
+
}, [client, refreshThreads]);
|
|
111
|
+
const markAsRead = (0, import_react.useCallback)(async (threadId) => {
|
|
107
112
|
await client.markThreadAsRead(threadId);
|
|
108
113
|
await refreshThreads();
|
|
109
|
-
};
|
|
110
|
-
(0, import_react.
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
}, [client, refreshThreads]);
|
|
115
|
+
const createThread = (0, import_react.useCallback)(async (options) => {
|
|
116
|
+
const result = await client.createThread(options);
|
|
117
|
+
if (result.success) {
|
|
118
|
+
await refreshThreads();
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}, [client, refreshThreads]);
|
|
122
|
+
const initializeBugBear = (0, import_react.useCallback)(async () => {
|
|
123
|
+
setIsLoading(true);
|
|
124
|
+
try {
|
|
125
|
+
const [qaEnabled, info] = await Promise.all([
|
|
126
|
+
client.isQAEnabled(),
|
|
127
|
+
client.getTesterInfo()
|
|
128
|
+
]);
|
|
129
|
+
setIsQAEnabled(qaEnabled);
|
|
130
|
+
setTesterInfo(info);
|
|
131
|
+
setIsTester(!!info);
|
|
132
|
+
if (info && qaEnabled) {
|
|
133
|
+
await Promise.all([
|
|
134
|
+
refreshAssignments(),
|
|
135
|
+
refreshThreads()
|
|
117
136
|
]);
|
|
118
|
-
setIsQAEnabled(qaEnabled);
|
|
119
|
-
setTesterInfo(info);
|
|
120
|
-
setIsTester(!!info);
|
|
121
|
-
if (info && qaEnabled) {
|
|
122
|
-
await Promise.all([
|
|
123
|
-
refreshAssignments(),
|
|
124
|
-
refreshThreads()
|
|
125
|
-
]);
|
|
126
|
-
}
|
|
127
|
-
} catch (err) {
|
|
128
|
-
console.error("BugBear: Init error", err);
|
|
129
|
-
} finally {
|
|
130
|
-
setIsLoading(false);
|
|
131
137
|
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error("BugBear: Init error", err);
|
|
140
|
+
} finally {
|
|
141
|
+
setIsLoading(false);
|
|
142
|
+
}
|
|
143
|
+
}, [client, refreshAssignments, refreshThreads]);
|
|
144
|
+
const refreshTesterStatus = (0, import_react.useCallback)(async () => {
|
|
145
|
+
await initializeBugBear();
|
|
146
|
+
}, [initializeBugBear]);
|
|
147
|
+
(0, import_react.useEffect)(() => {
|
|
148
|
+
if (enabled && !hasInitialized.current) {
|
|
149
|
+
hasInitialized.current = true;
|
|
150
|
+
initializeBugBear();
|
|
151
|
+
}
|
|
152
|
+
}, [enabled, initializeBugBear]);
|
|
153
|
+
(0, import_react.useEffect)(() => {
|
|
154
|
+
if (initCount > 0) {
|
|
155
|
+
initializeBugBear();
|
|
156
|
+
}
|
|
157
|
+
}, [initCount, initializeBugBear]);
|
|
135
158
|
const currentAssignment = assignments.find(
|
|
136
159
|
(a) => a.status === "in_progress"
|
|
137
160
|
) || assignments[0] || null;
|
|
@@ -156,7 +179,9 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
156
179
|
refreshThreads,
|
|
157
180
|
getThreadMessages,
|
|
158
181
|
sendMessage,
|
|
159
|
-
markAsRead
|
|
182
|
+
markAsRead,
|
|
183
|
+
createThread,
|
|
184
|
+
refreshTesterStatus
|
|
160
185
|
}
|
|
161
186
|
},
|
|
162
187
|
children
|
|
@@ -198,7 +223,8 @@ function BugBearButton({
|
|
|
198
223
|
refreshThreads,
|
|
199
224
|
getThreadMessages,
|
|
200
225
|
sendMessage,
|
|
201
|
-
markAsRead
|
|
226
|
+
markAsRead,
|
|
227
|
+
createThread
|
|
202
228
|
} = useBugBear();
|
|
203
229
|
const [modalVisible, setModalVisible] = (0, import_react2.useState)(false);
|
|
204
230
|
const [activeTab, setActiveTab] = (0, import_react2.useState)("tests");
|
|
@@ -209,6 +235,9 @@ function BugBearButton({
|
|
|
209
235
|
const [replyText, setReplyText] = (0, import_react2.useState)("");
|
|
210
236
|
const [sendingReply, setSendingReply] = (0, import_react2.useState)(false);
|
|
211
237
|
const [loadingMessages, setLoadingMessages] = (0, import_react2.useState)(false);
|
|
238
|
+
const [composeSubject, setComposeSubject] = (0, import_react2.useState)("");
|
|
239
|
+
const [composeMessage, setComposeMessage] = (0, import_react2.useState)("");
|
|
240
|
+
const [sendingNewMessage, setSendingNewMessage] = (0, import_react2.useState)(false);
|
|
212
241
|
const getInitialPosition = () => {
|
|
213
242
|
const buttonSize = 56;
|
|
214
243
|
const margin = 16;
|
|
@@ -371,6 +400,27 @@ function BugBearButton({
|
|
|
371
400
|
setSelectedThread(null);
|
|
372
401
|
setThreadMessages([]);
|
|
373
402
|
setReplyText("");
|
|
403
|
+
setComposeSubject("");
|
|
404
|
+
setComposeMessage("");
|
|
405
|
+
};
|
|
406
|
+
const handleStartNewMessage = () => {
|
|
407
|
+
setMessageView("compose");
|
|
408
|
+
setComposeSubject("");
|
|
409
|
+
setComposeMessage("");
|
|
410
|
+
};
|
|
411
|
+
const handleSendNewMessage = async () => {
|
|
412
|
+
if (!composeSubject.trim() || !composeMessage.trim()) return;
|
|
413
|
+
setSendingNewMessage(true);
|
|
414
|
+
const result = await createThread({
|
|
415
|
+
subject: composeSubject.trim(),
|
|
416
|
+
message: composeMessage.trim()
|
|
417
|
+
});
|
|
418
|
+
if (result.success) {
|
|
419
|
+
setComposeSubject("");
|
|
420
|
+
setComposeMessage("");
|
|
421
|
+
setMessageView("list");
|
|
422
|
+
}
|
|
423
|
+
setSendingNewMessage(false);
|
|
374
424
|
};
|
|
375
425
|
const formatRelativeTime = (dateString) => {
|
|
376
426
|
const date = new Date(dateString);
|
|
@@ -622,9 +672,46 @@ function BugBearButton({
|
|
|
622
672
|
},
|
|
623
673
|
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
|
|
624
674
|
)))
|
|
625
|
-
) : null), activeTab === "messages" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageView === "
|
|
675
|
+
) : null), activeTab === "messages" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageView === "compose" ? (
|
|
676
|
+
/* Compose New Message View */
|
|
677
|
+
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: handleBackToThreadList, style: styles.backButton }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.backButtonText }, "\u2190 Back to Messages")), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.composeHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.composeTitle }, "New Message"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.composeSubtitle }, "Send a message to the QA team")), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.composeForm }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.label }, "Subject"), /* @__PURE__ */ import_react2.default.createElement(
|
|
678
|
+
import_react_native2.TextInput,
|
|
679
|
+
{
|
|
680
|
+
style: styles.composeSubjectInput,
|
|
681
|
+
value: composeSubject,
|
|
682
|
+
onChangeText: setComposeSubject,
|
|
683
|
+
placeholder: "What's this about?",
|
|
684
|
+
placeholderTextColor: "#9CA3AF",
|
|
685
|
+
maxLength: 100
|
|
686
|
+
}
|
|
687
|
+
), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ import_react2.default.createElement(
|
|
688
|
+
import_react_native2.TextInput,
|
|
689
|
+
{
|
|
690
|
+
style: styles.composeMessageInput,
|
|
691
|
+
value: composeMessage,
|
|
692
|
+
onChangeText: setComposeMessage,
|
|
693
|
+
placeholder: "Write your message...",
|
|
694
|
+
placeholderTextColor: "#9CA3AF",
|
|
695
|
+
multiline: true,
|
|
696
|
+
numberOfLines: 6,
|
|
697
|
+
textAlignVertical: "top",
|
|
698
|
+
maxLength: 2e3
|
|
699
|
+
}
|
|
700
|
+
), /* @__PURE__ */ import_react2.default.createElement(
|
|
701
|
+
import_react_native2.TouchableOpacity,
|
|
702
|
+
{
|
|
703
|
+
style: [
|
|
704
|
+
styles.composeSendButton,
|
|
705
|
+
(!composeSubject.trim() || !composeMessage.trim() || sendingNewMessage) && styles.composeSendButtonDisabled
|
|
706
|
+
],
|
|
707
|
+
onPress: handleSendNewMessage,
|
|
708
|
+
disabled: !composeSubject.trim() || !composeMessage.trim() || sendingNewMessage
|
|
709
|
+
},
|
|
710
|
+
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.composeSendButtonText }, sendingNewMessage ? "Sending..." : "Send Message")
|
|
711
|
+
)))
|
|
712
|
+
) : messageView === "list" ? (
|
|
626
713
|
/* Thread List View */
|
|
627
|
-
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, threads.length === 0 ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyTitle }, "No messages yet"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptySubtitle }, "
|
|
714
|
+
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: handleStartNewMessage, style: styles.newMessageButton }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.newMessageButtonIcon }, "\u2709\uFE0F"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.newMessageButtonText }, "New Message")), threads.length === 0 ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyTitle }, "No messages yet"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, threads.map((thread) => /* @__PURE__ */ import_react2.default.createElement(
|
|
628
715
|
import_react_native2.TouchableOpacity,
|
|
629
716
|
{
|
|
630
717
|
key: thread.id,
|
|
@@ -1654,6 +1741,81 @@ var styles = import_react_native2.StyleSheet.create({
|
|
|
1654
1741
|
fontSize: 14,
|
|
1655
1742
|
fontWeight: "600",
|
|
1656
1743
|
color: "#fff"
|
|
1744
|
+
},
|
|
1745
|
+
// New message button styles
|
|
1746
|
+
newMessageButton: {
|
|
1747
|
+
flexDirection: "row",
|
|
1748
|
+
alignItems: "center",
|
|
1749
|
+
justifyContent: "center",
|
|
1750
|
+
backgroundColor: "#7C3AED",
|
|
1751
|
+
paddingVertical: 12,
|
|
1752
|
+
paddingHorizontal: 20,
|
|
1753
|
+
borderRadius: 12,
|
|
1754
|
+
marginBottom: 16
|
|
1755
|
+
},
|
|
1756
|
+
newMessageButtonIcon: {
|
|
1757
|
+
fontSize: 16,
|
|
1758
|
+
marginRight: 8
|
|
1759
|
+
},
|
|
1760
|
+
newMessageButtonText: {
|
|
1761
|
+
fontSize: 15,
|
|
1762
|
+
fontWeight: "600",
|
|
1763
|
+
color: "#fff"
|
|
1764
|
+
},
|
|
1765
|
+
// Compose view styles
|
|
1766
|
+
composeHeader: {
|
|
1767
|
+
marginBottom: 20
|
|
1768
|
+
},
|
|
1769
|
+
composeTitle: {
|
|
1770
|
+
fontSize: 20,
|
|
1771
|
+
fontWeight: "600",
|
|
1772
|
+
color: "#111827",
|
|
1773
|
+
marginBottom: 4
|
|
1774
|
+
},
|
|
1775
|
+
composeSubtitle: {
|
|
1776
|
+
fontSize: 14,
|
|
1777
|
+
color: "#6B7280"
|
|
1778
|
+
},
|
|
1779
|
+
composeForm: {
|
|
1780
|
+
backgroundColor: "#F9FAFB",
|
|
1781
|
+
borderRadius: 12,
|
|
1782
|
+
padding: 16
|
|
1783
|
+
},
|
|
1784
|
+
composeSubjectInput: {
|
|
1785
|
+
backgroundColor: "#fff",
|
|
1786
|
+
borderWidth: 1,
|
|
1787
|
+
borderColor: "#E5E7EB",
|
|
1788
|
+
borderRadius: 8,
|
|
1789
|
+
paddingHorizontal: 12,
|
|
1790
|
+
paddingVertical: 10,
|
|
1791
|
+
fontSize: 15,
|
|
1792
|
+
color: "#111827"
|
|
1793
|
+
},
|
|
1794
|
+
composeMessageInput: {
|
|
1795
|
+
backgroundColor: "#fff",
|
|
1796
|
+
borderWidth: 1,
|
|
1797
|
+
borderColor: "#E5E7EB",
|
|
1798
|
+
borderRadius: 8,
|
|
1799
|
+
paddingHorizontal: 12,
|
|
1800
|
+
paddingVertical: 10,
|
|
1801
|
+
fontSize: 15,
|
|
1802
|
+
color: "#111827",
|
|
1803
|
+
minHeight: 120
|
|
1804
|
+
},
|
|
1805
|
+
composeSendButton: {
|
|
1806
|
+
backgroundColor: "#7C3AED",
|
|
1807
|
+
paddingVertical: 14,
|
|
1808
|
+
borderRadius: 12,
|
|
1809
|
+
alignItems: "center",
|
|
1810
|
+
marginTop: 20
|
|
1811
|
+
},
|
|
1812
|
+
composeSendButtonDisabled: {
|
|
1813
|
+
backgroundColor: "#C4B5FD"
|
|
1814
|
+
},
|
|
1815
|
+
composeSendButtonText: {
|
|
1816
|
+
fontSize: 16,
|
|
1817
|
+
fontWeight: "600",
|
|
1818
|
+
color: "#fff"
|
|
1657
1819
|
}
|
|
1658
1820
|
});
|
|
1659
1821
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/BugBearProvider.tsx
|
|
2
|
-
import React, { createContext, useContext, useEffect, useState } from "react";
|
|
2
|
+
import React, { createContext, useContext, useEffect, useState, useCallback, useRef } from "react";
|
|
3
3
|
import {
|
|
4
4
|
createBugBear
|
|
5
5
|
} from "@bbearai/core";
|
|
@@ -24,12 +24,15 @@ var BugBearContext = createContext({
|
|
|
24
24
|
getThreadMessages: async () => [],
|
|
25
25
|
sendMessage: async () => false,
|
|
26
26
|
markAsRead: async () => {
|
|
27
|
+
},
|
|
28
|
+
createThread: async () => ({ success: false }),
|
|
29
|
+
refreshTesterStatus: async () => {
|
|
27
30
|
}
|
|
28
31
|
});
|
|
29
32
|
function useBugBear() {
|
|
30
33
|
return useContext(BugBearContext);
|
|
31
34
|
}
|
|
32
|
-
function BugBearProvider({ config, children, appVersion }) {
|
|
35
|
+
function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
33
36
|
const [client] = useState(() => createBugBear(config));
|
|
34
37
|
const [isTester, setIsTester] = useState(false);
|
|
35
38
|
const [isQAEnabled, setIsQAEnabled] = useState(false);
|
|
@@ -38,7 +41,9 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
38
41
|
const [isLoading, setIsLoading] = useState(true);
|
|
39
42
|
const [threads, setThreads] = useState([]);
|
|
40
43
|
const [unreadCount, setUnreadCount] = useState(0);
|
|
41
|
-
const
|
|
44
|
+
const [initCount, setInitCount] = useState(0);
|
|
45
|
+
const hasInitialized = useRef(false);
|
|
46
|
+
const getDeviceInfo = useCallback(() => {
|
|
42
47
|
const { width, height } = Dimensions.get("window");
|
|
43
48
|
return {
|
|
44
49
|
platform: Platform.OS,
|
|
@@ -46,56 +51,74 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
46
51
|
appVersion,
|
|
47
52
|
screenSize: { width, height }
|
|
48
53
|
};
|
|
49
|
-
};
|
|
50
|
-
const refreshAssignments = async () => {
|
|
54
|
+
}, [appVersion]);
|
|
55
|
+
const refreshAssignments = useCallback(async () => {
|
|
51
56
|
const newAssignments = await client.getAssignedTests();
|
|
52
57
|
setAssignments(newAssignments);
|
|
53
|
-
};
|
|
54
|
-
const refreshThreads = async () => {
|
|
58
|
+
}, [client]);
|
|
59
|
+
const refreshThreads = useCallback(async () => {
|
|
55
60
|
const newThreads = await client.getThreadsForTester();
|
|
56
61
|
setThreads(newThreads);
|
|
57
62
|
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
58
63
|
setUnreadCount(totalUnread);
|
|
59
|
-
};
|
|
60
|
-
const getThreadMessages = async (threadId) => {
|
|
64
|
+
}, [client]);
|
|
65
|
+
const getThreadMessages = useCallback(async (threadId) => {
|
|
61
66
|
return client.getThreadMessages(threadId);
|
|
62
|
-
};
|
|
63
|
-
const sendMessage = async (threadId, content) => {
|
|
67
|
+
}, [client]);
|
|
68
|
+
const sendMessage = useCallback(async (threadId, content) => {
|
|
64
69
|
const success = await client.sendMessage(threadId, content);
|
|
65
70
|
if (success) {
|
|
66
71
|
await refreshThreads();
|
|
67
72
|
}
|
|
68
73
|
return success;
|
|
69
|
-
};
|
|
70
|
-
const markAsRead = async (threadId) => {
|
|
74
|
+
}, [client, refreshThreads]);
|
|
75
|
+
const markAsRead = useCallback(async (threadId) => {
|
|
71
76
|
await client.markThreadAsRead(threadId);
|
|
72
77
|
await refreshThreads();
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
}, [client, refreshThreads]);
|
|
79
|
+
const createThread = useCallback(async (options) => {
|
|
80
|
+
const result = await client.createThread(options);
|
|
81
|
+
if (result.success) {
|
|
82
|
+
await refreshThreads();
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}, [client, refreshThreads]);
|
|
86
|
+
const initializeBugBear = useCallback(async () => {
|
|
87
|
+
setIsLoading(true);
|
|
88
|
+
try {
|
|
89
|
+
const [qaEnabled, info] = await Promise.all([
|
|
90
|
+
client.isQAEnabled(),
|
|
91
|
+
client.getTesterInfo()
|
|
92
|
+
]);
|
|
93
|
+
setIsQAEnabled(qaEnabled);
|
|
94
|
+
setTesterInfo(info);
|
|
95
|
+
setIsTester(!!info);
|
|
96
|
+
if (info && qaEnabled) {
|
|
97
|
+
await Promise.all([
|
|
98
|
+
refreshAssignments(),
|
|
99
|
+
refreshThreads()
|
|
81
100
|
]);
|
|
82
|
-
setIsQAEnabled(qaEnabled);
|
|
83
|
-
setTesterInfo(info);
|
|
84
|
-
setIsTester(!!info);
|
|
85
|
-
if (info && qaEnabled) {
|
|
86
|
-
await Promise.all([
|
|
87
|
-
refreshAssignments(),
|
|
88
|
-
refreshThreads()
|
|
89
|
-
]);
|
|
90
|
-
}
|
|
91
|
-
} catch (err) {
|
|
92
|
-
console.error("BugBear: Init error", err);
|
|
93
|
-
} finally {
|
|
94
|
-
setIsLoading(false);
|
|
95
101
|
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error("BugBear: Init error", err);
|
|
104
|
+
} finally {
|
|
105
|
+
setIsLoading(false);
|
|
106
|
+
}
|
|
107
|
+
}, [client, refreshAssignments, refreshThreads]);
|
|
108
|
+
const refreshTesterStatus = useCallback(async () => {
|
|
109
|
+
await initializeBugBear();
|
|
110
|
+
}, [initializeBugBear]);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (enabled && !hasInitialized.current) {
|
|
113
|
+
hasInitialized.current = true;
|
|
114
|
+
initializeBugBear();
|
|
115
|
+
}
|
|
116
|
+
}, [enabled, initializeBugBear]);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (initCount > 0) {
|
|
119
|
+
initializeBugBear();
|
|
120
|
+
}
|
|
121
|
+
}, [initCount, initializeBugBear]);
|
|
99
122
|
const currentAssignment = assignments.find(
|
|
100
123
|
(a) => a.status === "in_progress"
|
|
101
124
|
) || assignments[0] || null;
|
|
@@ -120,7 +143,9 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
120
143
|
refreshThreads,
|
|
121
144
|
getThreadMessages,
|
|
122
145
|
sendMessage,
|
|
123
|
-
markAsRead
|
|
146
|
+
markAsRead,
|
|
147
|
+
createThread,
|
|
148
|
+
refreshTesterStatus
|
|
124
149
|
}
|
|
125
150
|
},
|
|
126
151
|
children
|
|
@@ -128,7 +153,7 @@ function BugBearProvider({ config, children, appVersion }) {
|
|
|
128
153
|
}
|
|
129
154
|
|
|
130
155
|
// src/BugBearButton.tsx
|
|
131
|
-
import React2, { useState as useState2, useEffect as useEffect2, useRef } from "react";
|
|
156
|
+
import React2, { useState as useState2, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
132
157
|
import {
|
|
133
158
|
View,
|
|
134
159
|
Text,
|
|
@@ -175,7 +200,8 @@ function BugBearButton({
|
|
|
175
200
|
refreshThreads,
|
|
176
201
|
getThreadMessages,
|
|
177
202
|
sendMessage,
|
|
178
|
-
markAsRead
|
|
203
|
+
markAsRead,
|
|
204
|
+
createThread
|
|
179
205
|
} = useBugBear();
|
|
180
206
|
const [modalVisible, setModalVisible] = useState2(false);
|
|
181
207
|
const [activeTab, setActiveTab] = useState2("tests");
|
|
@@ -186,6 +212,9 @@ function BugBearButton({
|
|
|
186
212
|
const [replyText, setReplyText] = useState2("");
|
|
187
213
|
const [sendingReply, setSendingReply] = useState2(false);
|
|
188
214
|
const [loadingMessages, setLoadingMessages] = useState2(false);
|
|
215
|
+
const [composeSubject, setComposeSubject] = useState2("");
|
|
216
|
+
const [composeMessage, setComposeMessage] = useState2("");
|
|
217
|
+
const [sendingNewMessage, setSendingNewMessage] = useState2(false);
|
|
189
218
|
const getInitialPosition = () => {
|
|
190
219
|
const buttonSize = 56;
|
|
191
220
|
const margin = 16;
|
|
@@ -197,9 +226,9 @@ function BugBearButton({
|
|
|
197
226
|
return { x, y };
|
|
198
227
|
};
|
|
199
228
|
const initialPos = getInitialPosition();
|
|
200
|
-
const pan =
|
|
201
|
-
const isDragging =
|
|
202
|
-
const panResponder =
|
|
229
|
+
const pan = useRef2(new Animated.ValueXY(initialPos)).current;
|
|
230
|
+
const isDragging = useRef2(false);
|
|
231
|
+
const panResponder = useRef2(
|
|
203
232
|
PanResponder.create({
|
|
204
233
|
onStartShouldSetPanResponder: () => draggable,
|
|
205
234
|
onMoveShouldSetPanResponder: (_, gestureState) => {
|
|
@@ -348,6 +377,27 @@ function BugBearButton({
|
|
|
348
377
|
setSelectedThread(null);
|
|
349
378
|
setThreadMessages([]);
|
|
350
379
|
setReplyText("");
|
|
380
|
+
setComposeSubject("");
|
|
381
|
+
setComposeMessage("");
|
|
382
|
+
};
|
|
383
|
+
const handleStartNewMessage = () => {
|
|
384
|
+
setMessageView("compose");
|
|
385
|
+
setComposeSubject("");
|
|
386
|
+
setComposeMessage("");
|
|
387
|
+
};
|
|
388
|
+
const handleSendNewMessage = async () => {
|
|
389
|
+
if (!composeSubject.trim() || !composeMessage.trim()) return;
|
|
390
|
+
setSendingNewMessage(true);
|
|
391
|
+
const result = await createThread({
|
|
392
|
+
subject: composeSubject.trim(),
|
|
393
|
+
message: composeMessage.trim()
|
|
394
|
+
});
|
|
395
|
+
if (result.success) {
|
|
396
|
+
setComposeSubject("");
|
|
397
|
+
setComposeMessage("");
|
|
398
|
+
setMessageView("list");
|
|
399
|
+
}
|
|
400
|
+
setSendingNewMessage(false);
|
|
351
401
|
};
|
|
352
402
|
const formatRelativeTime = (dateString) => {
|
|
353
403
|
const date = new Date(dateString);
|
|
@@ -599,9 +649,46 @@ function BugBearButton({
|
|
|
599
649
|
},
|
|
600
650
|
/* @__PURE__ */ React2.createElement(Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
|
|
601
651
|
)))
|
|
602
|
-
) : null), activeTab === "messages" && /* @__PURE__ */ React2.createElement(View, null, messageView === "
|
|
652
|
+
) : null), activeTab === "messages" && /* @__PURE__ */ React2.createElement(View, null, messageView === "compose" ? (
|
|
653
|
+
/* Compose New Message View */
|
|
654
|
+
/* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: handleBackToThreadList, style: styles.backButton }, /* @__PURE__ */ React2.createElement(Text, { style: styles.backButtonText }, "\u2190 Back to Messages")), /* @__PURE__ */ React2.createElement(View, { style: styles.composeHeader }, /* @__PURE__ */ React2.createElement(Text, { style: styles.composeTitle }, "New Message"), /* @__PURE__ */ React2.createElement(Text, { style: styles.composeSubtitle }, "Send a message to the QA team")), /* @__PURE__ */ React2.createElement(View, { style: styles.composeForm }, /* @__PURE__ */ React2.createElement(Text, { style: styles.label }, "Subject"), /* @__PURE__ */ React2.createElement(
|
|
655
|
+
TextInput,
|
|
656
|
+
{
|
|
657
|
+
style: styles.composeSubjectInput,
|
|
658
|
+
value: composeSubject,
|
|
659
|
+
onChangeText: setComposeSubject,
|
|
660
|
+
placeholder: "What's this about?",
|
|
661
|
+
placeholderTextColor: "#9CA3AF",
|
|
662
|
+
maxLength: 100
|
|
663
|
+
}
|
|
664
|
+
), /* @__PURE__ */ React2.createElement(Text, { style: [styles.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ React2.createElement(
|
|
665
|
+
TextInput,
|
|
666
|
+
{
|
|
667
|
+
style: styles.composeMessageInput,
|
|
668
|
+
value: composeMessage,
|
|
669
|
+
onChangeText: setComposeMessage,
|
|
670
|
+
placeholder: "Write your message...",
|
|
671
|
+
placeholderTextColor: "#9CA3AF",
|
|
672
|
+
multiline: true,
|
|
673
|
+
numberOfLines: 6,
|
|
674
|
+
textAlignVertical: "top",
|
|
675
|
+
maxLength: 2e3
|
|
676
|
+
}
|
|
677
|
+
), /* @__PURE__ */ React2.createElement(
|
|
678
|
+
TouchableOpacity,
|
|
679
|
+
{
|
|
680
|
+
style: [
|
|
681
|
+
styles.composeSendButton,
|
|
682
|
+
(!composeSubject.trim() || !composeMessage.trim() || sendingNewMessage) && styles.composeSendButtonDisabled
|
|
683
|
+
],
|
|
684
|
+
onPress: handleSendNewMessage,
|
|
685
|
+
disabled: !composeSubject.trim() || !composeMessage.trim() || sendingNewMessage
|
|
686
|
+
},
|
|
687
|
+
/* @__PURE__ */ React2.createElement(Text, { style: styles.composeSendButtonText }, sendingNewMessage ? "Sending..." : "Send Message")
|
|
688
|
+
)))
|
|
689
|
+
) : messageView === "list" ? (
|
|
603
690
|
/* Thread List View */
|
|
604
|
-
/* @__PURE__ */ React2.createElement(View, null, threads.length === 0 ? /* @__PURE__ */ React2.createElement(View, { style: styles.emptyState }, /* @__PURE__ */ React2.createElement(Text, { style: styles.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ React2.createElement(Text, { style: styles.emptyTitle }, "No messages yet"), /* @__PURE__ */ React2.createElement(Text, { style: styles.emptySubtitle }, "
|
|
691
|
+
/* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: handleStartNewMessage, style: styles.newMessageButton }, /* @__PURE__ */ React2.createElement(Text, { style: styles.newMessageButtonIcon }, "\u2709\uFE0F"), /* @__PURE__ */ React2.createElement(Text, { style: styles.newMessageButtonText }, "New Message")), threads.length === 0 ? /* @__PURE__ */ React2.createElement(View, { style: styles.emptyState }, /* @__PURE__ */ React2.createElement(Text, { style: styles.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ React2.createElement(Text, { style: styles.emptyTitle }, "No messages yet"), /* @__PURE__ */ React2.createElement(Text, { style: styles.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ React2.createElement(View, null, threads.map((thread) => /* @__PURE__ */ React2.createElement(
|
|
605
692
|
TouchableOpacity,
|
|
606
693
|
{
|
|
607
694
|
key: thread.id,
|
|
@@ -1631,6 +1718,81 @@ var styles = StyleSheet.create({
|
|
|
1631
1718
|
fontSize: 14,
|
|
1632
1719
|
fontWeight: "600",
|
|
1633
1720
|
color: "#fff"
|
|
1721
|
+
},
|
|
1722
|
+
// New message button styles
|
|
1723
|
+
newMessageButton: {
|
|
1724
|
+
flexDirection: "row",
|
|
1725
|
+
alignItems: "center",
|
|
1726
|
+
justifyContent: "center",
|
|
1727
|
+
backgroundColor: "#7C3AED",
|
|
1728
|
+
paddingVertical: 12,
|
|
1729
|
+
paddingHorizontal: 20,
|
|
1730
|
+
borderRadius: 12,
|
|
1731
|
+
marginBottom: 16
|
|
1732
|
+
},
|
|
1733
|
+
newMessageButtonIcon: {
|
|
1734
|
+
fontSize: 16,
|
|
1735
|
+
marginRight: 8
|
|
1736
|
+
},
|
|
1737
|
+
newMessageButtonText: {
|
|
1738
|
+
fontSize: 15,
|
|
1739
|
+
fontWeight: "600",
|
|
1740
|
+
color: "#fff"
|
|
1741
|
+
},
|
|
1742
|
+
// Compose view styles
|
|
1743
|
+
composeHeader: {
|
|
1744
|
+
marginBottom: 20
|
|
1745
|
+
},
|
|
1746
|
+
composeTitle: {
|
|
1747
|
+
fontSize: 20,
|
|
1748
|
+
fontWeight: "600",
|
|
1749
|
+
color: "#111827",
|
|
1750
|
+
marginBottom: 4
|
|
1751
|
+
},
|
|
1752
|
+
composeSubtitle: {
|
|
1753
|
+
fontSize: 14,
|
|
1754
|
+
color: "#6B7280"
|
|
1755
|
+
},
|
|
1756
|
+
composeForm: {
|
|
1757
|
+
backgroundColor: "#F9FAFB",
|
|
1758
|
+
borderRadius: 12,
|
|
1759
|
+
padding: 16
|
|
1760
|
+
},
|
|
1761
|
+
composeSubjectInput: {
|
|
1762
|
+
backgroundColor: "#fff",
|
|
1763
|
+
borderWidth: 1,
|
|
1764
|
+
borderColor: "#E5E7EB",
|
|
1765
|
+
borderRadius: 8,
|
|
1766
|
+
paddingHorizontal: 12,
|
|
1767
|
+
paddingVertical: 10,
|
|
1768
|
+
fontSize: 15,
|
|
1769
|
+
color: "#111827"
|
|
1770
|
+
},
|
|
1771
|
+
composeMessageInput: {
|
|
1772
|
+
backgroundColor: "#fff",
|
|
1773
|
+
borderWidth: 1,
|
|
1774
|
+
borderColor: "#E5E7EB",
|
|
1775
|
+
borderRadius: 8,
|
|
1776
|
+
paddingHorizontal: 12,
|
|
1777
|
+
paddingVertical: 10,
|
|
1778
|
+
fontSize: 15,
|
|
1779
|
+
color: "#111827",
|
|
1780
|
+
minHeight: 120
|
|
1781
|
+
},
|
|
1782
|
+
composeSendButton: {
|
|
1783
|
+
backgroundColor: "#7C3AED",
|
|
1784
|
+
paddingVertical: 14,
|
|
1785
|
+
borderRadius: 12,
|
|
1786
|
+
alignItems: "center",
|
|
1787
|
+
marginTop: 20
|
|
1788
|
+
},
|
|
1789
|
+
composeSendButtonDisabled: {
|
|
1790
|
+
backgroundColor: "#C4B5FD"
|
|
1791
|
+
},
|
|
1792
|
+
composeSendButtonText: {
|
|
1793
|
+
fontSize: 16,
|
|
1794
|
+
fontWeight: "600",
|
|
1795
|
+
color: "#fff"
|
|
1634
1796
|
}
|
|
1635
1797
|
});
|
|
1636
1798
|
export {
|