javascriptreact-nativeasync-awaitpubnub

Fetch unread messages from PubNub and render notifications


I am trying to get unread messages from PubNub. The process is like this:

  1. Set the grant
  2. Get channel metadata
  3. get unread messages etc...

I am not having the luck running these functions in order. That one is executed after the previous one is done.

async function setGrant(){
     await pubnub.grant(
        {
            channels: [userId.toString() + '.*'],
            ttl: 55,
            read: true,
            write: true,
            update: true,
            get: true,
        },(status) => {
         return new Promise(function (resolve) {
             resolve(status)
     });
    });
}

 async function getMetadata() {
     await pubnub.objects.getAllChannelMetadata(
        {
            include: {
                customFields: true
            }
        }).then((res: any) => {
        return new Promise(function (resolve) {
            resolve(setChannelMetadata(res.data));
        });
    });
}


 async function getUnreadedMessages() {
     await pubnub.messageCounts({
        channels: channels,
        channelTimetokens: tokens,
    }).then((res: any) => {
        console.log(res);
        return new Promise(function (resolve) {
            resolve(setUnreadMessage(res));
        });
    });
}


async function setChannelsAndTokens(){
    channelMetadata.forEach((value, index) => {
        tokens.push(value.custom.lastToken);
        channels.push(value.id)
    });

    return new Promise(function (resolve) {
        getUnreadedMessages()
        resolve("Chanells and tokens set!");
    })
}

function getUnreadMessagesProccess() {
    return setGrant().then(getMetadata).then(setChannelsAndTokens).then(getUnreadedMessages)
}

EDIT:

Looks like this is the problem with rendering too. When I get getUnreadedMessages() then I am editing objects clients:

clients.forEach((value, index) => {
       if (res.channels[value.id + '-' + userId + '-chat']) {
           value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
        } else {
            value.unreadMessages = 0;
        }
      console.log("UNREAD MESSAGE", value.unreadMessages, value.username);
});

based on this I am setting showIcon:

<ClientItem
   client={item.item}
   isAdmin={isAdmin}
   navigation={navigation}
   fromAdminTab={fromAdminTab}
   showIcon={item.item.unreadMessages>0}
/>

But this icon is not showing corretly.

