Skip to content

Commit a936875

Browse files
authored
feat Custom command dashboard
1 parent 2a8e2ed commit a936875

File tree

13 files changed

+400
-42
lines changed

13 files changed

+400
-42
lines changed

docs/custom-commands.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Setting up custom commands
2+
3+
Reactotron supports registering custom commands that can do anything you want when they are executed. When there are registered commands you can find them in the "Custom Commands" tab in reactotron.
4+
5+
## Registering a command
6+
7+
```js
8+
// Way 1
9+
Reactotron.onCustomCommand({
10+
command: "test2",
11+
handler: () => console.log("This is an example 2"),
12+
13+
// Optional settings
14+
title: "A thing", // This shows on the button
15+
description: "The desc", // This shows below the button
16+
})
17+
18+
// Way 2
19+
Reactotron.onCustomCommand("test", () => console.log("This is an example"))
20+
```
21+
22+
## Unregistering a command
23+
24+
```js
25+
const selfRemoving = Reactotron.onCustomCommand({ // Save the result
26+
command: "remove",
27+
handler: () => {
28+
selfRemoving() // Calling it unregisters the command
29+
},
30+
})
31+
```

docs/tips.md

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -124,31 +124,3 @@ the state like this:
124124

125125
This will `nav` mutable. Note this is what you want to do when using
126126
[react-navigation's default reducer](https://github.com/react-community/react-navigation/blob/master/docs/guides/Redux-Integration.md).
127-
128-
129-
## Custom Commands
130-
131-
Need a custom Reactotron plugin but don't have time to write one?
132-
133-
In the Reactotron App, you can press `command+.` (mac) or `ctrl+.` (windows/linux) to send a custom command.
134-
135-
These commands can be useful for things like:
136-
137-
* clearing async storage
138-
* triggering an api call
139-
* calling functions on hard-to-reach places
140-
* sending info back to reactotron app
141-
142-
Writing your own Reactotron middleware makes this happen. Check out this example:
143-
144-
```js
145-
import Reactotron from 'reactotron-react-native'
146-
147-
Reactotron.use(tron => ({
148-
onCommand({ type, payload }) {
149-
if (type === 'custom') {
150-
tron.display({ name: 'ECHO', preview: payload })
151-
}
152-
},
153-
}))
154-
```

examples/ReactotronTester/app/config/ReactotronConfig.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,23 @@ Reactotron.use(reduxPlugin())
1717
Reactotron.use(sagaPlugin({}))
1818
Reactotron.use(mst())
1919

20+
Reactotron.onCustomCommand("test", () => console.log("This is an example"))
21+
22+
Reactotron.onCustomCommand({
23+
command: "test2",
24+
handler: () => console.log("This is an example 2"),
25+
26+
// Optional settings
27+
title: "A thing",
28+
description: "The desc",
29+
})
30+
31+
const selfRemoving = Reactotron.onCustomCommand({
32+
command: "remove",
33+
handler: () => {
34+
selfRemoving()
35+
},
36+
})
37+
2038
Reactotron.connect()
2139
Reactotron.clear()

examples/ReactotronTester/copy-internal-deps.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ rm -f node_modules/reactotron-apisauce
2323
cd ../../../reactotron-core-client
2424
yarn run build:dev
2525
cd ../reactotron/examples/ReactotronTester
26-
mkdir -p ./node_modules/reactotron-react-native/node_modules/reactotron-core-client
27-
cp ../../../reactotron-core-client/dist/index.js ./node_modules/reactotron-react-native/node_modules/reactotron-core-client/index.js
26+
mkdir -p ./node_modules/reactotron-core-client
27+
cp ../../../reactotron-core-client/dist/index.js ./node_modules/reactotron-core-client/index.js
2828

2929
# reactotron-react-native
3030
cd ../../../reactotron-react-native

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
"semantic-release": "semantic-release"
4040
},
4141
"dependencies": {
42+
"@types/react": "^16.8.15",
43+
"@types/react-dom": "^16.8.4",
4244
"color": "^3.0.0",
4345
"core-js": "2",
4446
"css-modules-require-hook": "^4.2.2",

readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Reactotron is a macOS, Windows, and Linux app for inspecting your [React JS](https://facebook.github.io/react/) and [React Native](https://facebook.github.io/react-native/) apps.
1010

1111
*Watch [Darin Wilson's](https://github.com/darinwilson) talk at [Chain React](https://infinite.red/ChainReactConf): [Chain React 2018: Debugging and Beyond with Reactotron](https://www.youtube.com/watch?v=UiPo9A9k7xc)!*
12-
12+
1313
Use it to:
1414

1515
* view your application state
@@ -36,6 +36,7 @@ You plug it into your app as a dev dependency so it adds nothing to your product
3636
* [Installing](./docs/installing.md)
3737
* Quick start for [React JS](./docs/quick-start-react-js.md)
3838
* Quick start for [React Native](./docs/quick-start-react-native.md)
39+
* [Custom Commands](./docs/custom-commands.md)
3940
* [Tracking errors globally](./docs/plugin-track-global-errors.md)
4041
* [Open in editor](./docs/plugin-open-in-editor.md)
4142
* [Image Overlays](./docs/plugin-overlay.md)
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import * as React from "react"
2+
import { inject, observer } from "mobx-react"
3+
import CustomCommandsListHeader from "./CustomCommandsListHeader"
4+
import Colors from "../Theme/Colors"
5+
import AppStyles from "../Theme/AppStyles"
6+
7+
const Styles = {
8+
container: {
9+
...AppStyles.Layout.vbox,
10+
margin: 0,
11+
flex: 1,
12+
},
13+
14+
buttonsContainer: {
15+
paddingTop: "20px",
16+
paddingLeft: "40px",
17+
overflowY: "scroll",
18+
overflowX: "hidden",
19+
display: "flex",
20+
flexDirection: "row",
21+
flexWrap: "wrap",
22+
},
23+
buttonContainer: {
24+
alignItems: "center",
25+
justifyContent: "center",
26+
width: "calc(50% - 8px)",
27+
padding: "4px",
28+
},
29+
commandTitle: {
30+
fontSize: "24px",
31+
color: "white",
32+
},
33+
commandDescription: {
34+
marginTop: "12px",
35+
color: "#929292",
36+
},
37+
button: {
38+
backgroundColor: Colors.backgroundLighter,
39+
borderRadius: "4px",
40+
minHeight: "50px",
41+
alignItems: "center",
42+
justifyContent: "center",
43+
display: "flex",
44+
width: "200px",
45+
marginTop: "18px",
46+
marginBottom: "24px",
47+
cursor: "pointer",
48+
color: "white",
49+
transition: "background-color 0.25s ease-in-out",
50+
},
51+
text: {
52+
color: Colors.foreground,
53+
textAlign: "center",
54+
},
55+
}
56+
57+
interface Props {
58+
session: any
59+
}
60+
61+
interface State {
62+
search: string
63+
}
64+
65+
@inject("session")
66+
@observer
67+
export default class CustomCommandsList extends React.Component<Props, State> {
68+
state = {
69+
search: "",
70+
}
71+
72+
handleSearchChange = e => {
73+
this.setState({
74+
search: e.target.value,
75+
})
76+
}
77+
78+
filterSearch = item => {
79+
const { search } = this.state
80+
81+
if (search === "") return true
82+
83+
const lowerSearch = search.toLowerCase()
84+
85+
return (
86+
item.command.toLowerCase().indexOf(lowerSearch) > -1 ||
87+
(!!item.title && item.title.toLowerCase().indexOf(lowerSearch) > -1) ||
88+
(!!item.description && item.description.toLowerCase().indexOf(lowerSearch) > -1)
89+
)
90+
}
91+
92+
executeCommand(command) {
93+
this.props.session.ui.sendCustomMessage(command)
94+
}
95+
96+
renderButton(item) {
97+
return (
98+
<div
99+
key={item.command}
100+
style={Styles.buttonContainer}
101+
onClick={() => this.executeCommand(item.command)}
102+
>
103+
<div style={Styles.commandTitle}>{item.title || item.command}</div>
104+
<div style={Styles.commandDescription}>
105+
{item.description ? item.description : "No Description Provided"}
106+
</div>
107+
108+
<div className="button custom-commands-list-button" style={Styles.button}>
109+
Send Command
110+
</div>
111+
</div>
112+
)
113+
}
114+
115+
render() {
116+
const { customCommands } = this.props.session
117+
const { search } = this.state
118+
119+
return (
120+
<div style={Styles.container as any}>
121+
<CustomCommandsListHeader search={search} onSearchChange={this.handleSearchChange} />
122+
<div style={Styles.buttonsContainer as any}>
123+
{customCommands.filter(this.filterSearch).map(cc => this.renderButton(cc))}
124+
</div>
125+
</div>
126+
)
127+
}
128+
}

0 commit comments

Comments
 (0)