Monday, November 30, 2020

 Custom one-to-one messaging service b/w agents using LWC






Chatter is a best way for the users to collaborate with each other in Salesforce, but what if we want to give the user feel of the chat that is provided by whatsapp/messenger ...??

The below code developed in LWC to create one's own personalized chatterbox to engage and connect.

chatterLWC.html

<template>
    <lightning-card>
        <div if:true={isLoading}"> 
            <lightning-spinner alternative-text="Loading...">
</lightning-spinner>
        </div>
        <div if:false={isLoading}>
        <h1 style="color: rgb(110, 41, 41); text-align: center; 
font-size: 17px;">
            <b>Collaboration chat for <i>{loggedInUser}</i></b>
        </h1>
        <div>
            <template if:true={feeds.length} iterator:it={feed}>
                <div class="slds-p-left_xx-large" 
                     if:true={it.value.isSpeaker} 
                     key={it.value.id}>
                    <lightning-layout multiple-rows key={it.value.id}
                               class="chatter-comments 
                                      slds-m-vertical_xx-small">
                        <lightning-layout-item size="6" 
                             class="slds-p-left_small">
                            {it.value.speaker} said...
                        </lightning-layout-item>
                        <lightning-layout-item size="6" 
class="slds-size_6-of-12 slds-p-right_small" 
                               style="text-align: right;">
                            {it.value.dateCreated}
                        </lightning-layout-item>
                        <lightning-layout-item size="12" 
                            class="slds-p-left_small">
                            <b><i>{it.value.comment}</i></b>
                        </lightning-layout-item>
                        <br/>
                    </lightning-layout>
                </div>
                <div class="slds-p-right_xx-large" if:false={it.value.isSpeaker} 
                     key={it.value.id}>
                    <lightning-layout multiple-rows key={it.value.id}
                                  class="chatter-comments 
                                         slds-m-vertical_xx-small" 
                                  if:false={it.value.isSpeaker}>
                        <lightning-layout-item size="6" 
                            class="slds-p-left_small">
                            {it.value.speaker} said...
                        </lightning-layout-item>
                        <lightning-layout-item size="6" 
                            class="slds-size_6-of-12 slds-p-right_small" 
                            style="text-align: right;">
                            {it.value.dateCreated}
                        </lightning-layout-item>
                        <lightning-layout-item size="12" 
                            class="slds-p-left_small">
                            <b><i>{it.value.comment}</i></b>
                        </lightning-layout-item>
                        <br/>
                    </lightning-layout>
                </div>
            </template>
        </div>
            <form onsubmit={handleSendChat}>
                <lightning-layout>
                    <lightning-layout-item size="4"></lightning-layout-item>
                    <lightning-layout-item size="6">
                        <lightning-input label="Add a comment" 
                            value={newComment} data-field="comment" 
                            class="slds-m-vertical_x-small">
                        </lightning-input>
                    </lightning-layout-item> 
                    <lightning-layout-item size="2">
                        <div class="slds-p-top_x-large">
                            <template if:false={sending}>
                                <lightning-button label="Send" 
                                    class="slds-float_left" 
                                    onclick={handleSendChat}>
                                </lightning-button>
                            </template>
                            <template if:true={sending}>
                                <lightning-button disabled 
                                    label="Sending..." 
                                    class="slds-float_left">
                                </lightning-button>
                            </template>
                        </div>
                    </lightning-layout-item>
                </lightning-layout>
            </form>
        </div>
    </lightning-card>
</template>

chatterLWC.js

import { LightningElementwire } from 'lwc';
import getFeeds from '@salesforce/apex/chatterClass.getFeeds';
import insertChatterPosts from '@salesforce/apex/chatterClass.
                                insertChatterPosts';
import getCurrentUser from '@salesforce/apex/chatterClass.getCurrentUser'

export default class ChatterLWC extends LightningElement {
    feeds = [];
    sending = false;
    newComment = '';
    leadId = '';
    isLoading = true;
    loggedInUser = '';

    @wire(getCurrentUser)
    wiredCurrentUser({errordata}) {
        if (error) {
        } else if (data) {
            this.loggedInUser = data;
        }
    }

    connectedCallback(){
        let address = window.location.href;
        this.leadId = address.split('Lead/')[1].
                      split('/')[0];
        
        this.getFeeds(this);
    }

    getFeeds(self){
        this.isLoading = true;
        getFeeds({
            leadId : this.leadId
        })
        .then(result => {
            this.feeds = result;
            this.isLoading = false;
            setTimeout(function () {
                self.getFeeds(self);
            }, 20000);
        })
        .catch(error => {
            this.isLoading = false;
        });
    }

