React Native Possible Unhandled Promise Rejections Error - react-native

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.

Related

(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);
};
})

redux-observables mergeMap w/ two outputs

I have an epic like this
/**
* Request a new account from the API
*/
export function signupApiRequest(action$: Observable, store: Store) {
return action$.ofType(SIGNUP)
.map(action => [ action, store.getState().accounts.signup ])
.mergeMap(([ action, signup ]) =>
sendCommand('/api/accounts/signup', {
name: signup.name,
email: signup.email
})
.map(response => [ response, signup.email ])
)
.mergeMap(([ response, email ]) => {
switch (response.status) {
case 409:
return [existingUser(email)];
case 201:
const succ = { type: successOf(SIGNUP) },
user = mapTokenToUser(response.body.data),
created = userCreated(user);
console.log('signup success', [succ, created]);
return [succ, created];
default:
throw new Error(`Unknown response code ${response.code}`);
}
});
}
When signing up, 'signup success' is printed and both actions are logged as they should.
However, only the first of the actions is outputted from the epic.
Why and what am I doing wrong?
Changing the order in the array makes the other value get outputted.
We've discovered this appears to be a bug in RxJS itself starting in 5.1.0. There's an exception being thrown in your reducer but it's being swallowed silently by RxJS.
https://github.com/redux-observable/redux-observable/issues/263#issuecomment-310180351
How it's interacting with redux/redux-observable is a little complicated, but ultimately not related as this can be reproduced with:
https://jsbin.com/dukixu/edit?html,js,output
const input$ = new Rx.Subject();
input$
.mergeMap(() => Rx.Observable.of(1, 2))
.subscribe(d => {
throw new Error('some error');
});
input$.next();

How to call a post method from api in a loop Angular

I am trying to do a put request to an api from array that I have. The post wants an object, and I have an array of objects. What I do is a loop itereting the length of my array of objects calling the method into my service. The problem is that just works the first one and the rest are not working. Do I should something like return promise and then call it recursively?
Here I let my method to call the api:
onUpdate() {
for (var i = 0; i < this.conditionsToUpdate.length; i++) {
this.ruleService.updateConditionsFromRule(this.rule.id, this.conditionsToUpdate[i])
.then(_ => {
this.notificationService.addToast('Condition Updated!', '', 2)
})
.catch(err => this.notificationService.handleError("Could not update the
condition!"))
}
}
Finally, on my Service I have my request:
updateConditionsFromRule(idRule: number, condition: ConditionUpdate):Promise<any> {
return this.http.post(`${this.organizationId}/rules/${idRule}/conditions`, condition)
.toPromise()
.then(res => {
const response = <{ id: String, error: IError[] }>res.json();
if (!!response && !!response.error) {
return Promise.reject(response.error)
} else {
return Promise.resolve(response)
}
}).catch(err => Promise.reject(err));
}
And as I said, it just returns me the first post we do, the rest are not being created.
Thank you a lot!
You can use Observable for this, promises will be too limited.
given your array updateConditionsFromRule, this is how to implement such a thing:
let requests:Observable<Response>[] = [];
updateConditionsFromRule.forEach( updateCondition => {
requests.push(this.http.post(`${this.organizationId}/rules/${idRule}/conditions`, condition));
});
// After our loop, requests is an array of Observables, not triggered at the moment.
//Now we use combineLatest to convert our Observable<Response>[] to a Observable<Response[]>.
//This means that the promise will resolve once the last request of the array has finished.
Observable.combineLatest(requests).toPromise()
.then(res => {
const response = <{ id: String, error: IError[] }>res.json();
if (!!response && !!response.error) {
return Promise.reject(response.error)
} else {
return Promise.resolve(response)
}
}).catch(err => Promise.reject(err));
}

RTCPeerConnection.iceConnectionState changed from checking to closed

Following the method here I'm trying to answer an audio call initiated with a Chrome browser from an iPhone simulator(with React Native).
A summary of the event sequence:
received call signal
got local stream
sent join call signal
received remote description(offer),
created PeerConnection
added local stream
received candidate
added candidate
7 and 8 repeated 15 times (that is 16 times in total)
onnegotiationneeded triggered
signalingState changed into have-remote-offer
onaddstream triggered
the callback function of setRemoteDescription was triggered, created answer.
signalingState changed into stable
iceconnectionstate changed into checking
onicecandidate triggered for the first time.
emited the candidate from 15
onicecandidate triggered for the 2nd time. The candidate is null
iceconnectionstate changed into closed
Step 7,8,9 may appear at different places after 6 and before 19.
I have been stuck on this problem for quite a while. I don't even know what to debug at this time. What are the possible causes of the closing of connection? I can post more logs if needed.
One observation is that the two RTCEvent corresponding to iceconnectionstatechange has the following properties:
isTrusted:false
The target RTCPeerConnection has
iceConnectionState:"closed"
iceGatheringState:"complete"
Here are my functions to handle remoteOffer and remoteCandidates:
WebRTCClass.prototype.onRemoteOffer = function(data) {
var ref;
if (this.active !== true) {
return;
}
var peerConnection = this.getPeerConnection(data.from);
console.log('onRemoteOffer', data,peerConnection.signalingState);
if (peerConnection.iceConnectionState !== 'new') {
return;
}
var onSuccess = (function(_this){
return function(){
console.log("setRemoteDescription onSuccess function");
_this.getLocalUserMedia((function(_this) {
return function(onSuccess,stream) {
peerConnection.addStream(_this.localStream);
var onAnswer = (function(_this) {
return function(answer) {
var onLocalDescription = function() {
return _this.transport.sendDescription({
to: data.from,
type: 'answer',
ts: peerConnection.createdAt,
description: {
sdp: answer.sdp,
type: answer.type
}
});
};
return peerConnection.setLocalDescription(new RTCSessionDescription(answer), onLocalDescription, _this.onError);
};
})(_this);
return peerConnection.createAnswer(onAnswer, _this.onError);
}
})(_this)
);
}
})(this);
return peerConnection.setRemoteDescription(new RTCSessionDescription(data.description),onSuccess,console.warn);
};
WebRTCClass.prototype.onRemoteCandidate = function(data) {
var peerConnection, ref;
if (this.active !== true) {
return;
}
if (data.to !== this.selfId) {
return;
}
console.log('onRemoteCandidate', data);
peerConnection = this.getPeerConnection(data.from);
if ((ref = peerConnection.iceConnectionState) !== "closed" && ref !== "failed" && ref !== "disconnected" && ref !== "completed") {
peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate));
}
};
I found that if I call the following two functions one by one, then it will work.
peerConnection.setRemoteDescription(new RTCSessionDescription(data.description),onSuccess,console.warn);
(...definition of onAnswer ...)
peerConnection.createAnswer(onAnswer, this.onError);
My previous codes called createAnswer within the onSuccess callback of setRemoteDescription. That did work for the react-native-webrtc demo, but not with Rocket.Chat. Still don't fully understand it. But my project can move on now.

Angular2 - Multiple dependent sequential http api calls

I am building an Angular2 app and one of the components needs to make multiple API calls which are dependent on the previous ones.
I currently have a service which makes an API call to get a list of TV shows. For each show, I then need to call a different API multiple times to step through the structure to determine if the show exists on a Plex server.
The API documentation is here
For each show, I need to make the following calls and get the correct data to determine if it exists: (Assume we have variables <TVShow>, <Season>, <Episode>)
http://baseURL/library/sections/?X-Plex-Token=xyz will tell me:
title="TV Shows" key="2"
http://baseURL/library/sections/2/all?X-Plex-Token=xyz&title=<TVShow> will tell me: key="/library/metadata/2622/children"
http://baseURL/library/metadata/2622/children?X-Plex-Token=xyz will tell me: index="<Season>" key="/library/metadata/14365/children"
http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz will tell me: index="<Episode>" which implies that the episode I have exists.
The responses are in json, I have removed a lot of the excess text. At each stage I need to check that the right fields exist (<TVShow>, <Season>, <Episode>) so that they can be used for the next call. If not, I need to return that the show does not exist. If it does, I will probably want to return an id for the show.
I have looked at lots of examples including promise, async & flatmap, but am not sure how to solve this based on the other examples I have seen.
How to chain Http calls in Angular2
Angular 2.0 And Http
Angular 2 - What to do when an Http request depends on result of another Http request
Angular 2 chained Http Get Requests with Iterable Array
nodejs async: multiple dependant HTTP API calls
How to gather the result of Web APIs on nodeJS with 'request' and 'async'
Here is what I have for getting the list of shows. (shows.service.ts)
export class ShowsHttpService {
getShows(): Observable<Show[]> {
let shows$ = this._http
.get(this._showHistoryUrl)
.map(mapShows)
.catch(this.handleError);
return shows$;
}
}
function mapShows(response:Response): Show[] {
return response.json().data.map(toShow);
}
function toShow(r:any): Show {
let show = <Show>({
episode: r.episode,
show_name: r.show_name,
season: r.season,
available : false, // I need to fill in this variable if the show is available when querying the Plex API mentioned above.
});
// My best guess is here would be the right spot to call the Plex API as we are dealing with a single show at a time at this point, but I cannot see how.
return show;
}
Here is the relevant code from the component (shows.component.ts)
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(w => this.shows = w);
console.log(this.shows);
}
Bonus points
Here are the obvious next questions that are interesting, but not necessary:
The first API query will be much faster than waiting for all of the other queries to take place (4 queries * ~10 shows). Can the initial list be returned and then updated with the available status when it is ready.
The first Plex call to get the key="2" only needs to be performed once. It could be hard coded, but instead, can it be performmed once and remembered?
Is there a way to reduce the number of API calls? I can see that I could remove the show filter, and search through the results on the client, but this doesn't seam ideal either.
The 4 calls for each show must be done sequentially, but each show can be queried in parallel for speed. Is this achievable?
Any thoughts would be much appreciated!
Not sure if I totally understand your question, but here is what I do:
I make the first http call, then when the subscribe fires, it calls completeLogin. I could then fire another http call with its own complete function and repeat the chain.
Here is the component code. The user has filled in the login information and pressed login:
onSubmit() {
console.log(' in on submit');
this.localUser.email = this.loginForm.controls["email"].value;
this.localUser.password = this.loginForm.controls["password"].value;
this.loginMessage = "";
this.checkUserValidation();
}
checkUserValidation() {
this.loginService.getLoggedIn()
.subscribe(loggedIn => {
console.log("in logged in user validation")
if(loggedIn.error != null || loggedIn.error != undefined || loggedIn.error != "") {
this.loginMessage = loggedIn.error;
}
});
this.loginService.validateUser(this.localUser);
}
This calls the loginservice ValidateUser method
validateUser(localUser: LocalUser) {
this.errorMessage = "";
this.email.email = localUser.email;
var parm = "validate~~~" + localUser.email + "/"
var creds = JSON.stringify(this.email);
var headers = new Headers();
headers.append("content-type", this.constants.jsonContentType);
console.log("making call to validate");
this.http.post(this.constants.taskLocalUrl + parm, { headers: headers })
.map((response: Response) => {
console.log("json = " + response.json());
var res = response.json();
var result = <AdminResponseObject>response.json();
console.log(" result: " + result);
return result;
})
.subscribe(
aro => {
this.aro = aro
},
error => {
console.log("in error");
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
},
() => this.completeValidateUser(localUser));
console.log("done with post");
}
completeValidateUser(localUser: LocalUser) {
if (this.aro != undefined) {
if (this.aro.errorMessage != null && this.aro.errorMessage != "") {
console.log("aro err " + this.aro.errorMessage);
this.setLoggedIn({ email: localUser.email, password: localUser.password, error: this.aro.errorMessage });
} else {
console.log("log in user");
this.loginUser(localUser);
}
} else {
this.router.navigate(['/verify']);
}
}
In my login service I make a call to the authorization service which returns an observable of token.
loginUser(localUser: LocalUser) {
this.auth.loginUser(localUser)
.subscribe(
token => {
console.log('token = ' + token)
this.token = token
},
error => {
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
this.setLoggedIn({ email: "", password: "", error: this.errorMessage });
},
() => this.completeLogin(localUser));
}
In the authorization service:
loginUser(localUser: LocalUser): Observable<Token> {
var email = localUser.email;
var password = localUser.password;
var headers = new Headers();
headers.append("content-type", this.constants.formEncodedContentType);
var creds:string = this.constants.grantString + email + this.constants.passwordString + password;
return this.http.post(this.constants.tokenLocalUrl, creds, { headers: headers })
.map(res => res.json())
}
The point here in this code, is to first call the validateUser method of the login service, upon response, based on the return information, if its valid, I call the loginUser method on the login service. This chain could continue as long as you need it to. You can set class level variables to hold the information that you need in each method of the chain to make decisions on what to do next.
Notice also that you can subscribe to the return in the service and process it there, it doesn't have to return to the component.
Okay, Here goes:
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(
w => this.shows = w,
error => this.errorMessage = error,
() => this.completeGetShows());
}
completeGetShow() {
//any logic here to deal with previous get;
this.http.get#2()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#2);
}
completeGet#2() {
//any logic here to deal with previous get;
this.http.get#3()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#3);
}
completeGet#3() {
//any logic here to deal with previous get;
//another http: call like above to infinity....
}