undefined

Messaging

ConnectyCube Chat (messaging) API is built on top of Real-time(XMPP) protocol. In order to use it you need to setup real-time connection with ConnectyCube Chat server and use it to exchange data.

By default Real-time Chat works over secure TLS connection.

Get started with SDK

Follow the Getting Started guide on how to connect ConnectyCube SDK and start building your first app.

Code samples

There is ready-to-go FREE Chat Sample to help you better understand how to integrate chat capabilities in your apps.

Connect to chat

CubeUser user = CubeUser(id: 4448514, password: "awesomepwd");

CubeChatConnection.instance.login(user)
      .then((loggedUser) {})
      .catchError((error) {});

Use connectionStateStream to listen different connection states:

CubeChatConnection.instance.connectionStateStream.listen((state) {
    log("New chat connection state is $state", TAG);

    switch (state){
        case CubeChatConnectionState.Idle:
          // instance of connection was created
          break;
        case CubeChatConnectionState.Authenticated:
          // user successfully authorised on ConnectyCube server
          break;
        case CubeChatConnectionState.AuthenticationFailure:
          // error(s) was occurred during authorisation on ConnectyCube server
          break;
        case CubeChatConnectionState.Reconnecting:
          // started reconnection to the chat
          break;
        case CubeChatConnectionState.Resumed:
          // chat connection was resumed
          break;
        case CubeChatConnectionState.Ready:
          // chat connection fully ready for realtime communications
          break;
        case CubeChatConnectionState.ForceClosed:
          // chat connection was interrupted
          break;
        case CubeChatConnectionState.Closed:
          // chat connection was closed
          break;
    }
});

Connect to chat using custom authentication providers

In some cases we don't have a user's password, for example when login via:

  • Facebook
  • Twitter
  • Firebase phone authorization
  • Custom identity authentication
  • etc.

In such cases ConnectyCube API provides possibility to use ConnectyCube session token as a password for chat connection:

// get current ConnectyCube session token and set as user's password
String token = CubeSessionManager.instance.activeSession?.token;

CubeUser user = CubeUser(id: 4448514, password: token);

Disconnect

CubeChatConnection.instance.logout();

To fully destroy chat connection use cubeChatConnection.destroy:

CubeChatConnection.instance.destroy();

Reconnection

The SDK will try to reconnect to the chat after lost connection.

To configure internal reconnection manager use next code snippet:

CubeChatConnectionSettings chatConnectionSettings = CubeChatConnectionSettings.instance;
chatConnectionSettings.totalReconnections = 5;     // set 0 to disable internal reconnection manager or value more than 0 to set quantity of times to try to reconnect, default 5 times
chatConnectionSettings.reconnectionTimeout = 5000; // timeout in milliseconds between reconnection attempts, default 5000 milliseconds

Additional to the internal reconnection manager or instead of it you can use your own reconnection manager. For it you can use for example connectivity library from pub.dev repo.

You can use code snippet below to listen internet connection state and start relogin to the chat when internet connection will be established:

connectivityStateSubscription = Connectivity().onConnectivityChanged.listen((connectivityType) {
    if (connectivityType != ConnectivityResult.none) {
        bool isChatDisconnected = CubeChatConnection.instance.chatConnectionState == CubeChatConnectionState.Closed;

        if (isChatDisconnected && CubeChatConnection.instance.currentUser != null) {
          CubeChatConnection.instance.relogin();
        }
    }
});

Additionaly you can try reconnect to the chat immediately after lost chat connection, for it use:

cubeChatConnectionStateSubscription = CubeChatConnection.instance.connectionStateStream.listen((state) {
    if (state == CubeChatConnectionState.Closed) {
        Connectivity().checkConnectivity().then((connectivityType) {
            if (connectivityType != ConnectivityResult.none) {
                if (CubeChatConnection.instance.currentUser != null) {
                    CubeChatConnection.instance.relogin();
                }
            }
        });
    }
});

Dialogs