    handleSendChat() {
        this.isLoading = true;
        this.sending = true;

        this.newComment = this.template.querySelector
                            ("[data-field='comment']")
                            .value;

        if (!this.newComment || !this.newComment.trim()) 
        {
            this.sending = false;
            return;
        }
        
        insertChatterPosts({
            leadId: this.leadId,
            comment: this.newComment
        })
        .then(result => {
            this.feeds = result;
            this.sending = false;
            this.newComment = '';
            this.isLoading = false;         
        })
        .catch(error => {
            this.sending = false;
            this.isLoading = false;         
        });
    }
}

chatterLWC.css

.chatter-comments {
    background-color#eee;
    width95%;
}

chatterClass.cls

public without sharing class chatterClass {
    
    @AuraEnabled(cacheable=true)
    public static String getCurrentUser(){
        return [SELECT IdName 
                FROM User 
                WHERE Id =:UserInfo.getUserId()
                ].Name;
    }

    @AuraEnabled
    public static List<chatterPostWrappergetFeeds(Id leadId) {
        List<feedWrapperfeedWrappers = new List<feedWrapper>();
        List<FeedItemfeedItems = [SELECT IdParentIdBody
                                        createdById
                                        createdBy.NameCreatedDate 
                                    FROM FeedItem 
                                    WHERE parentId = :leadId 
                                    AND Type = 'TextPost' 
                                    ORDER BY CreatedDate 
                                    DESC ];
        if(!feedItems.isEmpty()){
            for(FeedItem itemfeedItems){
                String dateString = getMessageDate(item.createdDate);
                String speakerName = item.createdBy.Name;
                Boolean isSpeaker = false;
                Id userId = UserInfo.getuserId();
                if(userId == item.createdById){
                    isSpeaker = true;
                }
                feedWrappers.add(new feedWrapper(item.Id
                                                item.createdById
                                                speakerName
                                                dateString
                                                item.Body
                                                .stripHtmlTags(), 
                                                isSpeaker));
            }
        }
        return postWrappers;
    }

    @AuraEnabled
    public static List<feedWrapperinsertChatterPosts(
                Id leadId
                String comment) {
        try{
            FeedItem post = new FeedItem();
            post.ParentId = leadId;
            post.createdById = UserInfo.getuserId();
            post.Body = comment;
            post.type = 'TextPost';
            INSERT post;
            return getFeeds(leadId);   
        }catch(exception e){
            system.debug('Something went wrong '+e.getMessage());
            return null;
        }
    }

    private static String getMessageDate(DateTime dTime){
        Long dt1Long = dTime.getTime();
        Long dt2Long = DateTime.now().getTime();
        Long milliseconds = dt2Long - dt1Long;
        Long seconds = milliseconds / 1000;
        Long minutes = seconds / 60;
        Long hours = minutes / 60;
        Long days = hours / 24;

        if(seconds < 60){
            return String.valueOf(seconds + ' seconds ago');
        }
        if(minutes < 60){
            return String.valueOf(minutes + ' minutes ago');
        }
        if(hours < 24){
            return String.valueOf(hours + ' hours ago');
        }
        if(days < 4){
            return String.valueOf(days + ' days ago');
        }
        else{
            return String.valueOf(dTime.format('MMM-d-yyyy'));
        }
    }

    public class feedWrapper{

        @AuraEnabled
        public String Id;
        @AuraEnabled
        public String speaker;
        @AuraEnabled
        public String dateCreated;
        @AuraEnabled
        public String comment;
        @AuraEnabled
        public Boolean isSpeaker;

        public chatterPostWrapper(String id
                                String createdById
                                String speaker
                                String dateCreated
                                String comment
                                Boolean isSpeaker){
            this.Id = id;
            this.speaker = speaker;
            this.dateCreated = dateCreated;
            this.comment = comment;
            this.isSpeaker = isSpeaker;
        }
    }
    
}

Thursday, November 5, 2020

Scroll to a specific element in an LWC form

We may want to let the user navigate to different sections of a page in LWC when s/he clicks a particular button.

Such an example can be a form where index is maintained and when a user clicks on a particular section, navigates him/her to the associated content.

This works both in Desktop as well as Mobile devices.

To achieve this, the user can add different components inside div elements as below:

<div class="scrollCls">
    <c-hello-world >
</c-hello-world >
</div>

When a button is clicked, it fires the below event to scroll down to a particular element on the form:

handleScroll(){
    this.template.querySelector('.scrollCls').scrollIntoView();
}