Develop a Windows 8 app in 30 days
Site Pinning provides a unique way to build engagement from your users through out-of-browser notifications. Previously, I went over how to use IE9′s Site Pinning API to implement overlay icons to enhance user notifications. The demo focused on how to display a numeric icon to indicate when a specific event (e.g.: messages in an inbox) had occurred.
Pinned site with overlay icon
It’s a really great way of letting your users know that there’s pending information for them to check into. But what happens if your site offers multiple types of notifications? With websites offering so much functionality nowadays, it’s pretty common for them to also serve up multiple types of notifications, from friend requests and event reminders to new messages and game invites.
Rotating Multiple Overlays Icons
The great thing about the Site Pinning API is that it’s very flexible and through some JavaScript magic, you can easily display multiple overlay icons for the various services you have. In this demo, I want to rotate through 3 different overlay icons that alert the user to pending messages, requests and actions.
As before, I had to flex some of my artistic talent by creating the overlay icons using the x-icon editor. I created 5 of each and here’s how the first three look:
The code changed slightly from the last demo in order to accommodate multiple bits of data per fetch. While previously, I was only fetching one piece of data, in this demo, I’m returning 3, one for each notification type:
myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
]);
As a reminder, the method getData()
simulates grabbing remote data. So if we look at the data above, we can simulate pulling back three distinct bits of data. This is why we call the method every 10 seconds using setInterval. This allows us to see how notifications might look over a period of time.
setInterval(function () { myPin.getData() }, 10000);
The next thing that changed is the use of a timer to allow a slight delay while rendering the overlay icons. Using setTimeout()
provides enough of delay so that an individual overlay icon is visible to the user before rotating on to the next icon. If we didn’t have this delay, the rotation would be way too fast to provide any useful notification. If we look at the following image, we can see what the notification will look like:
Overlay icon showing numeric notification
This is accomplished via the following code:
currData = this.dataBin[this.currIndex++].data;
for (var i=0; i < currData.length; i++ ){
(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1000 * idx); }( i ));
}
Here’s what’s happening. In the first line, I grab the current set of data that holds all of the notification information (messages, requests & actions). That data looks like this:
[{ "label" : "Messages", "ntype" : "M", "num": 2 },
{ "label" : "Requests", "ntype" : "R", "num": 1 },
{ "label" : "Actions", "ntype" : "A", "num": 3 }]
I loop through each group of data and assign a timer using setTimeout()
that will call dispOverlay()
at ~1 second intervals. That’s the magic code that allows for the gradual icon rendering delay I mentioned before. The expected functionality is that the "messages" icon will render followed by the "requests" icon 1 second later, and then finally the "actions" icon.
Now, you might be wondering why I have an anonymous function wrapping the setTimeout()
. It’s because I have a closure within setTimeout which can cause a common scoping issue in which the variable ‘i’, which I use to grab the current index of data, will only be updated to the last index value. James Padolsey has a great explanation on it and thanks to John David Dalton for helping me troubleshoot this.
The final change is in dispOverlay()
in which I need to determine which overlay icon needs to display. Since I now have three different types of notifications, I need a conditional statement to determine the type and build the correct icon name:
if (theData.ntype == "M") {
oImg = "images/messages-" + theData.num + ".ico";
} else if (theData.ntype == "R") {
oImg = "images/requests-" + theData.num + ".ico";
} else if (theData.ntype == "A") {
oImg = "images/actions-" + theData.num + ".ico";
}
This checks the type and serves up the right icon based on the type and the number of notifications pending for that type.
The Demo and Final Code
You can check out the demo by going here in IE9:
http://reybango.com/demos/sprotate/index.html
When the page renders, drag the tab down to your taskbar and pin it. You should see a new window appear with your newly pinned site. Next, you’ll see the overlay icons appear in the taskbar and they should begin to cycle every 10 seconds.
Here’s the full source code. You can also download everything here.
<!DOCTYPE html>
<html>
<head>
<title>Pinned Site - Rotating Overlay Icons</title>
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
<meta name="application-name" content="Pinned Site Test" />
<meta name="msapplication-starturl" content="http://reybango.com/demos/sprotate/index.html" />
<meta name="msapplication-navbutton-color" content="#3480C0" />
<meta name="msapplication-window" content="width=1024;height=768" />
<meta name="msapplication-tooltip" content="Testing the Pinned Site API" />
<style>
body {
background: none repeat scroll 0 0 #4492CE;
font: 440%/1.4em 'Segoe Light',Segoe,'Segoe UI','Meiryo Regular','Meiryo',sans-serif;
color: #EDEFF4;
}
</style>
</head>
<body>
<div>
<h1>Pinned Sites</h1>
<p>Rotating Overlay Icons</p>
</div>
<script>
var myData = [];
var myPin = {
currIndex: 0,
dataBin: [],
getData: function () {
var idx = 0, currData = [], cntr = 0, theData;
if (window.external.msIsSiteMode()) {
currData = this.dataBin[this.currIndex++].data;
for (var i=0; i < currData.length; i++ ){
(function(idx) { setTimeout( function(){ myPin.dispOverlay( currData[idx] ); }, 1e3 * idx); }( i ));
}
if (this.currIndex > 2) { this.currIndex = 0 }
}
},
dispOverlay: function (theData) {
var oImg = "";
if (theData) {
window.external.msSiteModeClearIconOverlay();
if (theData.ntype == "M") {
oImg = "images/messages-" + theData.num + ".ico";
} else if (theData.ntype == "R") {
oImg = "images/requests-" + theData.num + ".ico";
} else if (theData.ntype == "A") {
oImg = "images/actions-" + theData.num + ".ico";
}
this.setOverlay(oImg, theData.label);
}
},
setOverlay: function (icon, desc) {
window.external.msSiteModeSetIconOverlay(icon, desc);
window.external.msSiteModeActivate();
},
init: function (myData) {
this.dataBin = myData;
this.getData();
}
};
window.external.msSiteModeClearIconOverlay();
myPin.init([{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 2 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 3 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 1 }, { "label" : "Requests", "ntype" : "R", "num": 5 }, { "label" : "Actions", "ntype" : "A", "num": 2 }] },
{ "data" : [{ "label" : "Messages", "ntype" : "M", "num": 5 }, { "label" : "Requests", "ntype" : "R", "num": 1 }, { "label" : "Actions", "ntype" : "A", "num": 4 }] }
]);
setInterval(function () { myPin.getData() }, 10000);
</script>
</body>
</html>