SendBird chat SDK Rubymotion integration - rubymotion

I am trying to integrate the SendBird SDK chat with Rubymotion (via its CocoaPod).
The initial steps work fine and I have been able to add the pod to my Rakefile (using motion-cocoapods) and compile with no issues, login users and create a messaging channel. However, the event handler has me stumped.
These are the steps i have been able to translate from the SendBird docs:
Initialize in app delegate
SendBird Objective C:
#import <SendBirdSDK/SendBirdSDK.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
NSString *APP_ID = #"<YOUR_APP_ID>"; // You could get it from SendBird Dashboard.
[SendBird initAppId:APP_ID];
// ...
return YES;
}
Rubymotion:
APP_ID = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
SendBird.initAppId(APP_ID)
Login to SendBird when signing in / registering in my app
SendBird Objective C:
- (void)viewDidLoad
{
// other setup code
[SendBird loginWithUserId:USER_ID andUserName:USER_NICKNAME];
}
Rubymotion:
(I store user information in Auth.current_user hash once logged in)
SendBird.loginWithUserId(Auth.current_user["id"],andUserName:Auth.current_user["name"])
Create a Messaging Channel and setup an event handler
SendBird Objective C:
[SendBird startMessagingWithUserId:targetUserId]; // 1 on 1 channel
// In your event handler
[SendBird setEventHandlerConnectBlock:^(SendBirdChannel *channel) {
// Callback for [SendBird connectWithTS:] or [SendBird connect:]
} errorBlock:^(NSInteger code) {
// Error occured due to bad APP_ID (or other unknown reason)
} channelLeftBlock:^(SendBirdChannel *channel) {
// Callback for [SendBird leaveChannel:]
} messageReceivedBlock:^(SendBirdMessage *message) {
// Received a regular chat message
} fileReceivedBlock:^(SendBirdFileLink *fileLink) {
// Received a file
} messagingStartedBlock:^(SendBirdMessagingChannel *channel) {
// Callback for [SendBird startMessagingWithUserId:] or [SendBird inviteMessagingWithChannelUrl:]
} messagingUpdatedBlock:^(SendBirdMessagingChannel *channel) {
// Callback for [SendBird inviteMessagingWithChannelUrl:]
} messagingEndedBlock:^(SendBirdMessagingChannel *channel) {
// Callback for [SendBird endMessagingWithChannelUrl:]
} allMessagingEndedBlock:^ {
// Callback for [SendBird endAllMessaging:]
} readReceivedBlock:^(SendBirdReadStatus *status) {
// When ReadStatus has been received
} messageDeliveryBlock:^(BOOL send, NSString *message, NSString *data, NSString *tempId{
// To determine the message has been successfully sent.
} mutedMessagesReceivedBlock:^(SendBirdMessage *message) {
// When soft-muted messages have been received
} mutedFileReceivedBlock:^(SendBirdFileLink *message) {
// When soft-muted files have been received
}];
Rubymotion:
Here i get in trouble. I am able to create the messaging channel but I have no idea how to write that Objective-C event handler in Rubymotion. The online translator at http://objc2rubymotion.herokuapp.com/ fails due to the ^ and * syntax. Any help is appreciated.
SendBird.startMessagingWithUserId(id_of_target_user)
#Event Handler
EDIT:
A friend who knows more Rubymotion than I do pointed me in the right direction and so now I have the following event handler:
SendBird.startMessagingWithUserId(2)
SendBird.setEventHandlerConnectBlock(
-> (channel) {
# handle connect
},
messagingStartedBlock: -> (channel) {
mp "channel created:" + channel.to_s
})
However this fails with a NoMethodError:
2016-08-03 19:48:58.711 faces_ios_v1[75673:798798] chat_screen.rb:24:in `on_load': undefined method `setEventHandlerConnectBlock:messagingStartedBlock:' for SendBird:Class (NoMethodError)
from screen_module.rb:39:in `view_did_load'
from view_controller.rb:15:in `viewDidLoad'
2016-08-03 19:48:58.723 faces_ios_v1[75673:798798] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'chat_screen.rb:24:in `on_load': undefined method `setEventHandlerConnectBlock:messagingStartedBlock:' for SendBird:Class (NoMethodError)
from screen_module.rb:39:in `view_did_load'
from view_controller.rb:15:in `viewDidLoad'
If I don't include messagingStartedBlock in the Event Handler I get the following error:
2016-08-03 19:35:20.120 faces_ios_v1[74958:793380] chat_screen.rb:14:in `on_load': undefined method `setEventHandlerConnectBlock' for SendBird:Class (NoMethodError)
from screen_module.rb:39:in `view_did_load'
from view_controller.rb:15:in `viewDidLoad'
2016-08-03 19:35:20.132 faces_ios_v1[74958:793380] *** Terminating app due to uncaught exception 'NoMethodError', reason: 'chat_screen.rb:14:in `on_load': undefined method `setEventHandlerConnectBlock' for SendBird:Class (NoMethodError)
from screen_module.rb:39:in `view_did_load'
from view_controller.rb:15:in `viewDidLoad'
This makes no sense, as setEventHandlerConnectBlock should clearly be a method of the SendBird class.
The relevant SendBird SDK documentation can be found here:
https://docs.sendbird.com/ios#messaging_channel

Okay this was solved after a lot of help from a friend.
It turns out that the SendBird Event Handler method needs to be passed ALL event blocks or it will not work. So this will work:
SendBird.startMessagingWithUserId(2)
SendBird.setEventHandlerConnectBlock(
-> (channel) { },
errorBlock: -> (code) { },
channelLeftBlock: -> (channel) { },
messageReceivedBlock: -> (message) { },
systemMessageReceivedBlock: -> (message) { },
broadcastMessageReceivedBlock: -> (message) { },
fileReceivedBlock: -> (file_link) { },
messagingStartedBlock: -> (channel) {
mp "Messaging started"
mp channel
},
messagingUpdatedBlock: -> (channel) { },
messagingEndedBlock: -> (channel) { },
allMessagingEndedBlock: -> { },
messagingHiddenBlock: -> (channel) { },
allMessagingHiddenBlock: -> { },
readReceivedBlock: -> (status) { },
typeStartReceivedBlock: -> (status) { },
typeEndReceivedBlock: -> (status) { },
allDataReceivedBlock: -> (sendBirdDataType, count) { },
messageDeliveryBlock: -> (send, message, data, temp_id) { },
mutedMessagesReceivedBlock: -> (message) { },
mutedFileReceivedBlock: -> (message) { }
)
and will output:
"Messaging started"
#<SendBirdMessagingChannel:0x10f9bcdc0>
Once i get this integration working, I will upload a tutorial / github project.
I will post back the link as a comment once it is done

Related

How to add content_available when I use fcm by aws pinpoint? (iOS background message handler)

I was checked that setBackgroundMessageHandler of #react-native-firebase/messaging module works well when tested as follows body through FCM REST API.
{
"to" : DEVICE_TOKEN,
"notification" : {
"body" : "TEST BODY",
"title": "TEST TITLE"
},
"content_available":true
}
I want to send FCM message through aws pinpoint.
When I checked, #react-native-firebase/messaging module can receive notification type message.
Therefore, to send a notification message, I create and send a message as shown below.
let params = {
ApplicationId: applicationID /* required */,
SendUsersMessageRequest: {
/* required */
MessageConfiguration: {
/* required */
GCMMessage: {
"RawContent": `{\"notification\": {\"title\": \"${title}\",\"body\":\"${message}\", \"sound\":\"default\"}, \"content_available\": \"true\"}`
},
},
Users: users,
},
};
try {
return await pinpoint.sendUsersMessages(params).promise();
} catch (err) {
console.log('Error - ', err);
}
LOG A new FCM message arrived! {"messageId":"1656032665101862","data":{"pinpoint.campaign.campaign_id":"_DIRECT"},"notification":{"body":"TEST BODY","sound":"default","title":"TEST TITLE"},"from":"560179008583"}
However, when checking the remoteMessage, the content_available value cannot be received by the app.
Is there any way to send content_available using AWS Pinpoint's GCMMessage?
If you have any guides or advice for me to solve this problem, please.

