It looks fine to me, but you could make an helper if you wanted.
For example:
Mix1 subMsg ->
let
( updatedAudioPlayerModel, updatedCmd ) =
AudioPlayer.update subMsg model.mix1
in
( { model | mix1 = updatedAudioPlayerModel }
, Cmd.map Mix1 updatedCmd
)
could be written
Mix1 subMsg ->
AudioPlayer.update subMsg model.mix1
|> Tuple.mapBoth (\p -> {model | mix1 = p}) (Cmd.map Mix1)
So you could use that or have the following helper:
updatePlayer :
(Model -> AudioPlayer.Model)
-> (AudioPlayer.Msg -> Msg)
-> (AudioPlayer.Model -> Model)
-> AudioPlayer.Msg
-> Model
-> ( Model, Cmd Msg )
updatePlayer toPlayer toMsg toModel msg model =
AudioPlayer.update msg (toPlayer model)
|> Tuple.mapBoth toModel (Cmd.map toMsg)
and write
Mix1 subMsg ->
updatePlayer .mix1 Mix1 (\player -> { model | mix1 = player }) subMsg model
Also given the current source code, I would remove the whole PlayerGroup
thing that nests a lot of things for no obvious benefit.
So the whole Main.elm
could become something like:
module Main exposing (Model, Msg(..), init, main, update, view)
import AudioPlayer
import Browser
import Html exposing (Html, button, div, h1, p, text)
import Html.Attributes exposing (class)
import Html.Events exposing (onClick)
-- GLOBALS
siteTitle : String
siteTitle =
"Mix"
-- MODEL
type alias Model =
{ original : AudioPlayer.Model
, mix1 : AudioPlayer.Model
, mix2 : AudioPlayer.Model
}
init : () -> ( Model, Cmd Msg )
init _ =
( { original = AudioPlayer.new "sample-original" "sample/original.mp3"
, mix1 = AudioPlayer.new "sample-mix1" "sample/mix1.mp3"
, mix2 = AudioPlayer.new "sample-mix2" "sample/mix2.mp3"
}
, Cmd.none
)
-- UPDATE
type Msg
= Original AudioPlayer.Msg
| Mix1 AudioPlayer.Msg
| Mix2 AudioPlayer.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Original subMsg ->
updatePlayer .original Original (\player -> { model | original = player }) subMsg model
Mix1 subMsg ->
updatePlayer .mix1 Mix1 (\player -> { model | mix1 = player }) subMsg model
Mix2 subMsg ->
updatePlayer .mix2 Mix2 (\player -> { model | mix2 = player }) subMsg model
updatePlayer :
(Model -> AudioPlayer.Model)
-> (AudioPlayer.Msg -> Msg)
-> (AudioPlayer.Model -> Model)
-> AudioPlayer.Msg
-> Model
-> ( Model, Cmd Msg )
updatePlayer toPlayer toMsg toModel msg model =
AudioPlayer.update msg (toPlayer model)
|> Tuple.mapBoth toModel (Cmd.map toMsg)
-- VIEW
view : Model -> Html Msg
view model =
div []
[ viewHeader
, viewDemo model
, viewFooter
]
viewHeader : Html Msg
viewHeader =
div [ class "banner" ]
[ h1 [ class "banner-head" ] [ text siteTitle ]
]
viewDemo : Model -> Html Msg
viewDemo { original, mix1, mix2 } =
div [ class "pure-g" ]
[ viewPlayer original
|> Html.map Original
, viewPlayer mix1
|> Html.map Mix1
, viewPlayer mix2
|> Html.map Mix2
]
viewPlayer : AudioPlayer.Model -> Html AudioPlayer.Msg
viewPlayer player =
div [ class "pure-u-1-3" ]
[ p []
[ text player.tag ]
, div []
[ AudioPlayer.view player
]
]
viewFooter : Html Msg
viewFooter =
div [] []
-- MAIN
main : Program () Model Msg
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}