Skip to content

Commit fee814b

Browse files
feat(overmind): new action and pipe API to handle simple and complex use cases
BREAKING CHANGE: the action API is completely changed
1 parent 8b7a92d commit fee814b

File tree

34 files changed

+1637
-1609
lines changed

34 files changed

+1637
-1609
lines changed

package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@
6262
"concurrently": "3.6.1",
6363
"cz-customizable": "^5.2.0",
6464
"cz-customizable-ghooks": "^1.5.0",
65-
"eslint": "^5.0.1",
66-
"eslint-config-prettier": "^2.9.0",
67-
"eslint-config-standard": "^11.0.0",
68-
"eslint-config-standard-jsx": "^5.0.0",
69-
"eslint-plugin-import": "^2.13.0",
70-
"eslint-plugin-node": "^6.0.1",
71-
"eslint-plugin-prettier": "^2.6.1",
72-
"eslint-plugin-promise": "^3.8.0",
73-
"eslint-plugin-react": "^7.10.0",
74-
"eslint-plugin-standard": "^3.1.0",
75-
"eslint-plugin-typescript": "^0.12.0",
65+
"eslint": "^5.8.0",
66+
"eslint-config-prettier": "^3.1.0",
67+
"eslint-config-standard": "^12.0.0",
68+
"eslint-config-standard-jsx": "^6.0.2",
69+
"eslint-plugin-import": "^2.14.0",
70+
"eslint-plugin-node": "^8.0.0",
71+
"eslint-plugin-prettier": "^3.0.0",
72+
"eslint-plugin-promise": "^4.0.1",
73+
"eslint-plugin-react": "^7.11.1",
74+
"eslint-plugin-standard": "^4.0.0",
75+
"eslint-plugin-typescript": "^0.13.0",
7676
"html-webpack-plugin": "3.2.0",
7777
"husky": "^0.14.3",
7878
"jest": "23.5.0",
Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import * as React from 'react'
22
import { Action } from 'overmind'
3-
import * as mutations from './mutations'
4-
import * as operations from './operations'
5-
import { Todo } from './state'
63

74
type ChangeEvent = React.ChangeEvent<HTMLInputElement>
85

9-
export const changeNewTodoTitle: Action<ChangeEvent, string> = (action) =>
10-
action.map(operations.getEventValue).mutate(mutations.setNewTodoTitle)
6+
let nextTodoId = 0
117

12-
export const addTodo: Action<React.FormEvent> = (action) =>
13-
action
14-
.run(operations.preventEventDefault)
15-
.mutate(mutations.addTodo)
16-
.mutate(mutations.clearNewTodoTitle)
8+
export const changeNewTodoTitle: Action<ChangeEvent> = ({
9+
value: event,
10+
state,
11+
}) => {
12+
state.newTodoTitle = event.target.value
13+
}
1714

18-
export const toggleCompleted: Action<Todo> = ({ mutate }) =>
19-
mutate(mutations.toggleCompleted)
15+
export const addTodo: Action = ({ state }) => {
16+
state.todos.unshift({
17+
id: String(nextTodoId++),
18+
title: state.newTodoTitle,
19+
completed: false,
20+
})
21+
state.newTodoTitle = ''
22+
}
23+
24+
export const toggleCompleted: Action<string> = ({ value: todoId, state }) => {
25+
const todo = state.todos.find((todo) => todo.id === todoId)
26+
todo.completed = !todo.completed
27+
}

packages/demos/react-typescript-todomvc/src/app/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import * as effects from './effects'
66
import * as state from './state'
77

88
const config = {
9-
effects,
109
actions,
1110
state,
1211
}
1312

1413
declare module 'overmind' {
15-
interface App extends TApp<typeof config> {}
14+
interface IApp extends TApp<typeof config> {}
1615
}
1716

1817
export const app = new Overmind(config)

packages/demos/react-typescript-todomvc/src/app/mutations.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/demos/react-typescript-todomvc/src/app/operations.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

packages/demos/react-typescript-todomvc/src/components/AddTodo/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { Wrapper, Input, Button, Form } from './elements'
44