ClientList:

 import Colors from '@helper/Colors';
    import { useSelector } from '@redux/index';
    import { HealthierLifeOption } from '@redux/types';
    import React, { useEffect, useState } from 'react';
    import { View, Text, FlatList, ActivityIndicator } from 'react-native';
    import ClientItem from './ClientItem';
    import {usePubNub} from "pubnub-react";
    
    export type Client = {
        id: number;
        username: string;
        individualPlanPaid: boolean;
        healthierLifeOption?: HealthierLifeOption;
        company?: {
          id: number;
          companyName: string;
        };
        mentor?: {
          id: number;
          username: string;
        };
    }
    
    type Props = {
        navigation: any;
        clients: Client[];
        isAdmin?: boolean;
        isLoading: boolean;
        onEndReached: Function;
        fromAdminTab?: boolean
    }
    
    const ClientList = ({ navigation, clients, onEndReached, isLoading, isAdmin = false, fromAdminTab= false }: Props) => {
        const resultCount = useSelector(state => state.user.menteesResultCount);
        const [hasMoreResults, setHasMoreResults] = useState(true);
        const userId = useSelector(state => state.user.id);
        const pubnub = usePubNub();
    
        useEffect(() => {
    
            getUnreadMessagesProccess
          setHasMoreResults(resultCount !== clients?.length);
    
        }, [resultCount, clients]);
    
    
        async function setGrant() {
            return new Promise( async resolve => {
                await pubnub.grant({
                    channels: [
                        '100-68-chat',
                        '101-68-chat',
                        '22-2-chat',
                        '22-96-chat',
                        '73-68-chat',
                        '78-68-chat',
                        '79-68-chat',
                        '81-68-chat',
                        '90-68-chat',
                        '92-68-chat',
                        '93-68-chat',
                        '98-68-chat',
                        '99-68-chat' ],
                    ttl: 55,
                    read: true,
                    write: true,
                    update: true,
                    get: true,
                }, response => resolve(response));
            });
        }
    
        async function getMetadata() {
            const options = {include: {customFields: true}};
            return await pubnub.objects.getAllChannelMetadata(options);
        }
    
        function setChannelsAndTokens(channelMetadata, channels, tokens) {
    
            channelMetadata.data.forEach((value, index) => {
                tokens.push(value.custom.lastToken);
                channels.push(value.id.toString())
            });
        }
    
        async function getUnreadedMessages(channels, tokens) {
            return new Promise(async resolve => {
                pubnub.messageCounts({
                    channels: null,
                    channelTimetokens: tokens,
                }).then(res => {
                    clients.forEach((value, index) => {
                        if (res.channels[value.id + '-' + userId + '-chat']) {
                            value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
                        } else {
                            value.unreadMessages = 0;
                        }
                        console.log("UNREAD MESSAGE", value.unreadMessages, value.username);
                    });
                    resolve()
                })
                    .catch(error => resolve(error));
            });
        }
    
        async function getUnreadMessagesProccess() {
            const tokens = ['1000'];
            const channels = ['1234567'];
            const userId = 1234567;
    
            const auth = await setGrant(userId);
            log([{statusCode: auth.statusCode}]);
    
            const channelMetadata = await getMetadata();
            log([channelMetadata,channels,tokens]);
    
            setChannelsAndTokens(channelMetadata, channels, tokens);
            const unread = await getUnreadedMessages(channels, tokens);
            log([unread]);
    
            return unread;
        }
    
    
        return (
            <View>
                <FlatList
                    keyExtractor={item => item.id.toString()}
                    data={clients}
                    onEndReached={() => hasMoreResults ? onEndReached() : null}
                    onEndReachedThreshold={0.4}
                    renderItem={item => (
                        <ClientItem
                            client={item.item}
                            isAdmin={isAdmin}
                            navigation={navigation}
                            fromAdminTab={fromAdminTab}
                            showIcon={parseInt(item.item.unreadMessages) > 0 }
                        />
                    )}
                    ListFooterComponent={isLoading
                      ? () => (
                          <View style={{ padding: 20 }}>
                            <ActivityIndicator animating size="large" color={Colors.border_gray} />
                          </View>
                        )
                      : null
                    }
                />
            </View>
        );
    }
    export default ClientList;

ClientItem:

import React, {useEffect, useState} from 'react';
    import { Text, View, Image, TouchableOpacity } from 'react-native';
    import Images from '@helper/Images';
    import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
    import Colors from '@helper/Colors';
    import styles from '../styles';
    import ClientModal from './ClientModal/ClientModal';
    import { Client } from './ClientList';
    import { HealthierLifeOption } from '@redux/types';
    
    type Props = {
        client: Client;
        navigation: any;
        isAdmin?: boolean;
        fromAdminTab?: boolean;
        showIcon?: boolean
    }
    
    const ClientItem = ({ client, navigation, isAdmin = false, fromAdminTab= false, showIcon= false }: Props) => {
        const [showModal, setShowModal] = useState(false);
    
        console.log(showIcon); // its not passed correclty
    
        let clientIcon = Images.icon.logoIconOrange 
    
        const handleContinueButton = () => {
            if(!fromAdminTab) {
                navigation.navigate('ChatFromClientsList', { selectedId: client.id, showBackButton: true });
            }else {
                setShowModal(!showModal)
            }
        };
    
        return (
            <View style={styles.client_item_container}>
            

<TouchableOpacity style={styles.client_content_container} onPress={handleContinueButton}
                >
                <View style={styles.image_container}>
                    <Image
                        style={styles.client_profile_img}
                        source={clientIcon}
                        resizeMode="contain"
                        resizeMethod="resize"
                    />
                </View>
                <View style={styles.title_container}>
                    <Text style={styles.title} >{ client.username } </Text>
                </View>
                <View style={styles.dot_container}>
                {showIcon  &&  <FontAwesome5
                    name="comment-dots"
                    size={20}
                    color={Colors.red}

                />  }
                </View>
                <View style={styles.hamburger_container} >
                    <FontAwesome5
                        name="bars"
                        size={30}
                        color={Colors.black}
                        onPress={() => setShowModal(!showModal)}
                    />
                </View>
                <View>
                    <ClientModal
                        isVisible={showModal}
                        onCollapse={(value: boolean) => setShowModal(value)}
                        closeModal={(value: boolean) => setShowModal(value)}
                        client={client}
                        navigation={navigation}
                        isAdmin={isAdmin}
                    />
                </View>
            </TouchableOpacity>
        </View>
    );
};
export default ClientItem;