Event listeners does not trigger in expo, react-navigation but use to work previously

For :
expo --version 4.9.0 and
"#react-navigation/bottom-tabs": "^5.11.11",
"#react-navigation/drawer": "^5.12.5",
"#react-navigation/native": "^5.9.4",
"#react-navigation/stack": "^5.14.5",
Event listeners never get triggered after an event occurrences.
useFocusEffect(
React.useCallback(() => {
// Clearing all the pending information to be transferred..
OperationPending.emit("notPending");
// Listening on the refresh event
RefreshEventEmitter.addListener("refresh", () => {
sendMqttCmd("getTime");
});
// Listening for connection status on this event...
MqttConnection.addListener("status", () => {
// Setting the connection status
setIsConnected(getWSConnStatus());
});
let response = setTimeout(() => setNoResponse(true), 20000);
console.log("Just before event..");
// Creating a new listeners to execute on this event..
MqttEvents.addListener("freshDataAvailable", () => {
console.log("Executed event in getDevice");
// ----------------------------------------------
// Try to parse the got mqtt data object and if found error, handle
// it in catch section..
try {
// If length of the array to be rendered by flatlist is not 0
// then page is not refreshing any more!! Show the data..
if (getDeviceTime().hr !== undefined) {
console.log("Executed in getDevic");
// If time is readdy to fetched..
let mqttTime = getDeviceTime();
setDateTimeObj({
hourCount: Number(mqttTime.hr),
minCount: Number(mqttTime.min),
secCount: Number(mqttTime.sec),
});
// Also clear the timeout checking for no response as we got response
clearTimeout(response);
// Setting isPageRefreshing to false, let flatlist render the items
setTimeout(() => setIsPageRefeshing(false), 500);
}
} catch (error) {}
// Increasing the count, re-renders the screen, what we exactly want..
// setCount(count + 1);
});
// ----------------------------------------------
// Removing all the listeners, so that screen does not re-render after unmounting..
const unsubscribe = () => {
console.log("Also unsubscribed..");
clearTimeout(response);
clearInterval(shouldRenderInterval);
MqttConnection.removeAllListeners();
RefreshEventEmitter.removeAllListeners();
MqttEvents.removeAllListeners();
};
// ----------------------------------------------
// Returned function is called when screen is unmounted from display!!
return () => unsubscribe();
}, [
count,
MqttEvents,
OperationPending,
RefreshEventEmitter,
secCount,
isPageRefreshing,
])
);
On event triggering no console log is shown!!
Hopefully I'll get solution here,
Regards..

