Reorganize redux types to avoid unnecessary type casting

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-07-21 22:52:49 +02:00
parent a221ce4255
commit 6a43d0c5fb
27 changed files with 113 additions and 153 deletions

View file

@ -6,8 +6,8 @@
import { Editor, Hint, Hints, Pos } from 'codemirror'
import { DateTime } from 'luxon'
import { getUser } from '../../../../redux/user/methods'
import { findWordAtCursor, Hinter } from './index'
import { store } from '../../../../redux'
const wordRegExp = /^(\[(.*])?)$/
const allSupportedLinks = [
@ -24,6 +24,11 @@ const allSupportedLinks = [
'[color=#FFFFFF]'
]
const getUserName = (): string => {
const user = store.getState().user
return user ? user.name : 'Anonymous'
}
const linkAndExtraTagHint = (editor: Editor): Promise<Hints | null> => {
return new Promise((resolve) => {
const searchTerm = findWordAtCursor(editor)
@ -39,13 +44,11 @@ const linkAndExtraTagHint = (editor: Editor): Promise<Hints | null> => {
} else {
resolve({
list: suggestions.map((suggestion: string): Hint => {
const user = getUser()
const userName = user ? user.name : 'Anonymous'
switch (suggestion) {
case 'name':
// Get the user when a completion happens, this prevents to early calls resulting in 'Anonymous'
return {
text: `[name=${userName}]`
text: `[name=${getUserName()}]`
}
case 'time':
// show the current time when the autocompletion is opened and not when the function is loaded

View file

@ -8,9 +8,8 @@ import { store } from '..'
import { ApiUrlActionType, ApiUrlObject, SetApiUrlAction } from './types'
export const setApiUrl = (state: ApiUrlObject): void => {
const action: SetApiUrlAction = {
store.dispatch({
type: ApiUrlActionType.SET_API_URL,
state
}
store.dispatch(action)
} as SetApiUrlAction)
}

View file

@ -5,7 +5,7 @@
*/
import { Reducer } from 'redux'
import { ApiUrlActions, ApiUrlActionType, ApiUrlObject, SetApiUrlAction } from './types'
import { ApiUrlActions, ApiUrlActionType, ApiUrlObject } from './types'
export const initialState: ApiUrlObject = {
apiUrl: ''
@ -17,7 +17,7 @@ export const ApiUrlReducer: Reducer<ApiUrlObject, ApiUrlActions> = (
) => {
switch (action.type) {
case ApiUrlActionType.SET_API_URL:
return (action as SetApiUrlAction).state
return action.state
default:
return state
}

View file

@ -10,11 +10,10 @@ export enum ApiUrlActionType {
SET_API_URL = 'api-url/set'
}
export interface ApiUrlActions extends Action<ApiUrlActionType> {
type: ApiUrlActionType
}
export type ApiUrlActions = SetApiUrlAction
export interface SetApiUrlAction extends ApiUrlActions {
export interface SetApiUrlAction extends Action<ApiUrlActionType> {
type: ApiUrlActionType.SET_API_URL
state: ApiUrlObject
}

View file

@ -8,9 +8,8 @@ import { store } from '..'
import { BannerActionType, BannerState, SetBannerAction } from './types'
export const setBanner = (state: BannerState): void => {
const action: SetBannerAction = {
store.dispatch({
type: BannerActionType.SET_BANNER,
state
}
store.dispatch(action)
} as SetBannerAction)
}

View file

@ -5,7 +5,7 @@
*/
import { Reducer } from 'redux'
import { BannerActions, BannerActionType, BannerState, SetBannerAction } from './types'
import { BannerActions, BannerActionType, BannerState } from './types'
export const initialState: BannerState = {
text: undefined,
@ -18,7 +18,7 @@ export const BannerReducer: Reducer<BannerState, BannerActions> = (
) => {
switch (action.type) {
case BannerActionType.SET_BANNER:
return (action as SetBannerAction).state
return action.state
default:
return state
}

View file

@ -10,11 +10,9 @@ export enum BannerActionType {
SET_BANNER = 'banner/set'
}
export interface BannerActions extends Action<BannerActionType> {
type: BannerActionType
}
export type BannerActions = SetBannerAction
export interface SetBannerAction extends BannerActions {
export interface SetBannerAction extends Action<BannerActionType> {
type: BannerActionType.SET_BANNER
state: BannerState
}

View file

@ -9,9 +9,8 @@ import { Config } from '../../api/config/types'
import { ConfigActionType, SetConfigAction } from './types'
export const setConfig = (state: Config): void => {
const action: SetConfigAction = {
store.dispatch({
type: ConfigActionType.SET_CONFIG,
state: state
}
store.dispatch(action)
} as SetConfigAction)
}

View file

@ -6,7 +6,7 @@
import { Reducer } from 'redux'
import { Config } from '../../api/config/types'
import { ConfigActions, ConfigActionType, SetConfigAction } from './types'
import { ConfigActions, ConfigActionType } from './types'
export const initialState: Config = {
allowAnonymous: true,
@ -55,7 +55,7 @@ export const initialState: Config = {
export const ConfigReducer: Reducer<Config, ConfigActions> = (state: Config = initialState, action: ConfigActions) => {
switch (action.type) {
case ConfigActionType.SET_CONFIG:
return (action as SetConfigAction).state
return action.state
default:
return state
}

View file

@ -11,10 +11,9 @@ export enum ConfigActionType {
SET_CONFIG = 'config/set'
}
export interface ConfigActions extends Action<ConfigActionType> {
type: ConfigActionType
}
export type ConfigActions = SetConfigAction
export interface SetConfigAction extends ConfigActions {
export interface SetConfigAction extends Action<ConfigActionType> {
type: ConfigActionType.SET_CONFIG
state: Config
}

View file

@ -8,11 +8,10 @@ import { store } from '..'
import { DarkModeConfig, DarkModeConfigActionType, SetDarkModeConfigAction } from './types'
export const setDarkMode = (darkMode: boolean): void => {
const action: SetDarkModeConfigAction = {
store.dispatch({
type: DarkModeConfigActionType.SET_DARK_MODE,
darkMode: darkMode
}
store.dispatch(action)
} as SetDarkModeConfigAction)
}
export const saveToLocalStorage = (darkModeConfig: DarkModeConfig): void => {

View file

@ -6,7 +6,7 @@
import { Reducer } from 'redux'
import { determineDarkModeBrowserSetting, loadFromLocalStorage, saveToLocalStorage } from './methods'
import { DarkModeConfig, DarkModeConfigActions, DarkModeConfigActionType, SetDarkModeConfigAction } from './types'
import { DarkModeConfig, DarkModeConfigActions, DarkModeConfigActionType } from './types'
export const getInitialState = (): DarkModeConfig => {
const initialMode = loadFromLocalStorage() ??
@ -26,7 +26,7 @@ export const DarkModeConfigReducer: Reducer<DarkModeConfig, DarkModeConfigAction
case DarkModeConfigActionType.SET_DARK_MODE:
darkModeConfigState = {
...state,
darkMode: (action as SetDarkModeConfigAction).darkMode
darkMode: action.darkMode
}
saveToLocalStorage(darkModeConfigState)
return darkModeConfigState

View file

@ -14,10 +14,9 @@ export interface DarkModeConfig {
darkMode: boolean
}
export interface DarkModeConfigActions extends Action<DarkModeConfigActionType> {
type: DarkModeConfigActionType
}
export type DarkModeConfigActions = SetDarkModeConfigAction
export interface SetDarkModeConfigAction extends DarkModeConfigActions {
export interface SetDarkModeConfigAction extends Action<DarkModeConfigActionType> {
type: DarkModeConfigActionType.SET_DARK_MODE
darkMode: boolean
}

View file

@ -10,11 +10,11 @@ import { EditorMode } from '../../components/editor-page/app-bar/editor-view-mod
import {
EditorConfig,
EditorConfigActionType,
SetEditorConfigAction,
SetEditorLigaturesAction,
SetEditorPreferencesAction,
SetEditorSmartPasteAction,
SetEditorSyncScrollAction
SetEditorSyncScrollAction,
SetEditorViewModeAction
} from './types'
export const loadFromLocalStorage = (): EditorConfig | undefined => {
@ -39,7 +39,7 @@ export const saveToLocalStorage = (editorConfig: EditorConfig): void => {
}
export const setEditorMode = (editorMode: EditorMode): void => {
const action: SetEditorConfigAction = {
const action: SetEditorViewModeAction = {
type: EditorConfigActionType.SET_EDITOR_VIEW_MODE,
mode: editorMode
}

View file

@ -7,16 +7,7 @@
import { Reducer } from 'redux'
import { EditorMode } from '../../components/editor-page/app-bar/editor-view-mode'
import { loadFromLocalStorage, saveToLocalStorage } from './methods'
import {
EditorConfig,
EditorConfigActions,
EditorConfigActionType,
SetEditorConfigAction,
SetEditorLigaturesAction,
SetEditorPreferencesAction,
SetEditorSmartPasteAction,
SetEditorSyncScrollAction
} from './types'
import { EditorConfig, EditorConfigActions, EditorConfigActionType } from './types'
const initialState: EditorConfig = {
editorMode: EditorMode.BOTH,
@ -44,28 +35,28 @@ export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (
case EditorConfigActionType.SET_EDITOR_VIEW_MODE:
newState = {
...state,
editorMode: (action as SetEditorConfigAction).mode
editorMode: action.mode
}
saveToLocalStorage(newState)
return newState
case EditorConfigActionType.SET_SYNC_SCROLL:
newState = {
...state,
syncScroll: (action as SetEditorSyncScrollAction).syncScroll
syncScroll: action.syncScroll
}
saveToLocalStorage(newState)
return newState
case EditorConfigActionType.SET_LIGATURES:
newState = {
...state,
ligatures: (action as SetEditorLigaturesAction).ligatures
ligatures: action.ligatures
}
saveToLocalStorage(newState)
return newState
case EditorConfigActionType.SET_SMART_PASTE:
newState = {
...state,
smartPaste: (action as SetEditorSmartPasteAction).smartPaste
smartPaste: action.smartPaste
}
saveToLocalStorage(newState)
return newState
@ -74,7 +65,7 @@ export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (
...state,
preferences: {
...state.preferences,
...(action as SetEditorPreferencesAction).preferences
...action.preferences
}
}
saveToLocalStorage(newState)

View file

@ -9,7 +9,7 @@ import { Action } from 'redux'
import { EditorMode } from '../../components/editor-page/app-bar/editor-view-mode'
export enum EditorConfigActionType {
SET_EDITOR_VIEW_MODE = 'editor/mode/set',
SET_EDITOR_VIEW_MODE = 'editor/view-mode/set',
SET_SYNC_SCROLL = 'editor/syncScroll/set',
MERGE_EDITOR_PREFERENCES = 'editor/preferences/merge',
SET_LIGATURES = 'editor/preferences/setLigatures',
@ -24,26 +24,34 @@ export interface EditorConfig {
preferences: EditorConfiguration
}
export interface EditorConfigActions extends Action<EditorConfigActionType> {
type: EditorConfigActionType
}
export type EditorConfigActions =
| SetEditorSyncScrollAction
| SetEditorLigaturesAction
| SetEditorSmartPasteAction
| SetEditorViewModeAction
| SetEditorPreferencesAction
export interface SetEditorSyncScrollAction extends EditorConfigActions {
export interface SetEditorSyncScrollAction extends Action<EditorConfigActionType> {
type: EditorConfigActionType.SET_SYNC_SCROLL
syncScroll: boolean
}
export interface SetEditorLigaturesAction extends EditorConfigActions {
export interface SetEditorLigaturesAction extends Action<EditorConfigActionType> {
type: EditorConfigActionType.SET_LIGATURES
ligatures: boolean
}
export interface SetEditorSmartPasteAction extends EditorConfigActions {
export interface SetEditorSmartPasteAction extends Action<EditorConfigActionType> {
type: EditorConfigActionType.SET_SMART_PASTE
smartPaste: boolean
}
export interface SetEditorConfigAction extends EditorConfigActions {
export interface SetEditorViewModeAction extends Action<EditorConfigActionType> {
type: EditorConfigActionType.SET_EDITOR_VIEW_MODE
mode: EditorMode
}
export interface SetEditorPreferencesAction extends EditorConfigActions {
export interface SetEditorPreferencesAction extends Action<EditorConfigActionType> {
type: EditorConfigActionType.MERGE_EDITOR_PREFERENCES
preferences: EditorConfiguration
}

View file

@ -47,7 +47,7 @@ export const deleteAllHistoryEntries = (): Promise<void> => {
store.dispatch({
type: HistoryActionType.SET_ENTRIES,
entries: []
})
} as SetEntriesAction)
storeLocalHistory()
return deleteHistory()
}

View file

@ -5,33 +5,23 @@
*/
import { Reducer } from 'redux'
import {
HistoryAction,
HistoryActionType,
HistoryEntry,
RemoveEntryAction,
SetEntriesAction,
UpdateEntryAction
} from './types'
import { HistoryActions, HistoryActionType, HistoryEntry } from './types'
// Q: Why is the reducer initialized with an empty array instead of the actual history entries like in the config reducer?
// A: The history reducer will be created without entries because of async entry retrieval.
// Entries will be added after reducer initialization.
export const HistoryReducer: Reducer<HistoryEntry[], HistoryAction> = (
export const HistoryReducer: Reducer<HistoryEntry[], HistoryActions> = (
state: HistoryEntry[] = [],
action: HistoryAction
action: HistoryActions
) => {
switch (action.type) {
case HistoryActionType.SET_ENTRIES:
return (action as SetEntriesAction).entries
return action.entries
case HistoryActionType.UPDATE_ENTRY:
return [
...state.filter((entry) => entry.identifier !== (action as UpdateEntryAction).noteId),
(action as UpdateEntryAction).newEntry
]
return [...state.filter((entry) => entry.identifier !== action.noteId), action.newEntry]
case HistoryActionType.REMOVE_ENTRY:
return state.filter((entry) => entry.identifier !== (action as RemoveEntryAction).noteId)
return state.filter((entry) => entry.identifier !== action.noteId)
default:
return state
}

View file

@ -40,21 +40,19 @@ export enum HistoryActionType {
REMOVE_ENTRY = 'REMOVE_ENTRY'
}
export interface HistoryAction extends Action<HistoryActionType> {
type: HistoryActionType
}
export type HistoryActions = SetEntriesAction | AddEntryAction | UpdateEntryAction | RemoveEntryAction
export interface SetEntriesAction extends HistoryAction {
export interface SetEntriesAction extends Action<HistoryActionType> {
type: HistoryActionType.SET_ENTRIES
entries: HistoryEntry[]
}
export interface AddEntryAction extends HistoryAction {
export interface AddEntryAction extends Action<HistoryActionType> {
type: HistoryActionType.ADD_ENTRY
newEntry: HistoryEntry
}
export interface UpdateEntryAction extends HistoryAction {
export interface UpdateEntryAction extends Action<HistoryActionType> {
type: HistoryActionType.UPDATE_ENTRY
noteId: string
newEntry: HistoryEntry

View file

@ -18,7 +18,7 @@ import { EditorConfig } from './editor/types'
import { NoteDetailsReducer } from './note-details/reducers'
import { NoteDetails } from './note-details/types'
import { UserReducer } from './user/reducers'
import { MaybeUserState } from './user/types'
import { OptionalUserState } from './user/types'
import { UiNotificationState } from './ui-notifications/types'
import { UiNotificationReducer } from './ui-notifications/reducers'
import { HistoryEntry } from './history/types'
@ -27,7 +27,7 @@ import { RendererStatusReducer } from './renderer-status/reducers'
import { RendererStatus } from './renderer-status/types'
export interface ApplicationState {
user: MaybeUserState
user: OptionalUserState
config: Config
banner: BannerState
history: HistoryEntry[]

View file

@ -11,16 +11,7 @@ import {
NoteTextDirection,
NoteType
} from '../../components/editor-page/note-frontmatter/note-frontmatter'
import {
NoteDetails,
NoteDetailsAction,
NoteDetailsActionType,
SetCheckboxInMarkdownContentAction,
SetNoteDetailsAction,
SetNoteDetailsFromServerAction,
SetNoteFrontmatterFromRenderingAction,
UpdateNoteTitleByFirstHeadingAction
} from './types'
import { NoteDetails, NoteDetailsActions, NoteDetailsActionType } from './types'
import { noteDtoToNoteDetails } from '../../api/notes/dto-methods'
export const initialState: NoteDetails = {
@ -52,38 +43,34 @@ export const initialState: NoteDetails = {
}
}
export const NoteDetailsReducer: Reducer<NoteDetails, NoteDetailsAction> = (
export const NoteDetailsReducer: Reducer<NoteDetails, NoteDetailsActions> = (
state: NoteDetails = initialState,
action: NoteDetailsAction
action: NoteDetailsActions
) => {
switch (action.type) {
case NoteDetailsActionType.SET_DOCUMENT_CONTENT:
return {
...state,
markdownContent: (action as SetNoteDetailsAction).content
markdownContent: action.content
}
case NoteDetailsActionType.UPDATE_NOTE_TITLE_BY_FIRST_HEADING:
return {
...state,
firstHeading: (action as UpdateNoteTitleByFirstHeadingAction).firstHeading,
noteTitle: generateNoteTitle(state.frontmatter, (action as UpdateNoteTitleByFirstHeadingAction).firstHeading)
firstHeading: action.firstHeading,
noteTitle: generateNoteTitle(state.frontmatter, action.firstHeading)
}
case NoteDetailsActionType.SET_NOTE_DATA_FROM_SERVER:
return noteDtoToNoteDetails((action as SetNoteDetailsFromServerAction).note)
return noteDtoToNoteDetails(action.note)
case NoteDetailsActionType.SET_NOTE_FRONTMATTER:
return {
...state,
frontmatter: (action as SetNoteFrontmatterFromRenderingAction).frontmatter,
noteTitle: generateNoteTitle((action as SetNoteFrontmatterFromRenderingAction).frontmatter, state.firstHeading)
frontmatter: action.frontmatter,
noteTitle: generateNoteTitle(action.frontmatter, state.firstHeading)
}
case NoteDetailsActionType.SET_CHECKBOX_IN_MARKDOWN_CONTENT:
return {
...state,
markdownContent: setCheckboxInMarkdownContent(
state.markdownContent,
(action as SetCheckboxInMarkdownContentAction).lineInMarkdown,
(action as SetCheckboxInMarkdownContentAction).checked
)
markdownContent: setCheckboxInMarkdownContent(state.markdownContent, action.lineInMarkdown, action.checked)
}
default:
return state

View file

@ -35,31 +35,34 @@ export interface NoteDetails {
frontmatter: NoteFrontmatter
}
export interface NoteDetailsAction extends Action<NoteDetailsActionType> {
type: NoteDetailsActionType
}
export type NoteDetailsActions =
| SetNoteDetailsAction
| SetNoteDetailsFromServerAction
| UpdateNoteTitleByFirstHeadingAction
| SetNoteFrontmatterFromRenderingAction
| SetCheckboxInMarkdownContentAction
export interface SetNoteDetailsAction extends NoteDetailsAction {
export interface SetNoteDetailsAction extends Action<NoteDetailsActionType> {
type: NoteDetailsActionType.SET_DOCUMENT_CONTENT
content: string
}
export interface SetNoteDetailsFromServerAction extends NoteDetailsAction {
export interface SetNoteDetailsFromServerAction extends Action<NoteDetailsActionType> {
type: NoteDetailsActionType.SET_NOTE_DATA_FROM_SERVER
note: NoteDto
}
export interface UpdateNoteTitleByFirstHeadingAction extends NoteDetailsAction {
export interface UpdateNoteTitleByFirstHeadingAction extends Action<NoteDetailsActionType> {
type: NoteDetailsActionType.UPDATE_NOTE_TITLE_BY_FIRST_HEADING
firstHeading?: string
}
export interface SetNoteFrontmatterFromRenderingAction extends NoteDetailsAction {
export interface SetNoteFrontmatterFromRenderingAction extends Action<NoteDetailsActionType> {
type: NoteDetailsActionType.SET_NOTE_FRONTMATTER
frontmatter: NoteFrontmatter
}
export interface SetCheckboxInMarkdownContentAction extends NoteDetailsAction {
export interface SetCheckboxInMarkdownContentAction extends Action<NoteDetailsActionType> {
type: NoteDetailsActionType.SET_CHECKBOX_IN_MARKDOWN_CONTENT
lineInMarkdown: number
checked: boolean

View file

@ -5,23 +5,17 @@
*/
import { Reducer } from 'redux'
import {
DismissUiNotificationAction,
DispatchUiNotificationAction,
UiNotificationAction,
UiNotificationActionType,
UiNotificationState
} from './types'
import { UiNotificationActions, UiNotificationActionType, UiNotificationState } from './types'
export const UiNotificationReducer: Reducer<UiNotificationState, UiNotificationAction> = (
export const UiNotificationReducer: Reducer<UiNotificationState, UiNotificationActions> = (
state: UiNotificationState = [],
action: UiNotificationAction
action: UiNotificationActions
) => {
switch (action.type) {
case UiNotificationActionType.DISPATCH_NOTIFICATION:
return state.concat((action as DispatchUiNotificationAction).notification)
return state.concat(action.notification)
case UiNotificationActionType.DISMISS_NOTIFICATION:
return dismissNotification(state, (action as DismissUiNotificationAction).notificationId)
return dismissNotification(state, action.notificationId)
default:
return state
}

View file

@ -28,16 +28,14 @@ export interface UiNotification {
buttons?: UiNotificationButton[]
}
export interface UiNotificationAction extends Action<UiNotificationActionType> {
type: UiNotificationActionType
}
export type UiNotificationActions = DispatchUiNotificationAction | DismissUiNotificationAction
export interface DispatchUiNotificationAction extends UiNotificationAction {
export interface DispatchUiNotificationAction extends Action<UiNotificationActionType> {
type: UiNotificationActionType.DISPATCH_NOTIFICATION
notification: UiNotification
}
export interface DismissUiNotificationAction extends UiNotificationAction {
export interface DismissUiNotificationAction extends Action<UiNotificationActionType> {
type: UiNotificationActionType.DISMISS_NOTIFICATION
notificationId: number
}

View file

@ -21,7 +21,3 @@ export const clearUser: () => void = () => {
}
store.dispatch(action)
}
export const getUser = (): UserState | null => {
return store.getState().user
}

View file

@ -5,15 +5,15 @@
*/
import { Reducer } from 'redux'
import { MaybeUserState, SetUserAction, UserActions, UserActionType } from './types'
import { OptionalUserState, UserActions, UserActionType } from './types'
export const UserReducer: Reducer<MaybeUserState, UserActions> = (
state: MaybeUserState = null,
export const UserReducer: Reducer<OptionalUserState, UserActions> = (
state: OptionalUserState = null,
action: UserActions
) => {
switch (action.type) {
case UserActionType.SET_USER:
return (action as SetUserAction).state
return action.state
case UserActionType.CLEAR_USER:
return null
default:

View file

@ -11,15 +11,16 @@ export enum UserActionType {
CLEAR_USER = 'user/clear'
}
export interface UserActions extends Action<UserActionType> {
type: UserActionType
}
export type UserActions = SetUserAction | ClearUserAction
export interface SetUserAction extends UserActions {
export interface SetUserAction extends Action<UserActionType> {
type: UserActionType.SET_USER
state: UserState
}
export type ClearUserAction = UserActions
export interface ClearUserAction extends Action<UserActionType> {
type: UserActionType.CLEAR_USER
}
export interface UserState {
id: string
@ -42,4 +43,4 @@ export enum LoginProvider {
OPENID = 'openid'
}
export type MaybeUserState = UserState | null
export type OptionalUserState = UserState | null