BeforeInstallPromptEvent.prompt() should return void and throw exceptions, not a Promise |
||||
Issue descriptionVersion: 56 OS: Android prompt() currently returns a Promise which either resolves or rejects immediately. As discussed on https://github.com/w3c/manifest/issues/417, we tentatively decided to change prompt() to return void and throw an exception on failure. The current proposed spec language is to return void and throw an exception. Rationale: All the failure modes are based on the arguments, and can therefore be determined synchronously. It would make sense to have a Promise if this was going to resolve after the user makes a choice, but having a Promise that resolves immediately is pointless. This will be a breaking change to the existing implementation, but the usage is low enough and it is a minor detail (hopefully most usage isn't relying on this, since our sample code doesn't actually use the result of prompt()). Adding Alex and Mounir who may have not seen that discussion on GitHub.
,
Oct 24 2016
We discussed this with Marcos from Mozilla. The basic reason is that prompt() is really best specc'd as a fire and forgot - there's no real reason for the developer to need to know when the Promise resolves. We have userChoice for when the user makes a decision; you can't just use the Promise on prompt() because ~66% of cases don't call prompt (and hence the Promise on prompt is not that useful).
,
Oct 24 2016
But if someone calls `prompt()`, wouldn't it make sense to have them do:
```
prompt().then(choice => {
if (choice == FOOBAR)
doSomething();
});
```
,
Oct 25 2016
So if we go back to the drawing board (assume no pre-existing implementation), there are three alternatives in my view: 1. prompt() is fire-and-forget. Returns void, and throws exceptions on usage error. You access the user's decision using userChoice. [What is in the spec draft] 2. prompt() returns a promise that resolves immediately, or rejects immediately on usage error. [What we implemented] 3. prompt() returns a promise that resolves when the user makes a choice (with the user's choice as the value), or rejects immediately on usage error. In my view, #1 and #3 are sensible, and unfortunately we have #2 here, which doesn't make sense (there is no need for a promise that always resolves synchronously). My personal preference for this would be #3, which is what mlamouri is suggesting. That would imply no userChoice at all --- you just get the choice from the result of prompt()*. I don't think #3 is that great given that we already have userChoice (which is redundant), which is why I suggested #1 and Marcos agreed. It's also a less major change from #2. Thoughts? * "But what about an automatic prompt? How do you get the user choice from that?" My view is that you don't need to. If you care about the user choice, you do a manual prompt (even if you call preventDefault() and prompt() right away). If you only need to know when it's installed, there's an appinstalled for that.
,
Oct 25 2016
In my opinion, we should do #3 so prompt() is self contained. If `userChoice` does not make sense, maybe it should be deprecated (I have no opinion, didn't check the use cases). This API was designed, implemented and launched in a hurry so we should take the hit of dealing with legacy API. Obviously, if it's heavily used, it might be hard to deprecate and other implementations might need to implement it.
,
Oct 25 2016
If we are going "clean slate": I agree with 3, but .prompt() should not be on the event. The prompt() method should live somewhere else (e.g., navigator). Quite a few people have pointed out that the design of the current event is quite odd. It also simplifies implementation quite a bit.
Ideally, then BIP should fire a simple event (use Event interface, but cancelable), after which the developer can call prompt. We could event have `.canPrompt` (yes, name is terrible - bikeshed later) return a promise, so then we don't need to keep firing the event every time the user navigates.
so:
```
// Wanna prevent the default? no problem:
window.addEventListener("onbeforeinstallprompt", (ev)=>{
ev.preventDefault();
});
// Wanna know if you can prompt? no problem:
navigator.canPrompt.then(()=>{
navigator.prompt().then(userChoice => doSomethingWith(userChoice));
});
```
Note we probably want prompt() to return some kind of UserResponseObject, which we can extend. It would initially only have a userChoice attribute.
,
Oct 26 2016
Right so for Mounir's benefit, we discussed that approach before and the conclusion was that we don't want to take a clean slate approach, because there are already 3 independent implementations (Google, Samsung and Opera) and a bunch of sites depending on it. If we take that strictly, we shouldn't change anything and should go with Option 2 (from #4). If we are going to change anything: - I think Option 1 is the smallest breakage. - Option 3 is the same amount of breakage, unless we also remove userChoice, in which case that would increase the breakage surface. - The full proposal in #6 would fully break all users. The advantage of my Option 1 or 3 is that they will *only* break sites that are using the result of prompt(), which our sample code doesn't do and there is currently little utility in doing so (since it only allows you to catch errors that are your own programming errors). Therefore, I see Option 1 or 3 as acceptable breaks from what we have already established, whereas #6 is a far more drastic change.
,
Oct 26 2016
I agree with mguica@ that we can't just drop the current implementation. We could always deprecate the whole thing and ask new users to go use this new API Marcos is suggesting but maybe it makes sense to change how the promise works first? It sounds like a small step in the right direction that will break nothing.
,
Oct 26 2016
Ok, lets stick with ev.prompt() - but let's make the resolved value something we can extend (i.e., some UserResponseObject, which could just be a simple dictionary).
,
Oct 26 2016
Are you suggesting we stick with #2 and make a promise that immediately resolves or rejects? And errors are returned via rejection rather than exceptions?
,
Oct 26 2016
Sorry, I was suggesting we go with (a slightly modified) 3.
,
Oct 27 2016
Mounir: Are you happy to change the promise behaviour of prompt()? In a breaking way, but it won't affect anyone who isn't using the result prompt() currently.
,
Oct 27 2016
Matt, that sounds fine. You will have to do an Intent to Ship but the change is small enough. Note that I would recommend to keep the rejection as is if we still return a promise. Also, ping slightlyoff@ to make sure he is on board.
,
Oct 27 2016
#13 Yes, will keep the rejection, and just change the response. Marcos: Are you able to update the polyfill and spec draft with the new language (#4 proposal 3) to return a promise that resolves with some object? Then once that is concrete, I will send an Intent to Ship to blink-dev.
,
Oct 28 2016
Yep, will do.
,
Oct 28 2016
There is still some contention around .userChoice. As discussed yesterday, I would kindly urge the Blink team to consider deprecating that attribute, as the use case for the userChoice attribute is now covered the "appinstalled" event (which fires post install as a result the user accepting).
,
Nov 1 2016
mounir: What are your thoughts on this? I think the *primary* use case of userChoice is covered by appinstalled (detecting installation). The one other possible use case which doesn't have a direct replacement is detecting when the user cancels the automatic dialog (without using preventDefault+prompt to do a custom dialog). I'm not sure why you would want this, but if you really want it, you can still do that by simply calling prompt() immediately from the bip event handler and looking at its result. So we will not lose any ability if we remove userChoice. I think it makes sense to keep it around in Chrome for potentially forever, but display a deprecation warning and not put it into the spec.
,
Nov 1 2016
It's likely the goal was to have a way for websites to keep track of user choice and implement metrics. Indeed, it sounds reasonable that calling prompt() themselves would trigger the same result. The rationale sgtm. +paulkinlan@ who might have opinions.
,
Sep 8 2017
,
Feb 14 2018
Not sure what this was about at the time, but the spec now reads that prompt() should return a Promise that resolves or rejects when the user accepts or cancels it. That is, #3 in https://bugs.chromium.org/p/chromium/issues/detail?id=658638#c4. Right now, the implementation waits for the user to accept before resolving. However, it doesn't reject; covered by Issue 805744 . Therefore, closing this. |
||||
►
Sign in to add a comment |
||||
Comment 1 by mlamouri@chromium.org
, Oct 24 2016