I am creating a web app that is able to scan barcodes to pull up inventory of an item. I am using the QuaggaJS API to create this functionality. The problem is every time I scan the same item a different result is returned. I have tried fixing the lighting in my area so I don't think that is the issue.
import { useNavigate } from "react-router-dom";
import Quagga from "quagga";
const BarcodeScanner = () => {
const navigate = useNavigate()
const startScanner = () => {
Quagga.init(
{
inputStream: {
name: "Live",
type: "LiveStream",
constraints: {
facingMode: "environment",
},
locate: true,
},
decoder: {
readers: ["ean_reader"], //4171287788700
},
debug: {
drawBoundingBox: true,
showFrequency: true,
drawScanline: true,
showPattern: true
},
},
function (err) {
if (err) {
console.log(err);
return;
}
console.log("Initialization finished. Ready to start");
Quagga.start();
}
);
Quagga.onDetected((data) => {
console.log(data.codeResult.code);
Quagga.stop();
navigate('/home');
});
};
const stopScanner = () => {
Quagga.stop();
};
useEffect(() => {
startScanner();
return stopScanner;
}, []);
return <div id="interactive" className="viewport"></div>;
};
export default BarcodeScanner;
Related
I currently have a React App that is using QuaggaJS to create a barcode scanner component. The scanner works fine with phone cameras that only possess one camera. When dealing with newer phones that possess multiple cameras the scanner does not work because there is no way of focusing the camera so it continuously changes between all cameras.
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import Quagga from "#ericblade/quagga2";
import adapter from "webrtc-adapter";
import "./BarcodeScanner.css";
const BarcodeScanner = (props) => {
const navigate = useNavigate();
useEffect(() => {
startQuagga();
}, []);
if (
!navigator.mediaDevices &&
!(typeof navigator.mediaDevices.getUserMedia === "function")
) {
console.log("getUserMedia function is not available in this browser.");
props.onError("getUserMedia function is not available in this browser");
return;
}
function startQuagga() {
try{
Quagga.init(
{
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector("#interactive"),
constraints: {
width: 640,
height: 480,
facingMode: "environment",
},
},
locate: true,
decoder: {
readers: ["upc_reader", "code_128_reader"],
},
},
function (err) {
if (err != null) {
console.log(err);
props.onError(err);
stopScanner();
return;
}
console.log("Initialization finished. Ready to start");
Quagga.start();
}
);
}catch {
props.onError("Failed to open camera");
}
}
Quagga.onDetected((data) => {
let countDecodedCodes = 0;
let err = 0;
for (let id in data.codeResult.decodedCodes) {
let error = data.codeResult.decodedCodes[id];
if (error.error != undefined) {
countDecodedCodes++;
err += parseFloat(error.error);
}
}
if (err / countDecodedCodes < 0.9) {
props.onDetected(data.codeResult.code);
Quagga.stop();
}
});
const stopScanner = () => {
console.log("stopping Quagga")
Quagga.stop();
};
useEffect(() => {
if (props.showBottomSheet === "false") {
stopScanner();
}
}, [props.showBottomSheet]);
return <div className="barcode-scanner viewport" id="interactive"></div>;
};
export default BarcodeScanner;```
I found that by changing the resolution to 1281 (Changing the height in the constraints) Quagga will automatically choose the high resolution camera. Adding this with a box overlay to guide the user seems to have fixed the issue.
I'm trying to implement meilisearch api in React native and it is working fine with my simulator and after I publish the app some of the users cannot see the data returning from meilisearch, the error is
{"name":"Invariant Violation","framesToPop":1}
This is my code
Meilisearch.js
import axios from 'axios';
import { meilisearchConfig } from '../Config';
const MeilisearchApi = async (payload, success, failed) => {
try {
const response = await axios({
method: 'post',
url: `${meilisearchConfig?.host}indexes/activities/search`,
data: payload,
headers: {
'X-Meili-API-Key': meilisearchConfig?.apiKey,
},
});
success?.(response?.data);
} catch (err) {
failed?.(err);
}
};
export default MeilisearchApi;
This is the normalizer for returning data
import moment from 'moment';
import { IActivity, IByDateGroupFilter } from 'reducers/types';
export const activityNormalizer = (state, { hits, offset }) => {
const {
melisearchActivityData: { byDate, dates, all },
} = state;
const isRefreshing = offset === 0;
const newAll = isRefreshing ? hits : [...all, ...hits];
const datesNew: string[] = isRefreshing ? [] : dates;
const byDateNew: any = isRefreshing ? {} : byDate;
const byDateGroup: IByDateGroupFilter[] = [];
hits.forEach((activity: IActivity) => {
const date = getFormattedDate(activity.created_at);
if (byDateNew[date]) byDateNew[date].push({ ...activity });
else {
byDateNew[date] = [{ ...activity }];
datesNew.push(date);
}
});
Object.keys(byDateNew).forEach((key) => {
byDateGroup.push({
title: key,
data: byDateNew[key],
});
});
return {
dates: datesNew,
byDate: byDateNew,
byDateGroup,
all: newAll,
};
};
This is how i call my Meilisearch API method
MeilisearchApi(
{
q: search,
filters: filters,
offset: newOffset,
limit: PAGE_SIZE,
},
({ hits }: { hits: any[] }) => {
setDataLoaded(true);
setMelisearchActivitiesToRedux({ hits, offset: newOffset });
if (newOffset === 0) {
sectionList?.current?.scrollToLocation({
itemIndex: 1,
});
}
},
(err: any) => {
setDataLoaded(true);
log(err)
},
);
No Idea how this error happens, when users kill the app and logging again this works fine
The use case
I am writing a piece of code searching for movies' deatils into the https://themoviedb.org
the code is written in React 17.0.2 using yarn, visual studio code.
The issue
I am calling a function, from a useEffect, that will generate a json file from the result of the call to TheMovieDB
I got the following error, could you help me? **Error: Invalid hook call. Hooks can only be called inside of the body of a function component. **
Thank you in advance for the time you will invest on this issue
useEffect in browser_movie_db.js
useEffect(() => {
if (slideRows.length > 0 && searchTerm.length > 2) {
const searchResult = searchInTheMovieDB(category, searchTerm);
if (searchResult.results.length > 0) {
setSlideRows(searchResult);
} else {
setSlideRows(slides[category]);
}
} else {
setSlideRows(slides[category]);
console.log("slideRows", slideRows);
setSlideRows(slides[category]);
}, [category, searchTerm, slideRows, slides, theMovieDBApikey]);
}
searchInTheMovieDB function in selection-filter-themoviedb.js
export function searchInTheMovieDB(media_type, query) {
let result = {};
if (media_type === "tv") {
result = {
tv: [
{
title: `${media_type} search result`,
data: GetSearch(media_type, query),
},
],
};
} else {
result = {
movie: [
{
title: `${media_type} search result`,
data: GetSearch(media_type, query),
},
],
};
}
return result;
}
GetSearch function in selection-filter-themoviedb.js
export function GetSearch(media_type, query) {
const [content, setContent] = useState([]);
useEffect(() => {
console.log("GetSearch", `${endpoint.Search}/${media_type}?query=${query}`);
api
.get(`${endpoint.Search}/${media_type}`, {
params: {
api_key,
query,
},
})
.then((res) => {
setContent(res.data.results);
})
.catch((error) => {
console.error("error.message", error.message);
});
}, [media_type, query]);
return { [media_type]: content };
}
Error Invalid hook call.
the solution
I have increased the amount of useEffects to identify the process :
the *searchTerm changes
then, the web services is called
eventually, the slideRows variable is set with a new content
PS: I have created an async function fetching the data from the webservice
const [searchResults, setSearchResults] = useState([]);
import axios from "axios";
const api = axios.create({ baseURL: BASE_URL });
async function getSearch(media_type, query) {
api
.get(`${endpoint.Search}/${media_type}`, {
params: {
api_key,
query,
},
})
.then((res) => {
setSearchResults(res.data.results);
})
.catch((error) => {
console.error("error.message", error.message);
});
}
useEffect(() => {
if (searchResults.length > 0) {
setSlideRows({
tv: [
{
title: `${category} search result`,
data: searchResults,
},
],
});
}
}, [category, searchResults]);
useEffect(() => {
if (slideRows.length > 0 && searchTerm.length > 2) {
getSearch(category, searchTerm);
} else {
setSlideRows(slides[category]);
}
}, [searchTerm, slides]);
useEffect(() => {
if (searchResults.length > 0 && searchResults.results?.length > 0) {
setSlideRows(searchResults);
} else {
setSlideRows(slides[category]);
}
setSlideRows(slides[category]);
}, [searchResults]);
I'm trying to write a cloud function on Firebase that updates a document in Firebase when another one is written. I use the trigger method onWrite for this. I use Xstate for this, as my original code is more complex, but the idea is the same (and the problem as well). This is the code I use (Typescript):
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import { Machine, interpret } from "xstate";
admin.initializeApp({});
// This function will trigger as soon as a product of a company owner gets updated.
exports.productsOnUpdate = functions.firestore
.document(`companies/{companyId}/{products}/{productId}`)
.onWrite((change: any) => {
let done = false;
const PromiseFunction = (docIn: any) => {
console.log(docIn);
return admin
.firestore()
.collection("test")
.doc("testdoc")
.set({ products: docIn.products }, { merge: true })
.then((doc: FirebaseFirestore.WriteResult) => doc.writeTime);
};
const myMachine = Machine<any>({
id: "myMachine",
initial: "idle",
context: { doc: { products: "a product" } },
states: {
idle: {
on: {
INIT: "init"
}
},
init: {
invoke: {
id: "setDoc",
src: (context, event) => PromiseFunction(context.doc),
onDone: {
target: "success"
},
onError: {
target: "failure"
}
}
},
success: {},
failure: {}
}
}); // end of machine
const MyMachine = interpret(myMachine).onTransition(state => {
console.log(state.value);
// actually do something here
switch (state.value) {
case "INIT":
break;
case "success":
done = true;
console.log("Success");
case "failure":
console.log("Something went wrong");
default:
break;
}
});
MyMachine.start();
MyMachine.send("INIT");
while (done === false);
return "ok";
});
So, when trying to update a document in the subCollection 'products', this should trigger the function. In the log I see the following:
Absolutely nothing happens. When I make a small error in the context of MyMachine (change context: { doc: { products: "a product" } }, to context: { doc: { product: "a product" } }, I do see this:
So there seems to be something wrong with the promise handling or so. I already spent a day on this; any help is appreciated!
You should let xstate resolve your promises. Remove then statement from your PromiseFunction:
const PromiseFunction = (docIn: any) => {
console.log(docIn);
return admin
.firestore()
.collection("test")
.doc("testdoc")
.set({ products: docIn.products }, { merge: true })
};
Do handling with your resolved Promise in onDone block
onDone: {
target: "success",
actions: (ctx, e) => console.log(e), // do stuff with your resolved Promise
}
I realize there is probably a better way using cycle/time, but I'm just trying to understand the basics. Somehow, my action$ stream doesn't seem to be running; I've tried to construct multiple mock doms using xs.periodic. The test framework is mocha.
import 'mocha';
import {expect} from 'chai';
import xs from 'xstream';
import Stream from 'xstream';
import {mockDOMSource, DOMSource} from '#cycle/dom';
import {HTTPSource} from '#cycle/http';
import XStreamAdapter from '#cycle/xstream-adapter';
export interface Props {
displayAbs: boolean
}
export interface ISources {
DOM: DOMSource;
http: HTTPSource;
}
function testIntent(sources: ISources):Stream<Props> {
return xs.merge<Props>(
sources.DOM
.select('.absShow').events('click')
.mapTo( { displayAbs: true } ),
sources.DOM
.select('.absHide').events('click')
.mapTo( { displayAbs: false } )
).startWith( {displayAbs: false } );
}
describe( 'Test', ()=>{
describe( 'intent()', ()=>{
it('should change on click to shows and hides', () => {
let listenerGotEnd = false;
const mDOM$: Stream<DOMSource> = xs.periodic(1000).take(6).map(ii => {
if (ii % 2 == 0) {
return mockDOMSource(XStreamAdapter, {
'.absShow': {'click': xs.of({target: {}})}
})
}
else {
return mockDOMSource(XStreamAdapter, {
'.absHide': {'click': xs.of({target: {}})}
})
}
});
const action$ = mDOM$.map(mDOM => testIntent({
DOM: mDOM,
http: {} as HTTPSource,
})).flatten();
action$.addListener({
next: (x) => {
console.log("x is " + x.displayAbs);
},
error: (err) => {
console.log("error is:" + err);
throw err;
},
complete: () => { listenerGotEnd = true; }
});
expect(listenerGotEnd).to.equal(true);
});
});/* end of describe intent */
});
The primary reason the test is not running is because it's asynchronous, so in mocha we need to take in the done callback and then call it when our test is done.
Without using #cycle/time, this is how I would write this test:
import 'mocha';
import {expect} from 'chai';
import xs, {Stream} from 'xstream';
import {mockDOMSource, DOMSource} from '#cycle/dom';
import XStreamAdapter from '#cycle/xstream-adapter';
export interface Props {
displayAbs: boolean
}
export interface ISources {
DOM: DOMSource;
}
function testIntent(sources: ISources):Stream<Props> {
return xs.merge<Props>(
sources.DOM
.select('.absShow').events('click')
.mapTo( { displayAbs: true } ),
sources.DOM
.select('.absHide').events('click')
.mapTo( { displayAbs: false } )
).startWith( {displayAbs: false } );
}
describe('Test', () => {
describe('intent()', () => {
it('should change on click to shows and hides', (done) => {
const show$ = xs.create();
const hide$ = xs.create();
const DOM = mockDOMSource(XStreamAdapter, {
'.absShow': {
'click': show$
},
'.absHide': {
'click': hide$
}
});
const intent$ = testIntent({DOM});
const expectedValues = [
{displayAbs: false},
{displayAbs: true},
{displayAbs: false},
]
intent$.take(expectedValues.length).addListener({
next: (x) => {
expect(x).to.deep.equal(expectedValues.shift());
},
error: done,
complete: done
});
show$.shamefullySendNext({});
hide$.shamefullySendNext({});
});
});
});
This test runs in 11ms, which is a fair bit faster than using xs.periodic(1000).take(6)
For comparison, here is how I would write it with #cycle/time:
import {mockTimeSource} from '#cycle/time'
describe('Test', () => {
describe('intent()', () => {
it('should change on click to shows and hides', (done) => {
const Time = mockTimeSource();
const show$ = Time.diagram('---x-----');
const hide$ = Time.diagram('------x--');
const expected$ = Time.diagram('f--t--f--', {f: false, t: true});
const DOM = mockDOMSource({
'.absShow': {
'click': show$
},
'.absHide': {
'click': hide$
}
});
const intent$ = testIntent({DOM}).map(intent => intent.displayAbs);
Time.assertEqual(intent$, expected$);
Time.run(done);
});
});
});
The first version is effectively what #cycle/time is doing for you under the hood, this is just a slightly nicer way of writing it. It's also nice to have better error messages.