Torch working on Android but not in iOS (ReactJS) - reactjs

I'm building a QR scanner inside a ReactJS web app that is supposed to run on both Android and iOS. However, I cannot get the torch/flashlight to work on iOS.
I'm using the #blackbox-vision toolbox to handle both the torch and the QR scanner. As far as I understand you need to start the camera functionality and can use the video stream to manipulate the torch. Below code works fine on Android but not on iOS:
import { useState, useEffect, useRef } from "react";
import { QrReader } from "#blackbox-vision/react-qr-reader";
import { useTorchLight } from "#blackbox-vision/use-torch-light";
import styles from "./view.module.css";
import IconButton from "../../components/UI/iconbutton/view";
function SAQRView() {
const streamRef = useRef(null);
const [on, toggle] = useTorchLight(streamRef.current);
const [showTorchToggleButton, setShowTorchToggleButton] = useState(false);
const [msg, setMsg] = useState("");
const setRef = ({ stream }) => {
streamRef.current = stream;
setShowTorchToggleButton(true);
};
const previewStyle = {
width: "100%",
};
const onError = (error) => {
console.log(error);
};
const onTorchClick = (event) => {
toggle();
};
return (
<>
<div className={styles.container}>
<div className={styles.sub_container}>
<QrReader
delay={100}
showViewFinder={false}
style={previewStyle}
onLoad={setRef}
onError={onError}
onScan={setData}
constraints={{
facingMode: "environment",
video: true,
}}
/>
<div className={styles.footer}>
{showTorchToggleButton && (
<IconButton
icon="Flash_off"
toggleIcon="Flash_on"
isToggled={on}
onClick={onTorchClick}
/>
)}
</div>
{msg}
</div>
</div>
</>
);
}
export default SAQRView;
So then I tried manipulating the video stream manually:
import { useState, useEffect, useRef } from "react";
import { QrReader } from "#blackbox-vision/react-qr-reader";
import { useTorchLight } from "#blackbox-vision/use-torch-light";
import styles from "./view.module.css";
import IconButton from "../../components/UI/iconbutton/view";
function SAQRView() {
const streamRef = useRef(null);
const [on, toggle] = useTorchLight(streamRef.current);
const [showTorchToggleButton, setShowTorchToggleButton] = useState(false);
const [msg, setMsg] = useState("");
const setRef = ({ stream }) => {
streamRef.current = stream;
setShowTorchToggleButton(true);
};
const previewStyle = {
width: "100%",
};
const onError = (error) => {
console.log(error);
};
const onTorchClick = (event) => {
const tracks = streamRef.current.getVideoTracks();
const track = tracks[0];
setMsg(JSON.stringify(track.getCapabilities(), null, 2));
try {
if (!track.getCapabilities().torch) {
alert("No torch available.");
}
track.applyConstraints({
advanced: [
{
torch: true,
},
],
});
} catch (error) {
alert(error);
}
};
return (
<>
<div className={styles.container}>
<div className={styles.sub_container}>
<QrReader
delay={100}
showViewFinder={false}
style={previewStyle}
onLoad={setRef}
onError={onError}
onScan={setData}
constraints={{
facingMode: "environment",
video: true,
}}
/>
<div className={styles.footer}>
{showTorchToggleButton && (
<IconButton
icon="Flash_off"
toggleIcon="Flash_on"
isToggled={on}
onClick={onTorchClick}
/>
)}
</div>
{msg}
</div>
</div>
</>
);
}
export default SAQRView;
Again, this works on Android, but not iOS. Notice that I stringify the track capabilities and print them at the bottom of the screen. For Android this looks as follows:
And for iOS, it looks like this:
So it seems that iOS cannot access the torch capability. However, the torch will be greyed out when the QR scanner is active, so it does seem to grab hold of the torch.
Also we have tried installing the Chrome web browser but this gave exactly the same result.
Can I get around this and if so, how?

Related

react-player SeekTo is not a function

