react-native-firebase/admob rewardedAd.load() not working the second time - react-native

rewardedAd.load() does not load ad on the second time.
Everything works fine with the loading, showing, callbacks for the first ad shown, when I then do a rewardedAd.load() again to load another ad, nothing happens. No errors, no callbacks. Then obviously calling rewardedAd.show() rusults in a admob/not-ready error.
This problem occurs in admob/firebase versions 6.2.0 and 6.3.1
if(environment == 'development'){
rewardedAd = RewardedAd.createForAdRequest(TestIds.REWARDED)
}
else if(environment == 'production'){
rewardedAd = RewardedAd.createForAdRequest(settings.adsUnitIds.ios.real)
}
// --
rewardedAd.onAdEvent((type, error, reward) => {
console.log('onAdEvent, type:', type, ', error:', error)
if(type === RewardedAdEventType.LOADED){
dispatch({ type: 'AD_LOADED' })
}
if(type == 'closed'){ // RewardedAdEventType.CLOSED isn't defined (probably bug), so just made it "closed", which works
console.log('ad closed, rewarded:', rewarded)
if(rewarded == true){
if(!doubleReward){
dispatch(rewardCoins(settings.coins.ad, 'watched-ad'))
}
else {
dispatch(rewardCoins(settings.coins.ad*2, 'watched-ad'))
}
rewarded = false
}
dispatch({ type: 'AD_CLOSED', rewarded: rewarded })
if(showEnded) showEnded()
setTimeout(() => { // Cannot reproduce bug where re-request of ad isn't available so adding this just in case
console.log('Start ad re-request')
console.log('Do we have rewardedAd variable? :', rewardedAd)
rewardedAd.load()
}, 200)
}
if(type === RewardedAdEventType.EARNED_REWARD){
console.log('earned reward!')
dispatch({ type: 'AD_REWARDED' })
rewarded = true
}
if(type === RewardedAdEventType.ERROR){
console.info('Some error with ad:', error)
}
})
rewardedAd.load()

How about try this for auto load after shown?
rewardedAd.onAdEvent((type, error, reward) => {
...
if (!rewardedAd.loaded) rewardedAd.load()
})

Related

React Native Possible Unhandled Promise Rejections Error

