DEV Community

ibenge Uforo
ibenge Uforo

Posted on • Edited on

Entry-4: Building a telegram client with react-native (Part 2)

In my previous post on building a telegram client with react-native (Part 1). I outlined the basic steps in setting up and authenticating your app with Telegram.

In this post, I would be going through the process of displaying the chat list. So let's begin.

Prerequisites

  • Setup your project and Authenticated successfully. see post
  • A User Interface setup to call the methods required.

In order to display the chat list we would be building upon the Repository Example

Variables

    /** This is the client instance created from part 1, which
        is used throughout the app. You have the option of
        creating a singleton in order to use one instance of 
        the client or create a static function which returns
        the created instance.

        But note, the client should not be instantiated more       
        than once.
    **/
    private static Client client = null;


Enter fullscreen mode Exit fullscreen mode

Java Method.



/** Limit here is the number of chats to return, do note 
telegram also handles the amount of chats to be returned when 
appropriate.**/

public void loadChatList(final String limit, Promise promise) {

        if (client == null) {
            client = Telegram.getClientInstance();
        }

/** Here we run a synchronized method to avoid race conditions, situations wherein variables or stores are updated
while being accessed.

Rephrased. Since we are running on a different thread, 
we restrict access to the "getMainchatlist" variable while operations are performed on it.
**/
        synchronized (ChatHandler.getMainChatList()) {

            try {

                if (client != null) {

                    client.send(new TdApi.LoadChats(new TdApi.ChatListMain(), Integer.parseInt(limit) - ChatHandler.getMainChatList().size()), new Client.ResultHandler() {
                        @Override
                        public void onResult(TdApi.Object object) throws JSONException {
                            switch (object.getConstructor()) {
                                case TdApi.Error.CONSTRUCTOR:

                                    if (((TdApi.Error) object).code == 404) {
                                        synchronized (ChatHandler.getMainChatList()) {
                                            haveFullMainChatList = true;
                                            try {
                                                initChatlist(promise);
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                                promise.reject(e);
                                            }
                                        }

                                    } else if (((TdApi.Error) object).code == 401) { // user unauthorized
                                        Telegram.onAuthorizationStateUpdated(null);

                                    } else {
                                        System.err.println("Receive an error for LoadChats:" + newLine + object);
                                    }
                                    break;
                                case TdApi.Ok.CONSTRUCTOR:
                                    // chats had already been received through updates, let's retry request
                                    loadChatList(limit, promise);

                                    break;
                                default:
                                    System.err.println("Receive wrong response from TDLib:" + newLine + object);
                            }
                        }
                    });

                }


            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
Enter fullscreen mode Exit fullscreen mode

What the above snippet does in a nutshell is to.

  • Check if the client instance is available
  • Get the chat-list by calling the TDLIB API "loadchats" with its configurations
  • If we receive a 404, this means the chat-list has been processed by TDLIB and then initialize the chat-list.
  • In the case of a 401, we restart the authentication process.

Snippet for initialization.


/**
     * Handles the conversion to serializable data to be used in react-native
     * @throws JSONException
     */
    private void initChatlist(Promise promise) throws JSONException {
        java.util.Iterator<ChatHandler.OrderedChat> iter = ChatHandler.getMainChatList().iterator();
        Gson g = new Gson();

        WritableArray params = Arguments.createArray();
        DataHandler data = new DataHandler();


        for (int i = 0; i < chatLimits && i < ChatHandler.getMainChatList().size(); i++) {
            long chatId = iter.next().getChatId();
            TdApi.Chat chat = ChatHandler.getChat(chatId);
            synchronized (chat) {
                JSONObject jo = new JSONObject(g.toJson(chat));

                WritableMap wm = data.convertJsonToMap(jo);
                params.pushMap(wm);
            }

        }

        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("PopulatedList", params);


    }

Enter fullscreen mode Exit fullscreen mode

[Snippet above]

  • Due to the returned response from TDLIB not being compatible with React-Native, we convert it to JSON with the aid of GSON which is a ggogle library for easy serialization and deserialization of data.

  • Using the chatId as a key to access each chat, convert it to JSON, store in an array and send back as an event to React-Native

Why we make use of an iterator above is to create a "loopable" sequence as the mainChatlist is made up of a set collection, see below.


    private static final NavigableSet<OrderedChat> mainChatList = new TreeSet<>();

Enter fullscreen mode Exit fullscreen mode

Annnnnddddd "OrderedChat"


public static class OrderedChat implements Comparable<OrderedChat> {
        final long chatId;
        final TdApi.ChatPosition position;

        OrderedChat(long chatId, TdApi.ChatPosition position) {
            this.chatId = chatId;
            this.position = position;
        }

        public long getChatId() {
            return chatId;
        }

        @Override
        public int compareTo(OrderedChat o) {
            if (this.position.order != o.position.order) {
                return o.position.order < this.position.order ? -1 : 1;
            }
            if (this.chatId != o.chatId) {
                return o.chatId < this.chatId ? -1 : 1;
            }
            return 0;
        }

        @Override
        public boolean equals(Object obj) {
            OrderedChat o = (OrderedChat) obj;
            return this.chatId == o.chatId && this.position.order == o.position.order;
        }
    }

Enter fullscreen mode Exit fullscreen mode

[Snippet Above]
Built upon the repository example, this is done to quote the official page.

The positions of chats in chat lists are managed by TDLib, so the Application only needs to listen to updates that change the chat.positions field, maintain the list of all chats, sorted by the pair (position.order, chat.id) in descending order.

Se'fini und Merci.

External Links

Top comments (0)