I am using react-player with my next.js site and am having trouble using seekTo function. It results in error saying: "playerRef.seekTo is not a function"
I tried "playerRef.current.seekTo(parseFloat(e.target.value))" as well but same error occurs..
My code is based on react-player demo repo: https://github.com/cookpete/react-player/blob/master/src/demo/App.js
import { useRef } from "react";
const ReactPlayer = dynamic(() => import("react-player"), { ssr: false });
const Player = ({url}) => {
const playerRef = useRef();
const [player, setPlayer] = useState({
playing: true,
muted: true,
played: 0,
loaded: 0,
duration: 0,
seeking:false
});
const handlePlayPause = function () {
setPlayer({ ...player, playing: !player.playing });
};
const handleMuted = () => {
setPlayer({ ...player, muted: !player.muted });
};
const handleSeekMouseDown = (e) => {
setPlayer({ ...player, seeking: true });
};
const handleSeekChange = (e) => {
setPlayer({ ...player, played: parseFloat(e.target.value) });
};
const handleSeekMouseUp = (e) => {
setPlayer({ ...player, seeking: false });
playerRef.seekTo(parseFloat(e.target.value));
};
const handleProgress = (state) => {
if (!player.seeking) {
setPlayer(state);
}
};
return (
<div
className={`relative justify-center content-center w-full`}
>
<ReactPlayer
ref={playerRef}
className="absolute rounded"
width="100%"
height="100%"
url={url}
volume={1}
playIcon={<></>}
playing={player.playing}
loop={true}
muted={player.muted}
onProgress={handleProgress}
></ReactPlayer>
<div
className={"absolute w-full h-full"}
onClick={handlePlayPause}
></div>
<div>
<input
type="range"
min={0}
max={0.999999}
step="any"
value={player.played}
onMouseDown={handleSeekMouseDown}
onChange={handleSeekChange}
onMouseUp={handleSeekMouseUp}
/>
</div>
</div>
);
};
export default Player;
Thank you
create another component and pass the ref and other props to this component and import ReactPlayer manually
create VPlayer.jsx ==>
import React from "react";
import ReactPlayer from "react-player";
const VPlayer = ({
playerRef,
}) => {
return (
<ReactPlayer
ref={playerRef}
/>
);
};
export default VPlayer;
import that component dynamically and pass the props to it
and use it in another component or pages ==>
import React, {useRef} from "react";
import dynamic from "next/dynamic";
const VPlayer = dynamic(() => import("./VPlayer"), {
ssr: false,
});
const VideoPlayer = () => {
const videoRef = useRef();
return (
<VPlayer
playerRef={videoRef}/>
)
}
export default VideoPlayer;
playerRef is just a ref, to access it's actual value you need to use its current property, i.e
const handleSeekMouseUp = (e) => {
setPlayer({ ...player, seeking: false });
playerRef.current?.seekTo(parseFloat(e.target.value));
};

ReactJs feedback with SVG Emojis

`
import React, { useEffect, useRef, useState, useCallback } from "react";
import Button from "../../elements/buttons/Button";
import Icon from "../../elements/icons/Icon";
function useDynamicSVGImport(name, options = {}) {
const ImportedIconRef = useRef();
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const { onCompleted, onError } = options;
useEffect(() => {
setLoading(true);
const importIcon = async () => {
try {
ImportedIconRef.current = (
await import(`./${name}.svg`)
).ReactComponent;
if (onCompleted) {
onCompleted(name, ImportedIconRef.current);
}
} catch (err) {
if (onError) {
onError(err);
}
setError(err);
} finally {
setLoading(false);
}
};
importIcon();
}, [name, onCompleted, onError]);
return { error, loading, SvgIcon: ImportedIconRef.current };
}
/**
* Simple wrapper for dynamic SVG import hook. You can implement your own wrapper,
* or even use the hook directly in your components.
*/
/** const Icon = ({ name, onCompleted, onError, ...rest }) => {
const { error, loading, SvgIcon } = useDynamicSVGImport(name, {
onCompleted,
onError
});
if (error) {
return error.message;
}
if (loading) {
return "Loading...";
}
if (SvgIcon) {
return <SvgIcon {...rest} />;
}
return null;
};
*/
export default function FacialReactions() {
const [name, setName] = useState("svg1");
const handleOnCompleted = useCallback(
(iconName) => console.log(`${iconName} successfully loaded`),
[]
);
const handleIconError = useCallback((err) => console.error(err.message), []);
return (
<div className="App">
<button
onClick={() =>
setName((prevName) => (prevName === "svg1" ? "svg2" : "svg1"))
}
>
Change Icon
</button>
<section>
<Icon icon="mood-vgood" variant="horizontal" />
<Icon icon="mood-good" variant="horizontal" />
<Icon icon="mood-neutral" variant="horizontal" />
<Icon icon="mood-bad" variant="horizontal" />
<Icon icon="mood-vbad" variant="horizontal" />
/** now i had to change the entire functionality to fit the attached Picture in the head of the question! */
/**
<Icon
name={name}
fill="gray"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
<Icon
name="svg1"
fill="gray"
width="300"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
<Icon
name="svg2"
fill="darkblue"
height="100"
onCompleted={handleOnCompleted}
onError={handleIconError}
/>
*/
</section>
</div>
);
}
`
I have been struggling with implementing the functionality of the attached design, a react feedback reaction using predefined SVG facial icons . But I can not figure out from where to start.
I have already the Icon set ready to use, however, I do not know how to come up with such functionality, How to add these SVG icons into a reusable react component!. now I had to change the entire functionality to fit the attached Picture in the head of the question!
please any help with that matter will be deeply appreciated! Thanks
You could just add onClick functionality to the svg.
<path onClick={()=>setMood(1)}></path>
For adding svg to react/jsx you can take a look at this https://blog.logrocket.com/how-to-use-svgs-in-react/

