WICG / webmonetization

Proposed Web Monetization standard
https://webmonetization.org
Other
466 stars 152 forks source link

Maybe we don't need any states? #220

Closed marcoscaceres closed 1 year ago

marcoscaceres commented 3 years ago

I think we might need a more complete state machine to reflect the state changes that the document and the monetization agent can go through. There is a lot of IPC that goes on between the web page and the WM agent, so a more complete state machine will help us keep things sane.

We need to map this out, but I think:

We can surface these through monetization.state, and we should pair these with simple "statechange" events paired with .onstatechange.

The "progress" events only fire when in the interactive state.

sublimator commented 3 years ago

@marcoscaceres I'll be taking a stab at implementing the changes proposed in the open pull requests. I was told we'd create a working impl to trial before merging those. For that next iteration, we'll probably want a PR for this too?

Has there been any further discussion on this (sorry, I've been a bit out of the loop due to health reasons)

sublimator commented 3 years ago

see: https://github.com/WICG/webmonetization/pull/208#discussion_r740686014

marcoscaceres commented 3 years ago

Sorry, just coming back to this @sublimator! I'll try to catch up on your comments now...

marcoscaceres commented 3 years ago

Error could be the payment pointer or wallet is invalid. This would allow the developer to recover by providing a different payment payment pointer or wallet address with new shared secret.

@AlexLakatos, can you investigate on the protocol side what happens when the above occurs?

sublimator commented 3 years ago

Would we want some error detail surfaced?

AlexLakatos commented 3 years ago

@marcoscaceres The payment pointer holds an Interledger address, and a shared secret. When a connection is set up via STREAM, if either of those can't be reached, it should fail, and the error should be surfaced I think.

@sublimator I think that particular case is the one where a developer can do something about it. I.e. the payment pointer was invalid for some reason, change the payment pointer.

marcoscaceres commented 3 years ago

Another error might be that the monetization agent runs out of possible connections.

sublimator commented 3 years ago

"interactive" - processing payments.

@AlexLakatos You said as soon as a money packet was actually sent (but not fulfilled) on the wire would be a good time to switch to interactive ?

@marcoscaceres

Given we won't need a "previous" state, and the state is accessible via monetization.state will the "statechange" event actually need a property to hold the latest state ?

Some notes on the old states/events:

Before we settled on just pending | started | stopped there was some talk about adding a paused state, but we didn't really want to have too many states. However, there is one case where you want to know the difference between paused and stopped, and that is when the tag has been removed. The stream with that id can no longer be resumed!

We hacked a finalized: boolean property to the monetizationstop event.

People use it for things like react hooks, to reset counter and the like state: https://github.com/dacioromero/react-hook-wm/blob/3752faa311036cb056937729b89dc0328354d043/src/has-paid.tsx#L5-L11 https://github.com/coilhq/web-monetization-projects/blob/ed3cfb702ed625b6d73bbb1a128e72a2acb085b8/packages/webmonetization-react/src/global.ts#L97-L109 (Wish Github would inline these here as it does if the link is to the same repo!)

sublimator commented 3 years ago

Would/should the statechanged events hold the paymentPointer/requestId ?

marcoscaceres commented 3 years ago

Given we won't need a "previous" state, and the state is accessible via monetization.state will the "statechange" event actually need a property to hold the latest state ?

no. You would just do:

let previous = navigator.monetization.state;
navigator.monetization.onstatechange = ev => {
    console.log(`was: ${previous}`, `is now: `${navigator.monetization.state}`);
    previous = navigator.monetization.state;
}

Or directly via the event: event.target.state. (target is the monetization instance).

However, there is one case where you want to know the difference between paused and stopped, and that is when the tag has been removed. The stream with that id can no longer be resumed!

Right, but the web page make the same determination:

// something like, "no monetization" or "monetization, but no href attribute"
document.querySelector("link[rel='monetization']") === null || !document.querySelector("link[rel='monetization']:not([href])")  
sublimator commented 3 years ago

the web page make the same determination:

We did something like that before finalized was added yeah. iirc we wanted to make sure that the new tag href: requestId 1:1 mapping was intact and it just seemed a bit more convenient than digging around in the DOM

In the new proposed model: You have a SPA. You are currently streaming with requestId A, it's "interactive" You dynamically switch out the payment pointer You will get a statechange event to idle ? idle could mean the user has "paused" the stream (manually ? or once a single packet has been sent, it's considered forever interactive after ? ) or it could mean that a new payment pointer has been set and the stream is gearing up Within the handler, you could check the DOM payment pointer has changed against the paymentPointer from the last progress event ? What if you didn't actually get one ? (depending on definition of interactive )

Need to head out now. Would like to continue down this line of thought.

marcoscaceres commented 2 years ago

If SPSP errors can be handled while the <link> element is loading, then we might not need any state changes at all... That means that code would now look like this:

<link rel="monetization" href="https://some.endpoint">
<script>
const el = document.querySelector("link[rel='monetization']")

// SPSP JSON-parsed and loaded ok
el.addEventLinstener("load", ev => {});

// Couldn't load SPSP 
el.addEventLinstener("error", ev => {});

// Monetization event
navigator.monetization.addEvenListener("monetization", ev => {})
</script>

And that's it...