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

[Question] How to dispatch/fire messages not related to JS events? #4

Closed ModernRonin closed 5 years ago

ModernRonin commented 5 years ago

Hi,

I'm new to Websharper, so maybe my question will be super easy, that case, all the better :-)

I'm using Websharper.Mvu, with F#. And now I am trying to integrate auth0 authentication.

I wrap the auth0 javascript library like this (some details left out for brevity):

module Authentication

type Auth()=
    [<Name("isAuthenticated")>]
    [<Stub>]
    member this.IsLoggedIn(): Promise<bool>= X<_> 
    [<Name("loginWithRedirect")>]
    [<Stub>]
    member this.Login(options: LoginOptions): Promise<unit>= X<_>
    [<Name("logout")>]
    [<Stub>]
    member this.Logout(options: LogoutOptions): Promise<unit>= X<_>
    [<Name("handleRedirectCallback")>]
    [<Stub>]
    member this.finishLogin() : Promise<unit>= X<_>

[<Direct("createAuth0Client($options)")>]
let create(options: CreateOptions): Promise<Auth>= X<_>

let setup domain clientId=
    async {
        let! a= create({Domain= domain; ClientId=clientId}).AsAsync()
        // ...
    }

When I add a button Setup and bind its click event to a message, then I can do in my update function

let update msg (state: State) : Action<Message, State> =
    match msg with
    // ...
    | SetupAuth ->
    CommandAsync (fun _ -> Authentication.setup "myDomain" "myClientId")

So far so good. But actually I want to trigger this upon application startup. And I can't seem to find a way to dispatch a message without binding it to a JS-event.

Another example is finishLogin():

When auth0 is done with login, it calls a configured callback route. I got routing set up like this:

let Main () =
    let router = Router.Infer<Site.Route>()

    App.CreatePaged (Site.init()) Site.update Site.pageFor
    |> App.WithCustomRouting router Site.routeForState Site.goto
    // ...

with

module Site

let goto (route: Route) (state: State) : State =
    match route with
    | Welcome -> {state with Route=route; Screen= WelcomeScreen}
    // ...
    | AuthLoggedIn -> 
        // HERE
        state

let routeForState state = state.Route

In the AuthLoggedInBranch, I'd like to trigger a call to Authentication.finishLogin(). So again, I'd like to fire a message LoginFinished or similar to which I could react in my update function like this:

    match msg with
    // ...
    | LoginFinished ->
    CommandAsync (fun _ -> Authentication.finishLogin())

So, in both cases it's really the same question:

how can I fire/dispatch a message from within code, instead of from JS-events?

While I'm pretty sure the answer must be simple, I've been looking at the samples in github, at trywebsharper examples, googling for two weekends now (this is my personal hobby project, so I get to work on it only on the weekends), without any success.

So, please, can anyone help or provide me with a pointer to relevant documentation/samples/info?

Many thanks, Mark

Update: so by looking into the code here, in App.fs, I found that I can solve the first issue I mention by using WithInitMessage, but that still does not solve the more general question.

Jand42 commented 5 years ago

(This is duplicated at https://forums.websharper.com/topic/87044, copying my answer from there)

Hi, Mark!

For the first one, you can use |> App.WithInitMessage SetupAuth when setting up your app, if you want a message triggered after initialization.

Then in handling it, you need to make use of the dispatch function made available to you by CommandAsync if you want to react to setup being finished:

    | SetupAuth ->
        CommandAsync (fun dispatch ->
            async {
                do! Authentication.setup "myDomain" "myClientId"
                dispatch SetupAuthFinished
            }
        )

And similarly tie up other states. CommandAsync allows you to run some asynchronous computation then dispatch a message (or dispatch multiple ones while it is running), I think this would cover all your scenarios.

Hope this helps! András