Web Share API Fallback - web

I am implementing the new Web Share API for a site I am working on (https://developers.google.com/web/updates/2016/09/navigator-share). While it is supported by Safari Desktop, iOS Safari, and Android Chrome, it isn't supported by any other browsers. Is there a fallback that I can use for non-supported browsers to be able to share text and a link from my site?

One fallback I use is Blob, see an example in here https://codesandbox.io/s/bold-leaf-imu3w
It uses 'saveAs' library from npm.
const blob = new Blob(['"Name","Value"\r\n"Alice","100"\r\n"Bob","200"'], {
type: "text/csv"
});
saveAs(blob, "file.csv");
Another suggestion is to just show a regular page where the user can select data and copy it

Probably too late now, but as you can see from under Usage section, the line
`.catch((error) => console.log('Error sharing', error));`
provides a way to check for this. So then you can listen for the error, and write out custom sharing buttons if you want that.

Yes you can check.web share API uses navigator.share
if (navigator.share) {
navigator.share({
title: 'harish tech',
text: 'Check out harishtech.com.',
url: 'https://harishtech.com',
})
.then(() => console.log('Successful share'))
.catch((error) => console.log('Error sharing', error));
}else{
// Your fall back code here
}
For more info check out the link share like native app

If it can help, as a fallback in a React project, we built a second component triggering our own modal and used https://github.com/nygardk/react-share:
export const ShareButton = props => {
if (typeof navigator !== "undefined" && typeof navigator.share !== "undefined")
return <ShareButtonNativeModal {...props} />
else
return <ButtonShareModal {...props} />
}

Related

Chrome extension API for filepicker

There is an API for download handling for google chrome:
https://developer.chrome.com/docs/extensions/reference/downloads/
This is great and it works. But I would love to add a listener for whenever a user triggers an <input type="file /> as well. This can be done with some client side script in content_scripts, but I need to do this with the API and not custom logic
There are two APIs that sounds right but only works for ChromeOS: fileBrowserHandler and fileSystemProvider
// WORKS
chrome.downloads.onCreated.addListener((item) => {
alert(1)
})
// Doesn't work
chrome.fileBrowserHandler.onExecute.addListener((id, details) => {
alert(2)
})
// Doesn't work
chrome.fileSystemProvider.onOpenFileRequested.addListener(() => {
alert(3)
})

Best way to implement notification alerts in Node/Express app?

I have inherited a Node/Express code base and my task is to implement a notification alert in the navigation menu. The app database has a table of 'pending accounts', and the alert needs to expose the number of these pending accounts. When an account is 'approved' or 'denied', this notification alert needs to update and reflect the new total of pending accounts.
I know how to do the styling and html here, my question is how best to instantiate, maintain and pass a global dynamic variable that reflects the number of pending accounts, and how to get this variable exposed in the header view which contains the navbar where the notification is to be displayed.
This project a pretty standard Node/Express app, however it uses the view engine Pug. At the root of the view hierarchy is a layout.pug file, which loads most of the scripts and stylesheets, and this layout view in turn loads the header Pug view. When this header view loads, and every time it loads, I need this updated 'pending accounts count' value available to insert into the header view. This is what I am at a bit of a loss on how to go about.
Below is the layout.pug markup with the inclusion of the header pug view. Everything else in the project is pretty straightforward vanilla Node/Express I believe, but I am not very experienced with this stack so if any other code is needed please don't hesitate to ask and I will post. Thanks.
doctype html
html(lang="en")
head
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible', content='IE=edge')
meta(name='viewport', content='width=device-width, initial-scale=1.0, shrink-to-fit=no')
meta(name='theme-color', content='#4DA5F4')
meta(name='csrf-token', content=_csrf)
script(src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js")
block head
body
include partials/header
I tried including a script in my header.pug view, which contains the navbar element that I want to append the notification too...
link(href='/css/header/header.css' rel='stylesheet')
script(src='/js/registration/registrationsTable.js')
...
Which would run the following function on DOM load....
function getNumberOfPendingRegistrations() {
axios({
method: 'get',
url: '/admin/getNumberOfPendingRegistrations'
}).then((response) => {
if (response.status === 200) {
const { numPendingUsers } = response.data;
console.log(response.data);
} else {
console.log(response.status);
}
})
.catch((error) => {
console.error(error);
});
}
(() => {
document.addEventListener('DOMContentLoaded', () => {
getNumberOfPendingRegistrations();
which would then call the following express function....
exports.getNumberOfPendingRegistrations = (req, res) => {
RegisteredUser.find({ status: 'PENDING' }, (err, allPendingUsers) => {
if (!err) {
let numPendingUsers = 0;
allPendingUsers.forEach(() => {
numPendingUsers++;
});
return res.send(200, { numPendingUsers });
}
console.log('error');
throw err;
});
};
which would then return numPendingUsers to the axios then() function and make that variable available to the header.pug view here....
li.nav-item
a.nav-link(href='/admin/registeredUsers')
span.fa-solid.fa-pen-to-square(style="font-size: 0.7em;")
span(style="margin-left: 0.1em;") Registrations
span.notification=numPendingUsers
NumPendingUsers returns correctly to the axios .then() promise, but is somehow never made available in the header.pug view. It is always undefined. I'm not sure if its a timing issue w when the DOM is loaded, or if I'm making the variable available in .then() incorrecly or what. And also I feel like there must be a simpler way to accomplish all of this.
Figured out that I simply needed to implement a middleware to pass this data to every route. Duh.

"Scroll to Text" not working in Extension

I've built a Chrome Extension (pop-up) and one of the primary functions is opening different web pages when the user clicks on a link. Sometimes I want to focus on specific text on the new page so I'm trying to use the "scroll to text fragment" feature through my extension.
Unfortunately, when the page loads, this feature (scroll to text) fails. I have tested the exact same link manually and it works fine, but when I inject this link into the browser through my extension, nothing happens except the page loading as normal.
Here are a few more details that might help:
The problem I'm having is using Chrome.tabs.update() which is triggered by a user clicking a link in my popup
We are using manifest v2 not v3
The exact command from the popup javascript is (not tab id as it defaults to current tab):
chrome.tabs.update({ url: "http://example.com/#:~:text=example", })
In the manifest, we do not have the "tabs" permission.
Is there a special permission needed to use this feature in my extension? Is there something I need to do in my extension code to make this work as expected? I'm at a loss for next steps.
This is the exact feature I'm referring to: https://chromestatus.com/feature/4733392803332096
And here's an example of the feature in action:
https://chromestatus.com/feature/4733392803332096#:~:text=Motivation-,Navigating%20to%20a%20URL,-today%20will%20load
Any help would be greatly appreciated. Thank you.
There's no special permission so apparently it's a bug in Chrome: crbug.com/1241508
A simple workaround is to use chrome.tabs.create and close the original tab, but it flickers in the tab strip and loses the tab's back/forward history, sessionStorage, and so on.
function navigate(url) {
chrome.tabs.query({active: true, currentWindow: true}, ([tab]) => {
chrome.tabs.remove(tab.id);
chrome.tabs.create({ url, index: tab.index });
});
}
Another workaround is to set the hash part of the URL in the content script, but it requires host permissions for the navigated site in manifest.json like *://example.com/
async function navigate(url) {
if (await setUrlInContentScript(url)) {
return true;
}
const [base, hash] = url.split('#');
await onTabReceivedUrl(await new Promise(resolve => {
chrome.tabs.update({ url: base }, resolve);
}));
return setUrlInContentScript('#' + hash, 'hash');
function setUrlInContentScript(url, part = 'href') {
return new Promise(resolve => {
chrome.tabs.executeScript({
code: `location.${part}=${JSON.stringify(url)}`,
runAt: 'document_start',
}, () => resolve(!chrome.runtime.lastError));
});
}
function onTabReceivedUrl(tab) {
return new Promise(resolve => {
chrome.tabs.onUpdated.addListener(function onUpdated(tabId, info) {
if (tabId === tab.id && info.url) {
chrome.tabs.onUpdated.removeListener(onUpdated);
resolve();
}
});
});
}
}
In my case I discovered, that one of the characters ~ was encoded. You need the real characters to get Scroll to Text working.

How to inject a react app based chrome extension inside a webpage?

I'm developing a react app based chrome extension which uses Google's material design and has a couple of pages with navigation.
I want to inject the extension inside the browser tab when the extension is launched from the browser address toolbar. I've seen multiple extensions do so by injecting a div(inside the body of webpage) containing an iframe with src equal to the extension's pop-up HTML page.
I execute the following function when the extension is launched. Which basically injects the extension into the target webpage body but it appears multiple times inside the target web page.
function main() {
const extensionOrigin = "chrome-extension://" + chrome.runtime.id;
if (!location.ancestorOrigins.contains(extensionOrigin)) {
// Fetch the local React index.html page
fetch(chrome.runtime.getURL("index.html") /*, options */)
.then((response) => response.text())
.then((html) => {
const styleStashHTML = html.replace(
/\/static\//g,
`${extensionOrigin}/static/`
);
const body = document.getElementsByTagName("body")[0];
$(styleStashHTML).appendTo(body);
})
.catch((error) => {
console.warn(error);
});
}
}
See Image of Incorrect Injection
Any help or guidance would be very appreciated. Thanks!

Can you "visit" a chrome-extension when testing with Cypress?

I'm trying to test my chrome-extension using cypress.io
I can successfully load my extension by adding this to plugins/index.js:
module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, args) => {
if (browser.name === 'chrome') {
args.push('--load-extension=../bananatabs/build')
return args
}
})
}
I can open my extension's index.html on the cypress browser by navigating to
chrome-extension://ewoifjflksdjfioewjfoiwe/index.html
But when I try to "visit" it in a test, like this:
context('visit bananatabs', () => {
beforeEach(() => {
cy.visit('chrome-extension://inbalflcnihklpnmnnbdcinlfgnmplfl/index.html')
})
it('does nothing', () => {
assert(true);
});
});
it doesn't work. page reads:
Sorry, we could not load:
chrome-extension://inbalflcnihklpnmnnbdcinlfgnmplfl/index.html
In the docs all the examples use http or https protocols, not chrome-extension.
UPDATE
I can see the test page is http://localhost:54493/__/#/tests/integration/visit.spec.js and it contains an iframe with the page I'm testing, which uses chrome-extension:// protocol. I'm not sure that would ever work.
Can this be done?
Not Currently, but I've opened an issue for just that.
Cypress puts an arbitrary restriction for http/https, and could easily add support for browser specific protocols such as chrome://, resource://, and chrome-extension://
Feel free to throw a :+1: on it!

Resources