Skip to content

Instantly share code, notes, and snippets.

@farism
Created March 4, 2019 22:31
Show Gist options
  • Save farism/06840d8568b183b6b4ad9e0a42a5a02c to your computer and use it in GitHub Desktop.
Save farism/06840d8568b183b6b4ad9e0a42a5a02c to your computer and use it in GitHub Desktop.
import * as fs from 'fs'
import { createContext, useReducer } from 'react'
import { array, option } from 'fp-ts'
import actionCreatorFactory from 'typescript-fsa'
import { isType } from 'typescript-fsa'
import { Action } from 'redux'
import { omit } from 'ramda'
import { File } from './File'
export interface State {
openFiles: {
[key: string]: File
}
selectedFile: option.Option<string>
}
const actionCreator = actionCreatorFactory('state')
interface CloseFilePayload {
file: File
}
interface OpenFilePayload {
path: string
}
interface SaveFilePayload {
file: File
}
interface SelectFilePayload {
file: File
}
export const closeFile = actionCreator<CloseFilePayload>('CLOSE_FILE')
export const openFile = actionCreator<OpenFilePayload>('OPEN_FILE')
export const saveFile = actionCreator<SaveFilePayload>('SAVE_FILE')
export const selectFile = actionCreator<SelectFilePayload>('SELECT_FILE')
export const defaultState = (): State => {
return {
openFiles: {},
selectedFile: option.none,
}
}
export const closeFileReducer = (state: State, { file }: CloseFilePayload) => {
const path = file.path
const fileToClose = state.openFiles[path]
const openFiles = omit([path], state.openFiles)
// no open files remaining, clear the state
if (Object.keys(openFiles).length === 0) {
return {
...state,
openFiles: {},
selectedFile: option.none,
}
}
// if the selectedFile is being closed, change it to something else
if (fileToClose.path === state.selectedFile.getOrElse('')) {
return {
...state,
openFiles: openFiles,
selectedFile: option.some(Object.values(openFiles)[0].path),
}
}
return {
...state,
openFiles,
}
}
export const openFileReducer = (state: State, { path }: OpenFilePayload) => {
// file is already open, set it to be the selectedFile
if (state.openFiles[path]) {
return {
...state,
selectedFile: option.some(path),
}
}
const content = fs.readFileSync(path).toString()
const file = {
path,
content,
name: array.last(path.split('/')).getOrElse(''),
}
return {
...state,
openFiles: {
...state.openFiles,
[path]: file,
},
selectedFile: option.some(path),
}
}
export const saveFileReducer = (state: State, { file }: SaveFilePayload) => {
fs.writeFileSync(file.path, file.content)
return {
...state,
openFiles: {
[file.path]: file,
},
}
}
export const selectFileReducer = (state: State, { file }: SaveFilePayload) => {
return {
...state,
selectedFile: option.some(file.path),
}
}
export const reducer = (state: State, action: Action): State => {
if (isType(action, closeFile)) {
return closeFileReducer(state, action.payload)
} else if (isType(action, openFile)) {
return openFileReducer(state, action.payload)
} else if (isType(action, saveFile)) {
return saveFileReducer(state, action.payload)
} else if (isType(action, selectFile)) {
return selectFileReducer(state, action.payload)
}
return state
}
export const useState = (initialState: State = defaultState()) => {
const [state, dispatch] = useReducer(reducer, initialState)
return {
state,
actions: {
closeFile: (payload: CloseFilePayload) => {
dispatch(closeFile(payload))
},
openFile: (payload: OpenFilePayload) => {
dispatch(openFile(payload))
},
saveFile: (payload: SaveFilePayload) => {
dispatch(saveFile(payload))
},
selectFile: (payload: SelectFilePayload) => {
dispatch(selectFile(payload))
},
},
}
}
export const { Provider, Consumer } = createContext({
state: defaultState(),
actions: {
closeFile: (p: CloseFilePayload) => {},
openFile: (p: OpenFilePayload) => {},
saveFile: (p: SaveFilePayload) => {},
selectFile: (p: SelectFilePayload) => {},
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment