@buoy-gg/impersonate 7.0.41 → 7.0.43

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.
Files changed (78) hide show
  1. package/README.md +12 -80
  2. package/lib/commonjs/impersonate/components/DataNukeSettings.js +8 -675
  3. package/lib/commonjs/impersonate/components/ImpersonateBanner.js +1 -221
  4. package/lib/commonjs/impersonate/components/ImpersonateHistoryList.js +1 -181
  5. package/lib/commonjs/impersonate/components/ImpersonateModal.js +1 -319
  6. package/lib/commonjs/impersonate/components/ImpersonateOverlay.js +1 -26
  7. package/lib/commonjs/impersonate/components/ImpersonateStatusBar.js +1 -130
  8. package/lib/commonjs/impersonate/components/UserAvatar.js +1 -146
  9. package/lib/commonjs/impersonate/components/UserCard.js +1 -201
  10. package/lib/commonjs/impersonate/components/UserSearchView.js +1 -227
  11. package/lib/commonjs/impersonate/hooks/asyncStoragePeer.js +1 -17
  12. package/lib/commonjs/impersonate/hooks/asyncStoragePeer.web.js +1 -11
  13. package/lib/commonjs/impersonate/hooks/useAutoClearAsyncStorage.js +1 -134
  14. package/lib/commonjs/impersonate/hooks/useAutoClearReactQuery.js +1 -155
  15. package/lib/commonjs/impersonate/hooks/useAutoClearReactQuery.web.js +1 -20
  16. package/lib/commonjs/impersonate/hooks/useAutoClearRedux.js +1 -188
  17. package/lib/commonjs/impersonate/hooks/useAutoClearRedux.web.js +1 -27
  18. package/lib/commonjs/impersonate/hooks/useImpersonate.js +1 -215
  19. package/lib/commonjs/impersonate/hooks/useImpersonateHistory.js +1 -56
  20. package/lib/commonjs/impersonate/types/index.js +1 -16
  21. package/lib/commonjs/impersonate/types/types.js +1 -1
  22. package/lib/commonjs/impersonate/utils/impersonateListener.js +1 -350
  23. package/lib/commonjs/impersonate/utils/impersonateStore.js +1 -689
  24. package/lib/commonjs/index.js +1 -150
  25. package/lib/commonjs/preset.js +1 -232
  26. package/lib/commonjs/sync/impersonateSyncAdapter.js +1 -170
  27. package/lib/module/impersonate/components/DataNukeSettings.js +8 -670
  28. package/lib/module/impersonate/components/ImpersonateBanner.js +1 -215
  29. package/lib/module/impersonate/components/ImpersonateHistoryList.js +1 -176
  30. package/lib/module/impersonate/components/ImpersonateModal.js +1 -314
  31. package/lib/module/impersonate/components/ImpersonateOverlay.js +1 -21
  32. package/lib/module/impersonate/components/ImpersonateStatusBar.js +1 -125
  33. package/lib/module/impersonate/components/UserAvatar.js +1 -140
  34. package/lib/module/impersonate/components/UserCard.js +1 -196
  35. package/lib/module/impersonate/components/UserSearchView.js +1 -222
  36. package/lib/module/impersonate/hooks/asyncStoragePeer.js +1 -13
  37. package/lib/module/impersonate/hooks/asyncStoragePeer.web.js +1 -6
  38. package/lib/module/impersonate/hooks/useAutoClearAsyncStorage.js +1 -130
  39. package/lib/module/impersonate/hooks/useAutoClearReactQuery.js +1 -151
  40. package/lib/module/impersonate/hooks/useAutoClearReactQuery.web.js +1 -16
  41. package/lib/module/impersonate/hooks/useAutoClearRedux.js +1 -183
  42. package/lib/module/impersonate/hooks/useAutoClearRedux.web.js +1 -22
  43. package/lib/module/impersonate/hooks/useImpersonate.js +1 -212
  44. package/lib/module/impersonate/hooks/useImpersonateHistory.js +1 -52
  45. package/lib/module/impersonate/types/index.js +1 -3
  46. package/lib/module/impersonate/types/types.js +1 -1
  47. package/lib/module/impersonate/utils/impersonateListener.js +1 -341
  48. package/lib/module/impersonate/utils/impersonateStore.js +1 -686
  49. package/lib/module/index.js +1 -117
  50. package/lib/module/preset.js +1 -216
  51. package/lib/module/sync/impersonateSyncAdapter.js +1 -162
  52. package/lib/web/index.mjs +7 -8
  53. package/package.json +3 -6
  54. package/lib/typescript/impersonate/components/DataNukeSettings.d.ts.map +0 -1
  55. package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts.map +0 -1
  56. package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts.map +0 -1
  57. package/lib/typescript/impersonate/components/ImpersonateModal.d.ts.map +0 -1
  58. package/lib/typescript/impersonate/components/ImpersonateOverlay.d.ts.map +0 -1
  59. package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts.map +0 -1
  60. package/lib/typescript/impersonate/components/UserAvatar.d.ts.map +0 -1
  61. package/lib/typescript/impersonate/components/UserCard.d.ts.map +0 -1
  62. package/lib/typescript/impersonate/components/UserSearchView.d.ts.map +0 -1
  63. package/lib/typescript/impersonate/hooks/asyncStoragePeer.d.ts.map +0 -1
  64. package/lib/typescript/impersonate/hooks/asyncStoragePeer.web.d.ts.map +0 -1
  65. package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts.map +0 -1
  66. package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts.map +0 -1
  67. package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.web.d.ts.map +0 -1
  68. package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts.map +0 -1
  69. package/lib/typescript/impersonate/hooks/useAutoClearRedux.web.d.ts.map +0 -1
  70. package/lib/typescript/impersonate/hooks/useImpersonate.d.ts.map +0 -1
  71. package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts.map +0 -1
  72. package/lib/typescript/impersonate/types/index.d.ts.map +0 -1
  73. package/lib/typescript/impersonate/types/types.d.ts.map +0 -1
  74. package/lib/typescript/impersonate/utils/impersonateListener.d.ts.map +0 -1
  75. package/lib/typescript/impersonate/utils/impersonateStore.d.ts.map +0 -1
  76. package/lib/typescript/index.d.ts.map +0 -1
  77. package/lib/typescript/preset.d.ts.map +0 -1
  78. package/lib/typescript/sync/impersonateSyncAdapter.d.ts.map +0 -1
