Extend your ASP.NET SignalR web app functionality with Project Westminster (with examples!)

projwest

Project Westminster is one of the Universal Windows Platform (UWP) Bridges from which we can extend functionalities of our existing web applications using UWP runtime frameworks (toast notifications and responding to certain actions, camera API, contact sharing at the very least). In a nutshell, your web application published to the Windows Store with defined remote URL list that has access to the UWP APIs, and there you have your hosted app! Once published as a hosted web application (HWA), there’s no need to re-publish the app to Windows Store. Changes to your web application is pretty much your usual development workflow as most content delivered from HWA are coming from your app.

For this entry, we won’t be discussing exactly how to create a hosted app as there’s a short but easy-to-follow guide to get started building your first HWA. Taking cue from an ASP.NET SignalR enabled web application, we would be enabling scenarios sending toast notifications to all connected clients running the HWA (Windows 10 Mobile included), responding to these toast notifications and sharing photos from the camera and contacts in your contact list. For those who aren’t running HWA, they’ll get notifications from the web app.

The example application takes cue on a fictional company where it keeps track of statuses of their business objects (“requests”). For typical web applications, notifications on updates on business objects are done either through sending e-mail updates or some UI notifications from the web application. To get informed of these updates, you need to logon to the web application or scroll through your inbox (or in your folders) and read perhaps the new status. These are just fine, not until you have a large mailbox to manage or having the need to open up your browser and check statuses of these requests. To address these, we will be implementing this fictional application in vanilla Javascript and jQuery. In this way, when you use other JS-based frontend frameworks, you can do more and perhaps better, taking off from this example. Let’s get started!

One needs to “log-in” in the application to get started. It is no more than just providing a name to identify yourself in the application. In this app, you’re “allowed” to logon more than once using the same name, in the premise that a user can be present in multiple locations – think of one could be mobile, and another in desktop, perhaps tablet in another.  You would also notice that the new users that log-on to the system also appears in the status box. In a typical web application, you don’t necessarily show this not unless it is a chat box where users need to see who came in. This is also a good way for application developers to see or manage users that get into the application as connection IDs of these users, whether they’re logged on multiple environments are captured.

Looking at the views of the web application, the number of buttons differ if one is running on a hosted environment and that of the normal browser.

The available buttons show functionalities available from that specific environment. The web application outside the hosted environment cannot invoke Windows 10 APIs hence the limited set. Figuring out the common buttons available on both environments, we are down to the first two buttons – “Web Notification” and “Application Toast”.

Web Notification

Typical “Facebook-like” notification. The message that comes with this can be customized based on the needs for your application. In a previous problem that I had supporting a web application, it has always been a challenge for us to keep track of the users of the system at a given time when we are about to do a production release. Often times, people are stuck doing their business only to find out that they won’t be able to continue since the web application has been taken down. In Facebook, once you click on the notification, you would be redirected to the content where the update took place while here, the name is captured and can be manipulated based on your application’s needs.

Application Toast

This simulates the action of getting notified for request changes. Absent of a complete workflow in the sample application, once this button is clicked, the system will invoke a Web API and will return a randomly generated request with status that has been “acted upon”. Behavior of this action differ from which the environment the user is logged on. If the web application is running on a hosted environment, it will get a toast (eg Windows 10 Mobile, Windows 10 desktop) indicating the “updated request” while for non-hosted environments, they will receive an update on the status box with the same information.

See these buttons in action –

For the rest of the available options in the hosted apps, I shall be zeroing in on the custom toasts and on the camera and contact picker options.  The other options are sample implementations from which you can pattern yours but the ones I mentioned merits more for a discussion.

Custom Toasts

Building toasts are quite easy to build. There are a lot of sample code from which you can base your templates.  Not only you have base templates to build on, you can customize these base on the toast that you need to implement as long it follows the toast schema. Custom toasts developed in this project were built either from these two options –

  1. Adopt a base template and customize information
  2. Customize a toast schema as string to be loaded as a XML document.

In this sample application, I have used #1 for informational and display toasts, while #2 for toasts that consume Web API to populate options, which can be similar to responding to certain business objects for your application. A typical toast is structured as follows. The key elements that needs to be present in the toasts are: instance of the Notifications object, the template type that the toast will use, and last but not the least is the trigger/display of the toast.