I am not sure if it is because of fethichng data from Pubnub or because of rendering or how i process data in foreach.


Solution

  • Run function after previous one is done

    Using async and await in an empirical coding style. This helps organize the code. Making the code easy to read. This helps also for order of operations.

    What we did was add a return in front of the await and promise in each function. That allows us to assign result variables after each function is finished executing while preserving order of execution as the code is written.

    💡 Note: when the SDK is initialized with the secretKey, there is no need to grant yourself access to resources. Simply use any method as if you already had permissions. The SDK will automatically sign all requests using the your secret key.

    Test this by pressing the Run code snippet button below the code snippet.

    (async ()=>{
    'use strict';
    
    const publishKey   = "pub-c-51f1008b-7ddf-42b7-aec9-2d0153b0e766";
    const subscribeKey = "sub-c-1597b696-05af-11e6-a6dc-02ee2ddab7fe";
    const secretKey    = "sec-c-YTVjYjUzMWMtM2MxZC00YzdiLWE0ZjAtNWRmMWVmYmNkZGNl";
    const myUUID       = "myUniqueUUID"
    
    const pubnub = new PubNub({
        publishKey   : publishKey,
        subscribeKey : subscribeKey,
        secretKey    : secretKey,
        uuid         : myUUID,
    });
    
    const unread = await getUnreadMessagesProccess();
    
    async function getUnreadMessagesProccess() {
        const tokens = ['1000'];      // placeholder change me
        const channels = ['1234567']; // placeholder change me
        const userId = 1234567;       // placeholder change me
    
        const auth = await setGrant(userId);
        log([{statusCode: auth.statusCode}]);
    
        const channelMetadata = await getMetadata();
        log([channelMetadata,channels,tokens]);
    
        setChannelsAndTokens(channelMetadata, channels, tokens);
        const unread = await getUnreadedMessages(channels, tokens);
        log([unread]);
    
        return unread;
    }
    
    async function setGrant(userId) {
        return new Promise( async resolve => {
            await pubnub.grant({
                channels: [userId.toString() + '.*'],
                ttl: 55,
                read: true,
                write: true,
                update: true,
                get: true,
            }, response => resolve(response));
        });
    }
    
    async function getMetadata() {
        const options = {include: {customFields: true}};
        return await pubnub.objects.getAllChannelMetadata(options);
    }
    
    function setChannelsAndTokens(channelMetadata, channels, tokens) {
        channelMetadata.data.forEach((value, index) => {
            tokens.push(value.custom.lastToken);
            channels.push(value.id)
        });
    }
    
    async function getUnreadedMessages(channels, tokens) {
        try {
            return await pubnub.messageCounts({
                channels: channels,
                channelTimetokens: tokens,
            });
        }
        catch(error) {
            return error;
        }
    }
    
    async function getUnreadedMessagesAlternative(channels, tokens) {
        return new Promise(async resolve => {
            pubnub.messageCounts({
                channels: channels,
                channelTimetokens: tokens,
            }).then(result => resolve(result))
              .catch(error => resolve(error));
        });
    }
    
    function log(data) {
        // console.log(data);
        document.querySelector("#out").innerHTML += 
          `<div>${JSON.stringify(data)}</div>`;
    }
    
    })();
    <div id="out"></div>
    <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.33.0.js"></script>