How to reset recaptcha when using react-redux-firebase - reactjs

I am working with React-Redux-Firebase. I implemented signing in with phone number. Now I am trying to implement error handling. When number is invalid I display window alert with error message. The only thing left to do is to reset recaptcha. Without it, I am getting error:
reCAPTCHA has already been rendered in this element
I was trying to do according to Firebase documentation
grecaptcha.reset(window.recaptchaWidgetId);
// Or, if you haven't stored the widget ID:
window.recaptchaVerifier.render().then(function(widgetId) {
grecaptcha.reset(widgetId);
}
but it does not work in my code. I dont have grecaptcha implemented. I tried to add it with react-grecaptcha, but it did not work.
Could someone give me a hint how to reset recaptcha after each error, please?
state = {
phone: "",
confirmationResult: {},
};
handleClick = () => {
const recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"sign-in-button",
{
size: "invisible",
}
);
firebase
.signInWithPhoneNumber(`+${this.state.phone}`, recaptchaVerifier)
.then((confirmationResult) => {
this.setState({ confirmationResult });
})
.catch((error) => {
// Error; SMS not sent
// Handle Errors Here
window.alert(`${error.code}, ${error.message}`);
recaptchaVerifier.reset(); // How can I do that?
});
};

I've been struggling with this problem for several days, maybe my answer will help someone.
export const requestRecaptchVerifier = () => {
window.recaptchaVerifier = new RecaptchaVerifier(
"recapcha-container",
{
size: "invisible",
},
auth
);
};
I then call signInWithPhone from another function and handle the error like this:
await signInWithPhone(formik.values.phone)
.then(() => {
// ... my code
})
.catch(() => {
window.recaptchaVerifier.recaptcha.reset();
window.recaptchaVerifier.clear();
});
All the difference in
window.recaptchaVerifier.recaptcha.reset()
And
window.recaptchaVerifier.clear()

I'm no expert but from the documentation and by talking with you in the comment section I think you need to pass a callback. Like this:
const recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
firebase
.signInWithPhoneNumber(`+${this.state.phone}`, recaptchaVerifier)
.then((confirmationResult) => {
this.setState({ confirmationResult });
})
.catch((error) => {
// Error; SMS not sent
// Handle Errors Here
window.alert(`${error.code}, ${error.message}`);
recaptchaVerifier.reset();
});
}
});
Reference: https://firebase.google.com/docs/auth/web/phone-auth#use-invisible-recaptcha
Hope this helps!

Related

Saving an ID value from an API to a User with GraphQL

