React Webcam component doesn`t work inside chrome extension - reactjs

I have a problem with React Webcam - https://github.com/mozmorris/react-webcam
In desktop app everything works great - tried it with create-react-app 2.0 recently - web app starts, asks for permission to use camera and when user allows, camera starts, screenshots work, everything is fine.
However when i copy the exact same component inside our chrome extension there is a problem. When user clicks "change profile photo" the extension opens new page:
chrome-extension://ebjbbcedbjkaagbpbkdlhlnbeoehjmgn/page.html#/uploadsnapshot
The Uploadsnapshot component renders fine, no visible errors (nothing in the console), in the html there is the React-Webcam component element:
<video autoplay="" width="132" height="132" class="UploadPhotoSnapshot__webcam__3VOiZ" playsinline=""></video>
But the camera doesn`t start and no "allow / block camera" popup appears.
I searched for solutions, tried adding "audioCapture" and "videoCapture" to permissions in my "manifest.json" as mentioned here:
How to enable camera and microphone in packaged application for Chrome OS or Chrome extension?
It still will not ask for permission to use camera and will not start.
I also tried using different component: https://github.com/mabelanger/react-html5-camera-photo and got error when the component loads:
onCameraError DOMException: Invalid security origin
My component:
import React, {Component} from 'react';
import Webcam from "react-webcam";
import styles from "./UploadPhotoSnapshot.css";
class UploadPhotoSnapshot extends Component {
constructor(props) {
super(props);
this.state = {
activeImg: null,
imagesArr: []
}
}
setRef = webcam => {
this.webcam = webcam;
};
capture = () => {
let shot = this.webcam.getScreenshot();
let newArr = [...this.state.imagesArr];
newArr.unshift(shot);
this.setState({
activeImg: shot,
imagesArr: newArr
});
};
render() {
const videoConstraints = {
width: 132,
height: 132,
facingMode: "user"
};
let imagesPreview = null;
if (this.state.imagesArr.length > 0) {
imagesPreview = (
<div className={styles.webcamArrScroll}>
{this.state.imagesArr.map((image, index) => (
<img className={styles.webcamArrImg} src={image} alt="" key={index} />
))}
</div>
);
}
return (
<div className={styles.webcamDiv}>
<Webcam
audio={false}
height={132}
ref={this.setRef}
screenshotFormat="image/jpeg"
width={132}
videoConstraints={videoConstraints}
className={styles.webcam}
/>
<button className={styles.webcamBtnTakePhoto} onClick={this.capture}>Take a snapshot</button>
<div className={styles.webcamArr}>
{imagesPreview}
</div>
<div className={styles.buttons}>
<button className={styles.buttonText}>Cancel</button>
<button className={styles.buttonSetPhoto} onClick={this.hasFileUploaded}>Set a profile photo</button>
</div>
</div>
);
}
}
export default UploadPhotoSnapshot;
my manifest.json:
{
"manifest_version": 2,
"name": "DEV",
"description": "dev",
"version": "4.0.0",
"content_security_policy": "script-src 'self' 'unsafe-eval' https://ssl.google-analytics.com https://apis.google.com https://www.google-analytics.com; object-src 'self'",
"default_locale": "en",
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "/icons/icon_48.png",
"default_popup": "index.html"
},
"icons": {
"16": "/icons/icon_16.png",
"32": "/icons/icon_32.png",
"48": "/icons/icon_48.png",
"64": "/icons/icon_64.png",
"128": "/icons/icon_128.png"
},
"web_accessible_resources": [
"app/*",
"/images/*",
"favicon.ico"
],
"sandbox": {
"pages": ["page.html"]
},
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Alt+P"
}
}
},
"permissions": [
"tabs",
"storage",
"identity",
"audioCapture",
"videoCapture",
"identity.email"
],
"oauth2": {
"client_id": "12345.apps.googleusercontent.com",
"scopes": [
"email",
"profile",
"https://www.googleapis.com/auth/contacts.readonly"
]
},
"key": "12345"
}
Do i somehow have to invoke the "allow / deny camera" popup inside the Chrome extension component?
Would be very gratefull for any ideas/ help / hint / solutions.