I'm using Agora to send a message through a data stream, it throws this error when I try to do so after changing to another channel (so from channel 1 and 2). Can anyone please help explain what this error means and how to solve it? I've been googling and I couldn't seem to find anything. Thanks a bunch!
Error: invalid argument
promiseMethodWrapper#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:2275:45
sendStreamMessage#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:99747:47
http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:98393:35
onPress#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:98579:46
onPress#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:68941:35
_performTransitionSideEffects#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:54979:22
_receiveSignal#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:54921:45
onResponderRelease#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:54830:34
invokeGuardedCallbackImpl#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:12804:21
invokeGuardedCallback#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:12898:42
invokeGuardedCallbackAndCatchFirstError#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:12902:36
executeDispatch#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:12974:48
executeDispatchesInOrder#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:12994:26
executeDispatchesAndRelease#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14069:35
forEach#[native code]
forEachAccumulated#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:13136:22
runEventsInBatch#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14093:27
runExtractedPluginEventsInBatch#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14172:25
http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14148:42
batchedUpdates$1#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:24797:20
batchedUpdates#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14055:36
_receiveRootNodeIDEvent#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14147:23
receiveTouches#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:14200:34
__callFunction#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:2798:36
http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:2530:31
__guard#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:2752:15
callFunctionReturnFlushedQueue#http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false:2529:21
callFunctionReturnFlushedQueue#[native code]
Edit:
Class
// States for buttons (touchable highlights).
state = {
micPressed: false,
dotMorsePressed: false,
dashMorsePressed: false,
channel1Pressed: false,
channel2Pressed: false,
};
componentDidMount() {
SplashScreen.hide();
this.init()
}
init = async () => {
// Initialize RtcEngine and event listeners.
engine = await RtcEngine.create('74a529c906e84fe8bfe4a236869b736f');
try {
dataStreamId = await engine.createDataStream(true, true);
} catch(error) {
console.log(error.message);
}
console.log("dataStream: ", dataStreamId);
Sound.setCategory('Playback');
beep = new Sound('beep.wav', Sound.MAIN_BUNDLE, (error) => {
if (error) {
console.log('failed to load the sound', error);
return;
}
})
engine.addListener('StreamMessage', (uid, streamId, data) => {
console.log('stream message was received: ', data, ' from: ', streamId )
receivedMorse = data;
console.log(receivedMorse);
if(receivedMorse == 'dot'){
beep.setCurrentTime(9.9);
beep.play();
} else {
beep.setCurrentTime(9.7);
beep.play();
}
})
engine.addListener('StreamMessageError', (uid, streamId, err, missed, cached) => {
console.log('StreamMessageError: ', err)
})
engine.addListener('JoinChannelSuccess', (channel, uid, elapsed) => {
console.log('channel was joined succesfully')
})
engine.addListener('LeaveChannel', (channel, uid, elapsed) => {
console.log('channel was left succesfully')
})
engine.addListener('Warning', (warn) => {
console.log('Warning', warn)
})
engine.addListener('Error', (err) => {
console.log('Error', err)
})
}
Functions:
// Functions.
micPressedFunc = () => {
// Function to enable microphone and mute speaker when mic button is pressed.
engine.adjustRecordingSignalVolume(100);
engine.adjustAudioMixingVolume(0);
};
micReleasedFunc = () => {
// Function to mute microphone and enable speaker when mic button is released.
engine.adjustRecordingSignalVolume(0);
engine.adjustAudioMixingVolume(100);
};
dotMorsePressedFunc = () => {
// Function to send dot to other users in the channel.
engine.sendStreamMessage(dataStreamId, 'dot');
beep.setCurrentTime(9.9);
beep.play();
};
dashMorsePressedFunc = () => {
// Function to send dash to other users in the channel.
engine.sendStreamMessage(dataStreamId, 'dash');
beep.setCurrentTime(9.7);
beep.play();
};
channel1PressedFunc = () => {
// Function to leave previous channel and join channel 1.
engine.leaveChannel();
engine.joinChannel(null, 'walt-channel-1', null, 0);
engine.adjustRecordingSignalVolume(0);
engine.adjustAudioMixingVolume(100);
isConnected = true;
this.setState({
channel1Pressed: true,
channel2Pressed: false,
});
};
channel2PressedFunc = () => {
// Function to leave previous channel and join channel 2.
engine.leaveChannel();
engine.joinChannel(null, 'walt-channel-2', null, 0);
engine.adjustRecordingSignalVolume(0);
engine.adjustAudioMixingVolume(100);
isConnected = true;
this.setState({
channel2Pressed: true,
channel1Pressed: false,
});
};
By looking at the documentation, it seems you are handling the events and order in the wrong way. The leave channel for example says this:
"After joining a channel, the user must call the leaveChannel method to end the call before joining another channel. This method returns 0 if the user leaves the channel and releases all resources related to the call. This method call is asynchronous, and the user has not exited the channel when the method call returns. Once the user leaves the channel, the SDK triggers the onLeaveChannel callback."
So basically, the user joining/leaving a channel is not immediate and you have to deal with those. I can't say the exact issue, but I can say that I don't think you are handling the lifecycle correctly.
At the very least, you can try to await the methods from the engine, but that alone might not be enough. You might need to leave the channel, then join the new one once you get confirmation.

(AppsFlyer / ReactNative) How can I get attribution parameter from onAppOpenAttribution?

