dotnet-websharper / mvu

Model-View-Update architecture for WebSharper client-side applications.
https://dotnet-websharper.github.io/mvu/
Apache License 2.0
25 stars 3 forks source link

Add Action.Map #5

Open Tarmil opened 5 years ago

Tarmil commented 5 years ago

We need a function to wrap an action from a child update in a parent update, see https://forums.websharper.com/topic/87100:

module Action =

    let rec Map (wrapMsg: 'ChildMessage -> 'ParentMessage)
                (currentModel: 'ParentModel)
                (wrapModel: 'ParentModel -> 'ChildModel -> 'ParentModel)
                (unwrapModel: 'ParentModel -> 'ChildModel)
                (action: Action<'ChildMessage, 'ChildModel>)
                : Action<'ParentMessage, 'ParentModel> =
        match action with
        | DoNothing -> DoNothing
        | SetModel childModel -> UpdateModel (fun parent -> wrapModel parent childModel)
        | UpdateModel f -> UpdateModel (fun parent -> unwrapModel parent |> f |> wrapModel parent)
        | Command cmd -> Command (fun dispatch -> cmd (wrapMsg >> dispatch))
        | CommandAsync cmd -> CommandAsync (fun dispatch -> cmd (wrapMsg >> dispatch))
        | CombinedAction actions -> CombinedAction (List.map (Map wrapMsg currentModel wrapModel unwrapModel) actions)

// Use:
let update msg (state: State) : Action<Message, State> =
    match msg with
    | TagListMessage m -> 
        let updatedTagList= Tags.update m state.TagList
        SetModel {state with TagList=updatedTagList}
    | AuthMessage m ->
        Authentication.update m state.UserInfo
        |> Action.Map AuthMessage state (fun s i -> { s with UserInfo = i }) (fun s -> s.UserInfo)

We could also provide a "magic" macro, somewhat similar to Lens, that would allow passing this:

|> Action.MagicMap AuthMessage state.UserInfo

and would expand to the same as above:

|> Action.Map AuthMessage state (fun s i -> { s with UserInfo = i }) (fun s -> s.UserInfo)