Expo Google Authentication doesn't work on production

I implemented Expo Authentication on my app, following the code from the doc https://docs.expo.io/guides/authentication/#google.
On local with the Expo client its working fine, in the IOS simulator and also in the web browser but when I build the app (expo build android) and try on my Android phone, the Google popup comes, I put my id and it send me back to the login page but NOTHING happen.
I put some alert to understand what was going on but I dont even get any, useEffect doesn't fire, responseGoogle doesnt seem to change.
const [requestGoogle, responseGoogle, promptAsyncGoogle] =
Google.useAuthRequest({
expoClientId:
"my_id",
androidClientId:
"my_id,
webClientId:
"my_id",
});
useEffect(() => {
alert("useEffect fired (Google)");
if (responseGoogle?.type === "success") {
const { authentication } = responseGoogle;
// success
alert("success : "+JSON.stringify(responseGoogle));
// some code to check and log the user...
} else {
alert('no success : '+JSON.stringify(responseGoogle));
}
}, [responseGoogle]);
Any idea ?
Apparently its a know bug so here is not the answer but an alternative with expo-google-sign-in :
import * as GoogleSignIn from "expo-google-sign-in";
async function loginWithGoogle() {
try {
await GoogleSignIn.askForPlayServicesAsync();
const { type, user } = await GoogleSignIn.signInAsync();
if (type === "success") {
alert(JSON.stringify(user));
}
} catch ({ message }) {
toast.show("Erreur:" + message);
alert("login: Error:" + message);
}
}

Amazon Lex intent