I'm working on a video game website where a user can save a game to a list. How this is supposed to work is when the user clicks "Complete Game", the ID of the game is saved to a state that holds the value. The value is then passed into the mutation, then the mutation runs, saving the ID of the game to the users list of completed games. However, all I'm seeing in the console is this:
"GraphQLError: Variable \"$addGame\" got invalid value { gameId: 740, name: \"Halo: Combat Evolved\",
The above error continues, listing the entirety of the API response, instead of just the gameId.
I was able to successfully add the game to the list in the explorer with the following mutation:
mutation completeGame($addGame: AddNewGame!) {
completeGame(addGame: $addGame) {
_id
completedGameCount
completedGames {
gameId
}
}
}
with the following variable:
{
"addGame": {"gameId": 740}
}
How can I trim down what is being passed into the mutation to just be the gameId?
Below is the entirety of the page, except the return statement at the bottom.
const [selectedGame, setSelectedGame] = useState([]);
const [savedGameIds, setSavedGameIds] = useState(getSavedGameIds());
const [completeGame, { error }] = useMutation(COMPLETE_GAME);
const { id: gameId } = useParams();
useEffect(() => {
return () => saveGameIds(savedGameIds);
});
useEffect(() => {
async function getGameId(gameId) {
const response = await getSpecificGame(gameId);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = await response.json();
const gameData = result.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
summary: game.summary,
platforms: game.platforms,
platformId: game.platforms,
genres: game.genres,
genreId: game.genres,
}));
setSelectedGame(gameData);
}
getGameId(gameId);
}, [])
const handleCompleteGame = async (gameId) => {
const gameToComplete = selectedGame.find((game) => game.gameId === gameId);
const token = Auth.loggedIn() ? Auth.getToken() : null;
if (!token) {
return false;
}
try {
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
console.log(data);
setSavedGameIds([...savedGameIds, gameToComplete]);
} catch (err) {
console.error(err);
}
};
With the mutation working in the explorer when I'm able to explicitly define the variable, I am led to believe that the issue is not with the resolver or the typedef, so I'm going to omit those from this post because I don't want it to get too long.
However, I'd be happy to attach any extra code (resolver, typeDef, getSavedGameIds function, etc) if it would allow anyone to assist. The issue (I think) lies in getting my response to match the syntax I used in the explorer, which means trimming down everything except the gameId.
I specifically am extremely suspicious of this line
const gameToComplete = selectedGame.find((game) => game.gameId === gameId)
but I have fiddled around with that for awhile to no avail.
Thank you to anyone who is able to help!
It sounds like you're trying to pass more into your mutation then your schema is defined to allow. In this part:
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
You're spreading gameToComplete here which means everything in the gameToComplete object is going to be sent as a variable. If your schema is setup to just expect gameId to be passed in, but your error message is showing that name is also being passed in, you just need to adjust your variables to exclude everything you can't accept. Try:
const { data } = await completeGame({
variables: { addGame: { gameId } },
});

React JS Detect Close Button PiP ( Picture in picture)

I'm having trouble trying to detect an onClick or some type of event listener for the PIP feature in most browsers - specifically on Chrome.
Reading MDN docs and implementing their suggestions proved unsuccessful
https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/leavepictureinpicture_event
Here is my code so far which gives me a Cannot read properties of null error as there is no addEventListener for pipButton:
const video = document.getElementById('video');
const pipButton = document.getElementById('pipButton');
pipButton.addEventListener('click', () => {
if (document.pictureInPictureElement) {
document
.exitPictureInPicture()
.catch(error => {
// Error handling
})
} else {
// Request Picture-in-Picture
console.log('CLOSED PIP')
}
});
pipButton.addEventListener('leavepictureinpicture', function(event: any) {
console.log('Left PiP');
if (document.pictureInPictureElement) {
document.exitPictureInPicture().catch((error: any) => {
// Error handling
console.log('error :>> ', error);
});
}
});

React Native HealthKit getDailyStepCountSamples returning undefined or empty array

