Show Modal Box or Toast with Sound on Record update using Platform Event

Here we will be creating a Notification system on Case object where when a user updates the case’s status then his owner will get a notification with sound on his app in real time. We will use Platform Events, Trigger and Lightning Component to achieve this functionality.

Set up Platform Event for notification
Show Modal Box or Toast with Sound on Record update using Platform Event

Add a Case Notifcation Handler

CaseNotificationController

public class CaseNotificationController {
  @AuraEnabled
  public static String getSessionId() {
    return UserInfo.getSessionId();
  }
  
  public static void publishNotifications(List<Case> caseList) {
    
    List<Case_Notification__e> notificationList = new List<Case_Notification__e>();
    
    // Create Notifiation records from Case
    for(Case c:caseList) {
        Case_Notification__e notification = new Case_Notification__e();
        notification.Object_Id__c = c.Id;
        notification.Object_Name__c = c.CaseNumber;
        notification.Status__c = c.Status;
        notification.OwnerId__c = c.OwnerId;
        notificationList.add(notification);
    }
    
    List<Database.SaveResult> results = EventBus.publish(notificationList);
    // Inspect publishing results
    for (Database.SaveResult result : results) {
      if (!result.isSuccess()) {
        for (Database.Error error : result.getErrors()) {
          System.debug('Error returned: ' +
                 error.getStatusCode() +' - '+
                 error.getMessage());
        }
      }
    }
  }
}

Setup Case Trigger to show popup whenever Case’s Status is updated.;

CaseTrigger

trigger CaseTrigger on Case(after update) {
  if(Trigger.isAfter) {
    if(Trigger.isUpdate) {
      CaseTriggerController.afterUpdate(Trigger.new,Trigger.oldMap);
    }
  }
}

 

CaseTriggerController

public class CaseTriggerController {

  public static void afterUpdate(List<Case> newList,Map<Id,Case> oldMap) {
    List<Case> caseNotifiedList = new List<Case>();
    for(Case c:newList) {
      // filter out cases with has status field updated
      if(c.Status != oldMap.get(c.Id).Status) {
        caseNotifiedList.add(c);
      }
    }
    // Pass the filtered case to generate notification
    CaseNotificationController.publishNotifications(caseNotifiedList); 
  }
}

In order to subscribe this Platform Event in Lightning Component we must add cometD.js to static resource.
Download it from here CometD JavaScript client v3.1.1
Show Modal Box or Toast with Sound on Record update using Platform Event

Now its time to set up Lightning component to subscribe and catch Platform Event

CaseNotificationComponent.cmp

<aura:component controller="CaseNotificationController" implements="flexipage:availableForAllPageTypes" access="global">
  <aura:attribute name="notifications" type="Object[]"/>
  <aura:attribute name="sessionId" type="String"/>
  <aura:attribute name="cometd" type="Object"/>
  <aura:attribute name="cometdSubscriptions" type="Object[]"/>
  <aura:attribute name="playSound" type="Boolean" default="false"/>

  <aura:handler name="init" value="{!this}" action="{!c.onInit}"/>
  <ltng:require scripts="{!$Resource.cometd}" afterScriptsLoaded="{!c.onCometdLoaded}"/>

  <div class="container">
    <!-- Sound -->
    <aura:if isTrue="{!v.playSound}" >
    <audio autoplay="true"><source src='/img/reminder.mp3' type='audio/mpeg'/>
    <source src='/img/reminder.ogg' type='audio/ogg; codecs=vorbis'/></audio>
    </aura:if>
    <!-- Header -->
    <div class="slds-p-around--x-small slds-border--bottom slds-theme--shade">
      <div class="slds-grid slds-grid--align-spread slds-grid--vertical-align-center">
        <div>
          <span class="slds-badge">{!v.notifications.length}</span>
        </div>
        <div>
            <lightning:buttonIcon onclick="{!c.onClear}" iconName="utility:delete" title="Clear notifications"
            alternativeText="Clear notifications" variant="border-filled"/>
        </div>
      </div>
    </div>

  <!-- Notification list -->
  <div class="slds-container--fluid slds-scrollable--y content">
    <aura:iteration items="{!v.notifications}" var="notification">
      <div class="slds-p-around--small slds-border--top">
        <div class="slds-grid slds-grid--align-spread slds-has-flexi-truncate">
          <p>Case
          <a href="{!'/'+notification.recordId}">{!notification.recordName}</a>'s
          status is updated to {!notification.recordStatus}
          </p>
          <p class="slds-text-color--weak slds-p-left--x-small">{!notification.time}</p>
          </div>
        </div>
      </aura:iteration>
    </div>
  </div>
</aura:component>

 

CaseNotificationComponentController.js

({
    onCometdLoaded : function(component, event, helper) {
        var cometd = new org.cometd.CometD();
        component.set('v.cometd', cometd);
        if (component.get('v.sessionId') != null)
            helper.connectCometd(component);
    },
    onInit : function(component, event, helper) {
        component.set('v.cometdSubscriptions', []);
        component.set('v.notifications', []);
        // Disconnect CometD when leaving page
        window.addEventListener('unload', function(event) {
            helper.disconnectCometd(component);
        });
        // Retrieve session id
        var action = component.get('c.getSessionId');
        action.setCallback(this, function(response) {
            if (component.isValid() && response.getState() === 'SUCCESS') {
                component.set('v.sessionId', response.getReturnValue());
                if (component.get('v.cometd') != null)
                    helper.connectCometd(component);
            }
            else
                console.error(response);
        });
        $A.enqueueAction(action);
    },
    onClear : function(component, event, helper) {
    	component.set('v.notifications', []);
    },
})

 