All chats between users are organized in dialogs. The are 3 types of dialogs:

  • 1-1 chat - a dialog between 2 users.
  • group chat - a dialog between specified list of users.
  • public group chat - an open dialog. Any user from your app can chat there.
  • broadcast - chat where a message is sent to all users within application at once. All the users from the application are able to join this group. Broadcast dialogs can be created only via Admin panel.

You need to create a new dialog and then use it to chat with other users. You also can obtain a list of your existing dialogs.

Create new dialog

Create 1-1 chat

You need to pass type = CubeDialogType.PRIVATE and an id of an opponent you want to create a chat with:

CubeDialog newDialog = CubeDialog(
    CubeDialogType.PRIVATE,
    occupantsIds: [56]);

createDialog(newDialog)
    .then((createdDialog) {})
    .catchError((error) {});

Create group chat

You need to pass type = CubeDialogType.GROUP and ids of opponents you want to create a chat with:

CubeDialog newDialog = CubeDialog(
    CubeDialogType.GROUP,
    name: "Hawaii relax team",
    description: "Some description",
    occupantsIds: [56, 98, 34],
    photo: "https://some.url/to/avatar.jpeg");

  createDialog(newDialog)
      .then((createdDialog) {})
      .catchError((error) {});

Create public group chat

It's possible to create a public group chat, so any user from you application can join it. There is no a list with occupants,
this chat is just open for everybody.

You need to pass type = CubeDialogType.PUBLIC and ids of opponents you want to create a chat with:

CubeDialog newDialog = CubeDialog(
    CubeDialogType.PUBLIC,
    name: "Blockchain trends",
    description: "Public dialog Description",
    photo: "https://some.url/to/avatar.jpeg");

createDialog(newDialog)
    .then((createdDialog) {})
    .catchError((error) {});

List dialogs

It's common to request all your dialogs on every app login:

Map<String, dynamic> additionalParams = {
    'updated_at[gt]': 1583402980
};

getDialogs(additionalParams)
    .then((pagedResult) {})
    .catchError((error) {});

If you want to retrieve only dialogs updated after some specific date time, you can use updated_at[gt] filter.
This is useful if you cache dialogs somehow and do not want to obtain the whole list of your dialogs on every app start.

Update dialog

User can update group chat name, photo or add/remove occupants:

String dialogId = "5356c64ab35c12bd3b108a41";

CubeDialogCustomData customData = CubeDialogCustomData("CustomDataClassName");
customData.fields['integer_field_name'] = 12345;
customData.fields['string_field_name'] = "test string";

UpdateDialogParams updateDialogParams = UpdateDialogParams();

updateDialogParams.customData = customData;
updateDialogParams.newName = "New name";
updateDialogParams.newDescription = "New decription";
updateDialogParams.newPhoto = "";

updateDialogParams.addOccupantIds = {563547, 563549, 563550};
updateDialogParams.deleteOccupantIds = {88708};

updateDialogParams.addAdminIds = {563550};
updateDialogParams.deleteAdminIds = {88709};

updateDialogParams.addPinnedMsgIds = {"88709sdfkahfkahfk"};
updateDialogParams.deletePinnedMsgIds = {"87987sdjkgskldglsksdfkahfkahfk"};

updateDialog(dialogId, updateDialogParams.getUpdateDialogParams())
  .then((updatedDialog) {})
  .catchError((error) {});

Important note: Only group chat owner or admins can remove other users from group chat.

Remove dialog

The following snippet is used to delete a dialog:

String dialogId = "5e343635ca8bf479f70453f2";
bool force = false; // true - to delete everywhere, false - to delete for himself

deleteDialog(dialogId, force)
    .then((voidResult) {})
    .catchError((error) {});

This request will remove this dialog for current user, but other users still will be able to chat there. Only group chat owner can remove the group dialog for all users.

You can also delete multiple dialogs in a single request.

Set<String> ids = {"5e3434e0ca8bf479f70452f1", "5e3302f0ca8bf42b6c"};
bool force = false; // true - to delete everywhere, false - to delete for himself

