[w3c/editing] Delayed clipboard rendering API shape. (Issue #423)

From #417 There are few comments about the shape of the new delayed rendering API in the async clipboard API. Creating this issue to continue the discussion here.
Explainer: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/DelayedClipboard/DelayedClipboardRenderingExplainer.md
Proposed solution: [Add a callback to ClipboardItemData](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/DelayedClipboard/DelayedClipboardRenderingExplainer.md#proposed-solution-add-a-callback-to-clipboarditemdata)
Discussions related to this:
@annevk asked:
> Yes it does run immediately, but you don't have to call `resolve()` right away. You'd only call `resolve(blob)` once you're actually ready, which can be later on in the event loop based on some event. Am I misunderstanding something or were promises not understood?
> 
> I guess what you're saying is that there's currently no event for such a request?

This led to the following discussions about the shape of the API:
> To be pedantic, this could be done with promises with the addition of another signal, e.g. an event as @annevk mentions. I think this would be a poor developer experience compared to the callback approach proposed above. Here's what it could look like, just to demonstrate:
```
navigator.clipboard.write(new ClipboardItem({
  'text/html': new Promise(resolve => {
    // somehow this event target is scoped to this clipboard item?
    // event will only be fired once, even if paste happens again?
    some_target.addEventListener('some_event_type', async e => {
      // are we the type handler that's actually desired here?
      if (e.requestedType === 'text/html') {
        // do a bunch of stuff here, probably async
        resolve(results);
      }
    };
  }),
  /* repeat the above for every supported type, but we'll only ever call resolve() for one Promise */
}));
```
> There may be a cleaner way, but IMHO a callback seems much cleaner.

> A callback definitely seems like the way.

> Is there any value in having the clipboard item data ever be a Promise? In other words, rather than this:
```
typedef (Blob or DOMString) ClipboardItemValue;
callback ClipboardItemValueCallback = ClipboardItemValue ();
typedef Promise<(ClipboardItemValue or ClipboardItemValueCallback)> ClipboardItemData;
constructor(record<DOMString, ClipboardItemData> items);
```
> I might expect this:
```
typedef (Blob or DOMString) ClipboardItemValue;
callback ClipboardItemValueCallback = (ClipboardItemValue or Promise<ClipboardItemValue>) ();
typedef (ClipboardItemValue or ClipboardItemValueCallback) ClipboardItemData;
constructor(record<DOMString, ClipboardItemData> items);
```
> That signature lets the value be provided immediately, deferred, or deferred and resolved asynchronously.
> Is there any value in having the clipboard item data ever be a Promise?

> Yes, because we want to allow sites to construct the data asynchronously, but eagerly. (Which is how the API currently behaves.) I believe what we actually would like is:
```
typedef (Blob or DOMString) ClipboardItemValue;
callback ClipboardItemValueCallback = Promise<ClipboardItemValue>();
typedef (ClipboardItemValueCallback or Promise<ClipboardItemValue>) ClipboardItemData;
constructor(record<DOMString, ClipboardItemData> items);
```
> However, it's apparently not possible for a Promise to be part of a union. Thus I think the next best option is to be able to provide additional data to the ClipboardItem with setter methods rather than in the ctor.

> Is there any value in having the clipboard item data ever be a Promise?

> Yes, because we want to allow sites to construct the data asynchronously, but eagerly.

> Right, that makes sense. Four options, then:
`
Eager: a DOMString or Blob
Eager, but asynchronous: a Promise of a DOMString or Blob
Deferred: a callback returning a DOMString or Blob
Deferred, but asynchronous: a callback returning a Promise of a DOMString or Blob
`
> However, it's apparently not possible for a Promise to be part of a union.

> If that's the case, then the callback will have to return a promise. That seems okay, but I think it'd be more ergonomic for the application developer to return a value synchronously if they want to.
> Are these suggested alternative ctor param types? If so, they can't be or'd because of the promise. But I agree with the premise they could all be useful.

> I agree it would be nicer not to have to wrap your argument with Promise.resolve() but it also doesn't seem too awful, and it's what the API was launched with, presumably because of the Promise union problem.

> Not alternative params, no, just trying to enumerate the usage scenarios.

@evanstade @sanketj @tilgovi @inexorabletash @anaskim @whsieh

-- 
Reply to this email directly or view it on GitHub:
https://github.com/w3c/editing/issues/423
You are receiving this because you are subscribed to this thread.

Message ID: <w3c/editing/issues/423@github.com>

Received on Tuesday, 11 April 2023 19:18:23 UTC