From b6a120920bf02a60d3c1cf3211f6324a50903870 Mon Sep 17 00:00:00 2001
From: "fernando.basello" <>
Date: Fri, 19 Apr 2024 23:20:57 -0300
Subject: [PATCH] Separate network tab
---
apps/reactotron-app/src/renderer/App.tsx | 3 +
.../src/renderer/ReactotronBrain.tsx | 24 +-
.../renderer/components/SideBar/Sidebar.tsx | 2 +
.../renderer/contexts/Standalone/index.tsx | 2 +
.../contexts/Standalone/useStandalone.test.ts | 34 ++-
.../contexts/Standalone/useStandalone.ts | 28 ++-
.../src/renderer/pages/network/index.tsx | 219 ++++++++++++++++++
.../CustomCommands/useCustomCommands.test.tsx | 1 +
.../src/contexts/Network/index.tsx | 58 +++++
.../src/contexts/Network/useNetwork.test.ts | 84 +++++++
.../src/contexts/Network/useNetwork.ts | 121 ++++++++++
.../ReactNative/useStorybook.test.tsx | 1 +
.../src/contexts/Reactotron/index.tsx | 4 +
.../src/contexts/State/useSnapshots.test.tsx | 1 +
.../contexts/State/useSubscriptions.test.tsx | 1 +
.../src/contexts/Timeline/useTimeline.test.ts | 4 +-
.../src/contexts/Timeline/useTimeline.ts | 4 +-
lib/reactotron-core-ui/src/index.ts | 3 +
.../src/modals/TimelineFilterModal/index.tsx | 1 -
19 files changed, 577 insertions(+), 18 deletions(-)
create mode 100644 apps/reactotron-app/src/renderer/pages/network/index.tsx
create mode 100644 lib/reactotron-core-ui/src/contexts/Network/index.tsx
create mode 100644 lib/reactotron-core-ui/src/contexts/Network/useNetwork.test.ts
create mode 100644 lib/reactotron-core-ui/src/contexts/Network/useNetwork.ts
diff --git a/apps/reactotron-app/src/renderer/App.tsx b/apps/reactotron-app/src/renderer/App.tsx
index b5778c4b1..ba763f579 100644
--- a/apps/reactotron-app/src/renderer/App.tsx
+++ b/apps/reactotron-app/src/renderer/App.tsx
@@ -15,6 +15,7 @@ import Overlay from "./pages/reactNative/Overlay"
import Storybook from "./pages/reactNative/Storybook"
import CustomCommands from "./pages/customCommands"
import Help from "./pages/help"
+import NetworkPage from "./pages/network"
const AppContainer = styled.div`
position: absolute;
@@ -56,6 +57,8 @@ function App() {
{/* Timeline */}
} />
+ {/* Network */}
+ } />
{/* State */}
} />
diff --git a/apps/reactotron-app/src/renderer/ReactotronBrain.tsx b/apps/reactotron-app/src/renderer/ReactotronBrain.tsx
index 170a73741..83437821c 100644
--- a/apps/reactotron-app/src/renderer/ReactotronBrain.tsx
+++ b/apps/reactotron-app/src/renderer/ReactotronBrain.tsx
@@ -6,6 +6,7 @@ import {
CustomCommandsProvider,
ReactNativeProvider,
TimelineProvider,
+ NetworkProvider,
StateProvider,
} from "reactotron-core-ui"
@@ -15,6 +16,7 @@ interface Props {
commands: Command[]
sendCommand: (type: string, payload: any, clientId?: string) => void
clearCommands: () => void
+ clearNetworkCommands: () => void
addCommandListener: (callback: (command: Command) => void) => void
}
@@ -23,6 +25,7 @@ const ReactotronBrain: FunctionComponent> = ({
commands,
sendCommand,
clearCommands,
+ clearNetworkCommands,
addCommandListener,
children,
}) => {
@@ -31,17 +34,20 @@ const ReactotronBrain: FunctionComponent> = ({
commands={commands}
sendCommand={sendCommand}
clearCommands={clearCommands}
+ clearNetworkCommands={clearNetworkCommands}
addCommandListener={addCommandListener}
>
-
-
-
-
- {children}
-
-
-
-
+
+
+
+
+
+ {children}
+
+
+
+
+
)
}
diff --git a/apps/reactotron-app/src/renderer/components/SideBar/Sidebar.tsx b/apps/reactotron-app/src/renderer/components/SideBar/Sidebar.tsx
index 2ff34b503..0e4d22925 100644
--- a/apps/reactotron-app/src/renderer/components/SideBar/Sidebar.tsx
+++ b/apps/reactotron-app/src/renderer/components/SideBar/Sidebar.tsx
@@ -7,6 +7,7 @@ import {
MdWarning,
MdOutlineMobileFriendly,
MdMobiledataOff,
+ MdNetworkCheck,
} from "react-icons/md"
import { FaMagic } from "react-icons/fa"
import styled from "styled-components"
@@ -58,6 +59,7 @@ function SideBar({ isOpen, serverStatus }: { isOpen: boolean; serverStatus: Serv
+
= ({ children }) => {
selectedConnection,
selectConnection,
clearSelectedConnectionCommands,
+ clearNetworkCommands,
serverStarted,
serverStopped,
connectionEstablished,
@@ -89,6 +90,7 @@ const Provider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
commands={(selectedConnection || { commands: [] }).commands}
sendCommand={sendCommand}
clearCommands={clearSelectedConnectionCommands}
+ clearNetworkCommands={clearNetworkCommands}
addCommandListener={addCommandListener}
>
{children}
diff --git a/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.test.ts b/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.test.ts
index 195f0febc..82c7956a6 100644
--- a/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.test.ts
+++ b/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.test.ts
@@ -286,7 +286,7 @@ describe("contexts/Standalone/useStandalone", () => {
expect(result.current.orphanedCommands[0]).toEqual({ connectionId: 1, payload: true })
})
- it("should clear commands from a connection", () => {
+ it("should clear commands from a connection but keeping the api ones", () => {
const { result } = renderHook(() => useStandalone())
act(() => {
@@ -299,15 +299,43 @@ describe("contexts/Standalone/useStandalone", () => {
act(() => {
result.current.commandReceived({ clientId: "1234", payload: true })
+ result.current.commandReceived({ clientId: "1234", payload: true, type: "api.response" })
})
- expect(result.current.connections[0].commands.length).toEqual(1)
- expect(result.current.connections[0].commands[0]).toEqual({ clientId: "1234", payload: true })
+ expect(result.current.connections[0].commands.length).toEqual(2)
+ expect(result.current.connections[0].commands[0]).toEqual({ clientId: "1234", payload: true, type: "api.response"})
+ expect(result.current.connections[0].commands[1]).toEqual({ clientId: "1234", payload: true })
act(() => {
result.current.clearSelectedConnectionCommands()
})
+ expect(result.current.connections[0].commands.length).toEqual(1)
+ expect(result.current.connections[0].commands[0]).toEqual({ clientId: "1234", payload: true, type: "api.response"})
+ })
+
+ it("should clear network commands from a connection", () => {
+ const { result } = renderHook(() => useStandalone())
+
+ act(() => {
+ result.current.connectionEstablished({
+ clientId: "1234",
+ id: 0,
+ platform: "ios",
+ })
+ })
+
+ act(() => {
+ result.current.commandReceived({ clientId: "1234", payload: true, type: "api.response" })
+ })
+
+ expect(result.current.connections[0].commands.length).toEqual(1)
+ expect(result.current.connections[0].commands[0]).toEqual({ clientId: "1234", payload: true, type: "api.response" })
+
+ act(() => {
+ result.current.clearNetworkCommands()
+ })
+
expect(result.current.connections[0].commands.length).toEqual(0)
})
diff --git a/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.ts b/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.ts
index 96c6d5b2e..780d7d39f 100644
--- a/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.ts
+++ b/apps/reactotron-app/src/renderer/contexts/Standalone/useStandalone.ts
@@ -1,5 +1,6 @@
import { useCallback, useReducer } from "react"
import { produce } from "immer"
+import { CommandType } from "reactotron-core-contract"
export enum ActionTypes {
ServerStarted = "SERVER_STARTED",
@@ -7,6 +8,7 @@ export enum ActionTypes {
AddConnection = "ADD_CONNECTION",
RemoveConnection = "REMOVE_CONNECTION",
ClearConnectionCommands = "CLEAR_CONNECTION_COMMANDS",
+ ClearNetworkCommands = "CLEAR_NETWORK_COMMANDS",
CommandReceived = "COMMAND_RECEIVED",
ChangeSelectedClientId = "CHANGE_SELECTED_CLIENT_ID",
AddCommandHandler = "ADD_COMMAND_HANDLER",
@@ -52,6 +54,7 @@ type Action =
| { type: ActionTypes.ChangeSelectedClientId; payload: string }
| { type: ActionTypes.CommandReceived; payload: any } // TODO: Type this better!
| { type: ActionTypes.ClearConnectionCommands }
+ | { type: ActionTypes.ClearNetworkCommands }
| { type: ActionTypes.AddCommandHandler; payload: (command: any) => void }
| { type: ActionTypes.PortUnavailable; payload: undefined }
@@ -153,7 +156,25 @@ export function reducer(state: State, action: Action) {
if (!selectedConnection) return
- selectedConnection.commands = []
+ // for now we are keeping the api responses until we separate them out
+ // into their own list
+ selectedConnection.commands = selectedConnection.commands.filter(
+ (c) => c.type === CommandType.ApiResponse
+ )
+ })
+ case ActionTypes.ClearNetworkCommands:
+ return produce(state, (draftState) => {
+ if (!draftState.selectedClientId) return
+
+ const selectedConnection = draftState.connections.find(
+ (c) => c.clientId === draftState.selectedClientId
+ )
+
+ if (!selectedConnection) return
+
+ selectedConnection.commands = selectedConnection.commands.filter(
+ (c) => c.type !== CommandType.ApiResponse
+ )
})
case ActionTypes.ChangeSelectedClientId:
return produce(state, (draftState) => {
@@ -218,6 +239,10 @@ function useStandalone() {
dispatch({ type: ActionTypes.ClearConnectionCommands })
}, [])
+ const clearNetworkCommands = useCallback(() => {
+ dispatch({ type: ActionTypes.ClearNetworkCommands })
+ }, [])
+
const selectConnection = useCallback((clientId: string) => {
dispatch({ type: ActionTypes.ChangeSelectedClientId, payload: clientId })
}, [])
@@ -240,6 +265,7 @@ function useStandalone() {
connectionDisconnected,
commandReceived,
clearSelectedConnectionCommands,
+ clearNetworkCommands,
addCommandListener,
portUnavailable,
}
diff --git a/apps/reactotron-app/src/renderer/pages/network/index.tsx b/apps/reactotron-app/src/renderer/pages/network/index.tsx
new file mode 100644
index 000000000..9fe70a657
--- /dev/null
+++ b/apps/reactotron-app/src/renderer/pages/network/index.tsx
@@ -0,0 +1,219 @@
+import React, { useContext, useMemo } from "react"
+import { clipboard } from "electron"
+import fs from "fs"
+import {
+ Header,
+ filterCommands,
+ EmptyState,
+ ReactotronContext,
+ NetworkContext,
+ timelineCommandResolver,
+} from "reactotron-core-ui"
+import { MdSearch, MdDeleteSweep, MdSwapVert, MdReorder } from "react-icons/md"
+import { FaTimes } from "react-icons/fa"
+import styled from "styled-components"
+import { CommandType } from "reactotron-core-contract"
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`
+
+const NetworkPageContainer = styled.div`
+ height: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+`
+
+const SearchContainer = styled.div`
+ display: flex;
+ align-items: center;
+ padding-bottom: 10px;
+ padding-top: 4px;
+ padding-right: 10px;
+`
+const SearchLabel = styled.p`
+ padding: 0 10px;
+ font-size: 14px;
+ color: ${(props) => props.theme.foregroundDark};
+`
+const SearchInput = styled.input`
+ border-radius: 4px;
+ padding: 10px;
+ flex: 1;
+ background-color: ${(props) => props.theme.backgroundSubtleDark};
+ border: none;
+ color: ${(props) => props.theme.foregroundDark};
+ font-size: 14px;
+`
+export const ButtonContainer = styled.div`
+ padding: 10px;
+ cursor: pointer;
+`
+
+export const NetworkTable = styled.table`
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 1px solid ${(props) => props.theme.chromeLine};
+ border-radius: 4px;
+ margin-bottom: 10px;
+ color: ${(props) => props.theme.foregroundDark};
+`
+
+export const NetworkTableHeader = styled.thead`
+ background-color: ${(props) => props.theme.backgroundSubtleDark};
+ color: ${(props) => props.theme.foregroundDark};
+ font-size: 14px;
+ font-weight: bold;
+ text-align: left;
+`
+export const NetworkTableHeaderCell = styled.th`
+ padding: 0 10px;
+`
+
+export const NetworkTableHeaderRow = styled.tr`
+ height: 30px;
+ "& th": {
+ padding: 0 10px;
+ }
+`
+export const NetworkTableBody = styled.tbody`
+ font-size: 14px;
+ color: ${(props) => props.theme.foregroundDark};
+ font-weight: normal;
+ text-align: left;
+`
+export const NetworkTableRow = styled.tr`
+ height: 30px;
+`
+
+export const NetworkTableCell = styled.td`
+ padding: 0 10px;
+`
+
+export const NetworkContainer = styled.div`
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+`
+export const NetworkInspector = styled.div`
+ height: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ width: 400px;
+ resize: horizontal;
+ max-width: 600px;
+`
+
+function NetworkPage() {
+ const { sendCommand,clearNetworkCommands, commands, openDispatchModal } = useContext(ReactotronContext)
+ const {
+ isSearchOpen,
+ toggleSearch,
+ closeSearch,
+ setSearch,
+ search,
+ isReversed,
+ toggleReverse,
+ } = useContext(NetworkContext)
+
+ let filteredCommands = useMemo(() => {
+ const cmds = filterCommands(commands, search, []).filter((a) =>
+ a.type === CommandType.ApiResponse
+ )
+ return cmds;
+ }, [commands, search])
+
+ if (isReversed) {
+ filteredCommands = filteredCommands.reverse()
+ }
+
+ return (
+
+ {
+ toggleSearch()
+ },
+ },
+ {
+ tip: "Reverse Order",
+ icon: MdSwapVert,
+ onClick: () => {
+ toggleReverse()
+ },
+ },
+ {
+ tip: "Clear",
+ icon: MdDeleteSweep,
+ onClick: () => {
+ clearNetworkCommands()
+ },
+ },
+ ]}
+ >
+ {isSearchOpen && (
+
+ Search
+ setSearch(e.target.value)} />
+ {
+ if (search === "") {
+ closeSearch()
+ } else {
+ setSearch("")
+ }
+ }}
+ >
+
+
+
+ )}
+
+
+ {filteredCommands.length === 0 ? (
+
+ Once your app connects and starts sending events, they will appear here.
+
+ ) : (
+
+ filteredCommands.map((command) => {
+ const CommandComponent = timelineCommandResolver(command.type)
+
+ if (CommandComponent) {
+ return (
+ {
+ return new Promise((resolve, reject) => {
+ fs.readFile(path, "utf-8", (err, data) => {
+ if (err || !data) reject(new Error("Something failed"))
+ else resolve(data)
+ })
+ })
+ }}
+ sendCommand={sendCommand}
+ openDispatchDialog={openDispatchModal}
+ />
+ )
+ }
+
+ return null
+ })
+
+ )}
+
+
+ )
+}
+
+export default NetworkPage
diff --git a/lib/reactotron-core-ui/src/contexts/CustomCommands/useCustomCommands.test.tsx b/lib/reactotron-core-ui/src/contexts/CustomCommands/useCustomCommands.test.tsx
index 3e9d16f06..3ff0ddca8 100644
--- a/lib/reactotron-core-ui/src/contexts/CustomCommands/useCustomCommands.test.tsx
+++ b/lib/reactotron-core-ui/src/contexts/CustomCommands/useCustomCommands.test.tsx
@@ -12,6 +12,7 @@ function buildContextValues({ addCommandListener = null } = {}) {
commands: [],
sendCommand: jest.fn(),
clearCommands: jest.fn(),
+ clearNetworkCommands: jest.fn(),
addCommandListener: addCommandListener || jest.fn(),
isDispatchModalOpen: false,
dispatchModalInitialAction: "",
diff --git a/lib/reactotron-core-ui/src/contexts/Network/index.tsx b/lib/reactotron-core-ui/src/contexts/Network/index.tsx
new file mode 100644
index 000000000..575db6d75
--- /dev/null
+++ b/lib/reactotron-core-ui/src/contexts/Network/index.tsx
@@ -0,0 +1,58 @@
+import React, { FunctionComponent } from "react"
+
+import useNetwork from "./useNetwork"
+
+interface Context {
+ isSearchOpen: boolean
+ toggleSearch: () => void
+ openSearch: () => void
+ closeSearch: () => void
+ search: string
+ setSearch: (search: string) => void
+ isReversed: boolean
+ toggleReverse: () => void
+}
+
+const NetworkContext = React.createContext({
+ isSearchOpen: false,
+ toggleSearch: null,
+ openSearch: null,
+ closeSearch: null,
+ search: "",
+ setSearch: null,
+ isReversed: false,
+ toggleReverse: null,
+})
+
+const Provider: FunctionComponent = ({ children }) => {
+ const {
+ isSearchOpen,
+ toggleSearch,
+ openSearch,
+ closeSearch,
+ search,
+ setSearch,
+ isReversed,
+ toggleReverse,
+ } = useNetwork()
+
+ return (
+
+ {children}
+
+ )
+}
+
+export default NetworkContext
+export const NetworkProvider = Provider
diff --git a/lib/reactotron-core-ui/src/contexts/Network/useNetwork.test.ts b/lib/reactotron-core-ui/src/contexts/Network/useNetwork.test.ts
new file mode 100644
index 000000000..8dd87cb09
--- /dev/null
+++ b/lib/reactotron-core-ui/src/contexts/Network/useNetwork.test.ts
@@ -0,0 +1,84 @@
+import { act, renderHook } from "@testing-library/react"
+
+import useNetwork, { NetworkStorageKey } from "./useNetwork"
+
+describe("contexts/Network/useNetwork", () => {
+ beforeEach(() => {
+ localStorage.removeItem(NetworkStorageKey.ReversedOrder)
+ localStorage.removeItem(NetworkStorageKey.HiddenCommands)
+ })
+
+ describe("Initial Settings", () => {
+ it("should default to regular order", () => {
+ const { result } = renderHook(() => useNetwork())
+
+ expect(result.current.isReversed).toBeFalsy()
+ })
+
+ it("should load if user had the timeline reversed", () => {
+ localStorage.setItem(NetworkStorageKey.ReversedOrder, "reversed")
+
+ const { result } = renderHook(() => useNetwork())
+
+ expect(result.current.isReversed).toBeTruthy()
+ })
+
+ it("should load if user had the timeline regular order", () => {
+ localStorage.setItem(NetworkStorageKey.ReversedOrder, "regular")
+
+ const { result } = renderHook(() => useNetwork())
+
+ expect(result.current.isReversed).toBeFalsy()
+ })
+ })
+
+ describe("actions", () => {
+ it("should toggle search", () => {
+ const { result } = renderHook(() => useNetwork())
+
+ expect(result.current.isSearchOpen).toBeFalsy()
+ act(() => {
+ result.current.toggleSearch()
+ })
+ expect(result.current.isSearchOpen).toBeTruthy()
+ act(() => {
+ result.current.toggleSearch()
+ })
+ expect(result.current.isSearchOpen).toBeFalsy()
+ })
+
+ it("should set the search string", () => {
+ const { result } = renderHook(() => useNetwork())
+
+ expect(result.current.search).toEqual("")
+ act(() => {
+ result.current.setSearch("H")
+ })
+ expect(result.current.search).toEqual("H")
+ act(() => {
+ result.current.setSearch("L")
+ })
+ expect(result.current.search).toEqual("L")
+ act(() => {
+ result.current.setSearch("")
+ })
+ expect(result.current.search).toEqual("")
+ })
+
+ it("should toggle reverse", () => {
+ const { result } = renderHook(() => useNetwork())
+
+ expect(result.current.isReversed).toBeFalsy()
+ act(() => {
+ result.current.toggleReverse()
+ })
+ expect(localStorage.getItem(NetworkStorageKey.ReversedOrder)).toEqual("reversed")
+ expect(result.current.isReversed).toBeTruthy()
+ act(() => {
+ result.current.toggleReverse()
+ })
+ expect(localStorage.getItem(NetworkStorageKey.ReversedOrder)).toEqual("regular")
+ expect(result.current.isReversed).toBeFalsy()
+ })
+ })
+})
diff --git a/lib/reactotron-core-ui/src/contexts/Network/useNetwork.ts b/lib/reactotron-core-ui/src/contexts/Network/useNetwork.ts
new file mode 100644
index 000000000..e96090ddb
--- /dev/null
+++ b/lib/reactotron-core-ui/src/contexts/Network/useNetwork.ts
@@ -0,0 +1,121 @@
+import { useReducer, useEffect } from "react"
+
+import type { CommandTypeKey } from "reactotron-core-contract"
+
+export enum NetworkStorageKey {
+ ReversedOrder = "ReactotronNetworkReversedOrder",
+ HiddenCommands = "ReactotronNetworkHiddenCommands",
+}
+
+interface NetworkState {
+ isSearchOpen: boolean
+ search: string
+ isFilterOpen: boolean
+ isReversed: boolean
+ hiddenCommands: CommandTypeKey[]
+}
+
+enum NetworkActionType {
+ SearchOpen = "SEARCH_OPEN",
+ SearchClose = "SEARCH_CLOSE",
+ SearchSet = "SEARCH_SET",
+ OrderReverse = "ORDER_REVERSE",
+ OrderRegular = "ORDER_REGULAR",
+}
+
+type Action =
+ | {
+ type:
+ | NetworkActionType.SearchOpen
+ | NetworkActionType.SearchClose
+ | NetworkActionType.OrderReverse
+ | NetworkActionType.OrderRegular
+ }
+ | {
+ type: NetworkActionType.SearchSet
+ payload: string
+ }
+
+function networkReducer(state: NetworkState, action: Action) {
+ switch (action.type) {
+ case NetworkActionType.SearchOpen:
+ return { ...state, isSearchOpen: true }
+ case NetworkActionType.SearchClose:
+ return { ...state, isSearchOpen: false }
+ case NetworkActionType.SearchSet:
+ return { ...state, search: action.payload }
+ case NetworkActionType.OrderReverse:
+ return { ...state, isReversed: true }
+ case NetworkActionType.OrderRegular:
+ return { ...state, isReversed: false }
+ default:
+ return state
+ }
+}
+
+function useNetwork() {
+ const [state, dispatch] = useReducer(networkReducer, {
+ isSearchOpen: false,
+ search: "",
+ isFilterOpen: false,
+ isReversed: false,
+ hiddenCommands: [],
+ })
+
+ // Load some values
+ useEffect(() => {
+ const isReversed = localStorage.getItem(NetworkStorageKey.ReversedOrder) === "reversed"
+ dispatch({
+ type: isReversed ? NetworkActionType.OrderReverse : NetworkActionType.OrderRegular,
+ })
+ }, [])
+
+ // Setup event handlers
+ const toggleSearch = () => {
+ dispatch({
+ type: state.isSearchOpen ? NetworkActionType.SearchClose : NetworkActionType.SearchOpen,
+ })
+ }
+
+ const openSearch = () => {
+ dispatch({
+ type: NetworkActionType.SearchOpen,
+ })
+ }
+
+ const closeSearch = () => {
+ dispatch({
+ type: NetworkActionType.SearchClose,
+ })
+ }
+
+ const setSearch = (search: string) => {
+ dispatch({
+ type: NetworkActionType.SearchSet,
+ payload: search,
+ })
+ }
+
+ const toggleReverse = () => {
+ const isReversed = !state.isReversed
+
+ localStorage.setItem(NetworkStorageKey.ReversedOrder, isReversed ? "reversed" : "regular")
+
+ dispatch({
+ type: isReversed ? NetworkActionType.OrderReverse : NetworkActionType.OrderRegular,
+ })
+ }
+
+ return {
+ isSearchOpen: state.isSearchOpen,
+ toggleSearch,
+ openSearch,
+ closeSearch,
+ search: state.search,
+ setSearch,
+ isReversed: state.isReversed,
+ toggleReverse,
+ }
+}
+
+export default useNetwork
diff --git a/lib/reactotron-core-ui/src/contexts/ReactNative/useStorybook.test.tsx b/lib/reactotron-core-ui/src/contexts/ReactNative/useStorybook.test.tsx
index 674d6c9d2..64b8d97ff 100644
--- a/lib/reactotron-core-ui/src/contexts/ReactNative/useStorybook.test.tsx
+++ b/lib/reactotron-core-ui/src/contexts/ReactNative/useStorybook.test.tsx
@@ -12,6 +12,7 @@ function buildContextValues({ addCommandListener = null } = {}) {
commands: [],
sendCommand: jest.fn(),
clearCommands: jest.fn(),
+ clearNetworkCommands: jest.fn(),
addCommandListener: addCommandListener || jest.fn(),
isDispatchModalOpen: false,
dispatchModalInitialAction: "",
diff --git a/lib/reactotron-core-ui/src/contexts/Reactotron/index.tsx b/lib/reactotron-core-ui/src/contexts/Reactotron/index.tsx
index b13291ba7..3baf8c5c2 100644
--- a/lib/reactotron-core-ui/src/contexts/Reactotron/index.tsx
+++ b/lib/reactotron-core-ui/src/contexts/Reactotron/index.tsx
@@ -8,6 +8,7 @@ interface Props {
commands: Command[]
sendCommand: (type: string, payload: any, clientId?: string) => void
clearCommands: () => void
+ clearNetworkCommands: () => void
addCommandListener: (callback: (command: Command) => void) => void
}
@@ -31,6 +32,7 @@ const ReactotronContext = React.createContext({
commands: [],
sendCommand: null,
clearCommands: null,
+ clearNetworkCommands: null,
addCommandListener: null,
isDispatchModalOpen: false,
dispatchModalInitialAction: "",
@@ -45,6 +47,7 @@ const Provider: FunctionComponent> = ({
commands,
sendCommand,
clearCommands,
+ clearNetworkCommands,
addCommandListener,
children,
}) => {
@@ -64,6 +67,7 @@ const Provider: FunctionComponent> = ({
commands,
sendCommand,
clearCommands,
+ clearNetworkCommands,
addCommandListener,
isDispatchModalOpen,
dispatchModalInitialAction,
diff --git a/lib/reactotron-core-ui/src/contexts/State/useSnapshots.test.tsx b/lib/reactotron-core-ui/src/contexts/State/useSnapshots.test.tsx
index ae06d5e4c..141b4e9af 100644
--- a/lib/reactotron-core-ui/src/contexts/State/useSnapshots.test.tsx
+++ b/lib/reactotron-core-ui/src/contexts/State/useSnapshots.test.tsx
@@ -12,6 +12,7 @@ function buildContextValues({ addCommandListener = null } = {}) {
commands: [],
sendCommand: jest.fn(),
clearCommands: jest.fn(),
+ clearNetworkCommands: jest.fn(),
addCommandListener: addCommandListener || jest.fn(),
isDispatchModalOpen: false,
dispatchModalInitialAction: "",
diff --git a/lib/reactotron-core-ui/src/contexts/State/useSubscriptions.test.tsx b/lib/reactotron-core-ui/src/contexts/State/useSubscriptions.test.tsx
index ca57c6c5f..4ed9ad29b 100644
--- a/lib/reactotron-core-ui/src/contexts/State/useSubscriptions.test.tsx
+++ b/lib/reactotron-core-ui/src/contexts/State/useSubscriptions.test.tsx
@@ -11,6 +11,7 @@ function buildContextValues({ addCommandListener = null } = {}) {
commands: [],
sendCommand: jest.fn(),
clearCommands: jest.fn(),
+ clearNetworkCommands: jest.fn(),
addCommandListener: addCommandListener || jest.fn(),
isDispatchModalOpen: false,
dispatchModalInitialAction: "",
diff --git a/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.test.ts b/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.test.ts
index a64696f61..86f6c6784 100644
--- a/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.test.ts
+++ b/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.test.ts
@@ -36,7 +36,7 @@ describe("contexts/Timline/useTimeline", () => {
it("should default to no hidden commands", () => {
const { result } = renderHook(() => useTimline())
- expect(result.current.hiddenCommands).toEqual([])
+ expect(result.current.hiddenCommands).toEqual([CommandType.ApiResponse])
})
it("should have saved hidden commands", () => {
@@ -123,7 +123,7 @@ describe("contexts/Timline/useTimeline", () => {
it("should set hidden commands", () => {
const { result } = renderHook(() => useTimline())
- expect(result.current.hiddenCommands).toEqual([])
+ expect(result.current.hiddenCommands).toEqual([CommandType.ApiResponse])
act(() => {
result.current.setHiddenCommands([CommandType.ClientIntro])
})
diff --git a/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.ts b/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.ts
index 87a75ec19..f0d576bf5 100644
--- a/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.ts
+++ b/lib/reactotron-core-ui/src/contexts/Timeline/useTimeline.ts
@@ -1,6 +1,6 @@
import { useReducer, useEffect } from "react"
-import type { CommandTypeKey } from "reactotron-core-contract"
+import { CommandType, type CommandTypeKey } from "reactotron-core-contract"
export enum StorageKey {
ReversedOrder = "ReactotronTimelineReversedOrder",
@@ -80,7 +80,7 @@ function useTimeline() {
// Load some values
useEffect(() => {
const isReversed = localStorage.getItem(StorageKey.ReversedOrder) === "reversed"
- const hiddenCommands = JSON.parse(localStorage.getItem(StorageKey.HiddenCommands) || "[]")
+ const hiddenCommands = JSON.parse(localStorage.getItem(StorageKey.HiddenCommands) || JSON.stringify([CommandType.ApiResponse]))
dispatch({
type: isReversed ? TimelineActionType.OrderReverse : TimelineActionType.OrderRegular,
diff --git a/lib/reactotron-core-ui/src/index.ts b/lib/reactotron-core-ui/src/index.ts
index c456c1646..78d3a26ff 100644
--- a/lib/reactotron-core-ui/src/index.ts
+++ b/lib/reactotron-core-ui/src/index.ts
@@ -20,6 +20,7 @@ import CustomCommandsContext, { CustomCommandsProvider } from "./contexts/Custom
import ReactNativeContext, { ReactNativeProvider } from "./contexts/ReactNative"
import StateContext, { StateProvider } from "./contexts/State"
import TimelineContext, { TimelineProvider } from "./contexts/Timeline"
+import NetworkContext, { NetworkProvider } from "./contexts/Network"
// Modals
import DispatchActionModal from "./modals/DispatchActionModal"
@@ -65,6 +66,8 @@ export {
StateProvider,
TimelineContext,
TimelineProvider,
+ NetworkContext,
+ NetworkProvider,
}
export type { CustomCommand } from "./contexts/CustomCommands/useCustomCommands"
diff --git a/lib/reactotron-core-ui/src/modals/TimelineFilterModal/index.tsx b/lib/reactotron-core-ui/src/modals/TimelineFilterModal/index.tsx
index 8e5074af5..a94841f95 100644
--- a/lib/reactotron-core-ui/src/modals/TimelineFilterModal/index.tsx
+++ b/lib/reactotron-core-ui/src/modals/TimelineFilterModal/index.tsx
@@ -20,7 +20,6 @@ const GROUPS = [
items: [
{ value: CommandType.ClientIntro, text: "Connection" },
{ value: CommandType.Benchmark, text: "Benchmark" },
- { value: CommandType.ApiResponse, text: "API" },
],
},
{