<|start_filename|>web/src/Client/Client.fs<|end_filename|>
module Client

open Elmish
open Elmish.React

open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.PowerPack.Fetch

open Thoth.Json

open NewAppSharedAll
open NewAppSharedFrontend.CounterFrontend

let initialCounter = fetchAs<Counter> "/api/init" (Decode.Auto.generateDecoder())

let mapCommands = function
    | LoadInitialCount -> Cmd.ofPromise initialCounter [] (Ok >> InitialCountLoaded) (Error >> InitialCountLoaded)

let view (model : Model) (dispatch : Msg -> unit) =
    div []
        [ div [] [ str (displayStringFrom model) ]
          button [ OnClick (fun _ -> dispatch Decrement) ] [ str "-" ]
          button [ OnClick (fun _ -> dispatch Increment) ] [ str "+" ] ]

#if DEBUG
open Elmish.Debug
open Elmish.HMR
#endif

module Program =
    /// Typical program, new commands are produced discriminated unions returned by `init` and `update` along with the new state.
    /// From https://github.com/fsprojects/Fabulous/pull/418/commits/3bdf0c2d873f505afbd1941b06d85c8c2b7d8f76#diff-557fd3a3b56fdbf61831e911986bc166
    let mkProgramWithCmdMsg (init: unit -> 'model * 'cmdMsg list) (update: 'msg -> 'model -> 'model * 'cmdMsg list) (view: 'view) (mapToCmd: 'cmdMsg -> Cmd<'msg>) =
        let convert = fun (model, cmdMsgs) -> model, (cmdMsgs |> List.map mapToCmd |> Cmd.batch)
        Program.mkProgram (fun arg -> init arg |> convert) (fun msg model -> update msg model |> convert) view

Program.mkProgramWithCmdMsg init update view mapCommands
#if DEBUG
|> Program.withConsoleTrace
|> Program.withHMR
#endif
|> Program.withReact "elmish-app"
#if DEBUG
|> Program.withDebugger
#endif
|> Program.run


<|start_filename|>shared/NewAppSharedAll/SharedAll.fs<|end_filename|>
﻿namespace NewAppSharedAll

type Counter = { Value: int }


<|start_filename|>mobile/NewAppMobile/NewAppMobile.fs<|end_filename|>
﻿// Copyright 2018 Fabulous contributors. See LICENSE.md for license.
namespace NewAppMobile

open System.Diagnostics
open Fabulous.Core
open Fabulous.DynamicViews
open Xamarin.Forms
open NewAppSharedFrontend.CounterFrontend
open NewAppSharedAll
open FSharp.Data
open Thoth.Json.Net

open Utils

module App = 

    let initialCounter = async {
        do! Async.SwitchToThreadPool()
        let! response = Http.AsyncRequest("http://192.168.2.96:8085/api/init", silentHttpErrors = true)
        let mapToJson = Decode.Auto.fromString<Counter> >> mapErrorToException
        match response.StatusCode, response.Body with
            | 200, Text text -> return InitialCountLoaded ( mapToJson text )
            | _ -> return InitialCountLoaded ( Error (exn "Failed to get initial count") )
    }

    //fetchAs<Counter> "/api/init" (Decode.Auto.generateDecoder())
    
    let mapCommands = function
        | LoadInitialCount -> Cmd.ofAsyncMsg initialCounter

    let view model dispatch =
        View.ContentPage(
          content = View.StackLayout(padding = 20.0, verticalOptions = LayoutOptions.Center,
            children = [ 
                View.Label(text = displayStringFrom model, horizontalOptions = LayoutOptions.Center, widthRequest=200.0, horizontalTextAlignment=TextAlignment.Center)
                View.Button(text = "Increment", command = (fun () -> dispatch Increment), horizontalOptions = LayoutOptions.Center)
                View.Button(text = "Decrement", command = (fun () -> dispatch Decrement), horizontalOptions = LayoutOptions.Center)
                ]))

    // Note, this declaration is needed if you enable LiveUpdate
    let program = Program.mkProgramWithCmdMsg init update view mapCommands

type App () as app = 
    inherit Application ()

    let runner = 
        App.program
#if DEBUG
        |> Program.withConsoleTrace
#endif
        |> Program.runWithDynamicView app

#if DEBUG
    // Uncomment this line to enable live update in debug mode. 
    // See https://fsprojects.github.io/Fabulous/tools.html for further  instructions.
    //
    //do runner.EnableLiveUpdate()
#endif    

    // Uncomment this code to save the application state to app.Properties using Newtonsoft.Json
    // See https://fsprojects.github.io/Fabulous/models.html for further  instructions.
#if APPSAVE
    let modelId = "model"
    override __.OnSleep() = 

        let json = Newtonsoft.Json.JsonConvert.SerializeObject(runner.CurrentModel)
        Console.WriteLine("OnSleep: saving model into app.Properties, json = {0}", json)

        app.Properties.[modelId] <- json

    override __.OnResume() = 
        Console.WriteLine "OnResume: checking for model in app.Properties"
        try 
            match app.Properties.TryGetValue modelId with
            | true, (:? string as json) -> 

                Console.WriteLine("OnResume: restoring model from app.Properties, json = {0}", json)
                let model = Newtonsoft.Json.JsonConvert.DeserializeObject<App.Model>(json)

                Console.WriteLine("OnResume: restoring model from app.Properties, model = {0}", (sprintf "%0A" model))
                runner.SetCurrentModel (model, Cmd.none)

            | _ -> ()
        with ex -> 
            App.program.onError("Error while restoring model found in app.Properties", ex)

    override this.OnStart() = 
        Console.WriteLine "OnStart: using same logic as OnResume()"
        this.OnResume()
#endif




<|start_filename|>web/src/Server/Server.fs<|end_filename|>
open System.IO
open System.Threading.Tasks

open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.DependencyInjection
open FSharp.Control.Tasks.V2
open Giraffe
open Saturn
open NewAppSharedAll


let tryGetEnv = System.Environment.GetEnvironmentVariable >> function null | "" -> None | x -> Some x

let publicPath = Path.GetFullPath "../Client/public"

let port =
    "SERVER_PORT"
    |> tryGetEnv |> Option.map uint16 |> Option.defaultValue 8085us

let getInitCounter() : Task<Counter> = task { return { Value = 42 } }

let webApp = router {
    get "/api/init" (fun next ctx ->
        task {
            let! counter = getInitCounter()
            return! json counter next ctx
        })
}

let app = application {
    url ("http://0.0.0.0:" + port.ToString() + "/")
    use_router webApp
    memory_cache
    use_static publicPath
    use_json_serializer(Thoth.Json.Giraffe.ThothSerializer())
    use_gzip
}

run app


<|start_filename|>mobile/NewAppMobile/Utils.fs<|end_filename|>
﻿module Utils

let mapErrorToException = function
    | Ok value -> Ok value
    | Error str -> Error (exn str)

<|start_filename|>shared/NewAppSharedFrontend/SharedFrontend.fs<|end_filename|>
﻿namespace NewAppSharedFrontend

open NewAppSharedAll

module CounterFrontend =
    type Model = { Counter: Counter option }

    type Msg = 
        | Increment 
        | Decrement 
        | InitialCountLoaded of Result<Counter, exn>

    type CmdMsg =
        | LoadInitialCount

    let init () = { Counter = None }, [ LoadInitialCount ]

    let update msg model : Model * CmdMsg list =
       match model.Counter, msg with
       | Some counter, Increment ->
           { model with Counter = Some { Value = counter.Value + 1 } }, []
       | Some counter, Decrement ->
           { model with Counter = Some { Value = counter.Value - 1 } }, []
       | _, InitialCountLoaded (Ok initialCount) ->
           { Counter = Some initialCount }, []
       | _ -> model, []

    let displayStringFrom = function
        | { Counter = Some counter } -> string counter.Value
        | { Counter = None   } -> "Loading..."