55
const AddTodo: React.SFC<Connect> = ({ app }) => (
66
<Wrapper>
7-
<Form onSubmit={app.actions.addTodo}>
7+
<Form
8+
onSubmit={(event) => {
9+
event.preventDefault()
10+
app.actions.addTodo()
11+
}}
12+
>
813
<Input
914
placeholder="I need to..."
1015
value={app.state.newTodoTitle}

packages/demos/react-typescript-todomvc/src/components/Todo/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type Props = {
88
} & Connect
99

1010
const Todo: React.SFC<Props> = ({ todo, app }) => (
11-
<Item onClick={() => app.actions.toggleCompleted(todo)}>
11+
<Item onClick={() => app.actions.toggleCompleted(todo.id)}>
1212
<Completed completed={todo.completed}></Completed> {todo.title}
1313
</Item>
1414
)

packages/node_modules/overmind-devtools/src/BackendConnector.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { ipcRenderer } from 'electron'
22

3-
import { AppMessage } from './app/types'
3+
import { AppMessage, AppMessageType } from './app/types'
44

55
type Message = {
66
appName: string
7-
messages: AppMessage[]
7+
messages: AppMessage<any>[]
88
}
99

1010
type MessageCallback = (message: Message) => void
@@ -15,20 +15,20 @@ export class BackendConnector {
1515
constructor() {
1616
ipcRenderer.on('port:exists', this.onPortExists)
1717
}
18-
connect(port: string, messageCallback: MessageCallback) {
19-
this.messageCallback = messageCallback
18+
connect(port: string) {
2019
ipcRenderer.send('connect', port)
21-
ipcRenderer.on('message', this.onMessage)
2220
}
23-
onMessage = (_, message) => {
24-
this.messageCallback(message)
21+
onMessage = (onMessage: MessageCallback) => {
22+
this.messageCallback = onMessage
23+
ipcRenderer.on('message', (_, message) => onMessage(message))
2524
}
2625
onPortExists = (_) => {
2726
this.messageCallback({
2827
appName: null,
2928
messages: [
3029
{
31-
type: 'PORT_EXISTS',
30+
appName: '',
31+
type: AppMessageType.PORT_EXISTS,
3232
data: null,
3333
},
3434
],
Lines changed: 134 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,152 @@
1-
import { Action } from 'overmind'
2-
import * as mutations from './mutations'
3-
import * as operations from './operations'
4-
import { Message, Tab } from './types'
1+
import { Action, pipe, OnInitialize, Pipe } from 'overmind'
2+
import {
3+
Message,
4+
Tab,
5+
AppMessageType,
6+
ExecutionType,
7+
ActionsListItemType,
8+
} from './types'
9+
import {
10+
forkEachMessage,
11+
ensureCurrentApp,
12+
addMessagesFromClient,
13+
createNewApp,
14+
addFlushAndRunMutations,
15+
updateComponent,
16+
addComponent,
17+
removeComponent,
18+
updateDerived,
19+
updateFlushWithDerived,
20+
addAction,
21+
addOperator,
22+
updateFlushWithReactionUpdate,
23+
updateOperator,
24+
updateAction,
25+
addMutations,
26+
addEffect,
27+
setPortExists,
28+
isPortExistsMessage,
29+
} from './operators'
530

6-
const handleClientMessages: Action<Message> = (action) =>
7-
action
8-
.mutate(mutations.ensureCurrentApp)
9-
.mutate(mutations.performMutationsByMessageType)
10-
.mutate(mutations.addMessagesFromClient)
31+
export const onInitialize: OnInitialize = async ({
32+
value: actions,
33+
state,
34+
storage,
35+
connector,
36+
}) => {
37+
const port = await storage.get<string>('currentPort')
38+
if (port) {
39+
state.port = port
40+
}
41+
connector.onMessage(actions.onMessage)
42+
connector.connect(state.port)
43+
}
1144

12-
const setPortExists: Action<Message> = (action) =>
13-
action.mutate(mutations.setPortExists)
45+
const handleClientMessage: Pipe<Message> = pipe(
46+
ensureCurrentApp,
47+
forkEachMessage({
48+
[AppMessageType.PORT_EXISTS]: setPortExists,
49+
[ExecutionType.INIT]: createNewApp,
50+
[ExecutionType.FLUSH]: addFlushAndRunMutations,
51+
[ExecutionType.DERIVED]: updateDerived,
52+
[ExecutionType.MUTATIONS]: addMutations,
53+
[ExecutionType.EFFECT]: addEffect,
54+
[ExecutionType.COMPONENT_ADD]: addComponent,
55+
[ExecutionType.COMPONENT_UPDATE]: updateComponent,
56+
[ExecutionType.COMPONENT_REMOVED]: removeComponent,
57+
[ExecutionType.DERIVED_DIRTY]: updateFlushWithDerived,
58+
[ExecutionType.REACTION_UPDATE]: updateFlushWithReactionUpdate,
59+
[ExecutionType.ACTION_START]: addAction,
60+
[ExecutionType.OPERATOR_START]: addOperator,
61+
[ExecutionType.OPERATOR_END]: updateOperator,
62+
[ExecutionType.ACTION_END]: updateAction,
63+
}),
64+
addMessagesFromClient
65+
)
1466

15-
const onMessage: Action<Message> = (action) =>
16-
action.when(operations.isPortExistsMessage, {
67+
export const onMessage: Pipe<Message> = pipe(
68+
isPortExistsMessage({
1769
true: setPortExists,
18-
false: handleClientMessages,
70+
false: handleClientMessage,
1971
})
72+
)
2073

21-
export const loadDevtools: Action = (action) =>
22-
action
23-
// .run(({ storage }) => storage.clear())
24-
.map(operations.getCurrentPortFromStorage)
25-
.mutate(mutations.setPort)
26-
.run(operations.connectCurrentPort(onMessage(action as any)))
74+
export const setError: Action<string> = ({ value: error, state }) =>
75+
(state.error = error)
2776

28-
export const setError: Action<string> = ({ mutate }) =>
29-
mutate(mutations.setError)
77+
export const changeNewPortValue: Action<string> = ({ value: port, state }) =>
78+
(state.newPortValue = String(Number(port)))
3079

31-
export const changeNewPortValue: Action<string> = (action) =>
32-
action.map(operations.toNumber).mutate(mutations.setNewPortValue)
80+
export const addConnection: Action = ({ state, connector }) => {
81+
state.error = null
82+
state.isConnecting = true
83+
state.port = state.newPortValue
84+
state.newPortValue = ''
85+
connector.connect(state.port)
86+
}
3387

34-
export const addConnection: Action = (action) =>
35-
action
36-
.mutate(mutations.setConnecting)
37-
.map(operations.getNewPortFromState)
38-
.mutate(mutations.setPort)
39-
.mutate(mutations.resetNewPortValue)
40-
.run(operations.connectCurrentPort(onMessage))
88+
export const changeTab: Action<Tab> = ({ value: tab, state }) =>
89+
(state.currentTab = tab)
4190

42-
export const changeTab: Action<Tab> = ({ mutate }) =>
43-
mutate(mutations.changeTab)
91+
export const toggleExpandState: Action<string[]> = ({ value: path, state }) => {
92+
const pathString = path.join('.')
4493

45-
export const toggleExpandState: Action<string[]> = ({ mutate }) =>
46-
mutate(mutations.toggleExpandStatePath)
94+
if (state.expandedStatePaths.indexOf(pathString) >= 0) {
95+
state.expandedStatePaths.splice(
96+
state.expandedStatePaths.indexOf(pathString),
97+
1
98+
)
99+
} else {
100+
state.expandedStatePaths = state.expandedStatePaths.concat(pathString)
101+
}
102+
}
47103

48-
export const selectAction: Action<string> = ({ mutate }) =>
49-
mutate(mutations.toggleActionItemCollapse).mutate(mutations.selectAction)
104+
export const selectAction: Action<string> = ({ value: actionId, state }) => {
105+
for (let index in state.currentApp.actionsList) {
106+
const item = state.currentApp.actionsList[index]
107+
if (
108+
item.type === ActionsListItemType.GROUP &&
109+
item.id === actionId &&
110+
state.currentApp.currentActionId === actionId
111+
) {
112+
item.isCollapsed = !item.isCollapsed
113+
break
114+
}
115+
}
116+
state.currentApp.currentActionId = actionId
117+
}
50118

51-
type Collapsed = {
52-
isCollapsed: boolean
119+
export const toggleCollapsedFlush: Action<number> = ({ value: id, state }) => {
120+
if (!state.expandAllActionDetails) {
121+
state.currentApp.flushes[id].isCollapsed = !state.currentApp.flushes[id]
122+
.isCollapsed
123+
}
53124
}
54125

55-
export const toggleCollapsedActionItem: Action<Collapsed> = (action) =>
56-
action
57-
.filter(operations.isNotExpandingAllActions)
58-
.mutate(mutations.toggleCollapsed)
126+
export const toggleCollapsedOperator: Action<number> = ({
127+
value: operatorIndex,
128+
state,
129+
}) => {
130+
if (!state.expandAllActionDetails) {
131+
const operator = state.currentAction.operators[operatorIndex]
132+
operator.isCollapsed = !operator.isCollapsed
133+
}
134+
}
59135

60-
export const toggleGroupedComponent: Action<string> = (action) =>
61-
action.mutate(mutations.toggleGroupedComponent)
136+
export const toggleGroupedComponent: Action<string> = ({
137+
value: name,
138+
state,
139+
}) => {
140+
const index = state.expandedComponents.indexOf(name)
141+
if (index === -1) {
142+
state.expandedComponents.push(name)
143+
} else {
144+
state.expandedComponents.splice(index, 1)
145+
}
146+
}
62147

63-
export const selectApp: Action<string> = (action) =>
64-
action.mutate(mutations.selectApp)
148+
export const selectApp: Action<string> = ({ value: appName, state }) =>
149+
(state.currentAppName = appName)
65150

66-
export const toggleExpandAllActions: Action = ({ mutate }) =>
67-
mutate(mutations.toggleExpandAllActions)
151+
export const toggleExpandAllActions: Action = ({ state }) =>
152+
(state.expandAllActionDetails = !state.expandAllActionDetails)

packages/node_modules/overmind-devtools/src/app/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import * as effects from './effects'
66
import * as state from './state'
77

88
const config = {
9+
onInitialize: actions.onInitialize,
910
effects,
1011
actions,
1112
state,
1213
}
1314

1415
declare module 'overmind' {
15-
interface App extends TApp<typeof config> {}
16+
interface IApp extends TApp<typeof config> {}
1617
}
1718

1819
const app = new Overmind(config, {

0 commit comments

Comments
 (0)