GameForce Part 6. A pop-up dilemma

Fedir Kryvyi
6 min readNov 5, 2023

Hi everyone. If you were following this series, you might have noticed that it is been a while since the last time I posted anything on this blog (even though I was committed to writing more often). And the reason is simple — there was no good topic to write about. I was mostly working on LWC components to build a UI of the GameForce, and I don’t think there is anything special that I can share with you around this topic — just read the official documentation and follow the best practices and you should be good.

But we’ve finally arrived at the point of the most vague part of the project — showing an achievement notification to the user. The whole idea of GameForce relies on a tiny dopamine spike that people get when they feel that they have achieved something. And for that to work, there has to be some kind of a nice looking pop-up, a tiny celebration of a sort. That is why modern gaming consoles and launchers all have an additional, invisible layer on top of the game where they can show the notification to the player.

However, the problem is that there is no global notifications system in Salesforce. Well, you can use Salesforce Chatter for that (and I have plans to build an integration later on), but not everyone uses it. And it means that something custom needs to be built from scratch. Let’s reiterate a bit and formalize the design of the solution that we are trying to build here:

We already have the first part of this solution — the backend for platform events was completed and tested some time ago.

But now the tricky part starts. We need to find some way for UI to handle the event. So, we need to build a custom UI component that would utilize the “empApi” module to subscribe to our platform event. The problem is that this component has to be added everywhere in the system or otherwise we will end up in a situation where the user navigates from one page to another and misses the notification. As an option, we can ask admins to add our component to all record pages that users are working with as a part of an initial setup. But this would require quite a lot of effort on their end and doesn’t solve the problem when it comes to list views. Alternatively, we could use a utility bar component, that can be added to the scope of the whole application. We would still require some post-installation actions from admins, but the scope will be way smaller, and this solution should also work on all pages disregarding the type (granting that our utility bar component is configured in the app).

It might sound like the second option is a no-brainer in this case — it is easier to set up and has a wider reach. But it is not that simple. We haven’t yet discussed the way notifications are going to be shown to the end user. Sticking to the first scenario allows us to use a modal window that will be shown in the middle of the screen and is highly customizable.

This is just an example of a modal, and we would be free to redesign it however we want, especially if we are going to use raw HTML for a modal component from lightningdesignsystem.com instead of implementing LightningModal. Actually, I would try to make it look like some kind of notification from trailhead.salesforce.com:

Sticking to a second option confines us to using a relatively small utility bar component. We can still make it look good, but it will be hard to make it as stunning as a huge overlaying modal from the first option. Another problem that we would face with a utility bar approach is that we would have to use “lightning:utilityBarAPI” API to open the component whenever we receive an appropriate platform event. But that API is only available in Aura and not in LWC. Lastly, since the utility bar component is always visible to the user, they will have the ability to expand the component whenever they please, and we would need to show something useful there. As a solution for that, we can design a component to show the achievement that is not reached yet, but is the closest one for the user to get. This means we would need to develop two states for the component, and the state that shows “the closest achievement” requires extra backend code to be written.

After some consideration, I’ve decided to stick with the second option. A little extra development that is needed to properly cover extra functionality for the utility bar component isn’t that bad when compared to convincing admins to add a lightning component to each and every lightning record page in their org.

As I’ve mentioned before we would have to wrap our LWC component into an Aura component to use “lightning:utilityBarAPI”. This is necessary to open our utility bar component automatically whenever we receive a platform event from GameForce. It took me some time to make it work properly, so I’ve decided to share a snippet of the code directly in the article so it might save some time for someone else.

Aura Component (Wrapper for gameForceNotification LWC component):

<aura:component implements="lightning:utilityItem" access="global" >
<aura:attribute name="supportsPopOut" type="Boolean" default="false" />
<lightning:utilityBarAPI aura:id="utilitybar" />

<lightning:empApi aura:id="empApi"/>

<aura:handler name="init" value="{!this}" action="{!c.onInit}"/>

<!-- Holds the streaming event subscription -->
<aura:attribute name="achievementId" type="String" />

<!-- LWC component that visualises data from the platform event -->
<c:gameForceNotification achievementId="{! v.achievementId }"></c:gameForceNotification>
</aura:component>

Controller:

({
// Subscribes to AchievementReached__e platform event
// using empAPI
onInit : function(component, event, helper) {
const empApi = component.find('empApi');
const channel = '/event/AchievementReached__e';
const replayId = -1;

empApi.subscribe(channel, replayId, $A.getCallback(eventReceived => {
console.log('Received event ', JSON.stringify(eventReceived));
if (eventReceived.data && eventReceived.data.payload && eventReceived.data.payload.UserId__c) {
// Comparing the userId from the Platform Event and
// Current User id
var userId = $A.get("$SObjectType.CurrentUser.Id");
if (userId === eventReceived.data.payload.UserId__c) {
component.set("v.achievementId", eventReceived.data.payload.AchievementId__c);
var utilityAPI = component.find("utilitybar");
// Opening the utility bar component using
// lightning:utilityBarAPI API
utilityAPI.openUtility();
}
}
}))
.then(subscription => {
// Subscription response received.
// We haven't received an event yet.
console.log('Subscription request sent to: ', subscription.channel);
});

empApi.onError($A.getCallback(error => {
// console.error for deubgging purposes
//console.error('EMP API error: ', JSON.stringify(error));
}));
}
})

One important thing that you need to keep in mind is that you need to set utility bar component to “start automatically” or otherwise empAPI fails to subscribe to a platform event unless you manually open it.

With that, you should be ready to send your platform event which will trigger the opening of the utility bar component.

Here is what the current version looks like when the user receives a platform event:

I’ve also added a confetti effect using a custom-made LWC component which you can find in the project repository here: confetti lwc

Here is what it looks like when the user clicks on the component:

Part 7. TBD

Part 5. Backend

GameForce GitHub

--

--

Fedir Kryvyi

I am a Salesforce Developer with previous Dynamics 355 experience, and I am writing about my thoughts/discoveries about those two platforms or tech in general