This might be a dumb question, but currently I really need a help. Can someone please help me out?
I'm implementing AppsFlyer on my ReactNative Project (Android)
What I want to do is console.log attribution parameter.
But, there are no console.logging happening.
Could someone please read my snippet and how can I access to attribution parameter, please?
or, is there any proper way to console.log attribution parameter or save it to variable?
App.tsx
​import appsFlyer from 'react-native-appsflyer';
var testFunc = appsFlyer.onAppOpenAttribution(
    (data) => {
        console.log(data);
    }
);
appsFlyer.initSdk(
    {
        devKey: '***************************',
        isDebug: false,
    },
    (result) => {
        console.log(result);
    },
    (error) => {
        console.error(error);
    },
);
const Home: React.FC<Props> = props => {
    const [appState, setAppState] = useState(AppState.currentState);
    // ! when I press device's home button (appstate changes to background),
   // ! console.log in testFunc is not working...
  
    useEffect(() => {
        function handleAppStateChange(nextAppState) {
            if (appState.match(/active|foreground/) && nextAppState === 'background') {
                if (testFunc) {
                    testFunc();
                    testFunc = null;
                }
            }
          setAppState(nextAppState);
       }
        AppState.addEventListener('change', handleAppStateChange);
        return () => {
        AppState.removeEventListener('change', handleAppStateChange);
      };
  })
To my understanding, the onAppOpenAttribution event only triggers when you already have the app installed and click on a deep link. Try to use onInstallConversionData instead and see what happens, since it triggers once the SDK is initialized. I'd also remove the "useEffect" section entirely just to test. I hope this helps.
nevermind,
I added appsFlyer.onInstallConversionData
then it worked...
import appsFlyer from 'react-native-appsflyer';
var onInstallConversionDataCanceller = appsFlyer.onInstallConversionData((res) => {
if (JSON.parse(res.data.is_first_launch) == true) {
if (res.data.af_status === 'Non-organic') {
var media_source = res.data.media_source;
var campaign = res.data.campaign;
console.log('This is first launch and a Non-Organic install. Media source: ' + media_source + ' Campaign: ' + campaign);
} else if (res.data.af_status === 'Organic') {
console.log('This is first launch and a Organic Install');
}
} else {
console.log('This is not first launch');
}
});
var onAppOpenAttributionCanceller = appsFlyer.onAppOpenAttribution((res) => {
console.log(res)
});
appsFlyer.initSdk(
{
devKey: '***************************',
isDebug: false,
},
(result) => {
console.log(result);
},
(error) => {
console.error(error);
},
);
const Home: React.FC<Props> = props => {
const [appState, setAppState] = useState(AppState.currentState);
useEffect(() => {
function handleAppStateChange(nextAppState) {
if (appState.match(/active|foreground/) && nextAppState === 'background') {
if (onInstallConversionDataCanceller) {
onInstallConversionDataCanceller();
onInstallConversionDataCanceller = null;
}
if (onAppOpenAttributionCanceller) {
onAppOpenAttributionCanceller();
onAppOpenAttributionCanceller = null;
}
}
AppState.addEventListener('change', handleAppStateChange);
return () => {
AppState.removeEventListener('change', handleAppStateChange);
};
})

Cypress hangs in loop when running custom Chai assertion

I have been trying to create my own custom chai assertion (based on the Cypress recipe template: https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/extending-cypress__chai-assertions/cypress/support/index.js).
What I have found with the code below is that when it is run I end up with a constant loop of WRAP, if I swap this.obj with element it then results in a constant stream of GET. I do not seem to ever progress further than getRect(first).then((actual)
If anyone could help me out I'd be very grateful.
cypress/integration/test.js
describe('testing custom chai', () => {
it('uses a custom chai helper', () => {
cy.visit('https://www.bbc.co.uk/news');
cy.get('#orb-modules > header').should('be.leftAligned', '#orb-header');
});
});
cypress/support/index.js
function getRect(selector) {
if (selector === '&document') {
return cy.document().then(doc => doc.documentElement.getBoundingClientRect());
} if (typeof selector === 'string') {
return cy.get(selector).then($elem => $elem[0].getBoundingClientRect());
}
return cy.wrap(selector).then(elem => Cypress.$(elem)[0].getBoundingClientRect());
}
function getRects(first, second) {
return getRect(first).then((actual) => {
getRect(second).then(expected => [actual, expected]);
});
}
const aligned = (_chai, utils) => {
function leftAligned(element) {
getRects(element,this.obj).then((rects) => {
this.assert(
rects[0].left === rects[1].left,
'expected #{this} to be equal',
'expected #{this} to not be equal',
this._obj,
);
});
}
_chai.Assertion.addMethod('leftAligned', leftAligned);
};
chai.use(aligned);
The basic problem is that the async commands cy.get(), cy.wrap(), cy.document() can't be used in the custom assertion. My best guess is that the auto-retry mechanism is going bananas and giving you the constant loop.
Instead, you can use Cypress.$() which is the synchronous version (essentially jquery exposed on the Cypress object).
The following seems to work ok. (I renamed getRects() param to subject, as sometimes it's a selector and sometimes it's the object passed in to .should()).
Note also this._obj instead of this.obj.
function getRect(subject) {
if (subject === '&document') {
return Cypress.$(document).context.documentElement.getBoundingClientRect();
}
if (typeof subject === 'string') { // the selector passed in to assertion
return Cypress.$(subject)[0].getBoundingClientRect();
}
if (typeof subject === 'object') { // the element from cy.get() i.e this._obj
return subject[0].getBoundingClientRect();
}
return null; // something unkown
}
function getRects(first, second) {
const actual = getRect(first)
const expected = getRect(second)
return [actual, expected];
}
const aligned = (_chai, utils) => {
function leftAligned(element) {
const rects = getRects(element, this._obj)
this.assert(
rects[0].left === rects[1].left,
'expected #{this} to be equal',
'expected #{this} to not be equal',
this._obj,
);
}
_chai.Assertion.addMethod('leftAligned', leftAligned);
};
chai.use(aligned);
I was unable to test your BBC page directly, as there's a cross-origin problem occurring
Refused to display 'https://www.bbc.com/news' in a frame because it set 'X-Frame-Options' to 'sameorigin'
but it does work with a mockup page
cypress/app/bbc-sim.html
<div id="orb-modules">
<header>
<h1>Brexit: Boris Johnson's second attempt to trigger election fails</h1>
</header>
</div>
and testing like so
it('uses a custom chai helper', () => {
cy.visit('app/bbc-sim.html')
cy.get('#orb-modules > header').should('be.leftAligned', '#orb-modules');
});

React Native: Phone number not updating and continuing to give error message

I have a bug in my application where I am trying to update a phone number and when I click on save, I get the error message and the original phone number stays populated:
Obviously, something has gone wrong with validation. I was hoping it was perhaps the regex although it has a solid one, but I changed it like so:
const regex = {
userName: /^[-.\sa-zA-Z]+$/,
cardName: /^[-\sa-zA-Z]+$/,
password: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[##$%^&*()\-+!\\.]?).{8,}$/,
zip: /(^\d{5}$)|(^\d{5}-\d{4}$)/,
memberId: /^\d+$/,
// phoneNumber: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/,
phoneNumber: /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im,
email: /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/,
};
That did not help.
I am thinking it has to be the validation function, but I am staring at this thing and I can't see anything that sticks out:
_validate = props => {
const validationErrors = {
businessName: props.businessName ? '' : 'Is Required',
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ''
: 'Phone number must be valid and contain 10 digits',
};
const isValid = Object.keys(validationErrors).reduce((acc, curr) => {
if (validationErrors[curr] !== '') {
return false;
}
return acc;
}, true);
this.setState({validationErrors, displayErrors: !isValid});
return isValid;
};
UPDATE
I tried the solution in the below answer, but unfortunately that did not work.
Here is whats going on:
When I add the phone number and save it, it is in props here:
_validate = props => { and you can see that here:
{screenProps: undefined, navigation: {…}, businessName: "Ceramic Tile Distributors", businessWebsite: "", businessPhoneNumber: "8667073945", …}
but then it ceases to exist in the validationErrors object here:
const validationErrors = {
businessName: props.businessName ? "" : "Is Required",
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ""
: "Phone number must be valid and contain 10 digits"
};
and you can see that here:
{businessName: "", businessPhoneNumber: ""}
Why its re-rendering with the above as empty strings I do not know.
I can tell you that this here:
const isValid = Object.keys(validationErrors).reduce((acc, curr) => {
console.log("On line 84 of BusinessDetails: ", isValid);
if (validationErrors[acc] !== "") {
return false;
}
return acc;
}, true);
returns undefined, but why I do not know.
_validate is being used inside the _saveChanges function like so:
_saveChanges = () => {
const isValid = this._validate(this.props);
if (isValid) {
this.setState({ displaySpinner: true });
this.props
.updateInformation()
.then(() => {
this.setState({ displaySpinner: false }, () => {
this.props.navigation.goBack();
});
})
.catch(() => {
Alert.alert(
"Error",
this.props.businessPhoneNumber.length === 0
? "Please provide a business phone number. If your business phone number no longer exists, please call 1-800-NFIB-NOW to have this information deleted."
: "We couldn't save your changes. Please try again.",
[
{
text: "OK",
onPress: () => this.setState({ displaySpinner: false })
}
],
{ cancelable: false }
);
});
}
};
I can tell you that const isValid = this._validate(this.props); returns false.
When I test your code, it looks like there is no problem with your regex. But the below line is not correct
if (validationErrors[curr] !== '') {
return false;
}
You should use acc to get the values. consider the below code
if (validationErrors[acc] !== '') {
return false;
}
However, I can't run your code in my system. .reduce not working here. As a workaround, you can use below code
_validate = props => {
const validationErrors = {
businessName: props.businessName ? '' : 'Is Required',
businessPhoneNumber:
props.businessPhoneNumber.length === 0 ||
regex.phoneNumber.test(props.businessPhoneNumber)
? ''
: 'Phone number must be valid and contain 10 digits',
};
let isValid = true
Object.keys(validationErrors).map((acc, curr) => {
if (validationErrors[acc] !== '') {
isValid= false
}
});
this.setState({validationErrors, displayErrors: !isValid});
return isValid;
};

CasperJS: exit not working

Trying to open random pages through casperJS start method but some pages are loading properly and some of them are not, so in this scenario it is not exiting from casperjs.
It is getting stuck in console then need to manually exit from console using CTR+C.
casper.start("some url", function() {
if(this.status().currentHTTPStatus == 200) {
casper.echo("page is loading");
} else {
casper.echo("page is in error ");
this.exit();
}
});
Wrap it by a then step with a global stepTimeout option.
Sample code:
var casper = require('casper').create({
stepTimeout: 10000 //10s
})
casper.start()
casper.then(funtion(){
casper.open(url)
})
casper.run()
Try bypass() to ignore the next thens.
casper.start("some url", function() {
if(this.status().currentHTTPStatus == 200) {
casper.echo("page is loading");
} else {
casper.echo("page is in error ");
this.bypass(2); // Will not execute the then functions.
}
}).then(function() {
// The 1st then function.
}).then(function() {
// The 2nd then function.
})
casper.run(function() {
this.echo('Something');
this.exit(); // <--- Here.
});