deleteDialogs(ids, force)
    .then((deleteItemsResult) {})
    .catchError((error) {});

Subscribe to public dialog

In order to be able to chat in public dialog, you need to subscribe to it:

String dialogId = "5356c64ab35c12bd3b108a41";

subscribeToPublicDialog(dialogId)
    .then((cubeDialog) {})
    .catchError((error) {});

Unsubscribe from public dialog

String dialogId = "5356c64ab35c12bd3b108a41";

unSubscribeFromPublicDialog(dialogId)
    .then((voidResult) {})
    .catchError((error) {});

Retrieve public dialog occupants

A public chat dialog can have many occupants. There is a separated API to retrieve a list of public dialog occupants:

String dialogId = "5356c64ab35c12bd3b108a41";

getDialogOccupants(dialogId)
    .then((pagedResult) {})
    .catchError((error) {});

Add / Remove admins

Options to add or remove admins from the dialog can be done by Super admin (dialog's creator) only.
Options are supported in group chat, public or broadcast.

Up to 5 admins can be added to chat.

String dialogId = "5356c64ab35c12bd3b108a41";

addRemoveAdmins(dialogId, toAddIds: {45, 59}, toRemoveIds: {88708, 88709})
    .then((cubeDialog) {})
    .catchError((error) {});

Update notifications settings

A user can turn on/off push notifications for offline messages in a dialog.
By default push notification are turned ON, so offline user receives push notifications for new messages in a chat.

String dialogId = "5356c64ab35c12bd3b108a41";
bool enable = true; // true - to enable, false - to disable

updateDialogNotificationsSettings(dialogId, enable)
    .then((isEnabled) {})
    .catchError((error) {});

Get notifications settings

Check a status of notifications setting - either it is ON or OFF for a particular chat.

Available responses: true - enabled, false - disabled.

String dialogId = "5356c64ab35c12bd3b108a41";

getDialogNotificationsSettings(dialogId)
    .then((isEnabled) {})
    .catchError((error) {});

Chat history

Every chat dialog stores its chat history which you can retrieve:

String dialogId = "5356c64ab35c12bd3b108a41";

GetMessagesParameters params = GetMessagesParameters();
params.limit = 100;
params.filters = [RequestFilter("", "date_sent", QueryRule.GT, 1583402980)];
params.markAsRead = true;
params.sorter = RequestSorter(OrderType.DESC, "", "date_sent");

getMessages(dialogId, params.getRequestParameters())
    .then((pagedResult) {})
    .catchError((error) {});

If you want to retrieve chat messages that were sent after or before specific date time only, you can use "date_sent", QueryRule.GT or "date_sent", QueryRule.LT filter.
This is useful if you implement pagination for loading messages in your app.

Send/Receive chat messages

// some dialog, which must contains opponent's id in 'occupantsIds' for CubeDialogType.PRIVATE and
// 'dialogId' for other types of dialogs

CubeDialog cubeDialog; // some dialog, which must contains opponent's id in 'occupantsIds'
CubeMessage message = CubeMessage();
message.body = "How are you today?";
message.dateSent = DateTime.now().millisecondsSinceEpoch;
message.markable = true;
message.saveToHistory = true;

cubeDialog.sendMessage(message)
    .then((cubeMessage) {})
    .catchError((error) {});

// to listen messages
ChatMessagesManager chatMessagesManager = CubeChatConnection.instance.chatMessagesManager;
chatMessagesManager.chatMessagesStream.listen((newMessage) {
    // new message received
}).onError((error) {
    // error received
});

'Sent' status

(coming soon)

'Delivered' status

The following callback is used to track the 'delivered' status:

MessagesStatusesManager messagesStatusesManager = CubeChatConnection.instance.messagesStatusesManager;
messagesStatusesManager.deliveredStream.listen((messageStatuses){
    print("Message RECEIVED ${messageStatuses.userId}, ${messageStatuses.messageId}, ${messageStatuses.dialogId}");
});

The SDK sends the 'delivered' status automatically when the message is received by the recipient. This is controlled by markable = true parameter when you send a message. If markable is false or omitted,
then you can send the delivered status manually:

CubeDialog cubeDialog; // some dialog
CubeMessage originalMessage; // message to be marked as delivered

cubeDialog.deliverMessage(originalMessage)
    .then((voidResult) {})
    .catchError((error) {});

'Read' status

Send the 'read' status:

CubeDialog cubeDialog; // some dialog
CubeMessage originalMessage; // message to be marked as read

cubeDialog.readMessage(originalMessage)
    .then((voidResult) {})
    .catchError((error) {});

// listen read status
MessagesStatusesManager messagesStatusesManager = CubeChatConnection.instance.messagesStatusesManager;
messagesStatusesManager.readStream.listen((messageStatuses){
    print("Message READ ${messageStatuses.userId}, ${messageStatuses.messageId}, ${messageStatuses.dialogId}");
});

'Is typing' status

The following 'typing' notifications are supported:

  • typing: The user is composing a message. The user is actively interacting with a message input interface specific to this chat session (e.g., by typing in the input area of a chat screen)

  • stopped: The user had been composing but now has stopped. The user has been composing but has not interacted with the message input interface for a short period of time (e.g., 30 seconds)

Send the 'is typing' status:

CubeDialog cubeDialog; // some dialog

cubeDialog.sendIsTypingStatus();
cubeDialog.sendStopTypingStatus();

TypingStatusesManager typingStatusesManager = CubeChatConnection.instance.typingStatusesManager;
typingStatusesManager.isTypingStream.listen((typingStatus){
    // for CubeDialogType.PRIVATE typingStatus.dialogId will be null
    log("IS_TYPING received: ${typingStatus.userId}, ${typingStatus.dialogId}");
});

typingStatusesManager.stopTypingStream.listen((typingStatus){
    // for CubeDialogType.PRIVATE typingStatus.dialogId will be null
    log("STOP_TYPING received: ${typingStatus.userId}, ${typingStatus.dialogId}");
});

Attachments

Chat attachments are supported with the cloud storage API. In order to send a chat attachment you need
to upload the file to ConnectyCube cloud storage and obtain a link to the file (file UID).
Then you need to include this UID into chat message and send it.

CubeDialog cubeDialog;  // some dialog
File file;              // some file on device storage

uploadFile(file)
    .then((cubeFile) {
        CubeMessage message = CubeMessage();
        message.body = "Attachment";
        message.saveToHistory = true;
        message.markable = true;

        CubeAttachment attachment = CubeAttachment();
        attachment.uid = cubeFile.uid;
        attachment.type = CubeAttachmentType.IMAGE_TYPE;

        message.attachments = [attachment];

        return cubeDialog.sendMessage(message);
    }).catchError((error) {});

The same flow is supported on the receiver's side. When you receive a message, you need to get the file UID
and then download the file from the cloud storage.

ChatMessagesManager chatMessagesManager = CubeChatConnection.instance.chatMessagesManager;
chatMessagesManager.chatMessagesStream.listen((incomingMessage) {
  String attachmentUid = incomingMessage.attachments?.first?.uid;
  if (attachmentUid != null) {
    String attachmentUrl = getPrivateUrlForUid(attachmentUid);
  }
});

Update chat message

Update message/messages on a backend for dialog ID:

String messageId = "5e3938d3ca8bf410bc80008d";  // id of message to be updated
String dialogId = "5356c64ab35c12bd3b108a41";   // id of dialog, from which is message

UpdateMessageParameters updateMessageParameters = UpdateMessageParameters();
updateMessageParameters.newBody = "Updated body";
//updateMessageParameters.delivered = true; // mark message as delivered
updateMessageParameters.read = true;      // mark message as read

updateMessage(messageId, dialogId, updateMessageParameters.getRequestParameters())
    .then((voidResult) {})
    .catchError((error) {});

//update multiple messages
String dialogId = "5356c64ab35c12bd3b108a41";   // id of dialog, from which are messages
Set<String> messagesIds = {"5e3938d3ca8bf410bc80008d", "5e3938d3ca8bf410bc800bc80"}; //messages ids to be marked

UpdateMessageParameters updateMessageParameters = UpdateMessageParameters();
//updateMessageParameters.delivered = true; // mark message as delivered
updateMessageParameters.read = true;      // mark message as read

updateMessages(dialogId, parameters.getRequestParameters(), messagesIds)
    .then((voidResult) {})
    .catchError((error) {});

Mark as read all chat messages

The following snippet is used to mark all messages as read on a backend for dialog ID:

String dialogId = "5356c64ab35c12bd3b108a41";   // id of dialog, from which is message

UpdateMessageParameters updateMessageParameters = UpdateMessageParameters();
//updateMessageParameters.delivered = true; // mark message as delivered
updateMessageParameters.read = true;      // mark messages as read

updateMessages(dialogId, updateMessageParameters.getRequestParameters())
    .then((voidResult) {})
    .catchError((error) {});

Delete chat messages

The following snippet is used to remove chat message/messages:

List<String> ids = ["5e394e7bca8bf410bc8017b0", "5e394e7bca8bf466137fa1eb"];
bool force = false; // true - to delete everywhere, false - to delete for himself

deleteMessages(ids, force)
    .then((deleteItemsResult) {})
    .catchError((error) {});

This request will remove the messages from current user history only, without affecting the history of other users.

Unread messages count

You can request total unread messages count and unread count for particular dialog:

List<String> dialogsIds = ["5e3938d3ca8bf410bc80008c"]; // skip this parameter to get data about all dialogs
getUnreadMessagesCount(dialogsIds)
    .then((unreadCount) {}) // map contains 'total' field and matches dialogId:unreadCount 
    .catchError((error) {});

The following API is used to search for messages and chat dialogs:

GlobalSearchParams additionalParams = GlobalSearchParams();
additionalParams.limit = 3;
additionalParams.endDate = DateTime(2020, 1, 1);
additionalParams.startDate = DateTime.now();
additionalParams.dialogIds = ["5e3438c7ca8bf479f704560c"];

searchText("Search query", additionalParams.getSearchParams())
    .then((globalSearchResult) {})
    .catchError((error) {});

Please refer to Global search parameters for more info on how to form search params.

Chat alerts

When you send a chat message and the recipient/recipients is offline, then automatic push notification will be fired.

In order to receive push notifications you need to subscribe for it. Please refer to Push Notifications guide.

To configure push template which users receive - go to Dashboard Console, Chat Alerts page

Mark a client as Active/Inactive

(coming soon)

Contact list

(coming soon)

Privacy (black) list

(coming soon)

Get last activity

There is a way to get an info when a user was active last time, in seconds.

This is a modern approach for messengers apps, e.g. to display this info on a Contacts screen or on a User Profile screen.

int userId = 123234;

CubeChatConnection.instance.getLasUserActivity(userId)
    .then((seconds) {
        // 'userId' was 'seconds' ago
    }).catchError((error){
        // 'userId' never logged to the chat
    });

Last activity subscription

(coming soon)

System messages

In a case you want to send a non text message data, e.g. some meta data about chat,
some events or so - there is a system notifications API to do so:

SystemMessagesManager systemMessagesManager = CubeChatConnection.instance.systemMessagesManager;

CubeMessage systemMessage = CubeMessage();
systemMessage.recipientId = 563550;
systemMessage.properties["custom_param_1"] = "custom_param_1";
systemMessage.properties["custom_param_2"] = "custom_param_2";
systemMessage.properties["custom_param_3"] = "custom_param_3";

systemMessagesManager.sendSystemMessage(systemMessage);    

// listen system messages
systemMessagesManager.systemMessagesStream.listen((systemMessage) {
    log("Receive NEW system message: $systemMessage");
}).onError((error) {
    log("Receive system message ERROR $error"));
});