@bbearai/react-native 0.1.2 → 0.1.4
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 +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +176 -21
- package/dist/index.mjs +176 -21
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -27,6 +27,15 @@ 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
|
+
}>;
|
|
30
39
|
/** Re-check tester status (call after auth state changes) */
|
|
31
40
|
refreshTesterStatus: () => Promise<void>;
|
|
32
41
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,15 @@ 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
|
+
}>;
|
|
30
39
|
/** Re-check tester status (call after auth state changes) */
|
|
31
40
|
refreshTesterStatus: () => Promise<void>;
|
|
32
41
|
}
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,7 @@ var BugBearContext = (0, import_react.createContext)({
|
|
|
61
61
|
sendMessage: async () => false,
|
|
62
62
|
markAsRead: async () => {
|
|
63
63
|
},
|
|
64
|
+
createThread: async () => ({ success: false }),
|
|
64
65
|
refreshTesterStatus: async () => {
|
|
65
66
|
}
|
|
66
67
|
});
|
|
@@ -68,7 +69,7 @@ function useBugBear() {
|
|
|
68
69
|
return (0, import_react.useContext)(BugBearContext);
|
|
69
70
|
}
|
|
70
71
|
function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
71
|
-
const [client] = (0, import_react.useState)(
|
|
72
|
+
const [client, setClient] = (0, import_react.useState)(null);
|
|
72
73
|
const [isTester, setIsTester] = (0, import_react.useState)(false);
|
|
73
74
|
const [isQAEnabled, setIsQAEnabled] = (0, import_react.useState)(false);
|
|
74
75
|
const [testerInfo, setTesterInfo] = (0, import_react.useState)(null);
|
|
@@ -76,7 +77,6 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
76
77
|
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
77
78
|
const [threads, setThreads] = (0, import_react.useState)([]);
|
|
78
79
|
const [unreadCount, setUnreadCount] = (0, import_react.useState)(0);
|
|
79
|
-
const [initCount, setInitCount] = (0, import_react.useState)(0);
|
|
80
80
|
const hasInitialized = (0, import_react.useRef)(false);
|
|
81
81
|
const getDeviceInfo = (0, import_react.useCallback)(() => {
|
|
82
82
|
const { width, height } = import_react_native.Dimensions.get("window");
|
|
@@ -88,19 +88,23 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
88
88
|
};
|
|
89
89
|
}, [appVersion]);
|
|
90
90
|
const refreshAssignments = (0, import_react.useCallback)(async () => {
|
|
91
|
+
if (!client) return;
|
|
91
92
|
const newAssignments = await client.getAssignedTests();
|
|
92
93
|
setAssignments(newAssignments);
|
|
93
94
|
}, [client]);
|
|
94
95
|
const refreshThreads = (0, import_react.useCallback)(async () => {
|
|
96
|
+
if (!client) return;
|
|
95
97
|
const newThreads = await client.getThreadsForTester();
|
|
96
98
|
setThreads(newThreads);
|
|
97
99
|
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
98
100
|
setUnreadCount(totalUnread);
|
|
99
101
|
}, [client]);
|
|
100
102
|
const getThreadMessages = (0, import_react.useCallback)(async (threadId) => {
|
|
103
|
+
if (!client) return [];
|
|
101
104
|
return client.getThreadMessages(threadId);
|
|
102
105
|
}, [client]);
|
|
103
106
|
const sendMessage = (0, import_react.useCallback)(async (threadId, content) => {
|
|
107
|
+
if (!client) return false;
|
|
104
108
|
const success = await client.sendMessage(threadId, content);
|
|
105
109
|
if (success) {
|
|
106
110
|
await refreshThreads();
|
|
@@ -108,45 +112,58 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
108
112
|
return success;
|
|
109
113
|
}, [client, refreshThreads]);
|
|
110
114
|
const markAsRead = (0, import_react.useCallback)(async (threadId) => {
|
|
115
|
+
if (!client) return;
|
|
111
116
|
await client.markThreadAsRead(threadId);
|
|
112
117
|
await refreshThreads();
|
|
113
118
|
}, [client, refreshThreads]);
|
|
114
|
-
const
|
|
119
|
+
const createThread = (0, import_react.useCallback)(async (options) => {
|
|
120
|
+
if (!client) return { success: false, error: "Client not initialized" };
|
|
121
|
+
const result = await client.createThread(options);
|
|
122
|
+
if (result.success) {
|
|
123
|
+
await refreshThreads();
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}, [client, refreshThreads]);
|
|
127
|
+
const initializeBugBear = (0, import_react.useCallback)(async (bugBearClient) => {
|
|
115
128
|
setIsLoading(true);
|
|
116
129
|
try {
|
|
117
130
|
const [qaEnabled, info] = await Promise.all([
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
bugBearClient.isQAEnabled(),
|
|
132
|
+
bugBearClient.getTesterInfo()
|
|
120
133
|
]);
|
|
134
|
+
console.log("BugBear: Init complete", { qaEnabled, testerInfo: info });
|
|
121
135
|
setIsQAEnabled(qaEnabled);
|
|
122
136
|
setTesterInfo(info);
|
|
123
137
|
setIsTester(!!info);
|
|
124
138
|
if (info && qaEnabled) {
|
|
125
|
-
await Promise.all([
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
const [newAssignments, newThreads] = await Promise.all([
|
|
140
|
+
bugBearClient.getAssignedTests(),
|
|
141
|
+
bugBearClient.getThreadsForTester()
|
|
128
142
|
]);
|
|
143
|
+
setAssignments(newAssignments);
|
|
144
|
+
setThreads(newThreads);
|
|
145
|
+
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
146
|
+
setUnreadCount(totalUnread);
|
|
129
147
|
}
|
|
130
148
|
} catch (err) {
|
|
131
149
|
console.error("BugBear: Init error", err);
|
|
132
150
|
} finally {
|
|
133
151
|
setIsLoading(false);
|
|
134
152
|
}
|
|
135
|
-
}, [
|
|
153
|
+
}, []);
|
|
136
154
|
const refreshTesterStatus = (0, import_react.useCallback)(async () => {
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
const freshClient = (0, import_core.createBugBear)(config);
|
|
156
|
+
setClient(freshClient);
|
|
157
|
+
await initializeBugBear(freshClient);
|
|
158
|
+
}, [config, initializeBugBear]);
|
|
139
159
|
(0, import_react.useEffect)(() => {
|
|
140
160
|
if (enabled && !hasInitialized.current) {
|
|
141
161
|
hasInitialized.current = true;
|
|
142
|
-
|
|
162
|
+
const newClient = (0, import_core.createBugBear)(config);
|
|
163
|
+
setClient(newClient);
|
|
164
|
+
initializeBugBear(newClient);
|
|
143
165
|
}
|
|
144
|
-
}, [enabled, initializeBugBear]);
|
|
145
|
-
(0, import_react.useEffect)(() => {
|
|
146
|
-
if (initCount > 0) {
|
|
147
|
-
initializeBugBear();
|
|
148
|
-
}
|
|
149
|
-
}, [initCount, initializeBugBear]);
|
|
166
|
+
}, [enabled, config, initializeBugBear]);
|
|
150
167
|
const currentAssignment = assignments.find(
|
|
151
168
|
(a) => a.status === "in_progress"
|
|
152
169
|
) || assignments[0] || null;
|
|
@@ -172,6 +189,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
172
189
|
getThreadMessages,
|
|
173
190
|
sendMessage,
|
|
174
191
|
markAsRead,
|
|
192
|
+
createThread,
|
|
175
193
|
refreshTesterStatus
|
|
176
194
|
}
|
|
177
195
|
},
|
|
@@ -214,7 +232,8 @@ function BugBearButton({
|
|
|
214
232
|
refreshThreads,
|
|
215
233
|
getThreadMessages,
|
|
216
234
|
sendMessage,
|
|
217
|
-
markAsRead
|
|
235
|
+
markAsRead,
|
|
236
|
+
createThread
|
|
218
237
|
} = useBugBear();
|
|
219
238
|
const [modalVisible, setModalVisible] = (0, import_react2.useState)(false);
|
|
220
239
|
const [activeTab, setActiveTab] = (0, import_react2.useState)("tests");
|
|
@@ -225,6 +244,9 @@ function BugBearButton({
|
|
|
225
244
|
const [replyText, setReplyText] = (0, import_react2.useState)("");
|
|
226
245
|
const [sendingReply, setSendingReply] = (0, import_react2.useState)(false);
|
|
227
246
|
const [loadingMessages, setLoadingMessages] = (0, import_react2.useState)(false);
|
|
247
|
+
const [composeSubject, setComposeSubject] = (0, import_react2.useState)("");
|
|
248
|
+
const [composeMessage, setComposeMessage] = (0, import_react2.useState)("");
|
|
249
|
+
const [sendingNewMessage, setSendingNewMessage] = (0, import_react2.useState)(false);
|
|
228
250
|
const getInitialPosition = () => {
|
|
229
251
|
const buttonSize = 56;
|
|
230
252
|
const margin = 16;
|
|
@@ -387,6 +409,27 @@ function BugBearButton({
|
|
|
387
409
|
setSelectedThread(null);
|
|
388
410
|
setThreadMessages([]);
|
|
389
411
|
setReplyText("");
|
|
412
|
+
setComposeSubject("");
|
|
413
|
+
setComposeMessage("");
|
|
414
|
+
};
|
|
415
|
+
const handleStartNewMessage = () => {
|
|
416
|
+
setMessageView("compose");
|
|
417
|
+
setComposeSubject("");
|
|
418
|
+
setComposeMessage("");
|
|
419
|
+
};
|
|
420
|
+
const handleSendNewMessage = async () => {
|
|
421
|
+
if (!composeSubject.trim() || !composeMessage.trim()) return;
|
|
422
|
+
setSendingNewMessage(true);
|
|
423
|
+
const result = await createThread({
|
|
424
|
+
subject: composeSubject.trim(),
|
|
425
|
+
message: composeMessage.trim()
|
|
426
|
+
});
|
|
427
|
+
if (result.success) {
|
|
428
|
+
setComposeSubject("");
|
|
429
|
+
setComposeMessage("");
|
|
430
|
+
setMessageView("list");
|
|
431
|
+
}
|
|
432
|
+
setSendingNewMessage(false);
|
|
390
433
|
};
|
|
391
434
|
const formatRelativeTime = (dateString) => {
|
|
392
435
|
const date = new Date(dateString);
|
|
@@ -638,9 +681,46 @@ function BugBearButton({
|
|
|
638
681
|
},
|
|
639
682
|
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
|
|
640
683
|
)))
|
|
641
|
-
) : null), activeTab === "messages" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageView === "
|
|
684
|
+
) : null), activeTab === "messages" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageView === "compose" ? (
|
|
685
|
+
/* Compose New Message View */
|
|
686
|
+
/* @__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(
|
|
687
|
+
import_react_native2.TextInput,
|
|
688
|
+
{
|
|
689
|
+
style: styles.composeSubjectInput,
|
|
690
|
+
value: composeSubject,
|
|
691
|
+
onChangeText: setComposeSubject,
|
|
692
|
+
placeholder: "What's this about?",
|
|
693
|
+
placeholderTextColor: "#9CA3AF",
|
|
694
|
+
maxLength: 100
|
|
695
|
+
}
|
|
696
|
+
), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ import_react2.default.createElement(
|
|
697
|
+
import_react_native2.TextInput,
|
|
698
|
+
{
|
|
699
|
+
style: styles.composeMessageInput,
|
|
700
|
+
value: composeMessage,
|
|
701
|
+
onChangeText: setComposeMessage,
|
|
702
|
+
placeholder: "Write your message...",
|
|
703
|
+
placeholderTextColor: "#9CA3AF",
|
|
704
|
+
multiline: true,
|
|
705
|
+
numberOfLines: 6,
|
|
706
|
+
textAlignVertical: "top",
|
|
707
|
+
maxLength: 2e3
|
|
708
|
+
}
|
|
709
|
+
), /* @__PURE__ */ import_react2.default.createElement(
|
|
710
|
+
import_react_native2.TouchableOpacity,
|
|
711
|
+
{
|
|
712
|
+
style: [
|
|
713
|
+
styles.composeSendButton,
|
|
714
|
+
(!composeSubject.trim() || !composeMessage.trim() || sendingNewMessage) && styles.composeSendButtonDisabled
|
|
715
|
+
],
|
|
716
|
+
onPress: handleSendNewMessage,
|
|
717
|
+
disabled: !composeSubject.trim() || !composeMessage.trim() || sendingNewMessage
|
|
718
|
+
},
|
|
719
|
+
/* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.composeSendButtonText }, sendingNewMessage ? "Sending..." : "Send Message")
|
|
720
|
+
)))
|
|
721
|
+
) : messageView === "list" ? (
|
|
642
722
|
/* Thread List View */
|
|
643
|
-
/* @__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 }, "
|
|
723
|
+
/* @__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(
|
|
644
724
|
import_react_native2.TouchableOpacity,
|
|
645
725
|
{
|
|
646
726
|
key: thread.id,
|
|
@@ -1670,6 +1750,81 @@ var styles = import_react_native2.StyleSheet.create({
|
|
|
1670
1750
|
fontSize: 14,
|
|
1671
1751
|
fontWeight: "600",
|
|
1672
1752
|
color: "#fff"
|
|
1753
|
+
},
|
|
1754
|
+
// New message button styles
|
|
1755
|
+
newMessageButton: {
|
|
1756
|
+
flexDirection: "row",
|
|
1757
|
+
alignItems: "center",
|
|
1758
|
+
justifyContent: "center",
|
|
1759
|
+
backgroundColor: "#7C3AED",
|
|
1760
|
+
paddingVertical: 12,
|
|
1761
|
+
paddingHorizontal: 20,
|
|
1762
|
+
borderRadius: 12,
|
|
1763
|
+
marginBottom: 16
|
|
1764
|
+
},
|
|
1765
|
+
newMessageButtonIcon: {
|
|
1766
|
+
fontSize: 16,
|
|
1767
|
+
marginRight: 8
|
|
1768
|
+
},
|
|
1769
|
+
newMessageButtonText: {
|
|
1770
|
+
fontSize: 15,
|
|
1771
|
+
fontWeight: "600",
|
|
1772
|
+
color: "#fff"
|
|
1773
|
+
},
|
|
1774
|
+
// Compose view styles
|
|
1775
|
+
composeHeader: {
|
|
1776
|
+
marginBottom: 20
|
|
1777
|
+
},
|
|
1778
|
+
composeTitle: {
|
|
1779
|
+
fontSize: 20,
|
|
1780
|
+
fontWeight: "600",
|
|
1781
|
+
color: "#111827",
|
|
1782
|
+
marginBottom: 4
|
|
1783
|
+
},
|
|
1784
|
+
composeSubtitle: {
|
|
1785
|
+
fontSize: 14,
|
|
1786
|
+
color: "#6B7280"
|
|
1787
|
+
},
|
|
1788
|
+
composeForm: {
|
|
1789
|
+
backgroundColor: "#F9FAFB",
|
|
1790
|
+
borderRadius: 12,
|
|
1791
|
+
padding: 16
|
|
1792
|
+
},
|
|
1793
|
+
composeSubjectInput: {
|
|
1794
|
+
backgroundColor: "#fff",
|
|
1795
|
+
borderWidth: 1,
|
|
1796
|
+
borderColor: "#E5E7EB",
|
|
1797
|
+
borderRadius: 8,
|
|
1798
|
+
paddingHorizontal: 12,
|
|
1799
|
+
paddingVertical: 10,
|
|
1800
|
+
fontSize: 15,
|
|
1801
|
+
color: "#111827"
|
|
1802
|
+
},
|
|
1803
|
+
composeMessageInput: {
|
|
1804
|
+
backgroundColor: "#fff",
|
|
1805
|
+
borderWidth: 1,
|
|
1806
|
+
borderColor: "#E5E7EB",
|
|
1807
|
+
borderRadius: 8,
|
|
1808
|
+
paddingHorizontal: 12,
|
|
1809
|
+
paddingVertical: 10,
|
|
1810
|
+
fontSize: 15,
|
|
1811
|
+
color: "#111827",
|
|
1812
|
+
minHeight: 120
|
|
1813
|
+
},
|
|
1814
|
+
composeSendButton: {
|
|
1815
|
+
backgroundColor: "#7C3AED",
|
|
1816
|
+
paddingVertical: 14,
|
|
1817
|
+
borderRadius: 12,
|
|
1818
|
+
alignItems: "center",
|
|
1819
|
+
marginTop: 20
|
|
1820
|
+
},
|
|
1821
|
+
composeSendButtonDisabled: {
|
|
1822
|
+
backgroundColor: "#C4B5FD"
|
|
1823
|
+
},
|
|
1824
|
+
composeSendButtonText: {
|
|
1825
|
+
fontSize: 16,
|
|
1826
|
+
fontWeight: "600",
|
|
1827
|
+
color: "#fff"
|
|
1673
1828
|
}
|
|
1674
1829
|
});
|
|
1675
1830
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -25,6 +25,7 @@ var BugBearContext = createContext({
|
|
|
25
25
|
sendMessage: async () => false,
|
|
26
26
|
markAsRead: async () => {
|
|
27
27
|
},
|
|
28
|
+
createThread: async () => ({ success: false }),
|
|
28
29
|
refreshTesterStatus: async () => {
|
|
29
30
|
}
|
|
30
31
|
});
|
|
@@ -32,7 +33,7 @@ function useBugBear() {
|
|
|
32
33
|
return useContext(BugBearContext);
|
|
33
34
|
}
|
|
34
35
|
function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
35
|
-
const [client] = useState(
|
|
36
|
+
const [client, setClient] = useState(null);
|
|
36
37
|
const [isTester, setIsTester] = useState(false);
|
|
37
38
|
const [isQAEnabled, setIsQAEnabled] = useState(false);
|
|
38
39
|
const [testerInfo, setTesterInfo] = useState(null);
|
|
@@ -40,7 +41,6 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
40
41
|
const [isLoading, setIsLoading] = useState(true);
|
|
41
42
|
const [threads, setThreads] = useState([]);
|
|
42
43
|
const [unreadCount, setUnreadCount] = useState(0);
|
|
43
|
-
const [initCount, setInitCount] = useState(0);
|
|
44
44
|
const hasInitialized = useRef(false);
|
|
45
45
|
const getDeviceInfo = useCallback(() => {
|
|
46
46
|
const { width, height } = Dimensions.get("window");
|
|
@@ -52,19 +52,23 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
52
52
|
};
|
|
53
53
|
}, [appVersion]);
|
|
54
54
|
const refreshAssignments = useCallback(async () => {
|
|
55
|
+
if (!client) return;
|
|
55
56
|
const newAssignments = await client.getAssignedTests();
|
|
56
57
|
setAssignments(newAssignments);
|
|
57
58
|
}, [client]);
|
|
58
59
|
const refreshThreads = useCallback(async () => {
|
|
60
|
+
if (!client) return;
|
|
59
61
|
const newThreads = await client.getThreadsForTester();
|
|
60
62
|
setThreads(newThreads);
|
|
61
63
|
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
62
64
|
setUnreadCount(totalUnread);
|
|
63
65
|
}, [client]);
|
|
64
66
|
const getThreadMessages = useCallback(async (threadId) => {
|
|
67
|
+
if (!client) return [];
|
|
65
68
|
return client.getThreadMessages(threadId);
|
|
66
69
|
}, [client]);
|
|
67
70
|
const sendMessage = useCallback(async (threadId, content) => {
|
|
71
|
+
if (!client) return false;
|
|
68
72
|
const success = await client.sendMessage(threadId, content);
|
|
69
73
|
if (success) {
|
|
70
74
|
await refreshThreads();
|
|
@@ -72,45 +76,58 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
72
76
|
return success;
|
|
73
77
|
}, [client, refreshThreads]);
|
|
74
78
|
const markAsRead = useCallback(async (threadId) => {
|
|
79
|
+
if (!client) return;
|
|
75
80
|
await client.markThreadAsRead(threadId);
|
|
76
81
|
await refreshThreads();
|
|
77
82
|
}, [client, refreshThreads]);
|
|
78
|
-
const
|
|
83
|
+
const createThread = useCallback(async (options) => {
|
|
84
|
+
if (!client) return { success: false, error: "Client not initialized" };
|
|
85
|
+
const result = await client.createThread(options);
|
|
86
|
+
if (result.success) {
|
|
87
|
+
await refreshThreads();
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}, [client, refreshThreads]);
|
|
91
|
+
const initializeBugBear = useCallback(async (bugBearClient) => {
|
|
79
92
|
setIsLoading(true);
|
|
80
93
|
try {
|
|
81
94
|
const [qaEnabled, info] = await Promise.all([
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
bugBearClient.isQAEnabled(),
|
|
96
|
+
bugBearClient.getTesterInfo()
|
|
84
97
|
]);
|
|
98
|
+
console.log("BugBear: Init complete", { qaEnabled, testerInfo: info });
|
|
85
99
|
setIsQAEnabled(qaEnabled);
|
|
86
100
|
setTesterInfo(info);
|
|
87
101
|
setIsTester(!!info);
|
|
88
102
|
if (info && qaEnabled) {
|
|
89
|
-
await Promise.all([
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
const [newAssignments, newThreads] = await Promise.all([
|
|
104
|
+
bugBearClient.getAssignedTests(),
|
|
105
|
+
bugBearClient.getThreadsForTester()
|
|
92
106
|
]);
|
|
107
|
+
setAssignments(newAssignments);
|
|
108
|
+
setThreads(newThreads);
|
|
109
|
+
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
110
|
+
setUnreadCount(totalUnread);
|
|
93
111
|
}
|
|
94
112
|
} catch (err) {
|
|
95
113
|
console.error("BugBear: Init error", err);
|
|
96
114
|
} finally {
|
|
97
115
|
setIsLoading(false);
|
|
98
116
|
}
|
|
99
|
-
}, [
|
|
117
|
+
}, []);
|
|
100
118
|
const refreshTesterStatus = useCallback(async () => {
|
|
101
|
-
|
|
102
|
-
|
|
119
|
+
const freshClient = createBugBear(config);
|
|
120
|
+
setClient(freshClient);
|
|
121
|
+
await initializeBugBear(freshClient);
|
|
122
|
+
}, [config, initializeBugBear]);
|
|
103
123
|
useEffect(() => {
|
|
104
124
|
if (enabled && !hasInitialized.current) {
|
|
105
125
|
hasInitialized.current = true;
|
|
106
|
-
|
|
126
|
+
const newClient = createBugBear(config);
|
|
127
|
+
setClient(newClient);
|
|
128
|
+
initializeBugBear(newClient);
|
|
107
129
|
}
|
|
108
|
-
}, [enabled, initializeBugBear]);
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
if (initCount > 0) {
|
|
111
|
-
initializeBugBear();
|
|
112
|
-
}
|
|
113
|
-
}, [initCount, initializeBugBear]);
|
|
130
|
+
}, [enabled, config, initializeBugBear]);
|
|
114
131
|
const currentAssignment = assignments.find(
|
|
115
132
|
(a) => a.status === "in_progress"
|
|
116
133
|
) || assignments[0] || null;
|
|
@@ -136,6 +153,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
136
153
|
getThreadMessages,
|
|
137
154
|
sendMessage,
|
|
138
155
|
markAsRead,
|
|
156
|
+
createThread,
|
|
139
157
|
refreshTesterStatus
|
|
140
158
|
}
|
|
141
159
|
},
|
|
@@ -191,7 +209,8 @@ function BugBearButton({
|
|
|
191
209
|
refreshThreads,
|
|
192
210
|
getThreadMessages,
|
|
193
211
|
sendMessage,
|
|
194
|
-
markAsRead
|
|
212
|
+
markAsRead,
|
|
213
|
+
createThread
|
|
195
214
|
} = useBugBear();
|
|
196
215
|
const [modalVisible, setModalVisible] = useState2(false);
|
|
197
216
|
const [activeTab, setActiveTab] = useState2("tests");
|
|
@@ -202,6 +221,9 @@ function BugBearButton({
|
|
|
202
221
|
const [replyText, setReplyText] = useState2("");
|
|
203
222
|
const [sendingReply, setSendingReply] = useState2(false);
|
|
204
223
|
const [loadingMessages, setLoadingMessages] = useState2(false);
|
|
224
|
+
const [composeSubject, setComposeSubject] = useState2("");
|
|
225
|
+
const [composeMessage, setComposeMessage] = useState2("");
|
|
226
|
+
const [sendingNewMessage, setSendingNewMessage] = useState2(false);
|
|
205
227
|
const getInitialPosition = () => {
|
|
206
228
|
const buttonSize = 56;
|
|
207
229
|
const margin = 16;
|
|
@@ -364,6 +386,27 @@ function BugBearButton({
|
|
|
364
386
|
setSelectedThread(null);
|
|
365
387
|
setThreadMessages([]);
|
|
366
388
|
setReplyText("");
|
|
389
|
+
setComposeSubject("");
|
|
390
|
+
setComposeMessage("");
|
|
391
|
+
};
|
|
392
|
+
const handleStartNewMessage = () => {
|
|
393
|
+
setMessageView("compose");
|
|
394
|
+
setComposeSubject("");
|
|
395
|
+
setComposeMessage("");
|
|
396
|
+
};
|
|
397
|
+
const handleSendNewMessage = async () => {
|
|
398
|
+
if (!composeSubject.trim() || !composeMessage.trim()) return;
|
|
399
|
+
setSendingNewMessage(true);
|
|
400
|
+
const result = await createThread({
|
|
401
|
+
subject: composeSubject.trim(),
|
|
402
|
+
message: composeMessage.trim()
|
|
403
|
+
});
|
|
404
|
+
if (result.success) {
|
|
405
|
+
setComposeSubject("");
|
|
406
|
+
setComposeMessage("");
|
|
407
|
+
setMessageView("list");
|
|
408
|
+
}
|
|
409
|
+
setSendingNewMessage(false);
|
|
367
410
|
};
|
|
368
411
|
const formatRelativeTime = (dateString) => {
|
|
369
412
|
const date = new Date(dateString);
|
|
@@ -615,9 +658,46 @@ function BugBearButton({
|
|
|
615
658
|
},
|
|
616
659
|
/* @__PURE__ */ React2.createElement(Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
|
|
617
660
|
)))
|
|
618
|
-
) : null), activeTab === "messages" && /* @__PURE__ */ React2.createElement(View, null, messageView === "
|
|
661
|
+
) : null), activeTab === "messages" && /* @__PURE__ */ React2.createElement(View, null, messageView === "compose" ? (
|
|
662
|
+
/* Compose New Message View */
|
|
663
|
+
/* @__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(
|
|
664
|
+
TextInput,
|
|
665
|
+
{
|
|
666
|
+
style: styles.composeSubjectInput,
|
|
667
|
+
value: composeSubject,
|
|
668
|
+
onChangeText: setComposeSubject,
|
|
669
|
+
placeholder: "What's this about?",
|
|
670
|
+
placeholderTextColor: "#9CA3AF",
|
|
671
|
+
maxLength: 100
|
|
672
|
+
}
|
|
673
|
+
), /* @__PURE__ */ React2.createElement(Text, { style: [styles.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ React2.createElement(
|
|
674
|
+
TextInput,
|
|
675
|
+
{
|
|
676
|
+
style: styles.composeMessageInput,
|
|
677
|
+
value: composeMessage,
|
|
678
|
+
onChangeText: setComposeMessage,
|
|
679
|
+
placeholder: "Write your message...",
|
|
680
|
+
placeholderTextColor: "#9CA3AF",
|
|
681
|
+
multiline: true,
|
|
682
|
+
numberOfLines: 6,
|
|
683
|
+
textAlignVertical: "top",
|
|
684
|
+
maxLength: 2e3
|
|
685
|
+
}
|
|
686
|
+
), /* @__PURE__ */ React2.createElement(
|
|
687
|
+
TouchableOpacity,
|
|
688
|
+
{
|
|
689
|
+
style: [
|
|
690
|
+
styles.composeSendButton,
|
|
691
|
+
(!composeSubject.trim() || !composeMessage.trim() || sendingNewMessage) && styles.composeSendButtonDisabled
|
|
692
|
+
],
|
|
693
|
+
onPress: handleSendNewMessage,
|
|
694
|
+
disabled: !composeSubject.trim() || !composeMessage.trim() || sendingNewMessage
|
|
695
|
+
},
|
|
696
|
+
/* @__PURE__ */ React2.createElement(Text, { style: styles.composeSendButtonText }, sendingNewMessage ? "Sending..." : "Send Message")
|
|
697
|
+
)))
|
|
698
|
+
) : messageView === "list" ? (
|
|
619
699
|
/* Thread List View */
|
|
620
|
-
/* @__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 }, "
|
|
700
|
+
/* @__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(
|
|
621
701
|
TouchableOpacity,
|
|
622
702
|
{
|
|
623
703
|
key: thread.id,
|
|
@@ -1647,6 +1727,81 @@ var styles = StyleSheet.create({
|
|
|
1647
1727
|
fontSize: 14,
|
|
1648
1728
|
fontWeight: "600",
|
|
1649
1729
|
color: "#fff"
|
|
1730
|
+
},
|
|
1731
|
+
// New message button styles
|
|
1732
|
+
newMessageButton: {
|
|
1733
|
+
flexDirection: "row",
|
|
1734
|
+
alignItems: "center",
|
|
1735
|
+
justifyContent: "center",
|
|
1736
|
+
backgroundColor: "#7C3AED",
|
|
1737
|
+
paddingVertical: 12,
|
|
1738
|
+
paddingHorizontal: 20,
|
|
1739
|
+
borderRadius: 12,
|
|
1740
|
+
marginBottom: 16
|
|
1741
|
+
},
|
|
1742
|
+
newMessageButtonIcon: {
|
|
1743
|
+
fontSize: 16,
|
|
1744
|
+
marginRight: 8
|
|
1745
|
+
},
|
|
1746
|
+
newMessageButtonText: {
|
|
1747
|
+
fontSize: 15,
|
|
1748
|
+
fontWeight: "600",
|
|
1749
|
+
color: "#fff"
|
|
1750
|
+
},
|
|
1751
|
+
// Compose view styles
|
|
1752
|
+
composeHeader: {
|
|
1753
|
+
marginBottom: 20
|
|
1754
|
+
},
|
|
1755
|
+
composeTitle: {
|
|
1756
|
+
fontSize: 20,
|
|
1757
|
+
fontWeight: "600",
|
|
1758
|
+
color: "#111827",
|
|
1759
|
+
marginBottom: 4
|
|
1760
|
+
},
|
|
1761
|
+
composeSubtitle: {
|
|
1762
|
+
fontSize: 14,
|
|
1763
|
+
color: "#6B7280"
|
|
1764
|
+
},
|
|
1765
|
+
composeForm: {
|
|
1766
|
+
backgroundColor: "#F9FAFB",
|
|
1767
|
+
borderRadius: 12,
|
|
1768
|
+
padding: 16
|
|
1769
|
+
},
|
|
1770
|
+
composeSubjectInput: {
|
|
1771
|
+
backgroundColor: "#fff",
|
|
1772
|
+
borderWidth: 1,
|
|
1773
|
+
borderColor: "#E5E7EB",
|
|
1774
|
+
borderRadius: 8,
|
|
1775
|
+
paddingHorizontal: 12,
|
|
1776
|
+
paddingVertical: 10,
|
|
1777
|
+
fontSize: 15,
|
|
1778
|
+
color: "#111827"
|
|
1779
|
+
},
|
|
1780
|
+
composeMessageInput: {
|
|
1781
|
+
backgroundColor: "#fff",
|
|
1782
|
+
borderWidth: 1,
|
|
1783
|
+
borderColor: "#E5E7EB",
|
|
1784
|
+
borderRadius: 8,
|
|
1785
|
+
paddingHorizontal: 12,
|
|
1786
|
+
paddingVertical: 10,
|
|
1787
|
+
fontSize: 15,
|
|
1788
|
+
color: "#111827",
|
|
1789
|
+
minHeight: 120
|
|
1790
|
+
},
|
|
1791
|
+
composeSendButton: {
|
|
1792
|
+
backgroundColor: "#7C3AED",
|
|
1793
|
+
paddingVertical: 14,
|
|
1794
|
+
borderRadius: 12,
|
|
1795
|
+
alignItems: "center",
|
|
1796
|
+
marginTop: 20
|
|
1797
|
+
},
|
|
1798
|
+
composeSendButtonDisabled: {
|
|
1799
|
+
backgroundColor: "#C4B5FD"
|
|
1800
|
+
},
|
|
1801
|
+
composeSendButtonText: {
|
|
1802
|
+
fontSize: 16,
|
|
1803
|
+
fontWeight: "600",
|
|
1804
|
+
color: "#fff"
|
|
1650
1805
|
}
|
|
1651
1806
|
});
|
|
1652
1807
|
export {
|