@buoy-gg/react-query 3.0.1 → 4.0.1
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/lib/commonjs/index.js +20 -0
- package/lib/commonjs/preset.js +23 -0
- package/lib/commonjs/react-query/components/QueryFilterViewV3.js +5 -2
- package/lib/commonjs/react-query/components/QuerySelector.js +2 -1
- package/lib/commonjs/react-query/components/modals/MutationBrowserFooter.js +4 -2
- package/lib/commonjs/react-query/components/modals/QueryBrowserFooter.js +5 -2
- package/lib/commonjs/react-query/components/modals/ReactQueryModalHeader.js +24 -1
- package/lib/commonjs/react-query/components/modals/SwipeIndicator.js +2 -2
- package/lib/commonjs/react-query/components/query-browser/MutationStatusCount.js +10 -5
- package/lib/commonjs/react-query/components/query-browser/QueryRow.js +3 -0
- package/lib/commonjs/react-query/components/query-browser/QueryStatus.js +30 -5
- package/lib/commonjs/react-query/components/query-browser/QueryStatusCount.js +12 -1
- package/lib/commonjs/react-query/hooks/useQueryStatusCounts.js +6 -2
- package/lib/commonjs/react-query/sync/dehydrate.js +56 -0
- package/lib/commonjs/react-query/sync/reactQuerySyncAdapter.js +132 -0
- package/lib/commonjs/react-query/sync/useReactQuerySyncAdapter.js +26 -0
- package/lib/commonjs/react-query/utils/getQueryStatusColor.js +1 -1
- package/lib/commonjs/react-query/utils/getQueryStatusLabel.js +8 -2
- package/lib/module/index.js +5 -0
- package/lib/module/preset.js +23 -0
- package/lib/module/react-query/components/QueryFilterViewV3.js +5 -2
- package/lib/module/react-query/components/QuerySelector.js +2 -1
- package/lib/module/react-query/components/modals/MutationBrowserFooter.js +4 -2
- package/lib/module/react-query/components/modals/QueryBrowserFooter.js +5 -2
- package/lib/module/react-query/components/modals/ReactQueryModalHeader.js +24 -1
- package/lib/module/react-query/components/modals/SwipeIndicator.js +2 -1
- package/lib/module/react-query/components/query-browser/MutationStatusCount.js +10 -5
- package/lib/module/react-query/components/query-browser/QueryRow.js +3 -0
- package/lib/module/react-query/components/query-browser/QueryStatus.js +30 -5
- package/lib/module/react-query/components/query-browser/QueryStatusCount.js +12 -1
- package/lib/module/react-query/hooks/useQueryStatusCounts.js +6 -2
- package/lib/module/react-query/sync/dehydrate.js +52 -0
- package/lib/module/react-query/sync/reactQuerySyncAdapter.js +127 -0
- package/lib/module/react-query/sync/useReactQuerySyncAdapter.js +23 -0
- package/lib/module/react-query/utils/getQueryStatusColor.js +1 -1
- package/lib/module/react-query/utils/getQueryStatusLabel.js +8 -2
- package/lib/typescript/index.d.ts +3 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/preset.d.ts.map +1 -1
- package/lib/typescript/react-query/components/QueryFilterViewV3.d.ts.map +1 -1
- package/lib/typescript/react-query/components/QuerySelector.d.ts.map +1 -1
- package/lib/typescript/react-query/components/modals/MutationBrowserFooter.d.ts +3 -1
- package/lib/typescript/react-query/components/modals/MutationBrowserFooter.d.ts.map +1 -1
- package/lib/typescript/react-query/components/modals/QueryBrowserFooter.d.ts +4 -1
- package/lib/typescript/react-query/components/modals/QueryBrowserFooter.d.ts.map +1 -1
- package/lib/typescript/react-query/components/modals/ReactQueryModalHeader.d.ts +6 -1
- package/lib/typescript/react-query/components/modals/ReactQueryModalHeader.d.ts.map +1 -1
- package/lib/typescript/react-query/components/modals/SwipeIndicator.d.ts.map +1 -1
- package/lib/typescript/react-query/components/query-browser/MutationStatusCount.d.ts +2 -0
- package/lib/typescript/react-query/components/query-browser/MutationStatusCount.d.ts.map +1 -1
- package/lib/typescript/react-query/components/query-browser/QueryRow.d.ts.map +1 -1
- package/lib/typescript/react-query/components/query-browser/QueryStatus.d.ts +5 -0
- package/lib/typescript/react-query/components/query-browser/QueryStatus.d.ts.map +1 -1
- package/lib/typescript/react-query/components/query-browser/QueryStatusCount.d.ts +2 -0
- package/lib/typescript/react-query/components/query-browser/QueryStatusCount.d.ts.map +1 -1
- package/lib/typescript/react-query/hooks/useQueryStatusCounts.d.ts +2 -0
- package/lib/typescript/react-query/hooks/useQueryStatusCounts.d.ts.map +1 -1
- package/lib/typescript/react-query/sync/dehydrate.d.ts +30 -0
- package/lib/typescript/react-query/sync/dehydrate.d.ts.map +1 -0
- package/lib/typescript/react-query/sync/reactQuerySyncAdapter.d.ts +34 -0
- package/lib/typescript/react-query/sync/reactQuerySyncAdapter.d.ts.map +1 -0
- package/lib/typescript/react-query/sync/useReactQuerySyncAdapter.d.ts +9 -0
- package/lib/typescript/react-query/sync/useReactQuerySyncAdapter.d.ts.map +1 -0
- package/lib/typescript/react-query/utils/getQueryStatusColor.d.ts +1 -1
- package/lib/typescript/react-query/utils/getQueryStatusColor.d.ts.map +1 -1
- package/lib/typescript/react-query/utils/getQueryStatusLabel.d.ts +7 -1
- package/lib/typescript/react-query/utils/getQueryStatusLabel.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -6,12 +6,18 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getQueryStatusLabel = getQueryStatusLabel;
|
|
7
7
|
/**
|
|
8
8
|
* Converts a query object into a human-friendly status string for badge rendering.
|
|
9
|
-
* Priority order: disabled > fetching > inactive > paused > stale > fresh
|
|
9
|
+
* Priority order: disabled > fetching > error > inactive > paused > stale > fresh
|
|
10
|
+
*
|
|
11
|
+
* "error" sits just below "fetching" so a hard-failed query (status === "error",
|
|
12
|
+
* i.e. failed with no cached data) surfaces as an error instead of hiding under
|
|
13
|
+
* "inactive"/"stale", while an in-flight retry still reads as "fetching". This
|
|
14
|
+
* is the single source of truth every consumer (cards, dots, counts, the Error
|
|
15
|
+
* filter) reads, and it matches the dashboard sidebar's error dot exactly.
|
|
10
16
|
*/
|
|
11
17
|
function getQueryStatusLabel(query) {
|
|
12
18
|
// Check disabled first - disabled queries won't automatically fetch
|
|
13
19
|
if (query.isDisabled()) {
|
|
14
20
|
return "disabled";
|
|
15
21
|
}
|
|
16
|
-
return query.state.fetchStatus === "fetching" ? "fetching" : !query.getObserversCount() ? "inactive" : query.state.fetchStatus === "paused" ? "paused" : query.isStale() ? "stale" : "fresh";
|
|
22
|
+
return query.state.fetchStatus === "fetching" ? "fetching" : query.state.status === "error" ? "error" : !query.getObserversCount() ? "inactive" : query.state.fetchStatus === "paused" ? "paused" : query.isStale() ? "stale" : "fresh";
|
|
17
23
|
}
|
package/lib/module/index.js
CHANGED
|
@@ -27,6 +27,11 @@ export { reactQueryToolPreset, createReactQueryTool, wifiTogglePreset, createWif
|
|
|
27
27
|
// =============================================================================
|
|
28
28
|
export { setQueryClient, disconnectQueryClient } from "./react-query/stores/reactQueryEventStore";
|
|
29
29
|
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// EXTERNAL SYNC (Adapter factory for @buoy-gg/external-sync's useExternalSync)
|
|
32
|
+
// =============================================================================
|
|
33
|
+
export { createReactQuerySyncAdapter } from "./react-query/sync/reactQuerySyncAdapter";
|
|
34
|
+
export { useReactQuerySyncAdapter } from "./react-query/sync/useReactQuerySyncAdapter";
|
|
30
35
|
// =============================================================================
|
|
31
36
|
// COMPONENTS (For custom UI implementations)
|
|
32
37
|
// =============================================================================
|
package/lib/module/preset.js
CHANGED
|
@@ -34,6 +34,29 @@ const saveWifiState = async enabled => {
|
|
|
34
34
|
// Failed to save WiFi state
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Restore the persisted WiFi state into onlineManager on app load.
|
|
40
|
+
*
|
|
41
|
+
* The toggle is rendered as a `toggle-only` tool whose state lives in
|
|
42
|
+
* `onlineManager`, not in a mounted React component. The `useWifiState` hook
|
|
43
|
+
* only restores when its component (the modal) mounts, so without this the
|
|
44
|
+
* persisted offline state is lost on reload and defaults back to online.
|
|
45
|
+
* Running this at module load ensures the saved state is applied eagerly.
|
|
46
|
+
*/
|
|
47
|
+
const restoreWifiState = async () => {
|
|
48
|
+
try {
|
|
49
|
+
const savedState = await persistentStorage.getItem(devToolsStorageKeys.settings.wifiEnabled());
|
|
50
|
+
if (savedState !== null) {
|
|
51
|
+
onlineManager.setOnline(savedState === "true");
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// Failed to restore WiFi state
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Eagerly restore persisted WiFi state on import so offline mode survives reloads.
|
|
59
|
+
restoreWifiState();
|
|
37
60
|
import { useState, useEffect } from "react";
|
|
38
61
|
|
|
39
62
|
/**
|
|
@@ -31,9 +31,12 @@ export function QueryFilterViewV3({
|
|
|
31
31
|
error: 0
|
|
32
32
|
};
|
|
33
33
|
queries.forEach(query => {
|
|
34
|
+
// Count by the status LABEL (single source of truth) so the Error chip's
|
|
35
|
+
// count matches exactly what the cards show and what the Error filter
|
|
36
|
+
// returns. (Previously error was counted off `state.error`, a different
|
|
37
|
+
// axis than the label, so the count, cards, and filter could disagree.)
|
|
34
38
|
const status = getQueryStatusLabel(query);
|
|
35
|
-
if (status === "disabled") counts.disabled++;else if (status === "fresh") counts.fresh++;else if (status === "stale") counts.stale++;else if (status === "fetching") counts.fetching++;else if (status === "paused") counts.paused++;else if (status === "inactive") counts.inactive++;
|
|
36
|
-
if (query.state.error) counts.error++;
|
|
39
|
+
if (status === "disabled") counts.disabled++;else if (status === "error") counts.error++;else if (status === "fresh") counts.fresh++;else if (status === "stale") counts.stale++;else if (status === "fetching") counts.fetching++;else if (status === "paused") counts.paused++;else if (status === "inactive") counts.inactive++;
|
|
37
40
|
});
|
|
38
41
|
return counts;
|
|
39
42
|
}, [queries]);
|
|
@@ -76,7 +76,8 @@ export function QuerySelector({
|
|
|
76
76
|
gray: "#6B7280",
|
|
77
77
|
purple: "#8B5CF6",
|
|
78
78
|
yellow: "#F59E0B",
|
|
79
|
-
green: "#10B981"
|
|
79
|
+
green: "#10B981",
|
|
80
|
+
red: "#EF4444"
|
|
80
81
|
};
|
|
81
82
|
const statusColor = colorMap[statusColorName] || "#6B7280";
|
|
82
83
|
const isSelected = query === selectedQuery;
|
|
@@ -16,7 +16,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
16
16
|
export function MutationBrowserFooter({
|
|
17
17
|
activeFilter,
|
|
18
18
|
onFilterChange,
|
|
19
|
-
modalMode
|
|
19
|
+
modalMode,
|
|
20
|
+
size = "default"
|
|
20
21
|
}) {
|
|
21
22
|
const isFloatingMode = modalMode === "floating";
|
|
22
23
|
const insets = useSafeAreaInsets({
|
|
@@ -38,7 +39,8 @@ export function MutationBrowserFooter({
|
|
|
38
39
|
style: styles.filterContainer,
|
|
39
40
|
children: /*#__PURE__*/_jsx(MutationStatusCount, {
|
|
40
41
|
activeFilter: activeFilter,
|
|
41
|
-
onFilterChange: onFilterChange
|
|
42
|
+
onFilterChange: onFilterChange,
|
|
43
|
+
size: size
|
|
42
44
|
})
|
|
43
45
|
})
|
|
44
46
|
}, `footer-${isFloatingMode ? "floating" : "docked"}`);
|
|
@@ -15,7 +15,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
15
15
|
export function QueryBrowserFooter({
|
|
16
16
|
activeFilter,
|
|
17
17
|
onFilterChange,
|
|
18
|
-
isFloatingMode = true
|
|
18
|
+
isFloatingMode = true,
|
|
19
|
+
// Default to floating mode if not specified
|
|
20
|
+
size = "default"
|
|
19
21
|
}) {
|
|
20
22
|
// Use safe area insets with a minimum bottom padding of 16 for docked mode
|
|
21
23
|
// This ensures proper spacing even on resized simulators or devices without home indicator
|
|
@@ -32,7 +34,8 @@ export function QueryBrowserFooter({
|
|
|
32
34
|
style: styles.filterContainer,
|
|
33
35
|
children: /*#__PURE__*/_jsx(QueryStatusCount, {
|
|
34
36
|
activeFilter: activeFilter,
|
|
35
|
-
onFilterChange: onFilterChange
|
|
37
|
+
onFilterChange: onFilterChange,
|
|
38
|
+
size: size
|
|
36
39
|
})
|
|
37
40
|
})
|
|
38
41
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { ModalHeader, TabSelector, Search, X, Filter, Trash, buoyColors } from "@buoy-gg/shared-ui";
|
|
4
4
|
import { useState, useRef, useEffect } from "react";
|
|
5
5
|
import { View, TextInput, TouchableOpacity, StyleSheet } from "react-native";
|
|
6
|
+
import { WifiToggle } from "../WifiToggle";
|
|
6
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
8
|
/**
|
|
8
9
|
* Shared header for all React Query modals. Handles tab switching when browsing and presents
|
|
@@ -18,7 +19,8 @@ export function ReactQueryModalHeader({
|
|
|
18
19
|
onSearchChange,
|
|
19
20
|
onFilterPress,
|
|
20
21
|
hasActiveFilters = false,
|
|
21
|
-
onClearCache
|
|
22
|
+
onClearCache,
|
|
23
|
+
showWifiToggle = false
|
|
22
24
|
}) {
|
|
23
25
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
24
26
|
const searchInputRef = useRef(null);
|
|
@@ -108,6 +110,18 @@ export function ReactQueryModalHeader({
|
|
|
108
110
|
color: buoyColors.textSecondary
|
|
109
111
|
})
|
|
110
112
|
}) : null]
|
|
113
|
+
}) : showWifiToggle ? /*#__PURE__*/_jsxs(View, {
|
|
114
|
+
style: styles.tabRow,
|
|
115
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
116
|
+
style: styles.tabSelectorWrap,
|
|
117
|
+
children: /*#__PURE__*/_jsx(TabSelector, {
|
|
118
|
+
tabs: tabs,
|
|
119
|
+
activeTab: activeTab,
|
|
120
|
+
onTabChange: tab => onTabChange(tab)
|
|
121
|
+
})
|
|
122
|
+
}), /*#__PURE__*/_jsx(WifiToggle, {
|
|
123
|
+
size: 16
|
|
124
|
+
})]
|
|
111
125
|
}) : /*#__PURE__*/_jsx(TabSelector, {
|
|
112
126
|
tabs: tabs,
|
|
113
127
|
activeTab: activeTab,
|
|
@@ -144,6 +158,15 @@ export function ReactQueryModalHeader({
|
|
|
144
158
|
});
|
|
145
159
|
}
|
|
146
160
|
const styles = StyleSheet.create({
|
|
161
|
+
tabRow: {
|
|
162
|
+
flex: 1,
|
|
163
|
+
flexDirection: "row",
|
|
164
|
+
alignItems: "center",
|
|
165
|
+
gap: 12
|
|
166
|
+
},
|
|
167
|
+
tabSelectorWrap: {
|
|
168
|
+
flex: 1
|
|
169
|
+
},
|
|
147
170
|
headerSearchContainer: {
|
|
148
171
|
flexDirection: "row",
|
|
149
172
|
alignItems: "center",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { View, StyleSheet, Animated } from "react-native";
|
|
4
|
+
import { absoluteFill } from "@buoy-gg/shared-ui";
|
|
4
5
|
import { useMemo } from "react";
|
|
5
6
|
import { ChevronLeft, ChevronRight, buoyColors } from "@buoy-gg/shared-ui";
|
|
6
7
|
|
|
@@ -184,7 +185,7 @@ export function SwipeIndicator({
|
|
|
184
185
|
}
|
|
185
186
|
const styles = StyleSheet.create({
|
|
186
187
|
container: {
|
|
187
|
-
...
|
|
188
|
+
...absoluteFill,
|
|
188
189
|
zIndex: 1000,
|
|
189
190
|
justifyContent: "center",
|
|
190
191
|
alignItems: "center"
|
|
@@ -11,7 +11,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
*/
|
|
12
12
|
const MutationStatusCount = ({
|
|
13
13
|
activeFilter,
|
|
14
|
-
onFilterChange
|
|
14
|
+
onFilterChange,
|
|
15
|
+
size = "default"
|
|
15
16
|
}) => {
|
|
16
17
|
const {
|
|
17
18
|
pending,
|
|
@@ -57,7 +58,8 @@ const MutationStatusCount = ({
|
|
|
57
58
|
isActive: activeFilter === "pending",
|
|
58
59
|
onPress: event => handleFilterClick("pending", event),
|
|
59
60
|
onTouchStart: handleTouchStart,
|
|
60
|
-
showLabel: true
|
|
61
|
+
showLabel: true,
|
|
62
|
+
size: size
|
|
61
63
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
62
64
|
label: "Success",
|
|
63
65
|
color: "green",
|
|
@@ -65,7 +67,8 @@ const MutationStatusCount = ({
|
|
|
65
67
|
isActive: activeFilter === "success",
|
|
66
68
|
onPress: event => handleFilterClick("success", event),
|
|
67
69
|
onTouchStart: handleTouchStart,
|
|
68
|
-
showLabel: true
|
|
70
|
+
showLabel: true,
|
|
71
|
+
size: size
|
|
69
72
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
70
73
|
label: "Error",
|
|
71
74
|
color: "red",
|
|
@@ -73,7 +76,8 @@ const MutationStatusCount = ({
|
|
|
73
76
|
isActive: activeFilter === "error",
|
|
74
77
|
onPress: event => handleFilterClick("error", event),
|
|
75
78
|
onTouchStart: handleTouchStart,
|
|
76
|
-
showLabel: true
|
|
79
|
+
showLabel: true,
|
|
80
|
+
size: size
|
|
77
81
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
78
82
|
label: "Paused",
|
|
79
83
|
color: "purple",
|
|
@@ -81,7 +85,8 @@ const MutationStatusCount = ({
|
|
|
81
85
|
isActive: activeFilter === "paused",
|
|
82
86
|
onPress: event => handleFilterClick("paused", event),
|
|
83
87
|
onTouchStart: handleTouchStart,
|
|
84
|
-
showLabel: true
|
|
88
|
+
showLabel: true,
|
|
89
|
+
size: size
|
|
85
90
|
})]
|
|
86
91
|
})
|
|
87
92
|
});
|
|
@@ -13,8 +13,10 @@ const QueryStatus = ({
|
|
|
13
13
|
showLabel = true,
|
|
14
14
|
isActive = false,
|
|
15
15
|
onPress,
|
|
16
|
-
onTouchStart
|
|
16
|
+
onTouchStart,
|
|
17
|
+
size = "default"
|
|
17
18
|
}) => {
|
|
19
|
+
const isLarge = size === "large";
|
|
18
20
|
// Buoy theme color mapping for status indicators
|
|
19
21
|
const getStatusColors = colorName => {
|
|
20
22
|
const colorMap = {
|
|
@@ -60,7 +62,7 @@ const QueryStatus = ({
|
|
|
60
62
|
const statusColors = getStatusColors(color);
|
|
61
63
|
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
62
64
|
"sentry-label": "ignore devtools query status",
|
|
63
|
-
style: [styles.queryStatusTag, isActive && {
|
|
65
|
+
style: [styles.queryStatusTag, isLarge && styles.queryStatusTagLarge, isActive && {
|
|
64
66
|
backgroundColor: statusColors.dot + "15",
|
|
65
67
|
borderColor: statusColors.dot + "40"
|
|
66
68
|
}],
|
|
@@ -69,16 +71,16 @@ const QueryStatus = ({
|
|
|
69
71
|
onPressIn: onTouchStart,
|
|
70
72
|
activeOpacity: 0.7,
|
|
71
73
|
children: [/*#__PURE__*/_jsx(View, {
|
|
72
|
-
style: [styles.dot, {
|
|
74
|
+
style: [styles.dot, isLarge && styles.dotLarge, {
|
|
73
75
|
backgroundColor: statusColors.dot
|
|
74
76
|
}]
|
|
75
77
|
}), showLabel && /*#__PURE__*/_jsx(Text, {
|
|
76
|
-
style: [styles.label],
|
|
78
|
+
style: [styles.label, isLarge && styles.labelLarge],
|
|
77
79
|
numberOfLines: 1,
|
|
78
80
|
ellipsizeMode: "tail",
|
|
79
81
|
children: label
|
|
80
82
|
}), count > 0 && /*#__PURE__*/_jsx(Text, {
|
|
81
|
-
style: [styles.count, {
|
|
83
|
+
style: [styles.count, isLarge && styles.countLarge, {
|
|
82
84
|
color: statusColors.dot
|
|
83
85
|
}],
|
|
84
86
|
numberOfLines: 1,
|
|
@@ -99,23 +101,46 @@ const styles = StyleSheet.create({
|
|
|
99
101
|
borderWidth: 1,
|
|
100
102
|
borderColor: buoyColors.textMuted + "20"
|
|
101
103
|
},
|
|
104
|
+
queryStatusTagLarge: {
|
|
105
|
+
borderRadius: 16,
|
|
106
|
+
paddingHorizontal: 13,
|
|
107
|
+
paddingVertical: 6,
|
|
108
|
+
height: 32,
|
|
109
|
+
gap: 7
|
|
110
|
+
},
|
|
102
111
|
dot: {
|
|
103
112
|
width: 6,
|
|
104
113
|
height: 6,
|
|
105
114
|
borderRadius: 3
|
|
106
115
|
},
|
|
116
|
+
dotLarge: {
|
|
117
|
+
width: 8,
|
|
118
|
+
height: 8,
|
|
119
|
+
borderRadius: 4
|
|
120
|
+
},
|
|
107
121
|
label: {
|
|
108
122
|
fontSize: 11,
|
|
109
123
|
fontWeight: "500",
|
|
110
124
|
color: buoyColors.textSecondary,
|
|
111
125
|
fontFamily: "system"
|
|
112
126
|
},
|
|
127
|
+
labelLarge: {
|
|
128
|
+
fontSize: 13,
|
|
129
|
+
fontWeight: "600",
|
|
130
|
+
letterSpacing: 0.2,
|
|
131
|
+
fontFamily: '"SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
|
|
132
|
+
},
|
|
113
133
|
count: {
|
|
114
134
|
fontSize: 11,
|
|
115
135
|
fontVariant: ["tabular-nums"],
|
|
116
136
|
fontWeight: "600",
|
|
117
137
|
marginLeft: "auto",
|
|
118
138
|
fontFamily: "system"
|
|
139
|
+
},
|
|
140
|
+
countLarge: {
|
|
141
|
+
fontSize: 13,
|
|
142
|
+
fontWeight: "700",
|
|
143
|
+
fontFamily: '"SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
|
|
119
144
|
}
|
|
120
145
|
});
|
|
121
146
|
export default QueryStatus;
|
|
@@ -10,7 +10,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
*/
|
|
11
11
|
const QueryStatusCount = ({
|
|
12
12
|
activeFilter,
|
|
13
|
-
onFilterChange
|
|
13
|
+
onFilterChange,
|
|
14
|
+
size = "default"
|
|
14
15
|
}) => {
|
|
15
16
|
const {
|
|
16
17
|
fresh,
|
|
@@ -64,6 +65,8 @@ const QueryStatusCount = ({
|
|
|
64
65
|
onPress: event => handleFilterClick("fresh", event),
|
|
65
66
|
onTouchStart: handleTouchStart,
|
|
66
67
|
showLabel: true // Always show labels now
|
|
68
|
+
,
|
|
69
|
+
size: size
|
|
67
70
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
68
71
|
label: "Loading",
|
|
69
72
|
color: "blue",
|
|
@@ -72,6 +75,8 @@ const QueryStatusCount = ({
|
|
|
72
75
|
onPress: event => handleFilterClick("fetching", event),
|
|
73
76
|
onTouchStart: handleTouchStart,
|
|
74
77
|
showLabel: true // Always show labels now
|
|
78
|
+
,
|
|
79
|
+
size: size
|
|
75
80
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
76
81
|
label: "Paused",
|
|
77
82
|
color: "purple",
|
|
@@ -80,6 +85,8 @@ const QueryStatusCount = ({
|
|
|
80
85
|
onPress: event => handleFilterClick("paused", event),
|
|
81
86
|
onTouchStart: handleTouchStart,
|
|
82
87
|
showLabel: true // Always show labels now
|
|
88
|
+
,
|
|
89
|
+
size: size
|
|
83
90
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
84
91
|
label: "Stale",
|
|
85
92
|
color: "yellow",
|
|
@@ -88,6 +95,8 @@ const QueryStatusCount = ({
|
|
|
88
95
|
onPress: event => handleFilterClick("stale", event),
|
|
89
96
|
onTouchStart: handleTouchStart,
|
|
90
97
|
showLabel: true // Always show labels now
|
|
98
|
+
,
|
|
99
|
+
size: size
|
|
91
100
|
}), /*#__PURE__*/_jsx(QueryStatus, {
|
|
92
101
|
label: "Idle",
|
|
93
102
|
color: "gray",
|
|
@@ -96,6 +105,8 @@ const QueryStatusCount = ({
|
|
|
96
105
|
onPress: event => handleFilterClick("inactive", event),
|
|
97
106
|
onTouchStart: handleTouchStart,
|
|
98
107
|
showLabel: true // Always show labels now
|
|
108
|
+
,
|
|
109
|
+
size: size
|
|
99
110
|
})]
|
|
100
111
|
})
|
|
101
112
|
});
|
|
@@ -14,7 +14,9 @@ function useQueryStatusCounts() {
|
|
|
14
14
|
stale: 0,
|
|
15
15
|
fetching: 0,
|
|
16
16
|
paused: 0,
|
|
17
|
-
inactive: 0
|
|
17
|
+
inactive: 0,
|
|
18
|
+
error: 0,
|
|
19
|
+
disabled: 0
|
|
18
20
|
});
|
|
19
21
|
useEffect(() => {
|
|
20
22
|
const updateCounts = () => {
|
|
@@ -28,7 +30,9 @@ function useQueryStatusCounts() {
|
|
|
28
30
|
stale: 0,
|
|
29
31
|
fetching: 0,
|
|
30
32
|
paused: 0,
|
|
31
|
-
inactive: 0
|
|
33
|
+
inactive: 0,
|
|
34
|
+
error: 0,
|
|
35
|
+
disabled: 0
|
|
32
36
|
});
|
|
33
37
|
setTimeout(() => setCounts(newCounts), 0);
|
|
34
38
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dehydration for external sync — ported from buoy-desktop's
|
|
5
|
+
* react-query-external-sync/hydration.ts so the desktop dashboard can
|
|
6
|
+
* hydrate an identical mirror of the device's QueryClient.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function dehydrateQueryClient(client) {
|
|
10
|
+
const mutations = client.getMutationCache().getAll().map(mutation => dehydrateMutation(mutation));
|
|
11
|
+
const queries = client.getQueryCache().getAll().map(query => dehydrateQuery(query));
|
|
12
|
+
return {
|
|
13
|
+
mutations,
|
|
14
|
+
queries
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function dehydrateMutation(mutation) {
|
|
18
|
+
return {
|
|
19
|
+
mutationId: mutation.mutationId,
|
|
20
|
+
mutationKey: mutation.options.mutationKey,
|
|
21
|
+
state: mutation.state,
|
|
22
|
+
...(mutation.options.scope && {
|
|
23
|
+
scope: mutation.options.scope
|
|
24
|
+
}),
|
|
25
|
+
...(mutation.meta && {
|
|
26
|
+
meta: mutation.meta
|
|
27
|
+
})
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function dehydrateQuery(query) {
|
|
31
|
+
const observerStates = query.observers.map(observer => ({
|
|
32
|
+
queryHash: query.queryHash,
|
|
33
|
+
options: observer.options,
|
|
34
|
+
// Remove queryFn from observer options so the dashboard can't accidentally
|
|
35
|
+
// run device fetch functions (they aren't serializable anyway)
|
|
36
|
+
queryFn: undefined
|
|
37
|
+
}));
|
|
38
|
+
return {
|
|
39
|
+
state: {
|
|
40
|
+
...query.state,
|
|
41
|
+
...(query.state.data !== undefined && {
|
|
42
|
+
data: query.state.data
|
|
43
|
+
})
|
|
44
|
+
},
|
|
45
|
+
queryKey: query.queryKey,
|
|
46
|
+
queryHash: query.queryHash,
|
|
47
|
+
...(query.meta && {
|
|
48
|
+
meta: query.meta
|
|
49
|
+
}),
|
|
50
|
+
observers: observerStates
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sync adapter for the React Query tool, consumed by @buoy-gg/external-sync's
|
|
5
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
6
|
+
* this package doesn't need a dependency on it).
|
|
7
|
+
*
|
|
8
|
+
* Action handlers are ported from buoy-desktop's react-query-external-sync
|
|
9
|
+
* (useSyncQueries.ts) so the dashboard keeps identical behavior, including
|
|
10
|
+
* the trigger/restore loading and error simulations used by the TanStack
|
|
11
|
+
* devtools UI.
|
|
12
|
+
*/
|
|
13
|
+
import { onlineManager } from "@tanstack/react-query";
|
|
14
|
+
import { dehydrateQueryClient } from "./dehydrate";
|
|
15
|
+
export function createReactQuerySyncAdapter(queryClient) {
|
|
16
|
+
const getQuery = params => {
|
|
17
|
+
const {
|
|
18
|
+
queryHash
|
|
19
|
+
} = params;
|
|
20
|
+
const query = queryClient.getQueryCache().get(queryHash);
|
|
21
|
+
if (!query) {
|
|
22
|
+
throw new Error(`Query with hash "${queryHash}" not found`);
|
|
23
|
+
}
|
|
24
|
+
return query;
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
version: 1,
|
|
28
|
+
getSnapshot: () => ({
|
|
29
|
+
dehydratedState: dehydrateQueryClient(queryClient),
|
|
30
|
+
isOnlineManagerOnline: onlineManager.isOnline()
|
|
31
|
+
}),
|
|
32
|
+
subscribe: onChange => {
|
|
33
|
+
const unsubQueries = queryClient.getQueryCache().subscribe(onChange);
|
|
34
|
+
const unsubMutations = queryClient.getMutationCache().subscribe(onChange);
|
|
35
|
+
const unsubOnline = onlineManager.subscribe(onChange);
|
|
36
|
+
return () => {
|
|
37
|
+
unsubQueries();
|
|
38
|
+
unsubMutations();
|
|
39
|
+
unsubOnline();
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
actions: {
|
|
43
|
+
refetch: params => {
|
|
44
|
+
const query = getQuery(params);
|
|
45
|
+
// Swallow fetch rejections — the resulting error state syncs anyway
|
|
46
|
+
return query.fetch().catch(() => undefined);
|
|
47
|
+
},
|
|
48
|
+
invalidate: params => queryClient.invalidateQueries(getQuery(params)),
|
|
49
|
+
reset: params => queryClient.resetQueries(getQuery(params)),
|
|
50
|
+
remove: params => {
|
|
51
|
+
queryClient.removeQueries(getQuery(params));
|
|
52
|
+
},
|
|
53
|
+
setQueryData: params => {
|
|
54
|
+
const {
|
|
55
|
+
queryKey,
|
|
56
|
+
data
|
|
57
|
+
} = params;
|
|
58
|
+
queryClient.setQueryData(queryKey, data, {
|
|
59
|
+
updatedAt: Date.now()
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
triggerError: params => {
|
|
63
|
+
const query = getQuery(params);
|
|
64
|
+
const __previousQueryOptions = query.options;
|
|
65
|
+
query.setState({
|
|
66
|
+
status: "error",
|
|
67
|
+
error: new Error("Unknown error from devtools"),
|
|
68
|
+
fetchMeta: {
|
|
69
|
+
...query.state.fetchMeta,
|
|
70
|
+
// @ts-expect-error This does exist
|
|
71
|
+
__previousQueryOptions
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
restoreError: params => queryClient.resetQueries(getQuery(params)),
|
|
76
|
+
triggerLoading: params => {
|
|
77
|
+
const query = getQuery(params);
|
|
78
|
+
const __previousQueryOptions = query.options;
|
|
79
|
+
// Trigger a fetch in order to trigger suspense as well
|
|
80
|
+
query.fetch({
|
|
81
|
+
...__previousQueryOptions,
|
|
82
|
+
queryFn: () => new Promise(() => {
|
|
83
|
+
// Never resolve — simulates perpetual loading
|
|
84
|
+
}),
|
|
85
|
+
gcTime: -1
|
|
86
|
+
}).catch(() => undefined);
|
|
87
|
+
query.setState({
|
|
88
|
+
data: undefined,
|
|
89
|
+
status: "pending",
|
|
90
|
+
fetchMeta: {
|
|
91
|
+
...query.state.fetchMeta,
|
|
92
|
+
// @ts-expect-error This does exist
|
|
93
|
+
__previousQueryOptions
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
restoreLoading: params => {
|
|
98
|
+
const query = getQuery(params);
|
|
99
|
+
const previousState = query.state;
|
|
100
|
+
const previousOptions = query.state.fetchMeta ? query.state.fetchMeta.__previousQueryOptions : null;
|
|
101
|
+
query.cancel({
|
|
102
|
+
silent: true
|
|
103
|
+
});
|
|
104
|
+
query.setState({
|
|
105
|
+
...previousState,
|
|
106
|
+
fetchStatus: "idle",
|
|
107
|
+
fetchMeta: null
|
|
108
|
+
});
|
|
109
|
+
if (previousOptions) {
|
|
110
|
+
query.fetch(previousOptions).catch(() => undefined);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
clearQueryCache: () => {
|
|
114
|
+
queryClient.getQueryCache().clear();
|
|
115
|
+
},
|
|
116
|
+
clearMutationCache: () => {
|
|
117
|
+
queryClient.getMutationCache().clear();
|
|
118
|
+
},
|
|
119
|
+
setOnline: params => {
|
|
120
|
+
const {
|
|
121
|
+
online
|
|
122
|
+
} = params;
|
|
123
|
+
onlineManager.setOnline(online);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { createReactQuerySyncAdapter } from "./reactQuerySyncAdapter";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Context-based variant of createReactQuerySyncAdapter for zero-config
|
|
9
|
+
* wiring (FloatingDevTools' auto external sync): reads the QueryClient from
|
|
10
|
+
* the surrounding QueryClientProvider. Returns null when rendered outside a
|
|
11
|
+
* provider so callers can skip registering the query tool.
|
|
12
|
+
*/
|
|
13
|
+
export function useReactQuerySyncAdapter() {
|
|
14
|
+
let queryClient;
|
|
15
|
+
try {
|
|
16
|
+
// Just a context read — throws only when there is no provider above us,
|
|
17
|
+
// which never changes for a mounted tree, so hook order stays stable.
|
|
18
|
+
queryClient = useQueryClient();
|
|
19
|
+
} catch {
|
|
20
|
+
queryClient = null;
|
|
21
|
+
}
|
|
22
|
+
return useMemo(() => queryClient ? createReactQuerySyncAdapter(queryClient) : null, [queryClient]);
|
|
23
|
+
}
|
|
@@ -8,5 +8,5 @@ export function getQueryStatusColor({
|
|
|
8
8
|
observerCount,
|
|
9
9
|
isStale
|
|
10
10
|
}) {
|
|
11
|
-
return queryState.fetchStatus === "fetching" ? "blue" : !observerCount ? "gray" : queryState.fetchStatus === "paused" ? "purple" : isStale ? "yellow" : "green";
|
|
11
|
+
return queryState.fetchStatus === "fetching" ? "blue" : queryState.status === "error" ? "red" : !observerCount ? "gray" : queryState.fetchStatus === "paused" ? "purple" : isStale ? "yellow" : "green";
|
|
12
12
|
}
|
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Converts a query object into a human-friendly status string for badge rendering.
|
|
5
|
-
* Priority order: disabled > fetching > inactive > paused > stale > fresh
|
|
5
|
+
* Priority order: disabled > fetching > error > inactive > paused > stale > fresh
|
|
6
|
+
*
|
|
7
|
+
* "error" sits just below "fetching" so a hard-failed query (status === "error",
|
|
8
|
+
* i.e. failed with no cached data) surfaces as an error instead of hiding under
|
|
9
|
+
* "inactive"/"stale", while an in-flight retry still reads as "fetching". This
|
|
10
|
+
* is the single source of truth every consumer (cards, dots, counts, the Error
|
|
11
|
+
* filter) reads, and it matches the dashboard sidebar's error dot exactly.
|
|
6
12
|
*/
|
|
7
13
|
export function getQueryStatusLabel(query) {
|
|
8
14
|
// Check disabled first - disabled queries won't automatically fetch
|
|
9
15
|
if (query.isDisabled()) {
|
|
10
16
|
return "disabled";
|
|
11
17
|
}
|
|
12
|
-
return query.state.fetchStatus === "fetching" ? "fetching" : !query.getObserversCount() ? "inactive" : query.state.fetchStatus === "paused" ? "paused" : query.isStale() ? "stale" : "fresh";
|
|
18
|
+
return query.state.fetchStatus === "fetching" ? "fetching" : query.state.status === "error" ? "error" : !query.getObserversCount() ? "inactive" : query.state.fetchStatus === "paused" ? "paused" : query.isStale() ? "stale" : "fresh";
|
|
13
19
|
}
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { reactQueryToolPreset, createReactQueryTool, wifiTogglePreset, createWifiToggleTool, } from "./preset";
|
|
11
11
|
export { setQueryClient, disconnectQueryClient, type ReactQueryEvent, type ReactQueryEventType, } from "./react-query/stores/reactQueryEventStore";
|
|
12
|
+
export { createReactQuerySyncAdapter } from "./react-query/sync/reactQuerySyncAdapter";
|
|
13
|
+
export { useReactQuerySyncAdapter } from "./react-query/sync/useReactQuerySyncAdapter";
|
|
14
|
+
export type { DehydratedState, DehydratedQuery, DehydratedMutation, ObserverState, } from "./react-query/sync/dehydrate";
|
|
12
15
|
export { ReactQueryModal } from "./react-query/components/modals/ReactQueryModal";
|
|
13
16
|
export { ReactQueryModalHeader } from "./react-query/components/modals/ReactQueryModalHeader";
|
|
14
17
|
export { QueryBrowserModal } from "./react-query/components/modals/QueryBrowserModal";
|