Created
January 26, 2020 11:29
-
-
Save mattiamanzati/4d3cfde5cd0820644a70c50da6156689 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {cmd} from 'elm-ts' | |
// TODO: See what's Option from functional programming | |
type Option<A> = { | |
type: 'none' | |
} | { type: 'some', value: A} | |
/** | |
* This is the users updater, it stores a list of user objects and exposes selectors to get them | |
*/ | |
export type User = { id: number, name: string, defaultTodoDone: boolean} | |
export type UserModel = User[] | |
export type UserMsg = { type: 'userFetched', payload: User} | |
export function msgUserFetched(user: User): UserMsg{ | |
return { type: 'userFetched', payload: user } | |
} | |
export function userUpdater(msg: UserMsg, model: UserModel): UserModel{ | |
switch(msg.type){ | |
case "userFetched": | |
return model.concat([msg.payload]) | |
default: | |
return model | |
} | |
} | |
export function getUserById(model: UserModel): (id: number) => Option<User> { | |
return id => { | |
const foundUsers = model.filter(user => user.id === id) | |
if(foundUsers.length === 0) return { type: 'none'} | |
return { type: 'some', value: foundUsers[0]} | |
} | |
} | |
/** | |
* Inside another module, we "lift" the definition of the reducer/updater, in order to take first the definition of capabilities. | |
* Capabilities are the functionalities required by a reducer to run. | |
* This is some sort of Dependency Injection, and also works great in order to decouple reducers implementation to test them. | |
*/ | |
export type Todo = { id: number, name: string, user_id: number, done: boolean} | |
export type TodoModel = Todo[] | |
export type TodoMsg = { type: 'todoAdded', payload: { user_id: number, name: string }} | |
export function msgTodoAdded(user_id: number, name: string): TodoMsg { | |
return { type: 'todoAdded', payload: {user_id, name} } | |
} | |
type TodoCapabilities = { | |
getUserById: (id: number) => Option<User> | |
} | |
function createTodoUpdater(capabilities: TodoCapabilities) { | |
return function updater(msg: TodoMsg, model: TodoModel): TodoModel { | |
switch(msg.type){ | |
case "todoAdded": | |
const todoUser = capabilities.getUserById(msg.payload.user_id) | |
// if user is invalid, reject the update | |
if(todoUser.type === "none") return model | |
// else, create the todo using user defaults | |
const todo = { | |
id: model.reduce((id, todo) => Math.max(id, todo.id), 1), // Just use max id + 1, | |
name: msg.payload.name, | |
user_id: todoUser.value.id, | |
done: todoUser.value.defaultTodoDone | |
} | |
return model.concat([todo]) | |
default: | |
return model | |
} | |
} | |
} | |
/** | |
* Ok, now we have two different models and we need to wire the updaters! | |
*/ | |
type AppModel = { | |
users: UserModel, | |
todos: TodoModel | |
} | |
type AppMsg = UserMsg | TodoModel | |
export function updater(msg: AppMsg, model: AppModel): AppModel { | |
// users section | |
const users = userUpdater(msg as any, model.users) // TODO: better typing | |
// todos section | |
const todoCapabilities: TodoCapabilities = { | |
getUserById: id => getUserById(model.users)(id) | |
} | |
const todos = createTodoUpdater(todoCapabilities)(msg as any, model.todos) | |
// merge result | |
return { users, todos } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment