Skip to content

Commit 14d2cc6

Browse files
feat(website): use query to manage theme and typescript
1 parent 9a06b03 commit 14d2cc6

File tree

10 files changed

+175
-95
lines changed

10 files changed

+175
-95
lines changed

package-lock.json

Lines changed: 27 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,27 @@
2929
"color-hash": "1.0.3",
3030
"electron": "2.0.8",
3131
"electron-json-storage": "4.1.0",
32+
"emotion": "9.2.12",
3233
"express": "4.16.3",
3334
"install": "0.12.1",
3435
"is-plain-obj": "1.1.0",
3536
"marksy": "6.1.0",
3637
"npm": "6.3.0",
3738
"page": "1.8.6",
39+
"petit-dom": "0.2.2",
40+
"preact": "8.3.1",
41+
"preact-compat": "3.18.4",
3842
"prismjs": "1.15.0",
43+
"query-string": "^6.2.0",
44+
"react": "16.7.0-alpha.2",
45+
"react-dom": "16.7.0-alpha.2",
3946
"react-hot-loader": "^4.3.4",
4047
"styled-components": "3.3.3",
4148
"tslib": "1.9.3",
4249
"vue": "2.5.16",
4350
"vue-hot-reload-api": "2.3.0",
4451
"vue-styled-components": "1.3.0",
45-
"ws": "5.2.1",
46-
"preact": "8.3.1",
47-
"preact-compat": "3.18.4",
48-
"react": "16.7.0-alpha.2",
49-
"react-dom": "16.7.0-alpha.2",
50-
"emotion": "9.2.12",
51-
"petit-dom": "0.2.2"
52+
"ws": "5.2.1"
5253
},
5354
"devDependencies": {
5455
"@babel/core": "7.2.2",

packages/overmind-website/examples/guide/typescript/operatorinfer_solution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Operator, pipe, action } from 'overmind'
1717
import { doSomeStateChange } from './operators'
1818
1919
export const setInput: Operator<string> = pipe(
20-
doSomeStateChange<string>(),
20+
doSomeStateChange(),
2121
action(({ value: input, state }) => {
2222
state.input = input
2323
})

packages/overmind-website/guides/intermediate/03_typescript.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ You might create an operator that does not care about its input. For example:
9090
h(Example, { name: "guide/typescript/operatorinfer" })
9191
```
9292

93-
Composing **doSomeStateChange** into the **pipe** gives an error, cause this operator expects a **void** type. There is no other way to make this work than inlining the **doSomeStateChange** operator into the pipe in question, which infers the correct input.
93+
Composing **doSomeStateChange** into the **pipe** gives an error, cause this operator expects a **void** type.
9494

95-
You could use the same trick here though:
95+
To fix this you can do the same trick as above, though you do not need to pass in the type, it will be inferred:
9696

9797
```marksy
9898
h(Example, { name: "guide/typescript/operatorinfer_solution" })

packages/overmind-website/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"npm": "^6.3.0",
4343
"overmind-react": "next",
4444
"page": "^1.8.6",
45-
"prismjs": "^1.15.0"
45+
"prismjs": "^1.15.0",
46+
"query-string": "^6.2.0"
4647
}
4748
}
Lines changed: 52 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,80 @@
11
import { Action, pipe, action, filter, debounce, Operator } from 'overmind'
22
import { Page, RouteContext, GuideParams, VideoParams } from './types'
3+
import { ensureViewAndTypescript } from './operators'
34

4-
export const openHome: Action<RouteContext> = async ({ state, effects }) => {
5-
state.page = Page.HOME
6-
if (!state.demos.length) {
7-
state.demos = await effects.request('/backend/demos')
8-
}
9-
}
5+
export const openHome: Operator<RouteContext> = pipe(
6+
ensureViewAndTypescript(),
7+
action(async ({ state, effects }) => {
8+
state.page = Page.HOME
9+
if (!state.demos.length) {
10+
state.demos = await effects.request('/backend/demos')
11+
}
12+
})
13+
)
1014

11-
export const openGuides: Action<RouteContext> = async ({ state, effects }) => {
12-
state.page = Page.GUIDES
13-
if (!state.guides.length) {
14-
state.isLoadingGuides = true
15-
state.guides = await effects.request('/backend/guides')
16-
state.isLoadingGuides = false
17-
}
18-
}
15+
export const openGuides: Operator<RouteContext> = pipe(
16+
ensureViewAndTypescript(),
17+
action(async ({ state, effects }) => {
18+
state.page = Page.GUIDES
19+
if (!state.guides.length) {
20+
state.isLoadingGuides = true
21+
state.guides = await effects.request('/backend/guides')
22+
state.isLoadingGuides = false
23+
}
24+
})
25+
)
1926

20-
export const openVideos: Operator<RouteContext<VideoParams>> = action(
21-
async ({ state, effects }) => {
27+
export const openVideos: Operator<RouteContext<VideoParams>> = pipe(
28+
ensureViewAndTypescript(),
29+
action(async ({ state, effects }) => {
2230
state.page = Page.VIDEOS
2331
state.currentVideo = null
2432
if (!state.videos.length) {
2533
state.isLoadingVideos = true
2634
state.videos = await effects.request('/backend/videos')
2735
state.isLoadingVideos = false
2836
}
29-
}
37+
})
3038
)
3139

3240
export const openVideo: Operator<RouteContext<VideoParams>> = pipe(
41+
ensureViewAndTypescript(),
3342
openVideos,
3443
action(({ value: routeContext, state }) => {
3544
state.currentVideo = routeContext.params.title
3645
})
3746
)
3847

39-
export const openGuide: Action<RouteContext<GuideParams>> = async ({
40-
value: routeContext,
41-
state,
42-
}) => {
43-
state.page = Page.GUIDE
44-
state.currentGuide = routeContext.params
45-
}
48+
export const openGuide: Operator<RouteContext<GuideParams>> = pipe(
49+
ensureViewAndTypescript(),
50+
action(async ({ value: routeContext, state }) => {
51+
state.page = Page.GUIDE
52+
state.currentGuide = routeContext.params
53+
})
54+
)
4655

47-
export const openApi: Action<RouteContext<VideoParams>> = async ({
48-
value: routeContext,
49-
state,
50-
effects,
51-
}) => {
52-
state.page = Page.API
53-
state.currentApi = routeContext.params.title
54-
if (!state.apis.length) {
55-
state.isLoadingApis = true
56-
state.apis = await effects.request('/backend/apis')
57-
state.isLoadingApis = false
58-
}
59-
}
56+
export const openApi: Operator<RouteContext<VideoParams>> = pipe(
57+
ensureViewAndTypescript(),
58+
action(async ({ value: routeContext, state, effects }) => {
59+
state.page = Page.API
60+
state.currentApi = routeContext.params.title
61+
if (!state.apis.length) {
62+
state.isLoadingApis = true
63+
state.apis = await effects.request('/backend/apis')
64+
state.isLoadingApis = false
65+
}
66+
})
67+
)
6068

61-
export const selectTheme: Action<string> = ({
62-
value: selection,
63-
state,
64-
effects,
65-
}) => {
69+
export const selectTheme: Action<string> = ({ value: selection, effects }) => {
6670
const selectionArray = selection.split('_')
67-
const theme = selectionArray[0]
68-
const typescript = Boolean(selectionArray[1])
69-
70-
state.theme = theme
71-
state.typescript = typescript
71+
const view = selectionArray[0]
72+
const typescript = String(Boolean(selectionArray[1]))
7273

73-
effects.css.changePrimary(theme)
74-
effects.storage.set('theme', theme)
75-
effects.storage.set('typescript', typescript)
76-
}
77-
78-
export const toggleTypescript: Action = ({ state, effects }) => {
79-
state.typescript = !state.typescript
80-
effects.storage.set('typescript', state.typescript)
74+
effects.router.redirectWithQuery(effects.router.getPath(), {
75+
view,
76+
typescript,
77+
})
8178
}
8279

8380
export const closeSearch: Action = ({ state }) => {
@@ -105,6 +102,4 @@ export const changeQuery: Operator<React.ChangeEvent<HTMLInputElement>> = pipe(
105102

106103
export const viewHelpGotIt: Action = ({ state, effects }) => {
107104
state.showViewHelp = false
108-
effects.storage.set('typescript', false)
109-
effects.storage.set('theme', 'react')
110105
}

packages/overmind-website/src/overmind/effects.ts

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
11
import * as page from 'page'
2-
import { RouteContext } from './types'
3-
4-
export const router = {
5-
route(url: string, action: (payload: RouteContext) => void) {
6-
page(url, ({ params, path }) => action({ params, path }))
7-
},
8-
redirect: page.redirect,
9-
start: () => page.start({}),
10-
}
11-
12-
export const request = (url: string) =>
13-
fetch(url).then((response) => response.json())
2+
import { RouteContext, Query } from './types'
3+
import * as queryString from 'query-string'
144

155
export const storage = {
166
get(key: string) {
@@ -27,6 +17,62 @@ export const storage = {
2717
},
2818
}
2919

20+
export const router = (() => {
21+
let currentPath
22+
let currentQuery: Query = {
23+
view: storage.get('theme'),
24+
typescript: storage.get('typescript'),
25+
}
26+
27+
return {
28+
getPath() {
29+
return currentPath
30+
},
31+
route(url: string, action: (payload: RouteContext) => void) {
32+
page(url, ({ params, pathname, querystring }) => {
33+
// We want to preserve the query
34+
if (!querystring) {
35+
page.redirect(
36+
`${pathname}?view=${currentQuery.view}&typescript=${
37+
currentQuery.typescript
38+
}${location.hash}`
39+
)
40+
return
41+
}
42+
43+
const query = queryString.parse(querystring)
44+
45+
currentPath = pathname.split('?')[0]
46+
currentQuery = {
47+
view: query.view,
48+
typescript: query.typescript,
49+
}
50+
51+
action({
52+
params,
53+
path: pathname,
54+
query,
55+
})
56+
})
57+
},
58+
redirect: page.redirect,
59+
redirectWithQuery: (path: string, query: Query) => {
60+
currentPath = path
61+
currentQuery = query
62+
63+
page.redirect(
64+
`${path}?view=${query.view}&typescript=${query.typescript}${
65+
location.hash
66+
}`
67+
)
68+
},
69+
start: () => page.start({}),
70+
}
71+
})()
72+
73+
export const request = (url: string) =>
74+
fetch(url).then((response) => response.json())
75+
3076
export const css = {
3177
changePrimary(theme: string) {
3278
const colors = {

packages/overmind-website/src/overmind/onInitialize.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import { OnInitialize } from 'overmind'
22

33
const onInitialize: OnInitialize = ({ value: app, effects, state }) => {
4-
state.typescript = effects.storage.get('typescript') || false
5-
state.theme = effects.storage.get('theme') || 'react'
64
state.showViewHelp = !effects.storage.get('theme')
7-
effects.css.changePrimary(state.theme)
85

96
effects.router.route('/', app.actions.openHome)
107
effects.router.route('/guides', app.actions.openGuides)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Operator, action } from 'overmind'
2+
import { RouteContext } from './types'
3+
4+
export const ensureViewAndTypescript: <T extends object>() => Operator<
5+
RouteContext<T>
6+
> = () =>
7+
action(({ value: routeContext, state, effects }) => {
8+
if (
9+
state.typescript !== Boolean(routeContext.query.typescript) ||
10+
state.theme !== routeContext.query.view
11+
) {
12+
state.typescript = routeContext.query.typescript === 'true'
13+
state.theme = routeContext.query.view
14+
15+
effects.storage.set('theme', state.theme)
16+
effects.storage.set('typescript', state.typescript)
17+
effects.css.changePrimary(state.theme)
18+
}
19+
})

0 commit comments

Comments
 (0)