I'm using react-native-health to create an app to read daily step counts. I have manually added steps on the simulator Health app and the data source is showing my app and all the Health permissions are on but when I try to getDailyStepCountSamples, I get an empty array.
Also, another issue I have is that I need to initiate HealthKit each time before getting the empty array otherwise I get 'undefined'. Once I initiate HealthKit again, it will return 'undefined' AND log 'Steps: []'. Is there something wrong with my code?
import AppleHealthKit from 'react-native-health';
export function useHealthKit({
init = false,
steps = false,
}) {
const PERMS = AppleHealthKit.Constants.Permissions;
const initiate = () => {
let permissions = {
permissions: {
read: [
PERMS.StepCount,
],
},
};
AppleHealthKit.initHealthKit(permissions, (err, results) => {
if (err) {
console.log(err);
} else {
console.log('Initialized!', results),
}
});
};
const getSteps = () => {
let stepOptions = {
startDate: new Date(2021, 1, 1).toISOString(),
};
AppleHealthKit.getDailyStepCountSamples(
stepOptions,
(err, results) => {
if (err) {
return;
}
console.log('Steps: ', results);
},
);
};
init && initiate();
steps && getSteps();
}
I call this by doing the following:
const SomeView = () => {
<View>
<Button onPress={() => useHealthKit({init: true})>
<Text>Initiate HealthKit</Text>
</Button>
<Button onPress={() => console.log(useHealthKit({steps: true}))>
<Text>Console.log steps</Text>
</Button>
</View>
};
I've been trying to do something very similar all afternoon and getting the same result as you.
What fixed it for me was using this bizarre trick from one of the github threads
Basically you need to change your start date to one month earlier than you want to sample so in your case this would be 01/12/2020.
The original poster says this is caused by an issue in the C code.
I also set variables for includeManuallyAdded: true and period: 1444 in my options variable
Worked for me, hopefully it will for you!

Object is possibly undefined even with if statement

I've got a Typescript error TS2532: Object is possibly 'undefined'. I'm creating an SPA using Typescript, and on one of the pages, then you have the possibility of uploading a file. The backend is already written, so the file stored in state will be made into a binary code which the server side reads. The file setstate is set to undefined.
I've done a few searches, but the solutions don't seem to work for me. The first solution was to create an if statement:
if (this.state.uploadFile !== undefined) {
const terms = this.state.uploadFile;
// Logic
}
Whenever terms is used in the logic portion, then I get the error mentioned above.
The other solution is to tell the compiler that it is definetly defined:
const terms = this.state!.uploadFile;
This still throws the same error, and thought I might have placed the ! in the wrong place, but when I move it to const terms = this.state.termSheet!; then that causes a new error when calling terms.getAsBinary() I get the error Property 'getAsBinary' does not exist on type 'never'
Code in context:
// Imports
class SubmitIssue extends React.Component<StylesProps> {
state = {
alert: false,
amount: '',
uploadFile: undefined,
verify: false,
}
handleAmountChange(e) {
this.setState({ amount: e.target.value });
}
handleFileChange(e) {
this.setState({ uploadFile: e.target.files[0] });
}
handleVerifyChange() {
this.setState({ verify: !this.state.verify });
}
handleClick() {
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
const bodyFormData = new FormData();
bodyFormData.set('Amount', this.state.amount);
bodyFormData.set('uploadFile', this.state.termSheet.getAsBinary());
bodyFormData.set('Verify', this.state.verify.toString());
axios.post(
`${process.env.API_URL}RegisterIssue/Create`,
bodyFormData,
config
).then(res => {
console.log(res);
this.setState({ alert: true }, function() { this.sendData(); });
}).catch((error) => {
console.log(error);
})
}
sendData() {
const alertState = this.state.alert;
this.props.parentAlertCallback(alertState);
}
render() {
return (
// Design
);
}
}
export default withStyles(styles)(SubmitIssue);
So, I'm a little stumped as to what the correct way to handle this error is.
It is because you check only state.uploadFile but use state.termSheet.
There are two possible solutions:
You cat set a default value (in case terms is not defined):
const terms = this.state.termSheet ? this.state.termSheet : defaultValue;
Or you check the terms in the if statement, too:
if (this.state.uploadFile && this.state.termSheet)

Firebase - Phone number verification

So I'm building a simple app and I want to have phone number auth. However, after following the instructions on the site, nothing seems to be firing.
// App.js
handleNext = () => {
verifyPhoneNumber(this.state.phoneNumber);
this.setState(prevState => ({
activeStep: prevState.activeStep + 1
}));
};
...
<Button
size="small"
onClick={this.handleNext}
disabled={activeStep === maxSteps - 1}
id={"verify-phone-number-button"}
>
Next
</Button>
//Controller.js
export const verifyPhoneNumber = (phoneNumber) => {
console.log(phoneNumber) // +14084445555
firebase.auth().useDeviceLanguage();
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('verify-phone-number-button', {
'size': 'invisible',
'callback': function (response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
console.log("reCAPTCHA solved")
var appVerifier = window.recaptchaVerifier;
firebaseApp.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
console.log("success")
}).catch(function (error) {
console.log(error)
// Error; SMS not sent
// ...
});
}
});
}
reCAPTCHA solved never gets printed meaning it never makes it there. However, it also isn't printing any error messages so I'm unsure of what is happening.
Thanks for your help!

Resources