This might be related to this Chromium bug that makes the extension crash (although you don't get exactly the same error).
See a solution for a possible duplicate here: Chrome Extension - getUserMedia throws "NotAllowedError: Failed due to shutdown"

I found the problem and fixed it.
It all came down to "sandbox": {
"pages": ["page.html"]
}, in the manifest.json file. When you use "sandbox", the content security origin is blocking everything. I deleted it and everything is working. Camera, sound recording, screenshots.

Related

Local Safari extension can't access CSS file

I recently converted my Chrome extension to a Safari extension using the Apple Conversion tool (XCode CLI). The extension is developed with ReactJS and SaSS. It works very well on Google Chrome but when I try it on Safari after converting it, the local extension works but the CSS file that handles the extension is not applied.
There are no errors during the conversion but the Safari developer tool indicates several errors :
console tab : Failed to load resource: You are not allowed to access the required resource.
network tab : An error occurred while trying to load the resource and the resource was requested in an insecure manner.
In the extension, we isolate CSS using iframe :
/*global chrome*/
/* src/content.js */
import React from 'react';
import ReactDOM from 'react-dom';
import Frame, { FrameContextConsumer }from 'react-frame-component';
import "./content.css";
class Main extends React.Component {
render() {
return (
<Frame head={[<link type="text/css" rel="stylesheet" href={chrome.runtime.getURL("/static/css/content.css")} ></link>]}>
<FrameContextConsumer>
{
// Callback is invoked with iframe's window and document instances
({document, window}) => {
// Render Children
return (
<div className={'my-extension'}>
<h1>Hello world - My first Extension</h1>
</div>
)
}
}
</FrameContextConsumer>
</Frame>
)
}
}
Here is the manifest.json file:
{
"short_name": "My Extension",
"name": "My Extension",
"version": "1.0",
"manifest_version": 3,
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"action": {
"default_icon": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"default_title": ""
},
"background": {
"service_worker": "background.js"
},
"content_scripts" : [
{
"matches": ["<all_urls>"],
"css": ["/css/root.css"],
"js": ["/static/js/content.js"]
}
],
"permissions": [
"activeTab",
"scripting",
"storage"
],
"host_permissions": ["https://www.google.com/*"],
"web_accessible_resources": [{
"resources": [
"/static/css/content.css",
"/static/media/*"
],
"matches": ["<all_urls>"]
}]
}
After many attempts, I did not understand the exact origin of the problem and I do not know how to solve it. Do you have any suggestions ?

String contains an invalid character when running gatsby develop

I'm building a gatsby web app, I was working with wsl on windows, and it was working well, I even deployed it to a github pages repository, which worked fine, but then I decided I didn't need to use wsl anymore, so I uninstalled it, cloned the git repository and tried to build the app using gatsby develop but in the localhost page it shows this error with no more context:
Unhandled Runtime Error.
One unhandled runtime error found in your files. See the list below to fix it:
Unknown Runtime Error
String contains an invalid character
No codeFrame could be generated
Which isn't particularly useful.
Then, by running the gh-pages command to deploy my page to a github pages repository, I get this error:
ERROR
Page data from page-data.json for the failed page "/": {
"componentChunkName": "component---src-pages-index-js",
"path": "/",
"result": {
"data": {
"allFile": {
"nodes": [
{
"name": "1_corazon-de-maple",
"id": "ac582d47-3543-54cd-83b1-9366ca594fe3",
"childImageSharp": {
"gatsbyImageData": {
"layout": "constrained",
"backgroundColor": "#f8e8d8",
"images": {
"fallback": {
"src": "/static/9acd54f4f5e8efa9b11549c85e00ea4f/f76e2/1_corazon-de-maple.png",
"srcSet": "/static/9acd54f4f5e8efa9b11549c85e00ea4f/2fe1e/1_corazon-de-maple.png 480w,\n/static/9acd54f4f5e8efa9b11549c85e00ea4f/d1fbd/1_corazon-de-maple.png
961w,\n/static/9acd54f4f5e8efa9b11549c85e00ea4f/f76e2/1_corazon-de-maple.png 1921w",
"sizes": "(min-width: 1921px) 1921px, 100vw"
},
"sources": [
{
"srcSet": "/static/9acd54f4f5e8efa9b11549c85e00ea4f/3a3a2/1_corazon-de-maple.webp 480w,\n/static/9acd54f4f5e8efa9b11549c85e00ea4f/e58ca/1_corazon-de-maple.webp
961w,\n/static/9acd54f4f5e8efa9b11549c85e00ea4f/2d899/1_corazon-de-maple.webp 1921w",
"type": "image/webp",
"sizes": "(min-width: 1921px) 1921px, 100vw"
}
]
},
"width": 1921,
"height": 1081
}
}
},
{
"name": "2_appartar",
"id": "12e5156f-1297-5068-a800-2b063db9bf20",
"childImageSharp": {
"gatsbyImageData": {
"layout": "constrained",
"backgroundColor": "#f8f8f8",
"images": {
"fallback": {
"src": "/static/86855c6fbdba7e8237ff47290ac1c15f/eb413/2_appartar.png",
"srcSet": "/static/86855c6fbdba7e8237ff47290ac1c15f/a67ed/2_appartar.png 360w,\n/static/86855c6fbdba7e8237ff47290ac1c15f/379bb/2_appartar.png
720w,\n/static/86855c6fbdba7e8237ff47290ac1c15f/eb413/2_appartar.png 1440w",
"sizes": "(min-width: 1440px) 1440px, 100vw"
},
"sources": [
{
"srcSet": "/static/86855c6fbdba7e8237ff47290ac1c15f/28975/2_appartar.webp 360w,\n/static/86855c6fbdba7e8237ff47290ac1c15f/4b463/2_appartar.webp
720w,\n/static/86855c6fbdba7e8237ff47290ac1c15f/3b606/2_appartar.webp 1440w",
"type": "image/webp",
"sizes": "(min-width: 1440px) 1440px, 100vw"
}
]
},
"width": 1440,
"height": 900
}
}
},
{
"name": "3_sipago",
"id": "65960d9a-741b-5ce9-8519-ddca5800e91d",
"childImageSharp": {
"gatsbyImageData": {
"layout": "constrained",
"backgroundColor": "#f8f8f8",
"images": {
"fallback": {
"src": "/static/3ab66ed0d806a8922cb37b7737185c68/87926/3_sipago.png",
"srcSet": "/static/3ab66ed0d806a8922cb37b7737185c68/a3fa1/3_sipago.png 350w,\n/static/3ab66ed0d806a8922cb37b7737185c68/bc3b9/3_sipago.png
700w,\n/static/3ab66ed0d806a8922cb37b7737185c68/87926/3_sipago.png 1400w",
"sizes": "(min-width: 1400px) 1400px, 100vw"
},
"sources": [
{
"srcSet": "/static/3ab66ed0d806a8922cb37b7737185c68/26a00/3_sipago.webp 350w,\n/static/3ab66ed0d806a8922cb37b7737185c68/f23f0/3_sipago.webp
700w,\n/static/3ab66ed0d806a8922cb37b7737185c68/2c2d0/3_sipago.webp 1400w",
"type": "image/webp",
"sizes": "(min-width: 1400px) 1400px, 100vw"
}
]
},
"width": 1400,
"height": 788
}
}
}
]
},
"site": {
"siteMetadata": {
"title": "Raul Meza Montoya | Portfolio"
}
},
"file": {
"id": "df889c4b-9f80-5d5e-af33-55a721c9d098",
"publicURL": "/static/cd0bd82dbb5f2ff0d8d7ae76b84f470c/cv_raul-meza-montoya.pdf"
}
},
"pageContext": {}
},
"staticQueryHashes": [
"1796249492"
]
}
failed Building static HTML for pages - 1.092s
ERROR #95313
Building static HTML failed for path "/"
See our docs page for more info on this error: https://gatsby.dev/debug-html
15 |
16 | module.exports = _defineProperty;
> 17 | module.exports["default"] = module.exports, module.exports.__esModule = true;
I checked the components in my page and it seems disabling a list of svg components fixed the page, but I can't determine why. the UXUIDesign, FrontEnd, and GraphicDesign are all svg components imported like this:
import FrontEnd from "../images/svgs/front-end_sm.svg";
<ul id="specialty-list" className="list inline-list">
<li>
<UXUIDesign className="svg inline-svg svg-icon" />
<p>Diseño UX/UI</p>
</li>
<li>
<FrontEnd className="svg inline-svg svg-icon" />
<p>Desarrollo Front End</p>
</li>
<li>
<GraphicDesign className="svg inline-svg svg-icon" />
<p>Diseño Gráfico</p>
</li>
</ul>
Update:
Transforming the svg from a regular .svg file to a .js using jsx with transform.tools seems to fix the svgs causing an error, not sure what caused it to fail, and gatsby doesn't specify what went wrong.
I think your issue comes because of the way you are importing the SVG. In Gatsby, this will work:
return <div>
<svg>
{/* some SVG magic code here */}
</svg>
</div>
But this won't:
import SomeSvg from '../svg/path/some-svg.svg'
return <div>
<SomeSvg />
</div>
Because by default, webpack won't interpret SVG files as React components. You can follow this detailed answer: Import SVG as a component in Gatsby but basically, you need to use gatsby-plugin-react-svg. After installing it:
{
resolve: 'gatsby-plugin-react-svg',
options: {
rule: {
include: /svgs/
}
}
}
Note 1: just add the folder that contains the SVG. include is a regular expression (that's why is between slashes, /) so just add the folder name
Note 2: the SVG folder must only contain SVG assets, otherwise the compilation may fail
Then, import it like you were doing:
import FrontEnd from "../images/svgs/front-end_sm.svg";
Move site into allFile query or file query
export const query = graphql`
query {
allFile(
filter: { relativeDirectory: { eq: "thumbnails" } }
sort: { fields: name }
) {
nodes {
name
id
childImageSharp {
gatsbyImageData
}
}
site {
siteMetadata {
title
}
}
}
file(name: {eq: "cv_raul-meza-montoya"}) {
id
publicURL
}
}
`

Accessing a file through JSON object?

I have some paths in a react file that look like this:
import azureIcon from './azure.png';
import dropboxIcon from './dropbox.png';
I also have a separate JSON file that looks like this:
{
"posts": [
{
"id": 1,
"name": "azure",
"description": "blah blah",
"icon": "azureIcon"
},
{
"id": 2,
"name": "dropbox",
"description": "blah blah",
"icon": "dropboxIcon"
}
]
}
Is it possible to have it identify the variable like this? Or will it not work because currently the value of "icon" is set to a string value? What would be a good way to approach this?
Thank you!
It is impossible to do this directly, but you could do something like
const icons = { azureIcon, dropboxIcon };
const MyComponent = () => {
return (
<div>
{posts.map(post => (
/*or w/e the correct way to normally render it is*/
<img src={icons[post.icon]} />
)}
</div>
)
}
I would suggest to place images in public accessible folder. Here is example from create-react-app docs section in case you use it.
So to simplify your code you can use such approach:
Place icons in public accessible folder (let`s say we have icons folder inside)
Update your config
const posts = [
{
"id": 1,
"name": "azure",
"description": "blah blah",
"icon": "azure.png"
},
{
"id": 2,
"name": "dropbox",
"description": "blah blah",
"icon": "dropbox.png"
}
]
Show icons in such manner
const Component = (posts) => (
<React.Fragment>
{posts.map(post => <img
key={post.id}
src={process.env.PUBLIC_URL + `/icons/${post.icon}`}
/>}
</React.Fragment>
)
If I have to use icons as images I would choose the way #Kison suggests.But it is nice if you could font awesome, here it is showed how to install/import font awesome https://fontawesome.com/start and you can search icon here https://fontawesome.com/icons?d=gallery and all you have to do is paste the website says html code it is like following:
<i class="fab fa-accusoft"></i> this will render azure icon and
<i class="fab fa-dropbox"></i> this will render dropbox icon.
Here these are two icons' links:
(azure icon) https://fontawesome.com/icons/accusoft?style=brands
(dropbox icon) https://fontawesome.com/icons/dropbox?style=brands

Accessing Cookies API from a React WebExtension

Summary
I'm trying to access the Cookies API from a WebExtension which uses React, but I don't know why, it throws a TypeException, as if browser.cookies did not exist.
I've checked permissions and the code is copy/pasted from the MDN docs... but still I can't get it to work.
Exception:
TypeError: browser.cookies is undefined
Code
This is the main component:
import React from 'react';
import ReactDOM from 'react-dom';
class Popup extends React.Component {
constructor(props) {
super(props);
this.state = { sessionId: null };
}
componentDidMount() {
var getting = browser.cookies.getAllCookieStores(); // This throws "TypeError: browser.cookies is undefined"
getting.then(logStores);
}
render() {
return (
<div>
<h1>My App</h1>
</div>
);
}
logStores(cookieStores) {
for (let store of cookieStores) {
console.log(`Cookie store: ${store.id}\n Tab IDs: ${store.tabIds}`);
}
}
}
ReactDOM.render(<Popup />, document.getElementById('app'));
And this is the manifest.json with the according permissions:
{
"manifest_version": 2,
"name": "MyApp",
"version": "1.0",
"browser_action": {
"browser_style": true,
"default_icon": {
"48": "images/Watermelon-48.png",
"96": "images/Watermelon-96.png"
},
"default_title": "MyApp",
"default_popup": "popup.html"
},
"permissions": ["browser", "cookies","<all_urls>","tabs"]
}
could you replace
"permissions": ["browser", "cookies","<all_urls>","tabs"]
By
"permissions": ["browserSettings", "cookies","<all_urls>","tabs"]

Right way to parse Redux state props - using super json objects

Following my trip with React and Redux, I'm facing a problem, simple in appearance, but hard to solve : I'm setting my Redux state with a very big JSON object. Those datas are retrieved from an async call. When I'm setting it, I created an entry in my reducer with
let initialState = {
pages: []
}
Then, I'm putting different pages, with their params and datas into this pagesarray. So far so good, the state is well updated.
BUT, my different app pages use only parts of it as you can imagine. For instance, I have a page named Gallery which might need my state to look like this :
"pages": [
{
"component":"Gallery",
"key": "gallery",
"title": "Galerie photos",
"url": "/galerie-photos",
"sections": [
{
"component": "Gallery",
"params": {
"images": [
{
"id": 1,
"src": "http://lorempicsum.com/futurama/627/300/1",
"alt": "alternate text"
},
{
"id": 2,
"src": "http://lorempicsum.com/futurama/500/400/2",
"alt": "alternate text"
},
{
"id": 3,
"src": "http://lorempicsum.com/futurama/400/320/3",
"alt": "alternate text"
},
{
"id": 4,
"src": "http://lorempicsum.com/futurama/800/500/4",
"alt": "alternate text"
},
{
"id": 5,
"src": "http://lorempicsum.com/futurama/320/300/5",
"alt": "alternate text"
},
{
"id": 6,
"src": "http://lorempicsum.com/futurama/420/360/6",
"alt": "alternate text"
}
]
}
}
]
}
]
In my GalleryContainer, I'm requesting only the images property as this is the only concern of my Gallery component. I'm able to retrieve this data with no problem. Testing with the following code returns the desired images array :
But as far as I tested this, the images are not retrieved by my Gallerycomponent : when console logging on the component side, I got undefined, and on the container side, no problem.
I tested something different : I set a galleryImages property via the reducer, and set it with the gallery images directly. This worked.
The questions are : Why doesn't it work in the first case, and do in the second ? Do I have to work only with datas that are set as state properties only ? Can't I have a "super" json set in my state to work with directly ?
Thanks to light me up on this one :)
// Component
import React from 'react'
const Gallery = ({images}) => (
<div>
{images.map((image, key) =>
<div key={key} className="image-element-class" style={card}>
<img src={image.src} alt={image.alt} style={imageStyle}/>
</div>
)}
</div>
)
const card = {
width: 'calc((100% / 3) - (15px / 1.5))',
marginBottom: '15px',
boxSizing: 'border-box'
}
const imageStyle = {
width: '100%'
}
export default Gallery
// Container
import { connect } from 'react-redux'
import Gallery from '../../components/pages/Gallery'
const getImages = state => {
return {
images: state.data.pages.filter(page => page.component === 'Gallery' &&(
page.sections.filter(section => section.component === 'Gallery' &&(
section.params.images
))
)),
}
}
const mapStateToProps = state => {
return getImages(state)
}
const GalleryContainer = connect(
mapStateToProps,
{}
)(Gallery)
export default GalleryContainer
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>

Resources