Skip to content

Instantly share code, notes, and snippets.

@kenchris
Last active September 22, 2017 01:39
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenchris/0acec2790cd38dfdff0a7197ff00d1de to your computer and use it in GitHub Desktop.
Save kenchris/0acec2790cd38dfdff0a7197ff00d1de to your computer and use it in GitHub Desktop.

Shortcuts API Specification

This explainer is for the Shortcut API

Introduction

Some platforms like Windows and Android have ways to add menu items to the app launcher icon itself, here after called shortcuts. These can perform certain app actions. On Android in addition, you can drag these shortcuts to the homescreen.

The shortcut list (aka menu) contains a mixture of static and dynamic shortcuts and has a limit which may be different per platform or per launcher used. The amount of shortcuts on the homescreen is not limited so though the shortcut list may change overtime, existing shortcuts on the homescreen can still be updated (say change icon) though not removed.

Use-cases

The shortcuts are quite useful as /shortcuts/ to common actions, like starting a chat with the person you chat with the most, or say launch the web app to a specific place inside the app (deep link).

These are becoming quite popular, like for instance in GMaps you can add a shortcut to your contributions page.

API proposal

The ShortcutInfo dictionary is reusing name, short_name and icons from the manifest specification and can itself be embedded in the manifest.json inside the shortcuts member which takes an array of ShortcutInfo.

dictionary ShortcutInfo {
  DOMString id;
  DOMString name;
  DOMString short_name;
  sequence icons;
  dictionary data;
}

partial interface Navigator {
    readonly attribute int maxVisibleShortcuts;
    sequence<ShortcutInfo> getManifestShortcuts();
    sequence<ShortcutInfo> getDynamicShortcuts();
    Promise addShortcut(ShortcutInfo);
    Promise removeShortcut(ShortcutInfo);
    Promise setShortcut(ShortcutInfo); 
    Promise requestPinShortcut(ShortcutInfo);
};

[Constructor(DOMString type, optional ShortcutEventInit eventInitDict), Exposed=ServiceWorker]
Interface ShortcutEvent : ExtendableEvent {
  readonly attribute DOMString id;
  readonly attribute any data;
};

Examples

Static shortcuts inside a link manifest file

{
  "name": "Cool Email",
  "start_url": "https://coolemail.com",

  …

  "shortcuts": [{
     "name": "Email Mom",
     "id": "send-email",
     "data": { "email": "mom@coolemail.com" },
     "icons": [{
        "src": "icon/hd_hi.svg",
        "sizes": "any"
     }]
  }]
}

Responding to a shortcut activation

Similarly to the Push API, when shortcuts are clicked, the shortcutclick event is fired in the Service Worker.

At that point you have access to the id and data of the shortcut and can easily access any existing open window for the PWA or open the app using openWindow. postMessage can be used to talk to the open instance.

This is quite flexible. Using a Service Worker, the app can load URLs, execute JavaScript and communicate with a running instance of the PWA, even without giving it focus.

self.addEventListener('shortcutclick', function(event) {
  console.log('[Service Worker] Shortcut click Received.');

  if (event.id != "send-email) {
    return;
  }

  async function getWindow() {
    const client = await clients.matchAll(options).then(function(clientList) {
      if (clients.length > 0) {
        return clients[0];
      }
      return clients.openWindow('https://inbox.coolemail.com'); 
    });

    client.postMessage({ action: event.id, data: event.data });
    await client.focus();
  }

  event.waitUntil(getWindow());
});

Manually add a /dynamic/ shortcut

navigator.requestPinShortcut({
  name: "Email Mom",
  id: "send-email",
  data: { email: "mom@coolemail.com" },
  icons: [{
    src: "icon/hd_hi.svg",
    sizes: "any"
  }]
}).then(info => {
  console.log(`Added ${info.title} shortcut`); 
})

Opens

As it is quite a common case to just load different URLs (deep links into the app), it makes sense to add start_url to the ShortcutInfo. Questions is how to handle that. Without Service Worker it could just load the URL direclty.

  • Should shortcutclick be dispatched when a start_url is available as the URL might already be part of fetch event if in scope?

Relevant links

https://developer.android.com/guide/topics/ui/shortcuts.html https://developer.android.com/preview/features/pinning-shortcuts-widgets.html https://docs.microsoft.com/en-us/uwp/api/windows.ui.startscreen.jumplist https://github.com/android/platform_frameworks_base/blob/master/core/java/android/content/pm/ShortcutManager.java https://developer.android.com/reference/android/content/pm/ShortcutManager.html https://developer.android.com/reference/android/content/pm/ShortcutInfo.html https://developer.apple.com/ios/3d-touch/ https://developer.apple.com/documentation/uikit/uiapplicationshortcutitem https://electron.atom.io/docs/tutorial/desktop-environment-integration/

@mgiuca
Copy link

mgiuca commented Sep 22, 2017

I've written up a counter-proposal here. The major points are:

  1. URL-based, not event based, as discussed above.
  2. Focus on static first (much less complexity), and punt dynamic shortcuts to later.

The second point means it can be entirely done in the manifest spec, not introducing any new JavaScript APIs (which at least the two of us are most comfortable working with, spec-wise).

If anyone else wants to comment on that, please do so, then I think the next steps are perhaps just drafting a CL for the Manifest spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment