Collaboration Store, Part LXXVIII

“Don’t dwell on what went wrong. Instead, focus on what to do next. Spend your energies on moving forward toward finding the answer.”
Denis Waitley

Last time, we were able to get to the point where we could bring up our new storefront and take a quick peek at how things were looking. It was a good start, but there are still a number of things that we need to do to, and some important decisions to be made before we can wrap this up. Visually, I think we want to distinguish between the apps that were developed on the local instance, and the apps that we pulled in from the other members of the community. Also, of the apps that have been pulled in from other member instances, we want to somehow distinguish between those that have been installed locally and those that have not, and of those that have been installed, which are running the most current version and which are eligible for an upgrade.

Before we get into all of that, though, let’s jump into something easy. There are two views on the original widget, the tile/card view and the table view. The default is the tile/card view, which we were able to bring up, but to switch views, we will need some client-side code. Taking a quick peek at the HTML for the view selectors, we can see which function is being called.

<i id="tab-card"
  role="tab"
  class="fa fa-th tab-card-padding"
  ng-click="changeView('card')" 
  ng-keydown="switchTab($event)"
  aria-label="${Card View}" 
  ng-class="{'active' : view == 'card'}"
  title="${Card View}"
  data-toggle="{{!isTouchDevice() ? 'tooltip' : undefined}}"
  data-placement="top"
  data-container="body"
  aria-selected="{{view == 'card'}}" 
  aria-label="${Card View}"
  ng-attr-aria-controls="{{view == 'card' ? 'tabpanel-card-' + (data.category_id ? data.category_id : '') : undefined}}"
  tabindex="{{view == 'card' ? '0' : '-1'}}">
</i>

There are actually two functions referenced here, one for the ng-click and a different one for the ng-keydown. We should be able to locate both of those in the original widget, and we should be able to use them just the way that that appear in the original.

$scope.changeView = function (view) {
	$scope.view = view;
};

$scope.switchTab = function($event) {
	if ($event.which == 37 || $event.which == 39) {
		$event.stopPropagation();
		var layout = $scope.view === 'card' ? 'grid' : 'card';
		$scope.changeView(layout);
		$('#tab-' + layout).focus();
	}
};

With those in place, we should be able to pull up our new page and use the selectors to toggle over to the other view.

Collaboration Store table view

So that works, which is good. One other thing that you might have noticed is that we added the Host instance logo to the header of the store. That just took a little bit of extra HTML that we copied from the application list.

<div class="col-xs-9">
  <img ng-src="{{::c.data.store.logo}}.iix?t=small" ng-if="c.data.store.logo" alt="" class="m-r-sm m-b-sm item-image pull-left" aria-hidden="true"/>
  <h2 class="h4 m-t-none break-word">{{c.data.store.name}} Collaboration Store</h2>
  <p class="hidden-xs break-word">
    {{c.data.store.description}}
  </p>
</div>

Now, back to our earlier dilemma of differentiating between the various states of the applications from the store. The first thing that we will need to do is to pull the data that we need from the database and also get rid of all of that left-over catalog related stuff that we neglected to strip out earlier. Building up the item object now looks like this.

var item = {};
item.name = appGR.getDisplayValue('name');
item.description = appGR.getDisplayValue('description');
item.logo = appGR.getValue('logo');
item.version = appGR.getDisplayValue('current_version');
item.provider = appGR.getDisplayValue('provider.name');
item.providerLogo = appGR.provider.getRefRecord().getValue('logo');
item.local = appGR.getDisplayValue('provider.instance') == gs.getProperty('instance_name');
item.sys_id = appGR.getUniqueValue();
item.state = 0;
if (appGR.getValue('application')) {
	item.state = 1;
	item.installedVersion = appGR.getDisplayValue('application.version');
	if (item.version == item.installedVersion) {
		item.state = 2;
	}
}

Now that we have all of the data that we need, the next question is how do we want things to behave. For the local applications, maybe just a slightly different background would make those visually distinct. Let’s add the following class to the widget’s CSS:

.local-app {
	background-color: #b5ebd4;
}

Then, in the HTML for the card/tile layout, we can tweak the first DIV in the list item to look like this:

<div class="panel item-card b sc-panel{{item.local?' local-app':''}}">

This will add the local-app class to the DIV if the item is a local application. We can pull up the store and take a quick peek to see how that looks.

Collaboration Store with local apps visually distinguished

Not bad. We may still want to tinker with the CSS a bit to get things to our liking, but at least we have a method now to make the local apps look different than the other apps in the store.

Beyond looking different, though, we are also going to want store apps to behave differently than local apps, since local apps are published to the store, and store apps that are not local are meant to be pulled down from the store and installed on the local instance. Those that are already installed will have different options than those that are installed and up to date, and those that are not up to date will have different options compared with those that are. Let’s dive into all of that next time out.