How to create infinite scroll in React and Redux?

import React, {useState, useEffect} from 'react';
import {connect} from 'react-redux';
import {
fetchRecipes
} from '../../store/actions';
import './BeerRecipes.css';
const BeerRecipes = ({recipesData, fetchRecipes}) => {
const [page, setPage] = useState(1);
const [recipes, setRecipes] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchRecipes();
}, [])
return (
<div className='beer_recipes_block'>
<div className='title_wrapper'>
<h2 className='title'>Beer recipes</h2>
</div>
<div className='beer_recipes'>
<ul className='beer_recipes_items'>
{
recipesData && recipesData.recipes && recipesData.recipes.map(recipe =>
<li className='beer_recipes_item' id={recipe.id}>{recipe.name}</li>
)
}
</ul>
</div>
</div>
);
};
const mapStateToProps = state => {
return {
recipesData: state.recipes
}
}
const mapDispatchToProps = dispatch => {
return {
fetchRecipes: () => dispatch(fetchRecipes())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(BeerRecipes);
this is my component where I would like to create infinite scroll and below is my redux-action with axios:
import axios from "axios";
import * as actionTypes from "./actionTypes";
export const fetchRecipesRequest = () => {
return {
type: actionTypes.FETCH_RECIPES_REQUEST
}
}
export const fetchRecipesSuccess = recipes => {
return {
type: actionTypes.FETCH_RECIPES_SUCCESS,
payload: recipes
}
}
export const fetchRecipesFailure = error => {
return {
type: actionTypes.FETCH_RECIPES_FAILURE,
payload: error
}
}
export const fetchRecipes = (page) => {
return (dispatch) => {
dispatch(fetchRecipesRequest)
axios
.get('https://api.punkapi.com/v2/beers?page=1')
.then(response => {
const recipes = response.data;
dispatch(fetchRecipesSuccess(recipes));
})
.catch(error => {
const errorMsg = error.message;
dispatch(fetchRecipesFailure(errorMsg));
})
}
}
I want to create a scroll. I need, firstly, to display first 10 elements and then to add 5 elements with every loading. I have 25 elements altogether and when the list is done it should start from the first five again.
Assuming you already have everything ready to load your next page. You can probably simplify the entire process by using a package like react-in-viewport so you don't have to deal with all the scroll listeners.
then you use it like this way.
import handleViewport from 'react-in-viewport';
const Block = (props: { inViewport: boolean }) => {
const { inViewport, forwardedRef } = props;
const color = inViewport ? '#217ac0' : '#ff9800';
const text = inViewport ? 'In viewport' : 'Not in viewport';
return (
<div className="viewport-block" ref={forwardedRef}>
<h3>{ text }</h3>
<div style={{ width: '400px', height: '300px', background: color }} />
</div>
);
};
const ViewportBlock = handleViewport(Block, /** options: {}, config: {} **/);
const Component = (props) => (
<div>
<div style={{ height: '100vh' }}>
<h2>Scroll down to make component in viewport</h2>
</div>
<ViewportBlock
onEnterViewport={() => console.log('This is the bottom of the content, lets dispatch to load more post ')}
onLeaveViewport={() => console.log('We can choose not to use this.')} />
</div>
))
What happen here is, it creates a 'div' which is outside the viewport, once it comes into the view port ( it means user already scrolled to the bottom ), you can call a function to load more post.
To Note: Remember to add some kind of throttle to your fetch function.

Using Draft js mention plugin with react hooks

I have been trying to get draft js mention plugin to work with react hooks but can't seem to figure what's wrong with the code. Appreciate any help on this.
import React, { useRef, useState, useEffect } from "react";
import { EditorState } from "draft-js";
import Editor from "draft-js-plugins-editor";
import createMentionPlugin, { defaultSuggestionsFilter } from "draft-js-mention-plugin";
import mentions from "./mentions";
export default function MentionEditor() {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const [suggestions, setSuggestions] = useState(mentions);
const editor = useRef(null);
useEffect(() => {
editor.current.focus();
}, [])
const mentionPlugin = createMentionPlugin();
const { MentionSuggestions } = mentionPlugin;
const plugins = [mentionPlugin];
const onSearchChange = ({ value }) => {
setSuggestions(defaultSuggestionsFilter(value, mentions))
};
return (
<div style={{ border: "1px solid gray" }}>
<Editor
editorState={editorState}
onChange={editorState => setEditorState(editorState)}
plugins={plugins}
ref={editor}
/>
<MentionSuggestions
onSearchChange={onSearchChange}
suggestions={suggestions}
/>
</div>
);
}
You need to move the draft-js plugin configuration outside the component arrow function. This is a pretty basic Draft-JS implementation using a functional component and hooks:
import React, { useState, useRef } from 'react'
import { EditorState } from 'draft-js'
import Editor from 'draft-js-plugins-editor'
import createMentionPlugin, { defaultSuggestionsFilter } from 'draft-js-mention-plugin'
import 'draft-js/dist/Draft.css'
import 'draft-js-mention-plugin/lib/plugin.css'
import mentions from "./mentions"
// Draft-JS-Mentions plugin configuration
const mentionPlugin = createMentionPlugin()
const { MentionSuggestions } = mentionPlugin
const plugins = [mentionPlugin]
const MyEditor= () => {
const [suggestions, setSuggestions] = useState(mentions)
// Draft-JS editor configuration
const [editorState, setEditorState] = useState(
() => EditorState.createEmpty(),
)
const editor = useRef(null)
// Check editor text for mentions
const onSearchChange = ({ value }) => {
setSuggestions(defaultSuggestionsFilter(value, mentions))
}
const onAddMention = () => {
}
// Focus on editor window
const focusEditor = () => {
editor.current.focus()
}
return (
<div onClick={() => focusEditor()}>
<Editor
ref={editor}
editorState={editorState}
plugins={plugins}
onChange={editorState => setEditorState(editorState)}
placeholder={'Type here...'}
/>
<MentionSuggestions
onSearchChange={onSearchChange}
suggestions={suggestions}
onAddMention={onAddMention}
/>
</div>
)
}
export default MyEditor
Just move these lines outside component and it will work:
const mentionPlugin = createMentionPlugin();
const { MentionSuggestions } = mentionPlugin;
const plugins = [mentionPlugin];
export default function MentionEditor() {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
.. ... ...
}
!!!!!!!!!!!!!!!! PAY ATTENTION !!!!!!!!!!!!
The onSearchChange method will be triggered once the '#' character is typed, so in this case it will return just 5 items that fit the empty string...
To prevent this to be happened, just check that the value we want to search is not empty:
const onSearchChange = ({ value }) => {
if (value) {
setSuggestions(defaultSuggestionsFilter(value, mentions));
}
};

React update list on state change with hooks

I am trying to make a photo album component in React which retrieves photo-urls from Firebase Storage and adds them to state with React Hooks. The problem is that the <img/> tags are never created, even though I can see that the image-urls are set correctly as state via the React Chrome Extension.
Complete code of the component:
import React, {useEffect, useState} from 'react';
import {Fab} from "#material-ui/core";
import AddIcon from '#material-ui/icons/Add';
import {colorTheme} from "../constants/colors";
import firebase from 'firebase/app';
import '#firebase/firestore';
import '#firebase/auth';
import '#firebase/storage';
export default function PhotoAlbum() {
const storageRef = firebase.storage().ref();
const [images, setImages] = useState([]);
useEffect(() => {
loadImages();
}, []);
function loadImages() {
let imageUrls = [];
const imagesRef = storageRef.child('/images');
imagesRef.listAll().then(res => {
res.items.forEach(resItem => {
resItem.getDownloadURL().then( url => {
imageUrls.push(url)
})
})
}).then(() => setImages(imageUrls)); // I think this works, I can see the urls on the state
}
function handleChange(e) {
let files = e.target.files;
for(let i = 0; i < files.length; i++){
const file = files[i];
storageRef
.child( `/images/${file.name}` )
.put(file)
.then( () => {
console.log( "Added file to storage! ", file.name );
});
}
}
return (
<div>
{images.map((url, index) => (
<img src={url} key={index.toString()} alt={'this is an image'}/> // These are never rendered
))}
<div style={styles.floatingContainer}>
<input
accept="image/*"
style={styles.input}
id="contained-button-file"
multiple
type="file"
onChange={handleChange}
/>
<label htmlFor={"contained-button-file"}>
<Fab
color='primary'
aria-label='add'
style={styles.floatingButton}
component="span">
<AddIcon/>
</Fab>
</label>
</div>
</div>
)
}
const styles = {
floatingContainer: {
borderRadius: '30px',
position: 'absolute',
right: '2vw',
bottom: '2vh'
},
floatingButton: {
backgroundColor: colorTheme.darkGreen,
},
input: {
display: 'none',
},
};
I am not that familiar with React and I am sure I have just misunderstood something. I apriciate any tips and help!
I actually solved this, the problem was that I didn't update the state when the images were loaded, I just pushed them to an array, so the view never re-rendered. I changed it to this:
function loadImages() {
const imagesRef = storageRef.child('/images');
imagesRef.listAll().then(res => {
res.items.forEach(resItem => {
resItem.getDownloadURL().then(url => {
setImages(oldArray => [...oldArray, url]) // This line has changed!
})
})
});
}
Now it works!

Resources