@@ -1,319 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ImpersonateModal = ImpersonateModal;
7
- exports.ImpersonateToolView = ImpersonateToolView;
8
- var _react = _interopRequireWildcard(require("react"));
9
- var _reactNative = require("react-native");
10
- var _sharedUi = require("@buoy-gg/shared-ui");
11
- var _useImpersonate = require("../hooks/useImpersonate");
12
- var _useAutoClearReactQuery = require("../hooks/useAutoClearReactQuery");
13
- var _useAutoClearRedux = require("../hooks/useAutoClearRedux");
14
- var _useAutoClearAsyncStorage = require("../hooks/useAutoClearAsyncStorage");
15
- var _UserSearchView = require("./UserSearchView");
16
- var _ImpersonateHistoryList = require("./ImpersonateHistoryList");
17
- var _DataNukeSettings = require("./DataNukeSettings");
18
- var _jsxRuntime = require("react/jsx-runtime");
19
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
20
- /**
21
- * ImpersonateModal Component
22
- *
23
- * Main modal for the impersonation tool. Uses JsModal with ModalHeader
24
- * pattern matching other Buoy dev tools (Network, Storage, etc.).
25
- */
26
-
27
- // Type assertion to work around React types compatibility issue with memo components
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- const JsModal = _sharedUi.JsModal;
30
-
31
- // Local Tab type (matches shared-ui's TabSelector)
32
-
33
- const ALL_TABS = [{
34
- key: "search",
35
- label: "Search"
36
- }, {
37
- key: "history",
38
- label: "History"
39
- }, {
40
- key: "settings",
41
- label: "Settings"
42
- }];
43
- const TABS_WITHOUT_SETTINGS = [{
44
- key: "search",
45
- label: "Search"
46
- }, {
47
- key: "history",
48
- label: "History"
49
- }];
50
- function ImpersonateModal({
51
- visible,
52
- onClose,
53
- onBack,
54
- onMinimize,
55
- onSearchUsers,
56
- onClearReactQuery,
57
- onClearRedux,
58
- onClearAsyncStorage,
59
- onClearMMKV,
60
- showSettingsTab = true,
61
- inline = false
62
- }) {
63
- // Select tabs based on showSettingsTab prop
64
- const tabs = showSettingsTab ? ALL_TABS : TABS_WITHOUT_SETTINGS;
65
- const [activeTab, setActiveTab] = (0, _react.useState)("search");
66
- const [isSearchActive, setIsSearchActive] = (0, _react.useState)(false);
67
- // True while the header's clear ✕ is being pressed. With a mouse, pressing
68
- // the ✕ blurs the input FIRST, and the blur handler closes search — which
69
- // unmounts the ✕ before its own press can fire, so the query was never
70
- // cleared (desktop). Press-in lands before the blur, so the blur handler can
71
- // tell this dismissal apart from a click elsewhere.
72
- const clearPressRef = (0, _react.useRef)(false);
73
- const [searchText, setSearchText] = (0, _react.useState)("");
74
- const searchInputRef = (0, _react.useRef)(null);
75
-
76
- // Auto-detect React Query and get clear function if user didn't provide one
77
- const {
78
- clearCache: autoClearReactQuery,
79
- isAvailable: isReactQueryDetected
80
- } = (0, _useAutoClearReactQuery.useAutoClearReactQuery)();
81
-
82
- // Auto-detect Redux and get reset function if user didn't provide one
83
- const {
84
- resetStore: autoClearRedux,
85
- isAvailable: isReduxDetected
86
- } = (0, _useAutoClearRedux.useAutoClearRedux)();
87
-
88
- // Auto-detect AsyncStorage and get clear function if user didn't provide one
89
- const {
90
- clearStorage: autoClearAsyncStorage,
91
- isAvailable: isAsyncStorageDetected
92
- } = (0, _useAutoClearAsyncStorage.useAutoClearAsyncStorage)();
93
-
94
- // Use user-provided callbacks, or fall back to auto-detected ones
95
- const effectiveClearReactQuery = onClearReactQuery ?? autoClearReactQuery ?? undefined;
96
- const effectiveClearRedux = onClearRedux ?? autoClearRedux ?? undefined;
97
- const effectiveClearAsyncStorage = onClearAsyncStorage ?? autoClearAsyncStorage ?? undefined;
98
-
99
- // Detection status for settings display
100
- // Shows "detected" if either user provided callback OR auto-detected
101
- const detectionStatus = {
102
- reactQuery: !!onClearReactQuery || isReactQueryDetected,
103
- redux: !!onClearRedux || isReduxDetected,
104
- asyncStorage: !!onClearAsyncStorage || isAsyncStorageDetected,
105
- mmkv: !!onClearMMKV // TODO: implement auto-detection
106
- };
107
- const {
108
- isActive,
109
- currentUser,
110
- headerKey,
111
- dataNukeSettings,
112
- showBanner,
113
- searchQuery,
114
- searchResults,
115
- isSearching,
116
- searchError,
117
- searchUsers,
118
- startImpersonation,
119
- stopImpersonation,
120
- updateHeaderKey,
121
- updateDataNukeSettings,
122
- updateShowBanner,
123
- history,
124
- removeFromHistory,
125
- clearHistory
126
- } = (0, _useImpersonate.useImpersonate)({
127
- onSearchUsers,
128
- onClearReactQuery: effectiveClearReactQuery,
129
- onClearRedux: effectiveClearRedux,
130
- onClearAsyncStorage: effectiveClearAsyncStorage,
131
- onClearMMKV
132
- });
133
-
134
- // Focus input when search becomes active
135
- (0, _react.useEffect)(() => {
136
- if (isSearchActive && searchInputRef.current) {
137
- setTimeout(() => {
138
- searchInputRef.current?.focus();
139
- }, 100);
140
- }
141
- }, [isSearchActive]);
142
-
143
- // Reset to search tab if settings tab is hidden while active
144
- (0, _react.useEffect)(() => {
145
- if (!showSettingsTab && activeTab === "settings") {
146
- setActiveTab("search");
147
- }
148
- }, [showSettingsTab, activeTab]);
149
- const handleSearchTextChange = (0, _react.useCallback)(text => {
150
- setSearchText(text);
151
- }, []);
152
- const handleSubmitSearch = (0, _react.useCallback)(() => {
153
- if (searchText.trim()) {
154
- searchUsers(searchText.trim());
155
- }
156
- setIsSearchActive(false);
157
- }, [searchText, searchUsers]);
158
- const handleSelectUser = (0, _react.useCallback)(async user => {
159
- await startImpersonation(user);
160
- }, [startImpersonation]);
161
- const handleSaveSettings = (0, _react.useCallback)(async (newHeaderKey, settings, newShowBanner) => {
162
- // Save header key, data nuke settings, and show banner
163
- await updateHeaderKey(newHeaderKey);
164
- await updateDataNukeSettings(settings);
165
- await updateShowBanner(newShowBanner);
166
- }, [updateHeaderKey, updateDataNukeSettings, updateShowBanner]);
167
- const handleTabChange = (0, _react.useCallback)(tabKey => {
168
- setActiveTab(tabKey);
169
- // Close search when switching tabs
170
- setIsSearchActive(false);
171
- }, []);
172
-
173
- // Header content renderer (matching NetworkModal pattern)
174
- const renderHeaderContent = () => {
175
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
176
- children: [onBack && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
177
- onBack: onBack
178
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
179
- title: "",
180
- children: isSearchActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Search, {
181
- value: searchText,
182
- onChangeText: handleSearchTextChange
183
- // ✕ clears and closes; with no text it simply closes.
184
- ,
185
- onCancel: () => {
186
- setSearchText("");
187
- setIsSearchActive(false);
188
- },
189
- onDismiss: () => setIsSearchActive(false),
190
- dismissOnBlur: true,
191
- placeholder: "Search by email, name, or ID...",
192
- iconSize: 14,
193
- testID: "buoy-impersonate-search-input",
194
- inputProps: {
195
- onSubmitEditing: handleSubmitSearch
196
- }
197
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TabSelector, {
198
- tabs: tabs,
199
- activeTab: activeTab,
200
- onTabChange: handleTabChange
201
- })
202
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Actions, {
203
- children: activeTab === "search" && !isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
204
- testID: "buoy-impersonate-search-toggle",
205
- onPress: () => setIsSearchActive(true),
206
- style: styles.headerActionButton,
207
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
208
- size: 14,
209
- color: _sharedUi.night.color.textSecondary
210
- })
211
- })
212
- })]
213
- });
214
- };
215
- if (!visible) {
216
- return null;
217
- }
218
- const body = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
219
- style: styles.container,
220
- children: [activeTab === "search" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_UserSearchView.UserSearchView, {
221
- currentUser: currentUser,
222
- isActive: isActive,
223
- searchQuery: searchQuery,
224
- searchResults: searchResults,
225
- isSearching: isSearching,
226
- searchError: searchError,
227
- onSelectUser: handleSelectUser,
228
- onStopImpersonation: stopImpersonation,
229
- searchAvailable: !!onSearchUsers
230
- }), activeTab === "history" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ImpersonateHistoryList.ImpersonateHistoryList, {
231
- history: history,
232
- currentUserId: currentUser?.id ?? null,
233
- onSelectUser: handleSelectUser,
234
- onStopImpersonation: stopImpersonation,
235
- onRemoveFromHistory: removeFromHistory,
236
- onClearHistory: clearHistory
237
- }), activeTab === "settings" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DataNukeSettings.DataNukeSettings, {
238
- headerKey: headerKey,
239
- settings: dataNukeSettings,
240
- showBanner: showBanner,
241
- onSave: handleSaveSettings,
242
- onShowBannerChange: updateShowBanner,
243
- detectionStatus: detectionStatus
244
- })]
245
- });
246
-
247
- // The ONLY difference between the phone and the desktop dashboard is which
248
- // shell the header and body go in. Everything above this line is shared.
249
- if (inline) {
250
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TickProvider, {
251
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
252
- style: styles.inlineRoot,
253
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
254
- style: styles.inlineHeader,
255
- ..._sharedUi.windowDragProps,
256
- children: renderHeaderContent()
257
- }), body]
258
- })
259
- });
260
- }
261
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TickProvider, {
262
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(JsModal, {
263
- visible: visible,
264
- onClose: onClose,
265
- onMinimize: onMinimize,
266
- header: {
267
- showToggleButton: true,
268
- customContent: renderHeaderContent()
269
- },
270
- enablePersistence: true,
271
- initialMode: "bottomSheet",
272
- disableScrollWrapper: true,
273
- children: body
274
- })
275
- });
276
- }
277
-
278
- /**
279
- * The same impersonation tool as `ImpersonateModal`, rendered INLINE for hosts
280
- * that place tools in a panel rather than a modal. Exists so there is exactly
281
- * one implementation — the desktop dashboard used to ship its own copy of this
282
- * component's header and tab router.
283
- */
284
- function ImpersonateToolView(props) {
285
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(ImpersonateModal, {
286
- ...props,
287
- inline: true,
288
- visible: true,
289
- onClose: () => {}
290
- });
291
- }
292
- const styles = _reactNative.StyleSheet.create({
293
- inlineRoot: {
294
- flex: 1,
295
- minHeight: 0,
296
- // Transparent so the desktop workbench's hoisted sky (one field spanning
297
- // sidebar + panel) shows through — the shell paints its base beneath it.
298
- backgroundColor: "transparent"
299
- },
300
- inlineHeader: {
301
- paddingHorizontal: 12,
302
- paddingVertical: 8,
303
- borderBottomWidth: 1,
304
- borderBottomColor: _sharedUi.buoyColors.border,
305
- // Clear, like Bench — the host's hoisted sky shows through the header.
306
- backgroundColor: "transparent"
307
- },
308
- container: {
309
- flex: 1
310
- },
311
- headerActionButton: {
312
- width: 28,
313
- height: 28,
314
- borderRadius: 6,
315
- backgroundColor: _sharedUi.night.color.buttonSurface,
316
- justifyContent: "center",
317
- alignItems: "center"
318
- }
319
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ImpersonateModal=ImpersonateModal;exports.ImpersonateToolView=ImpersonateToolView;var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _useImpersonate=require("../hooks/useImpersonate");var _useAutoClearReactQuery=require("../hooks/useAutoClearReactQuery");var _useAutoClearRedux=require("../hooks/useAutoClearRedux");var _useAutoClearAsyncStorage=require("../hooks/useAutoClearAsyncStorage");var _UserSearchView=require("./UserSearchView");var _ImpersonateHistoryList=require("./ImpersonateHistoryList");var _DataNukeSettings=require("./DataNukeSettings");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(i,y){if("function"==typeof WeakMap)var l=new WeakMap,S=new WeakMap;return(_interopRequireWildcard=function(e,c){if(!c&&e&&e.__esModule)return e;var r,s,a={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return a;if(r=c?S:l){if(r.has(e))return r.get(e);r.set(e,a)}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((s=(r=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(s.get||s.set)?r(a,t,s):a[t]=e[t]);return a})(i,y)}const JsModal=_sharedUi.JsModal;const ALL_TABS=[{key:"search",label:"Search"},{key:"history",label:"History"},{key:"settings",label:"Settings"}];const TABS_WITHOUT_SETTINGS=[{key:"search",label:"Search"},{key:"history",label:"History"}];function ImpersonateModal({visible:i,onClose:y,onBack:l,onMinimize:S,onSearchUsers:e,onClearReactQuery:c,onClearRedux:r,onClearAsyncStorage:s,onClearMMKV:a,showSettingsTab:t=true,inline:_=false}){const w=t?ALL_TABS:TABS_WITHOUT_SETTINGS;const[o,p]=(0,_react.useState)("search");const[d,u]=(0,_react.useState)(false);const ae=(0,_react.useRef)(false);const[h,g]=(0,_react.useState)("");const b=(0,_react.useRef)(null);const{clearCache:H,isAvailable:T}=(0,_useAutoClearReactQuery.useAutoClearReactQuery)();const{resetStore:M,isAvailable:D}=(0,_useAutoClearRedux.useAutoClearRedux)();const{clearStorage:q,isAvailable:B}=(0,_useAutoClearAsyncStorage.useAutoClearAsyncStorage)();const P=c??H??void 0;const U=r??M??void 0;const Q=s??q??void 0;const N={reactQuery:!!c||T,redux:!!r||D,asyncStorage:!!s||B,mmkv:!!a};const{isActive:O,currentUser:m,headerKey:V,dataNukeSettings:W,showBanner:E,searchQuery:L,searchResults:z,isSearching:F,searchError:J,searchUsers:v,startImpersonation:C,stopImpersonation:x,updateHeaderKey:A,updateDataNukeSettings:k,updateShowBanner:f,history:K,removeFromHistory:G,clearHistory:X}=(0,_useImpersonate.useImpersonate)({onSearchUsers:e,onClearReactQuery:P,onClearRedux:U,onClearAsyncStorage:Q,onClearMMKV:a});(0,_react.useEffect)(()=>{if(d&&b.current){setTimeout(()=>{b.current?.focus()},100)}},[d]);(0,_react.useEffect)(()=>{if(!t&&o==="settings"){p("search")}},[t,o]);const Y=(0,_react.useCallback)(n=>{g(n)},[]);const Z=(0,_react.useCallback)(()=>{if(h.trim()){v(h.trim())}u(false)},[h,v]);const j=(0,_react.useCallback)(async n=>{await C(n)},[C]);const $=(0,_react.useCallback)(async(n,te,re)=>{await A(n);await k(te);await f(re)},[A,k,f]);const ee=(0,_react.useCallback)(n=>{p(n);u(false)},[]);const R=()=>{return(0,_jsxRuntime.jsxs)(_sharedUi.ModalHeader,{children:[l&&(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation,{onBack:l}),(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Content,{title:"",children:d?(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Search,{value:h,onChangeText:Y,onCancel:()=>{g("");u(false)},onDismiss:()=>u(false),dismissOnBlur:true,placeholder:"Search by email, name, or ID...",iconSize:14,testID:"buoy-impersonate-search-input",inputProps:{onSubmitEditing:Z}}):(0,_jsxRuntime.jsx)(_sharedUi.TabSelector,{tabs:w,activeTab:o,onTabChange:ee})}),(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Actions,{children:o==="search"&&!d&&(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{testID:"buoy-impersonate-search-toggle",onPress:()=>u(true),style:styles.headerActionButton,children:(0,_jsxRuntime.jsx)(_sharedUi.Search,{size:14,color:_sharedUi.night.color.textSecondary})})})]})};if(!i){return null}const I=(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.container,children:[o==="search"&&(0,_jsxRuntime.jsx)(_UserSearchView.UserSearchView,{currentUser:m,isActive:O,searchQuery:L,searchResults:z,isSearching:F,searchError:J,onSelectUser:j,onStopImpersonation:x,searchAvailable:!!e}),o==="history"&&(0,_jsxRuntime.jsx)(_ImpersonateHistoryList.ImpersonateHistoryList,{history:K,currentUserId:m?.id??null,onSelectUser:j,onStopImpersonation:x,onRemoveFromHistory:G,onClearHistory:X}),o==="settings"&&(0,_jsxRuntime.jsx)(_DataNukeSettings.DataNukeSettings,{headerKey:V,settings:W,showBanner:E,onSave:$,onShowBannerChange:f,detectionStatus:N})]});if(_){return(0,_jsxRuntime.jsx)(_sharedUi.TickProvider,{children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.inlineRoot,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.inlineHeader,..._sharedUi.windowDragProps,children:R()}),I]})})}return(0,_jsxRuntime.jsx)(_sharedUi.TickProvider,{children:(0,_jsxRuntime.jsx)(JsModal,{visible:i,onClose:y,onMinimize:S,header:{showToggleButton:true,customContent:R()},enablePersistence:true,initialMode:"bottomSheet",disableScrollWrapper:true,children:I})})}function ImpersonateToolView(i){return(0,_jsxRuntime.jsx)(ImpersonateModal,{...i,inline:true,visible:true,onClose:()=>{}})}const styles=_reactNative.StyleSheet.create({inlineRoot:{flex:1,minHeight:0,backgroundColor:"transparent"},inlineHeader:{paddingHorizontal:12,paddingVertical:8,borderBottomWidth:1,borderBottomColor:_sharedUi.buoyColors.border,backgroundColor:"transparent"},container:{flex:1},headerActionButton:{width:28,height:28,borderRadius:6,backgroundColor:_sharedUi.night.color.buttonSurface,justifyContent:"center",alignItems:"center"}});
@@ -1,26 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ImpersonateOverlay = ImpersonateOverlay;
7
- var _react = _interopRequireDefault(require("react"));
8
- var _ImpersonateBanner = require("./ImpersonateBanner");
9
- var _jsxRuntime = require("react/jsx-runtime");
10
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
- /**
12
- * ImpersonateOverlay
13
- *
14
- * Auto-rendered by FloatingDevTools (`@buoy-gg/core`) when
15
- * `@buoy-gg/impersonate` is installed — the host does NOT need to mount
16
- * anything. It renders the floating impersonation banner, which self-gates
17
- * on store state (only visible while an impersonation session is active and
18
- * `showBanner` is enabled), so it's safe to mount unconditionally.
19
- */
20
-
21
- function ImpersonateOverlay() {
22
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ImpersonateBanner.ImpersonateBanner, {
23
- position: "top",
24
- offset: 50
25
- });
26
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ImpersonateOverlay=ImpersonateOverlay;var _react=_interopRequireDefault(require("react"));var _ImpersonateBanner=require("./ImpersonateBanner");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function ImpersonateOverlay(){return(0,_jsxRuntime.jsx)(_ImpersonateBanner.ImpersonateBanner,{position:"top",offset:50})}
@@ -1,130 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ImpersonateStatusBar = ImpersonateStatusBar;
7
- var _react = _interopRequireDefault(require("react"));
8
- var _reactNative = require("react-native");
9
- var _sharedUi = require("@buoy-gg/shared-ui");
10
- var _UserAvatar = require("./UserAvatar");
11
- var _jsxRuntime = require("react/jsx-runtime");
12
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
- /**
14
- * ImpersonateStatusBar Component
15
- *
16
- * Shows the current impersonation status with user avatar, info and stop button.
17
- * Displays at the top of the modal when impersonation is active.
18
- */
19
-
20
- function ImpersonateStatusBar({
21
- user,
22
- onStopImpersonation
23
- }) {
24
- const displayName = user.displayName || user.email || user.id;
25
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
26
- style: styles.container,
27
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
28
- style: styles.userSection,
29
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_UserAvatar.UserAvatar, {
30
- userId: user.id,
31
- name: displayName,
32
- size: "small",
33
- showActiveIndicator: false
34
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
35
- style: styles.userInfo,
36
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
37
- style: styles.userName,
38
- numberOfLines: 1,
39
- children: displayName
40
- })
41
- })]
42
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
43
- style: styles.statusPill,
44
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
45
- style: styles.pulseDot
46
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
47
- style: styles.statusText,
48
- children: "Active"
49
- })]
50
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
51
- style: styles.stopButton,
52
- onPress: onStopImpersonation,
53
- activeOpacity: 0.7,
54
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
55
- size: 12,
56
- color: _sharedUi.night.color.danger
57
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
58
- style: styles.stopButtonText,
59
- children: "Stop"
60
- })]
61
- })]
62
- });
63
- }
64
- const styles = _reactNative.StyleSheet.create({
65
- container: {
66
- flexDirection: "row",
67
- alignItems: "center",
68
- justifyContent: "space-between",
69
- paddingVertical: 10,
70
- paddingHorizontal: 16,
71
- backgroundColor: _sharedUi.buoyColors.success + "10",
72
- borderBottomWidth: 1,
73
- borderBottomColor: _sharedUi.buoyColors.success + "25",
74
- gap: 12
75
- },
76
- userSection: {
77
- flexDirection: "row",
78
- alignItems: "center",
79
- flex: 1,
80
- gap: 10,
81
- minWidth: 0
82
- },
83
- userInfo: {
84
- flex: 1,
85
- minWidth: 0
86
- },
87
- userName: {
88
- fontSize: 14,
89
- fontWeight: "600",
90
- color: _sharedUi.night.color.text
91
- },
92
- statusPill: {
93
- flexDirection: "row",
94
- alignItems: "center",
95
- gap: 6,
96
- backgroundColor: _sharedUi.buoyColors.success + "20",
97
- paddingHorizontal: 10,
98
- paddingVertical: 5,
99
- borderRadius: 12
100
- },
101
- pulseDot: {
102
- width: 6,
103
- height: 6,
104
- borderRadius: 3,
105
- backgroundColor: _sharedUi.buoyColors.success
106
- },
107
- statusText: {
108
- fontSize: 11,
109
- fontWeight: "600",
110
- color: _sharedUi.buoyColors.success,
111
- textTransform: "uppercase",
112
- letterSpacing: 0.5
113
- },
114
- stopButton: {
115
- flexDirection: "row",
116
- alignItems: "center",
117
- gap: 4,
118
- paddingHorizontal: 12,
119
- paddingVertical: 6,
120
- backgroundColor: _sharedUi.night.color.dangerSoft,
121
- borderRadius: 6,
122
- borderWidth: 1,
123
- borderColor: _sharedUi.night.color.danger + "30"
124
- },
125
- stopButtonText: {
126
- fontSize: 12,
127
- fontWeight: "600",
128
- color: _sharedUi.night.color.danger
129
- }
130
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ImpersonateStatusBar=ImpersonateStatusBar;var _react=_interopRequireDefault(require("react"));var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _UserAvatar=require("./UserAvatar");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function ImpersonateStatusBar({user:e,onStopImpersonation:r}){const t=e.displayName||e.email||e.id;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.container,children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.userSection,children:[(0,_jsxRuntime.jsx)(_UserAvatar.UserAvatar,{userId:e.id,name:t,size:"small",showActiveIndicator:false}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.userInfo,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.userName,numberOfLines:1,children:t})})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.statusPill,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.pulseDot}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.statusText,children:"Active"})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:styles.stopButton,onPress:r,activeOpacity:.7,children:[(0,_jsxRuntime.jsx)(_sharedUi.X,{size:12,color:_sharedUi.night.color.danger}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.stopButtonText,children:"Stop"})]})]})}const styles=_reactNative.StyleSheet.create({container:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",paddingVertical:10,paddingHorizontal:16,backgroundColor:_sharedUi.buoyColors.success+"10",borderBottomWidth:1,borderBottomColor:_sharedUi.buoyColors.success+"25",gap:12},userSection:{flexDirection:"row",alignItems:"center",flex:1,gap:10,minWidth:0},userInfo:{flex:1,minWidth:0},userName:{fontSize:14,fontWeight:"600",color:_sharedUi.night.color.text},statusPill:{flexDirection:"row",alignItems:"center",gap:6,backgroundColor:_sharedUi.buoyColors.success+"20",paddingHorizontal:10,paddingVertical:5,borderRadius:12},pulseDot:{width:6,height:6,borderRadius:3,backgroundColor:_sharedUi.buoyColors.success},statusText:{fontSize:11,fontWeight:"600",color:_sharedUi.buoyColors.success,textTransform:"uppercase",letterSpacing:.5},stopButton:{flexDirection:"row",alignItems:"center",gap:4,paddingHorizontal:12,paddingVertical:6,backgroundColor:_sharedUi.night.color.dangerSoft,borderRadius:6,borderWidth:1,borderColor:_sharedUi.night.color.danger+"30"},stopButtonText:{fontSize:12,fontWeight:"600",color:_sharedUi.night.color.danger}});
@@ -1,146 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.AVATAR_COLORS = void 0;
7
- exports.UserAvatar = UserAvatar;
8
- exports.getAvatarColor = getAvatarColor;
9
- exports.getInitials = getInitials;
10
- var _react = _interopRequireWildcard(require("react"));
11
- var _reactNative = require("react-native");
12
- var _sharedUi = require("@buoy-gg/shared-ui");
13
- var _jsxRuntime = require("react/jsx-runtime");
14
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
15
- /**
16
- * UserAvatar Component
17
- *
18
- * Displays a user avatar with initials and deterministic color.
19
- * Supports size variants and active indicator.
20
- */
21
-
22
- // Avatar background colors - deterministic based on user ID
23
- const AVATAR_COLORS = exports.AVATAR_COLORS = ["#6366F1",
24
- // Indigo
25
- "#8B5CF6",
26
- // Purple
27
- "#EC4899",
28
- // Pink
29
- "#F43F5E",
30
- // Rose
31
- "#F97316",
32
- // Orange
33
- "#EAB308",
34
- // Yellow
35
- "#22C55E",
36
- // Green
37
- "#14B8A6",
38
- // Teal
39
- "#06B6D4",
40
- // Cyan
41
- "#3B82F6" // Blue
42
- ];
43
- const SIZES = {
44
- small: {
45
- container: 24,
46
- font: 10,
47
- indicator: 8
48
- },
49
- medium: {
50
- container: 36,
51
- font: 14,
52
- indicator: 10
53
- },
54
- large: {
55
- container: 44,
56
- font: 16,
57
- indicator: 12
58
- }
59
- };
60
- /**
61
- * Get initials from a name or email
62
- */
63
- function getInitials(name) {
64
- if (!name) return "?";
65
-
66
- // If it's an email, use first letter before @
67
- if (name.includes("@")) {
68
- return name.charAt(0).toUpperCase();
69
- }
70
-
71
- // Split by spaces and get first letter of first two words
72
- const parts = name.trim().split(/\s+/);
73
- if (parts.length === 1) {
74
- return parts[0].charAt(0).toUpperCase();
75
- }
76
- return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
77
- }
78
-
79
- /**
80
- * Get deterministic color based on user ID
81
- */
82
- function getAvatarColor(userId) {
83
- let hash = 0;
84
- for (let i = 0; i < userId.length; i++) {
85
- hash = userId.charCodeAt(i) + ((hash << 5) - hash);
86
- }
87
- return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
88
- }
89
- function UserAvatar({
90
- userId,
91
- name,
92
- size = "medium",
93
- showActiveIndicator = false,
94
- backgroundColor
95
- }) {
96
- const sizeConfig = SIZES[size];
97
- const avatarColor = (0, _react.useMemo)(() => backgroundColor ?? getAvatarColor(userId), [userId, backgroundColor]);
98
- const initials = (0, _react.useMemo)(() => getInitials(name), [name]);
99
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
100
- style: styles.wrapper,
101
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
102
- style: [styles.container, {
103
- width: sizeConfig.container,
104
- height: sizeConfig.container,
105
- borderRadius: sizeConfig.container / 2,
106
- backgroundColor: avatarColor
107
- }],
108
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
109
- style: [styles.initials, {
110
- fontSize: sizeConfig.font
111
- }],
112
- children: initials
113
- })
114
- }), showActiveIndicator && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
115
- style: [styles.indicator, {
116
- width: sizeConfig.indicator,
117
- height: sizeConfig.indicator,
118
- borderRadius: sizeConfig.indicator / 2,
119
- right: -1,
120
- bottom: -1
121
- }]
122
- })]
123
- });
124
- }
125
- const styles = _reactNative.StyleSheet.create({
126
- wrapper: {
127
- position: "relative"
128
- },
129
- container: {
130
- alignItems: "center",
131
- justifyContent: "center"
132
- },
133
- initials: {
134
- color: "#FFFFFF",
135
- fontWeight: "600",
136
- letterSpacing: 0.5
137
- },
138
- indicator: {
139
- position: "absolute",
140
- backgroundColor: _sharedUi.buoyColors.success,
141
- borderWidth: 2,
142
- borderColor: _sharedUi.night.color.surface
143
- }
144
- });
145
-
146
- // Export helper functions for reuse
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.AVATAR_COLORS=void 0;exports.UserAvatar=UserAvatar;exports.getAvatarColor=getAvatarColor;exports.getInitials=getInitials;var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _jsxRuntime=require("react/jsx-runtime");function _interopRequireWildcard(r,e){if("function"==typeof WeakMap)var n=new WeakMap,l=new WeakMap;return(_interopRequireWildcard=function(t,i){if(!i&&t&&t.__esModule)return t;var o,a,c={__proto__:null,default:t};if(null===t||"object"!=typeof t&&"function"!=typeof t)return c;if(o=i?l:n){if(o.has(t))return o.get(t);o.set(t,c)}for(const s in t)"default"!==s&&{}.hasOwnProperty.call(t,s)&&((a=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(t,s))&&(a.get||a.set)?o(c,s,a):c[s]=t[s]);return c})(r,e)}const AVATAR_COLORS=exports.AVATAR_COLORS=["#6366F1","#8B5CF6","#EC4899","#F43F5E","#F97316","#EAB308","#22C55E","#14B8A6","#06B6D4","#3B82F6"];const SIZES={small:{container:24,font:10,indicator:8},medium:{container:36,font:14,indicator:10},large:{container:44,font:16,indicator:12}};function getInitials(r){if(!r)return"?";if(r.includes("@")){return r.charAt(0).toUpperCase()}const e=r.trim().split(/\s+/);if(e.length===1){return e[0].charAt(0).toUpperCase()}return(e[0].charAt(0)+e[e.length-1].charAt(0)).toUpperCase()}function getAvatarColor(r){let e=0;for(let n=0;n<r.length;n++){e=r.charCodeAt(n)+((e<<5)-e)}return AVATAR_COLORS[Math.abs(e)%AVATAR_COLORS.length]}function UserAvatar({userId:r,name:e,size:n="medium",showActiveIndicator:l=false,backgroundColor:t}){const i=SIZES[n];const o=(0,_react.useMemo)(()=>t??getAvatarColor(r),[r,t]);const a=(0,_react.useMemo)(()=>getInitials(e),[e]);return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.wrapper,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.container,{width:i.container,height:i.container,borderRadius:i.container/2,backgroundColor:o}],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.initials,{fontSize:i.font}],children:a})}),l&&(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.indicator,{width:i.indicator,height:i.indicator,borderRadius:i.indicator/2,right:-1,bottom:-1}]})]})}const styles=_reactNative.StyleSheet.create({wrapper:{position:"relative"},container:{alignItems:"center",justifyContent:"center"},initials:{color:"#FFFFFF",fontWeight:"600",letterSpacing:.5},indicator:{position:"absolute",backgroundColor:_sharedUi.buoyColors.success,borderWidth:2,borderColor:_sharedUi.night.color.surface}});