I have two intents:
This is the chat bot now:
After this there will be a confirmation whether he wants to invest in equity or not. If he says Yes then another intent must be started without him typing anything.
How do I achieve this?
Here is my Lambda function:
// --------------- Intents -----------------------
var type;
/**
* Called when the user specifies an intent for this skill.
*/
function dispatch(intentRequest, callback) {
// console.log(JSON.stringify(intentRequest, null, 2));
console.log(`dispatch userId=${intentRequest.userId}, intent=${intentRequest.currentIntent.name}`);
const name = intentRequest.currentIntent.name;
// Dispatch to your skill's intent handlers
if (name === 'FinancialType') {
return getFinancialType(intentRequest,callback);
}
throw new Error(`Intent with name ${name} not supported`);
}
// --------------- Main handler -----------------------
function loggingCallback(response, originalCallback) {
// console.log(JSON.stringify(response, null, 2));
originalCallback(null, response);
}
// Route the incoming request based on intent.
// The JSON body of the request is provided in the event slot.
exports.handler = (event, context, callback) => {
try {
// By default, treat the user request as coming from the America/New_York time zone.
process.env.TZ = 'America/New_York';
console.log(`event.bot.name=${event.bot.name}`);
/**
* Uncomment this if statement and populate with your Lex bot name and / or version as
* a sanity check to prevent invoking this Lambda function from an undesired Lex bot or
* bot version.
*/
/*
if (event.bot.name !== 'MakeAppointment') {
callback('Invalid Bot Name');
}
*/
dispatch(event, (response) => loggingCallback(response, callback));
} catch (err) {
callback(err);
}
};
function close(fulfillmentState, message) {
return {
dialogAction: {
type: 'Close',
fulfillmentState,
message,
},
};
}
function elicitSlot(intentName, slots, slotToElicit, message) {
return {
dialogAction: {
type: 'ElicitSlot',
intentName,
slots,
slotToElicit,
message,
},
};
}
function buildValidationResult(isValid, violatedSlot, messageContent) {
return {
isValid,
violatedSlot,
message: { contentType: 'PlainText', content: messageContent },
};
}
function getFinancialType(intentRequest,callback){
var age = intentRequest.currentIntent.slots.age;
var amount = intentRequest.currentIntent.slots.amount;
const source = intentRequest.invocationSource;
if(amount >= 10000){
type = 'Equity';
}
callback(close('Fulfilled',{contentType: 'PlainText',
content: `You have choosen to invest ` + amount + ' in ' + type }));
}
There is an option in the AWS Console for Lex to include a confirmation message. You can ask the user for confirmation there.
Documentation: http://docs.aws.amazon.com/lex/latest/dg/howitworks-manage-prompts.html#msg-prompts-context-for-msgs

The system crashes and can not display the error message

assume the code is correct and webservice timeout occurs.
The problem :
The system crashes and can not display the error message.
How to display error message? So I can provide an alternative to user when there is an error?
1)
I add this Class in the project :
public class MyClass
{
public static async Task LogInSuccess()
{
try
{
-- calling a web service here
}
catch (System.Exception _ex)
{
_strErrorMsg = _ex.InnerException.Message;
throw new Exception("LogInSuccess() " + _strErrorMsg);
}
}
}
--- In the MainPage,
2)
private async void SetUp ()
{
-- code for doing setUp task--
CallWebSvc();
}
3)
private void CallWebSvc()
{
bool ShowError = false;
System.Exception MyException = new Exception();
try
{
-- calling a web service thru the MyClass
System.Threading.Tasks.Task _blnLogInSuccess = MyClass.LogInSuccess();
await _blnLogInSuccess;
if (_blnLogInSuccess.IsCompleted)
{
g_blnLoginStatus = _blnLogInSuccess.Result;
}
}
catch (System.Exception _ex)
{
ShowError = true;
MyException = ex;
}
if (ShowError)
{
var MyMessageBox = new Windows.UI.Popups.MessageDialog("Remote Login Error:" + MyException.Message, "Start Login" );
await MyMessageBox.ShowAsync();
}
}
I assume your CallWebSvc method is async void (as, without async you cannot perform an await) If this is the case, you need to know async void doesn't do the same treatament to exceptions as async task. they aren't catched correctly. If you change your CallWebSvc from async void to async Task, you are going to receive the exception correctly.