touchpage 0.2.1

control panel server with shared-state web controls
Documentation
port module Main exposing (init, main)

import Browser
import Browser.Events as BE
import Html
import Json.Decode as JD
import Json.Encode as JE
import SvgCommand exposing (Command(..))
import SvgControl
import SvgControlPage
import SvgLabel
import SvgTextSize exposing (TextSizeReply, decodeTextSizeReply, encodeTextSizeRequest)
import SvgThings
import Util exposing (RectSize)
import WebSocket


port receiveSocketMsg : (JD.Value -> msg) -> Sub msg


port sendSocketCommand : JE.Value -> Cmd msg


port requestTextSize : JE.Value -> Cmd msg


port receiveTextMetrics : (JD.Value -> msg) -> Sub msg


wssend =
    WebSocket.send sendSocketCommand


wsreceive =
    receiveSocketMsg <| WebSocket.receive WsMsg


type Msg
    = WsMsg (Result JD.Error WebSocket.WebSocketMsg)
    | TextSize (Result JD.Error TextSizeReply)
    | ScpMsg SvgControlPage.Msg


type alias Flags =
    { location : String
    , wsport : Int
    , width : Int
    , height : Int
    }


type alias Model =
    { scpModel : SvgControlPage.Model
    , wsUrl : String
    }


commandToCmd : SvgCommand.Command -> Cmd Msg
commandToCmd scmd =
    case scmd of
        Send dta ->
            wssend <|
                WebSocket.Send
                    { name = "touchpage"
                    , content = dta
                    }

        RequestTextWidth rtw ->
            requestTextSize <|
                encodeTextSizeRequest <|
                    rtw

        None ->
            Cmd.none

        Batch cmds ->
            Cmd.batch (List.map commandToCmd cmds)


main : Program Flags Model Msg
main =
    Browser.document
        { init =
            \flags ->
                let
                    ( mod, cmd ) =
                        init flags
                in
                ( mod
                , Cmd.batch
                    [ wssend <|
                        WebSocket.Connect
                            { name = "touchpage"
                            , address = mod.wsUrl
                            , protocol = "rust-websocket"
                            }
                    , commandToCmd cmd
                    ]
                )
        , subscriptions =
            \_ ->
                Sub.batch
                    [ Sub.map ScpMsg <|
                        BE.onResize
                            (\a b ->
                                SvgControlPage.Resize <|
                                    RectSize (toFloat a) (toFloat b)
                            )
                    , wsreceive
                    , receiveTextMetrics (TextSize << JD.decodeValue decodeTextSizeReply)
                    ]
        , update =
            \msg mod ->
                case msg of
                    ScpMsg sm ->
                        let
                            ( umod, cmd ) =
                                SvgControlPage.update sm mod.scpModel
                        in
                        ( { mod | scpModel = umod }
                        , commandToCmd cmd
                        )

                    WsMsg x ->
                        case x of
                            Ok (WebSocket.Data wsd) ->
                                let
                                    ( scpModel, scpCommand ) =
                                        SvgControlPage.update (SvgControlPage.JsonMsg wsd.data) mod.scpModel
                                in
                                ( { mod | scpModel = scpModel }, commandToCmd scpCommand )

                            Ok (WebSocket.Error wse) ->
                                ( mod, Cmd.none )

                            Err _ ->
                                ( mod, Cmd.none )

                    TextSize ts ->
                        case ts of
                            Ok tsr ->
                                ( { mod | scpModel = SvgControlPage.onTextSize tsr mod.scpModel }
                                , Cmd.none
                                )

                            Err _ ->
                                ( mod, Cmd.none )
        , view =
            \model ->
                Browser.Document "svg control"
                    [ Html.map ScpMsg <| SvgControlPage.view model.scpModel ]
        }


init : Flags -> ( Model, Command )
init flags =
    let
        wsUrl =
            String.split ":" flags.location
                |> List.tail
                |> Maybe.andThen List.head
                |> Maybe.map (\loc -> "ws:" ++ loc ++ ":" ++ String.fromInt flags.wsport)
                |> Maybe.withDefault ""

        rmargin =
            4

        ( sm, cmd ) =
            SvgControlPage.init
                (SvgThings.Rect 0 0 (flags.width - rmargin) (flags.height - rmargin))
                (SvgControlPage.Spec
                    wsUrl
                    (SvgControl.CsLabel (SvgLabel.Spec "empty" "no controls loaded!"))
                    Nothing
                    Nothing
                    Nothing
                    Nothing
                    Nothing
                    Nothing
                    Nothing
                )
    in
    ( { scpModel = sm
      , wsUrl = wsUrl
      }
    , cmd
    )