This code of the default toast is shown below
This code of the default toast is shown below. Check the source code link at the end of this post for the definition of the methods.

function showDefaultToast() {
if (isWindows10()) {
 var notifications = Windows.UI.Notifications;
 var template = notifications.ToastTemplateType.toastImageAndText02;
   var toastXml = notifications.ToastNotificationManager.getTemplateContent(template);
   var toastKey = "defaultToast";

   var toastTextElements = toastXml.getElementsByTagName("text");
   toastTextElements[0].appendChild(toastXml.createTextNode("Default Toast"));
   toastTextElements[1].appendChild(toastXml.createTextNode("This is the default toast of the application."));

   setToastImage(toastXml, imgAnonymous, 0, 1);
   setToastActivation(toastXml, toastKey);
   toggleToast(notifications, toastXml, toastKey);

   gotoTopPage();
 } else {
   displayAPIError("default toast");
 }
}

Custom Toasts – Text with Action Buttons

This toast follows adopting a base template and customize the information by referencing the appropriate section/tags in the schema and changing values as needed. Note that upon clicking one of the actions in this toast, the results would be appended in the status box. Again, as with other toasts, the information captured here can be used to process business logic.


function showCustomToastTextYesNo() {
    if (isWindows10()) {
        var message = "This is a random question.";

        var notifications = Windows.UI.Notifications,
            templateType = notifications.ToastTemplateType.toastImageAndText02,
            templateContent = notifications.ToastNotificationManager.getTemplateContent(templateType),
            toastMessage = templateContent.getElementsByTagName("text");
        var toastKey = "tynToast";

        // Set message & image in toast template
        toastMessage[0].appendChild(templateContent.createTextNode(message));

        setToastActivation(templateContent, toastKey, "");
        setToastImage(templateContent, "round_man.png", 0, 1);

        // Add actions
        var actions = templateContent.createElement("actions");
        templateContent.firstChild.appendChild(actions);

        // Create an input box
        var input = templateContent.createElement("input");
        input.setAttribute("type", "text");
        input.setAttribute("title", "Reply with");
        input.setAttribute("id", "textReply");
        input.setAttribute("placeHolderContent", "Random answer goes here.");
        actions.appendChild(input);

        // Create a yes button
        var btnYes = templateContent.createElement("action");
        btnYes.setAttribute("content", "Yes");
        btnYes.setAttribute("arguments", "inputYesNo-yes");
        btnYes.setAttribute("launchType", "foreground");
        actions.appendChild(btnYes);

        //Create a no button - clipped

        // Show the toast
        toggleToast(notifications, templateContent, toastKey);

        gotoTopPage();
    } else {
        displayAPIError("custom toast");
    }
}

It would be important to note as well the function on line 21. Each toast in this sample application passes a variable from which how the hosted web application would respond to toast activation (ergo, when the toast has been clicked/pressed/touched as it appeared or as in the action center).


function getLaunchParameters(objName, objAdditionalInfo) {

    var strLaunchParams = {
        type: "toast",
        name: objName,
        caller: getCurrentUser()
    };

    // Add additional launch parameters as needed per toast
    switch (objName) {
    case "appToast":
        strLaunchParams["requestNumber"] = objAdditionalInfo;
        break;
    // clipped
    }

    return strLaunchParams;

}

function setToastActivation(templateContent, strIdentifier, objAddInfo) {
    var toastLaunchString = JSON.stringify(getLaunchParameters(strIdentifier, objAddInfo));
    var toastElement = templateContent.selectSingleNode("/toast");
    toastElement.setAttribute("duration", "long");
    toastElement.setAttribute("launch", toastLaunchString);
}

Custom Toasts – Selections

There are two toasts in this sample application that uses selections. The difference of this with the preceding type is that this passes a XML formatted string, customized based on the information that needs to be displayed. The first one is a selection of hard coded values, simulating defined actions that needs to be responded to. The options that the selection toast will display are hardcoded. It is important to note that the XML formatted string should still follow the toast schema, otherwise it won’t be displayed as desired.

 