CaseNotificationComponentController.js

({
    connectCometd : function(component) {
        var helper = this;
        // Configure CometD
        var cometdUrl = window.location.protocol+'//'+window.location.hostname+'/cometd/40.0/';
        var cometd = component.get('v.cometd');
        cometd.configure({
            url: cometdUrl,
            requestHeaders: { Authorization: 'OAuth '+ component.get('v.sessionId')},
            appendMessageTypeToURL : false
        });
        cometd.websocketEnabled = false;
        // Establish CometD connection
        console.log('Connecting to CometD: '+ cometdUrl);
        cometd.handshake(function(handshakeReply) {
            if (handshakeReply.successful) {
                console.log('Connected to CometD.');
                // Subscribe to platform event
                var newSubscription = cometd.subscribe('/event/Case_Notification__e',
                                                       function(platformEvent) {
                                                           console.log('Platform event received: '+ JSON.stringify(platformEvent));
                                                           helper.onReceiveNotification(component, platformEvent);
                                                       }
                                                      );
                // Save subscription for later
                var subscriptions = component.get('v.cometdSubscriptions');
                subscriptions.push(newSubscription);
                component.set('v.cometdSubscriptions', subscriptions);
            }
            else
                console.error('Failed to connected to CometD.');
        });
    },
    disconnectCometd : function(component) {
        var cometd = component.get('v.cometd');
        // Unsuscribe all CometD subscriptions
        cometd.batch(function() {
            var subscriptions = component.get('v.cometdSubscriptions');
            subscriptions.forEach(function (subscription) {
                cometd.unsubscribe(subscription);
            });
        });
        component.set('v.cometdSubscriptions', []);
        // Disconnect CometD
        cometd.disconnect();
        console.log('CometD disconnected.');
    },
    onReceiveNotification : function(component, platformEvent) {
        var helper = this;
        //Check if the message is for right User
		var recordOwnerId = platformEvent.data.payload.OwnerId__c.substring(0, 15);
        if( recordOwnerId == $A.get("$SObjectType.CurrentUser.Id")) {
            
            // Extract notification from platform event
            var newNotification = {
                time : $A.localizationService.formatDateTime(
                    platformEvent.data.payload.CreatedDate, 'HH:mm'),
                recordId : platformEvent.data.payload.Object_Id__c,
                recordName : platformEvent.data.payload.Object_Name__c,
                recordStatus : platformEvent.data.payload.Status__c
            };
    	
            // Save notification in history
            var notifications = component.get('v.notifications');
            notifications.push(newNotification);
            component.set('v.notifications', notifications);
            component.set('v.playSound',true);      
            setTimeout(function() {
                component.set('v.playSound',false);
            }, 2000);
            helper.displayToast(component, 'info', 'Case '+platformEvent.data.payload.Object_Name__c+
                                					'\'s status is updated to '+platformEvent.data.payload.Status__c);
        }
    },
    displayToast : function(component, type, message) {
        var toastEvent = $A.get('e.force:showToast');
        toastEvent.setParams({
            type: type,
            message: message
        });
        toastEvent.fire();
    }
})

Add CaseNotificationComponent to Utility bar in your Salesforce App
1. Go to App Manager in Lightning
2. Edit your app
3. In utlity Bar select CaseNotificationComponent
4. Make sure that Load in background when app opens is checked.
5. Save
Show Modal Box or Toast with Sound on Record update using Platform Event

Go to the app and you will see this Component in utility bar
Show Modal Box or Toast with Sound on Record update using Platform Event

We are done. Now whenever a Case’s status is updated where the owner of that case is you then while you are on this app you will see a toast and hear a notification sound.
Show Modal Box or Toast with Sound on Record update using Platform Event

Do let me know in comments if you have any questions or suggestions.
Thanks

Was this post useful? If yes then please share.



Comments (4)

  1. sharan says:

    Hi, May i know the pre-requisite before implementing the above code Like Creating the Platform Event & Fields in that Event

  2. Bharath says:

    Hi, can you update your code
    public class CaseNotificationController {
    @AuraEnabled
    public static String getSessionId() {
    return UserInfo.getSessionId();
    }
    public static void publishNotifications(List caseList) {

    List notificationList = new List();

    // Create Notifiation records from Case
    for(Case c:caseList) {
    Case_Notification__e notification = new Case_Notification__e();
    notification.Object_Id__c = c.Id;
    notification.Object_Name__c = c.CaseNumber;
    notification.Status__c = c.Status;
    notification.OwnerId__c = c.OwnerId;
    notificationList.add(notification);
    }

    List results = EventBus.publish(notificationList);
    // Inspect publishing results
    for (Database.SaveResult result : results) {
    if (!result.isSuccess()) {
    for (Database.Error error : result.getErrors()) {
    System.debug(‘Error returned: ‘ +
    error.getStatusCode() +’ – ‘+
    error.getMessage());
    }
    }
    }
    }
    }

    I’m getting an Error while saving the class

Leave a Comment

All fields marked with an asterisk (*) are required


shares