diff --git a/lib/state/action-types.ts b/lib/state/action-types.ts index c32960c35..3d6201842 100644 --- a/lib/state/action-types.ts +++ b/lib/state/action-types.ts @@ -1,3 +1,7 @@ +import * as T from '../types'; + +import { AuthState } from './auth/constants'; + export const AUTH_SET = 'AUTH_SET'; export const FILTER_NOTES = 'FILTER_NOTES'; export const TAG_DRAWER_TOGGLE = 'TAG_DRAWER_TOGGLE'; @@ -7,7 +11,71 @@ export type Action< Args extends { [extraProps: string]: unknown } = {} > = { type: T } & Args; -export type ActionType = never; +/* + * Legacy action-creators that are more like global setters than Redux actions + */ +export type SetAccountName = Action<'setAccountName', { accountName: string }>; +export type SetAutoHideMenuBar = Action< + 'setAutoHideMenuBar', + { autoHideMenuBar: boolean } +>; +export type SetFocusMode = Action< + 'setFocusMode', + { focusModeEnabled: boolean } +>; +export type SetFontSize = Action<'setFontSize', { fontSize?: number }>; +export type SetLineLength = Action< + 'setLineLength', + { lineLength: T.LineLength } +>; +export type SetMarkdownEnabled = Action< + 'setMarkdownEnabled', + { markdownEnabled: boolean } +>; +export type SetNoteDisplay = Action< + 'setNoteDisplay', + { noteDisplay: T.ListDisplayMode } +>; +export type SetSortReversed = Action< + 'setSortReversed', + { sortReversed: boolean } +>; +export type SetSortTagsAlpha = Action< + 'setSortTagsAlpha', + { sortTagsAlpha: boolean } +>; +export type SetSortType = Action<'setSortType', { sortType: T.SortType }>; +export type SetSpellCheck = Action< + 'setSpellCheck', + { spellCheckEnabled: boolean } +>; +export type SetTheme = Action<'setTheme', { theme: T.Theme }>; +export type SetWPToken = Action<'setWPToken', { token: string }>; + +/* + * Normal action types + */ +export type FilterNotes = Action<'FILTER_NOTES', { notes: T.NoteEntity[] }>; +export type SetAuth = Action<'AUTH_SET', { status: AuthState }>; +export type ToggleTagDrawer = Action<'TAG_DRAWER_TOGGLE', { show: boolean }>; + +export type ActionType = + | FilterNotes + | SetAccountName + | SetAuth + | SetAutoHideMenuBar + | SetFocusMode + | SetFontSize + | SetLineLength + | SetMarkdownEnabled + | SetNoteDisplay + | SetSortReversed + | SetSortTagsAlpha + | SetSortType + | SetSpellCheck + | SetTheme + | SetWPToken + | ToggleTagDrawer; export type ActionCreator = (...args: any[]) => A; export type Reducer = (state: S | undefined, action: ActionType) => S; diff --git a/lib/state/actions.ts b/lib/state/actions.ts index da2aa708e..8eb2b0370 100644 --- a/lib/state/actions.ts +++ b/lib/state/actions.ts @@ -1,7 +1,9 @@ import * as auth from './auth/actions'; import * as settings from './settings/actions'; +import * as ui from './ui/actions'; export default { auth, settings, + ui, }; diff --git a/lib/state/auth/actions.ts b/lib/state/auth/actions.ts index bc3a9c813..ce360684f 100644 --- a/lib/state/auth/actions.ts +++ b/lib/state/auth/actions.ts @@ -1,34 +1,26 @@ -import { AUTH_SET } from '../action-types'; +import * as A from '../action-types'; -import { - Authorized, - Authorizing, - InvalidCredentials, - LoginError, - NotAuthorized, -} from './constants'; - -export const reset = () => ({ - type: AUTH_SET, - status: NotAuthorized, +export const reset: A.ActionCreator = () => ({ + type: 'AUTH_SET', + status: 'not-authorized', }); -export const setInvalidCredentials = () => ({ - type: AUTH_SET, - status: InvalidCredentials, +export const setInvalidCredentials: A.ActionCreator = () => ({ + type: 'AUTH_SET', + status: 'invalid-credentials', }); -export const setLoginError = () => ({ - type: AUTH_SET, - status: LoginError, +export const setLoginError: A.ActionCreator = () => ({ + type: 'AUTH_SET', + status: 'login-error', }); -export const setPending = () => ({ - type: AUTH_SET, - status: Authorizing, +export const setPending: A.ActionCreator = () => ({ + type: 'AUTH_SET', + status: 'authorizing', }); -export const setAuthorized = () => ({ - type: AUTH_SET, - status: Authorized, +export const setAuthorized: A.ActionCreator = () => ({ + type: 'AUTH_SET', + status: 'authorized', }); diff --git a/lib/state/auth/constants.ts b/lib/state/auth/constants.ts index 74b4d100d..f70e7679f 100644 --- a/lib/state/auth/constants.ts +++ b/lib/state/auth/constants.ts @@ -1,5 +1,6 @@ -export const Authorized = Symbol(); -export const Authorizing = Symbol(); -export const InvalidCredentials = Symbol(); -export const LoginError = Symbol(); -export const NotAuthorized = Symbol(); +export type AuthState = + | 'authorized' + | 'authorizing' + | 'invalid-credentials' + | 'login-error' + | 'not-authorized'; diff --git a/lib/state/auth/reducer.ts b/lib/state/auth/reducer.ts index 45d2f42b5..fb23a195e 100644 --- a/lib/state/auth/reducer.ts +++ b/lib/state/auth/reducer.ts @@ -1,11 +1,12 @@ import { combineReducers } from 'redux'; -import { AUTH_SET } from '../action-types'; +import * as A from '../action-types'; +import { AuthState } from './constants'; -import { NotAuthorized } from './constants'; - -export const authStatus = (state = NotAuthorized, { type, status }) => - AUTH_SET === type ? status : state; +export const authStatus: A.Reducer = ( + state = 'not-authorized', + action +) => ('AUTH_SET' === action.type ? action.status : state); export default combineReducers({ authStatus, diff --git a/lib/state/auth/selectors.ts b/lib/state/auth/selectors.ts index dfffc82b3..e292f7096 100644 --- a/lib/state/auth/selectors.ts +++ b/lib/state/auth/selectors.ts @@ -1,20 +1,13 @@ -import { get } from 'lodash'; +import * as S from '../'; -import { - Authorized, - Authorizing, - InvalidCredentials, - LoginError, -} from './constants'; +export const authIsPending = (state: S.State) => + 'authorizing' === state.auth.authStatus; -export const authIsPending = state => - Authorizing === get(state, 'auth.authStatus'); +export const hasInvalidCredentials = (state: S.State) => + 'invalid-credentials' === state.auth.authStatus; -export const hasInvalidCredentials = state => - InvalidCredentials === get(state, 'auth.authStatus'); +export const hasLoginError = (state: S.State) => + 'login-error' === state.auth.authStatus; -export const hasLoginError = state => - LoginError === get(state, 'auth.authStatus'); - -export const isAuthorized = state => - Authorized === get(state, 'auth.authStatus'); +export const isAuthorized = (state: S.State) => + 'authorized' === state.auth.authStatus; diff --git a/lib/state/index.ts b/lib/state/index.ts index c677213c6..eaabbfccf 100644 --- a/lib/state/index.ts +++ b/lib/state/index.ts @@ -4,7 +4,13 @@ * All data should flow through here */ -import { compose, createStore, combineReducers, applyMiddleware } from 'redux'; +import { + Store as ReduxStore, + compose, + createStore, + combineReducers, + applyMiddleware, +} from 'redux'; import thunk from 'redux-thunk'; import persistState from 'redux-localstorage'; import { omit } from 'lodash'; @@ -73,6 +79,8 @@ export const store = createStore( ) ); +export type Store = ReduxStore; + export type MapState = ( state: State, ownProps: OwnProps diff --git a/lib/state/settings/actions.ts b/lib/state/settings/actions.ts index 509351157..042516330 100644 --- a/lib/state/settings/actions.ts +++ b/lib/state/settings/actions.ts @@ -1,8 +1,12 @@ import { getIpcRenderer } from '../../utils/electron'; +import * as A from '../action-types'; + const ipc = getIpcRenderer(); -export const setFontSize = fontSize => ({ +export const setFontSize: A.ActionCreator = ( + fontSize?: number +) => ({ type: 'setFontSize', fontSize, }); @@ -23,19 +27,20 @@ export const decreaseFontSize = () => (dispatch, getState) => { dispatch(setFontSize(fontSize - 1)); }; -export const resetFontSize = () => setFontSize(undefined); +export const resetFontSize: A.ActionCreator = () => + setFontSize(undefined); -export const activateTheme = theme => ({ +export const activateTheme: A.ActionCreator = theme => ({ type: 'setTheme', theme, }); -export const setNoteDisplay = noteDisplay => ({ +export const setNoteDisplay: A.ActionCreator = noteDisplay => ({ type: 'setNoteDisplay', noteDisplay, }); -export const setLineLength = lineLength => ({ +export const setLineLength: A.ActionCreator = lineLength => ({ type: 'setLineLength', lineLength, }); @@ -47,7 +52,7 @@ export const toggleSortOrder = () => (dispatch, getState) => { }); }; -export const setSortType = sortType => ({ +export const setSortType: A.ActionCreator = sortType => ({ type: 'setSortType', sortType, }); @@ -59,17 +64,17 @@ export const toggleSortTagsAlpha = () => (dispatch, getState) => { }); }; -export const setMarkdown = markdownEnabled => ({ +export const setMarkdown: A.ActionCreator = markdownEnabled => ({ type: 'setMarkdownEnabled', markdownEnabled, }); -export const setAccountName = accountName => ({ +export const setAccountName: A.ActionCreator = accountName => ({ type: 'setAccountName', accountName, }); -export const setWPToken = token => ({ +export const setWPToken: A.ActionCreator = token => ({ type: 'setWPToken', token, }); diff --git a/lib/state/settings/reducer.ts b/lib/state/settings/reducer.ts index 020282e9b..d101618fc 100644 --- a/lib/state/settings/reducer.ts +++ b/lib/state/settings/reducer.ts @@ -1,22 +1,28 @@ import { clamp } from 'lodash'; +import * as A from '../action-types'; +import * as T from '../../types'; + export const initialState = { - accountName: null, + accountName: null as string | null, autoHideMenuBar: false, focusModeEnabled: false, fontSize: 16, - lineLength: 'narrow', + lineLength: 'narrow' as T.LineLength, markdownEnabled: false, - noteDisplay: 'comfy', + noteDisplay: 'comfy' as T.ListDisplayMode, sortReversed: false, sortTagsAlpha: false, - sortType: 'modificationDate', + sortType: 'modificationDate' as T.SortType, spellCheckEnabled: true, - theme: 'system', - wpToken: false, + theme: 'system' as T.Theme, + wpToken: false as string | boolean, }; -function reducer(state = initialState, action) { +const reducer: A.Reducer = ( + state = initialState, + action +) => { switch (action.type) { case 'setAccountName': return { ...state, accountName: action.accountName }; @@ -50,6 +56,6 @@ function reducer(state = initialState, action) { default: return state; } -} +}; export default reducer; diff --git a/lib/state/ui/actions.ts b/lib/state/ui/actions.ts index 946f46e05..d0c58dc12 100644 --- a/lib/state/ui/actions.ts +++ b/lib/state/ui/actions.ts @@ -1,13 +1,16 @@ -import { FILTER_NOTES, TAG_DRAWER_TOGGLE } from '../action-types'; - +import * as A from '../action-types'; import * as T from '../../types'; -export const filterNotes = (notes: T.NoteEntity[]) => ({ - type: FILTER_NOTES, +export const filterNotes: A.ActionCreator = ( + notes: T.NoteEntity[] +) => ({ + type: 'FILTER_NOTES', notes, }); -export const toggleTagDrawer = (show: boolean) => ({ - type: TAG_DRAWER_TOGGLE, +export const toggleTagDrawer: A.ActionCreator = ( + show: boolean +) => ({ + type: 'TAG_DRAWER_TOGGLE', show, }); diff --git a/lib/state/ui/middleware.ts b/lib/state/ui/middleware.ts index b6fdee910..4cb1506c1 100644 --- a/lib/state/ui/middleware.ts +++ b/lib/state/ui/middleware.ts @@ -1,10 +1,12 @@ -import { AnyAction } from 'redux'; +import { AnyAction, Dispatch, Middleware } from 'redux'; import { filterNotes as filterAction } from './actions'; import filterNotes from '../../utils/filter-notes'; +import * as S from '../'; + let searchTimeout: NodeJS.Timeout; -export default store => { +export const middleware: Middleware<{}, S.State, Dispatch> = store => { const updateNotes = () => store.dispatch(filterAction(filterNotes(store.getState().appState))); @@ -43,3 +45,5 @@ export default store => { return result; }; }; + +export default middleware; diff --git a/lib/state/ui/reducer.ts b/lib/state/ui/reducer.ts index 14592a846..5b780de2a 100644 --- a/lib/state/ui/reducer.ts +++ b/lib/state/ui/reducer.ts @@ -1,20 +1,23 @@ import { difference, union } from 'lodash'; import { combineReducers } from 'redux'; -import { FILTER_NOTES, TAG_DRAWER_TOGGLE } from '../action-types'; +import * as A from '../action-types'; import * as T from '../../types'; const defaultVisiblePanes = ['editor', 'noteList']; const emptyList: unknown[] = []; -const filteredNotes = ( +const filteredNotes: A.Reducer = ( state = emptyList as T.NoteEntity[], - { type, notes }: { type: string; notes: T.NoteEntity[] } -) => (FILTER_NOTES === type ? notes : state); + action +) => ('FILTER_NOTES' === action.type ? action.notes : state); -const visiblePanes = (state = defaultVisiblePanes, { type, show }) => { - if (TAG_DRAWER_TOGGLE === type) { - return show +const visiblePanes: A.Reducer = ( + state = defaultVisiblePanes, + action +) => { + if ('TAG_DRAWER_TOGGLE' === action.type) { + return action.show ? union(state, ['tagDrawer']) : difference(state, ['tagDrawer']); } diff --git a/lib/types.ts b/lib/types.ts index b1f507607..b6bc68ac8 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -63,4 +63,8 @@ export type Bucket = { /////////////////////////////////////// export type EditorMode = 'edit' | 'markdown' | 'preview'; +export type LineLength = 'full' | 'narrow'; +export type ListDisplayMode = 'expanded' | 'comfy' | 'condensed'; +export type SortType = 'alphabetical' | 'creationDate' | 'modificationDate'; +export type Theme = 'system' | 'light' | 'dark'; export type TranslatableString = string;