function getCustomXml(requestNumber) {
    return "<toast launch='selectionToast'>" +
        "<visual>" +
        "<binding template='ToastGeneric'>" +
        "  <text>Kaptan (Fictional Company)</text>" +
        "  <text>Please act on the status of the request (" +
        requestNumber +
        ").</text>" +
        "  <image></image>" +
        "</binding>" +
        "</visual>" +
        "<actions>" +
        "<input id='time' type='selection' defaultInput='1' >" +
        "  <selection id='1' content='For Review' />" +
        "  <selection id='2' content='Approve' />" +
        "  <selection id='3' content='Reject' />" +
        "</input>" +
        "<action activationType='foreground' content='Send Reply' arguments='selection-reply' imageUri='" + getImagePath("statusreply.png") + "' />" +
        "</actions>" +
        "</toast>";
}

function showCustomToastDropDown(objRequest) {
    if (isWindows10()) {
        var notifications = Windows.UI.Notifications;
        var toastDom = new Windows.Data.Xml.Dom.XmlDocument();
        toastDom.loadXml(getCustomXml(objRequest));
        var toastKey = "selectionToast";

        setToastActivation(toastDom, toastKey, "");
        setToastImage(toastDom, docCheck, 0, 1);
        toggleToast(notifications, toastDom, toastKey);

        gotoTopPage();
    } else {
        displayAPIError("custom selection toast");
    }
}

One of the advantages, for me personally of doing this approach is that you have more control on what gets displayed in the toast. Since the declarations of toast elements are explicitly declared,  it is much more easier to work on each element that needs to be customized.

Another type of selection toast that I have created is similar to the previous one, only that the selection options are based from a result of a Web API call. The resulting JSON file is an output from using GenFu, simulating values derived from a data store.

Values of these change for every invocation. In an ideal scenario, one can filter the list say to the list of persons from which we can assign a request

function getUserList() {
    var jsonOption;
    var apiPath = getApiPath("DemoApplication");

    var getJson = $.getJSON(apiPath,
        function(data) {
            var myobj = JSON.parse(data);

            $.each(myobj,
                function(index, element) {
                    jsonOption += "<selection id='" + element.Id + "' content=' " + element.LastName + ", " + element.FirstName + "' />";
                });

            showCustomToastDropDownJson(jsonOption);
        });
}

function showCustomToastDropDownJson(userList) {
    if (isWindows10()) {

        var notifications = Windows.UI.Notifications;
        var toastDom = new Windows.Data.Xml.Dom.XmlDocument();
        toastDom.loadXml(jsonDataXml("Sample Json Data Fetch", "Select Employee", "toastJsonEmployee", userList));
        var toastKey = "jsonToast";

        setToastImage(toastDom, "json.png", 0, 1);
        setToastActivation(toastDom, toastKey, "");
        toggleToast(notifications, toastDom, toastKey);

        gotoTopPage();
    } else {
        displayAPIError("custom Json-selection toast");
    }
}

Phone Screenshots

Camera and People Picker

These are additional features from which you can enhance your applications. The camera functionality takes a photo from your device and shares it to other people via the status box. Depending on how you would store the photo – you can opt to have a file upload approach or use Azure Blob storage to store your photos. On the other hand, the contact picker allows you to pick a contact from your address book and share the information. For this application, the contact is shared as a toast delivered to all.

function contactPicker() {
    if (isWindows10()) {
        var picker = new Windows.ApplicationModel.Contacts.ContactPicker();

        picker.desiredFieldsWithContactFieldType.append(Windows.ApplicationModel.Contacts.ContactFieldType.email);
        picker.desiredFieldsWithContactFieldType.append(Windows.ApplicationModel.Contacts.ContactFieldType.phoneNumber);

        // Open the picker for the user to select a contact
        picker.pickContactAsync()
            .done(function(contact) {
                if (contact !== null) {
                    getContactDetails(contact);
                    gotoTopPage();
                }
            });
    } else {
        displayAPIError("contact");
    }
}

Emphasizing the highlighted code above, these filters set that only phone or e-mail addresses of contacts can be selected and shared to others. Of course, broadcasting the contact is not an ideal scenario although it could be used to endorse or forward contact details directly from Windows 10, perhaps, to another user in your system.

Final Words

The source code of the application can be found here, provided as-is and no warranty of any kind.  The examples provided here are something that you can enhance and work on further for your applications, on needs where you deem fit.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s