How to make your web app accept sharing
I am building a web app that helps people manage frequently used links and notes (called easyy.click), I have added several ways to make collecting links/notes easyy, for example:
People can add multiple links on the creation page;
People can import their browser bookmarks (it still works if you have thousands of links, takes some time though);
Created a browser extension, so people can save the current tab, or any link / selected text by right clicking it, this makes collecting really easyy on laptop;
The next step is, is it possible to accept shared link / text from other apps on the phone? So it’s also easyy to collect things on the phone.
I have the question, GPT has the answer, and the answer is Yes (partially). You may guessed it, it works with Android, but not iOS, details here.
I will show you how I made it work, to accept sharing for my web app on Android.
1). Add share_target to manifest.json
Your web app needs to be a PWA, so you have to have a valid manifest.json, and it needs to have a share_target
field, like this:
{
"share_target": {
"action": "/easyy-share",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
}
}
This tell Android, if people share a link/text to your web app, it will make a POST
request to ${appDomain}/easyy-share
(in my case https://app.easyy.click/easyy-share
), with the payload defined by enctype
and params
.
(If your web app is a SPA, you don’t need to prepare a /easyy-share
route, this request will handled by service worker, see below)
This is my full manifest.json file: https://github.com/penghuili/easyy.click/blob/master/public/manifest.json
2). Register a service worker
You can process the request with a real backend endpoint (see the MDN doc), or, you can process it within service worker.
My web app is a SPA, and I need to end-to-end encrypt the link/note before sending it to backend, so I process the request in service worker.
When my SPA starts, I register a service worker:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
}
The sw.js file has the logic to process the request.
3). Process request in sw.js
The sw.js needs to have a listener for the fetch
event, like this
self.addEventListener('fetch', event => {
if (event.request.method === 'POST' && event.request.url.includes('/easyy-share')) {
event.respondWith(handleShare(event.request));
}
});
We only process it if the request method is POST and the path is /easyy-share. All other fetch requests will behave normally.
Now let’s see how the handleShare()
looks:
async function handleShare(request) {
const formData = await request.formData();
const title = formData.get('title');
const content = formData.get('url') || formData.get('text');
if (!content) {
return Response.redirect('/shared', 303);
}
saveContent(title, content);
return Response.redirect(`/shared?shared=${isValidUrl(content) ? 'link' : 'text'}`, 303);
}
Very straightforward, we get the payload from request, save it, and redirect to a /shared
page with some query params. Some notes:
saveContent()
is an async function, but here we don’t need toawait
it, the requests will finish in the background, and users will see the success page quickly;In 1) we said we don’t need a
/easyy-share
route, but here we need a/shared
route, becauseResponse.redirect('/shared', 303)
will open our app, and open the/shared
page. This is our good chance to tell users, that the share is done;service worker doesn’t have access to localStorage, but it has access to indexDB, so if your
saveContent()
needs to read something from the localStorage (access token maybe?), you need to save that thing also in indexDB in your main app, so service worker can get the value;
Check the full code of my sw.js: https://github.com/penghuili/easyy.click/blob/master/src/sw.js
This is the end result:
Let me know if you have any feedback or question.
Subscribe to my newsletter
Read articles from Peng directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by