Add theme toggle

This commit is contained in:
Thomas Nordquist
2019-04-03 01:55:57 +02:00
parent 84a92ad522
commit 6f86a8d471
6 changed files with 84 additions and 29 deletions

View File

@@ -1,6 +1,13 @@
import { ActionTypes } from '../reducers' import { ActionTypes, AppState, CustomAction } from '../reducers'
import { Dispatch } from 'redux';
export const showError = (error?: string) => ({ export const showError = (error?: string) => ({
error, error,
type: ActionTypes.showError, type: ActionTypes.showError,
}) })
export const toggleTheme = () => (dispatch: Dispatch<CustomAction>, getState: () => AppState) => {
dispatch({
type: getState().globalState.theme === 'light' ? ActionTypes.setDarkTheme : ActionTypes.setLightTheme,
})
}

View File

@@ -106,12 +106,13 @@ class BrokerStatistics extends React.Component<Props, {}> {
public renderStat(tree: q.Tree<TopicViewModel>, stat: Stats) { public renderStat(tree: q.Tree<TopicViewModel>, stat: Stats) {
const node = tree.findNode(stat.topic) const node = tree.findNode(stat.topic)
if (!node) { if (!node || !node.message) {
return null return null
} }
let value = (node.message && node.message.value) ? parseFloat(Base64Message.toUnicodeString(node.message.value)) : NaN const str = node.message.value ? Base64Message.toUnicodeString(node.message.value) : ''
value = !isNaN(value) ? abbreviate(value) : value let value = (node.message && node.message.value) ? parseFloat(str) : NaN
value = !isNaN(value) ? abbreviate(value) : str
return ( return (
<div key={stat.title}> <div key={stat.title}>

View File

@@ -4,7 +4,7 @@ import ChevronRight from '@material-ui/icons/ChevronRight'
import { AppState } from '../reducers' import { AppState } from '../reducers'
import { bindActionCreators } from 'redux' import { bindActionCreators } from 'redux'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { settingsActions } from '../actions' import { settingsActions, globalActions } from '../actions'
import { shell } from 'electron' import { shell } from 'electron'
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles' import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
import { TopicOrder } from '../reducers/Settings' import { TopicOrder } from '../reducers/Settings'
@@ -70,6 +70,7 @@ const styles: StyleRulesCallback = theme => ({
interface Props { interface Props {
actions: typeof settingsActions actions: typeof settingsActions
globalActions: typeof globalActions
autoExpandLimit: number autoExpandLimit: number
classes: any classes: any
highlightTopicUpdates: boolean highlightTopicUpdates: boolean
@@ -77,6 +78,7 @@ interface Props {
store?: any store?: any
topicOrder: TopicOrder topicOrder: TopicOrder
visible: boolean visible: boolean
theme: 'light' | 'dark'
} }
class Settings extends React.Component<Props, {}> { class Settings extends React.Component<Props, {}> {
@@ -112,6 +114,7 @@ class Settings extends React.Component<Props, {}> {
{this.renderNodeOrder()} {this.renderNodeOrder()}
{this.renderHighlightTopicUpdates()} {this.renderHighlightTopicUpdates()}
{this.selectTopicsOnMouseOver()} {this.selectTopicsOnMouseOver()}
{this.toggleTheme()}
</div> </div>
<Tooltip placement="top" title="App Author"> <Tooltip placement="top" title="App Author">
<Typography className={classes.author} onClick={this.openGithubPage}> <Typography className={classes.author} onClick={this.openGithubPage}>
@@ -173,6 +176,12 @@ class Settings extends React.Component<Props, {}> {
return this.renderSwitch('Quick Preview', selectTopicWithMouseOver, toggle, 'Select topics on mouse over') return this.renderSwitch('Quick Preview', selectTopicWithMouseOver, toggle, 'Select topics on mouse over')
} }
private toggleTheme() {
const { globalActions, theme } = this.props
return this.renderSwitch('Theme', theme === 'light', globalActions.toggleTheme, 'Select a theme')
}
private renderAutoExpand() { private renderAutoExpand() {
const { classes, autoExpandLimit } = this.props const { classes, autoExpandLimit } = this.props
@@ -233,12 +242,14 @@ const mapStateToProps = (state: AppState) => {
visible: state.settings.visible, visible: state.settings.visible,
highlightTopicUpdates: state.settings.highlightTopicUpdates, highlightTopicUpdates: state.settings.highlightTopicUpdates,
selectTopicWithMouseOver: state.settings.selectTopicWithMouseOver, selectTopicWithMouseOver: state.settings.selectTopicWithMouseOver,
theme: state.globalState.theme,
} }
} }
const mapDispatchToProps = (dispatch: any) => { const mapDispatchToProps = (dispatch: any) => {
return { return {
actions: bindActionCreators(settingsActions, dispatch), actions: bindActionCreators(settingsActions, dispatch),
globalActions: bindActionCreators(globalActions, dispatch),
} }
} }

View File

@@ -284,7 +284,7 @@ class Publish extends React.Component<Props, State> {
private history() { private history() {
const items = this.state.history.reverse().map(message => ({ const items = this.state.history.reverse().map(message => ({
title: message.topic, title: message.topic,
value: message.payload, value: message.payload || '',
})) }))
return <History items={items} onClick={this.didSelectHistoryEntry} /> return <History items={items} onClick={this.didSelectHistoryEntry} />

View File

@@ -1,18 +1,15 @@
import * as React from 'react' import * as React from 'react'
import * as ReactDOM from 'react-dom' import * as ReactDOM from 'react-dom'
import App from './App' import App from './App'
import Demo from './components/demo' import Demo from './components/Demo'
import reducers from './reducers' import reducers, { AppState } from './reducers'
import reduxThunk from 'redux-thunk' import reduxThunk from 'redux-thunk'
import { applyMiddleware, compose, createStore } from 'redux' import { applyMiddleware, compose, createStore } from 'redux'
import { batchDispatchMiddleware } from 'redux-batched-actions' import { batchDispatchMiddleware } from 'redux-batched-actions'
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles' import { createMuiTheme, MuiThemeProvider, Theme } from '@material-ui/core/styles'
import { Provider } from 'react-redux' import { Provider, connect } from 'react-redux'
import './tracking' import './tracking'
const composeEnhancers = /*(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || */ compose const composeEnhancers = /*(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || */ compose
const store = createStore( const store = createStore(
reducers, reducers,
@@ -24,12 +21,6 @@ const store = createStore(
), ),
) )
const theme = createMuiTheme({
palette: {
type: 'dark',
},
})
setTimeout(() => { setTimeout(() => {
const splash = document.getElementById('splash') const splash = document.getElementById('splash')
if (splash) { if (splash) {
@@ -38,12 +29,42 @@ setTimeout(() => {
} }
}, 300) }, 300)
ReactDOM.render( function createTheme(type: 'light' | 'dark') {
<MuiThemeProvider theme={theme}> if (type === 'dark') {
<Provider store={store}> return createMuiTheme({
palette: {
type: 'dark',
},
})
} else {
return createMuiTheme({
palette: {
type: 'light',
},
})
}
}
function ApplicationRenderer(props: {theme: 'light' | 'dark'}) {
return (
<MuiThemeProvider theme={createTheme(props.theme)}>
<App /> <App />
<Demo /> <Demo />
</Provider> </MuiThemeProvider>
</MuiThemeProvider>, )
}
const mapStateToProps = (state: AppState) => {
return {
theme: state.globalState.theme,
}
}
const Application = connect(mapStateToProps)(ApplicationRenderer)
ReactDOM.render(
<Provider store={store}>
<Application />
</Provider>,
document.getElementById('app'), document.getElementById('app'),
) )

View File

@@ -6,11 +6,12 @@ import { settingsReducer, SettingsState } from './Settings'
import { trackEvent } from '../tracking' import { trackEvent } from '../tracking'
import { treeReducer, TreeState } from './Tree' import { treeReducer, TreeState } from './Tree'
export enum ActionTypes { export enum ActionTypes {
showUpdateNotification = 'SHOW_UPDATE_NOTIFICATION', showUpdateNotification = 'SHOW_UPDATE_NOTIFICATION',
showUpdateDetails = 'SHOW_UPDATE_DETAILS', showUpdateDetails = 'SHOW_UPDATE_DETAILS',
showError = 'SHOW_ERROR', showError = 'SHOW_ERROR',
setDarkTheme = 'GLOBAL_SET_DARK_THEME',
setLightTheme = 'GLOBAL_SET_LIGHT_THEME',
} }
export interface CustomAction extends Action { export interface CustomAction extends Action {
@@ -33,13 +34,15 @@ export interface GlobalState {
showUpdateNotification?: boolean showUpdateNotification?: boolean
showUpdateDetails: boolean showUpdateDetails: boolean
error?: string error?: string
theme: 'light' | 'dark'
} }
const initialBigState: GlobalState = { const initialGlobalState: GlobalState = {
showUpdateDetails: false, showUpdateDetails: false,
theme: 'dark',
} }
const globalState: Reducer<GlobalState | undefined, CustomAction> = (state = initialBigState, action) => { const globalState: Reducer<GlobalState | undefined, CustomAction> = (state = initialGlobalState, action) => {
if (!state) { if (!state) {
throw Error('No initial state') throw Error('No initial state')
} }
@@ -58,6 +61,18 @@ const globalState: Reducer<GlobalState | undefined, CustomAction> = (state = ini
error: action.error, error: action.error,
} }
case ActionTypes.setDarkTheme:
return {
...state,
theme: 'dark',
}
case ActionTypes.setLightTheme:
return {
...state,
theme: 'light',
}
case ActionTypes.showUpdateDetails: case ActionTypes.showUpdateDetails:
if (action.showUpdateDetails === undefined) { if (action.showUpdateDetails === undefined) {
return state return state