Subscription

This module allows a user to subscribe to a resource. This module does
not include reading an events stream of changes to subscribed resources.
Provided are the following:

- A subscription directive that is configurable by resource and ID. It
is sensitive to the logged-in state and the current ID, and will resolve
its subscription state accordingly.
- A service which allows a user to read and write their
subscriptions.
- Subscription UI added to various resource list items.

Depends on https://review.openstack.org/108516

Change-Id: I2e9181e93209ce519f0a5eeb61630b1724a2527c
This commit is contained in:
Michael Krotscheck 2014-07-01 11:20:04 -07:00
parent 8667bada77
commit 3921f6bb80
12 changed files with 289 additions and 9 deletions

View File

@ -14,6 +14,9 @@
<story-status-label story="story"/>
</td>
<td>
<subscribe class="pull-right"
resource="story"
resource-id="story.id"></subscribe>
<p>
<a href="#!/story/{{story.id}}">
{{story.title}}

View File

@ -116,6 +116,7 @@
</i>
</a>
</th>
<th></th>
</tr>
</thead>
<tbody ng-if="searchResults.length != 0">

View File

@ -20,4 +20,9 @@
class="text-muted">
Not updated
</em>
</td>
</td>
<td>
<subscribe class="pull-right"
resource="project"
resource-id="project.id"></subscribe>
</td>

View File

@ -17,4 +17,9 @@
</td>
<td class="text-right col-xs-1">
<story-status-label story="story"/>
</td>
<td class="text-right">
<subscribe class="pull-right"
resource="story"
resource-id="story.id"></subscribe>
</td>

View File

@ -5,11 +5,6 @@
/>
</td>
<td>
<task-status-dropdown
editable="false"
status="{{task.status}}"
class="pull-right"
></task-status-dropdown>
{{task.title}}
<br/>
<small class="text-muted"
@ -17,4 +12,16 @@
<a href="#!/story/{{story.id}}">
{{story.id}}: {{story.title}}</a>
</small>
</td>
</td>
<td>
<task-status-dropdown
editable="false"
status="{{task.status}}"
></task-status-dropdown>
</td>
<td>
<subscribe class="pull-right"
resource="story"
resource-id="story.id"></subscribe>
</td>

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The angular resource for resource subscriptions.
*
* @see storyboardApiSignature
*/
angular.module('sb.services').factory('Subscription',
function (ResourceFactory) {
'use strict';
return ResourceFactory.build(
'/subscriptions/:id',
'/subscriptions/search',
{id: '@id'}
);
});

View File

@ -124,6 +124,7 @@
</i>
</a>
</th>
<th></th>
</tr>
</thead>
<tbody ng-if="searchResults.length != 0">

View File

@ -14,6 +14,8 @@
</td>
<td>
<p>
<subscribe resource="story"
resource-id="story.id"></subscribe>
<strong>
<a href="#!/story/{{story.id}}">
{{story.title}}

View File

@ -25,8 +25,8 @@
angular.module('storyboard',
[ 'sb.services', 'sb.templates', 'sb.dashboard', 'sb.pages', 'sb.projects',
'sb.auth', 'sb.story', 'sb.profile', 'sb.notification', 'sb.search',
'sb.admin', 'ui.router', 'ui.bootstrap', 'monospaced.elastic',
'angularMoment'])
'sb.admin', 'sb.subscription', 'ui.router', 'ui.bootstrap',
'monospaced.elastic', 'angularMoment'])
.constant('angularMomentConfig', {
preprocess: 'utc',
timezone: 'UTC'

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A directive which checks, enables, and disables subscriptions by resource.
*
* @author Michael Krotscheck
*/
angular.module('sb.util').directive('subscribe',
function (CurrentUser, Notification, Priority, SessionState, Session,
Subscription) {
'use strict';
return {
restrict: 'E',
scope: {
resource: '@',
resourceId: '='
},
templateUrl: 'app/subscription/template/subscribe.html',
link: function ($scope) {
/**
* When we start, create a promise for the current user.
*/
var cuPromise = CurrentUser.resolve();
/**
* Is this control currently enabled?
*
* @type {boolean}
*/
$scope.enabled = Session.isLoggedIn();
/**
* Is this user subscribed to this resource?
*
* @type {boolean}
*/
$scope.subscribed = false;
/**
* Is the control currently trying to resolve the user's
* subscription?
*
* @type {boolean}
*/
$scope.resolving = false;
/**
* The loaded subscription resource
*
* @type {Object}
*/
$scope.subscription = null;
/**
* The current user.
*
* @param currentUser
*/
$scope.currentUser = null;
cuPromise.then(function (user) {
$scope.currentUser = user;
});
/**
* Set or clear the subscription.
*/
function setSubscription(subscription) {
$scope.subscription = subscription || null;
$scope.subscribed = !!$scope.subscription;
}
// Subscribe to login/logout events for enable/disable/resolve.
var removeNotifier = Notification.intercept(function (message) {
switch (message.type) {
case SessionState.LOGGED_IN:
$scope.enabled = true;
resolveSubscription();
break;
case SessionState.LOGGED_OUT:
$scope.enabled = false;
$scope.subscribed = false;
break;
}
}, Priority.LAST);
// Remove the notifier when this scope is destroyed.
$scope.$on('$destroy', removeNotifier);
/**
* Resolve whether the current user already has a subscription
* to this resource.
*/
function resolveSubscription() {
if (!Session.isLoggedIn()) {
setSubscription();
return;
}
$scope.resolving = true;
cuPromise.then(
function (user) {
Subscription.query({
user_id: user.id,
target_type: $scope.resource,
target_id: $scope.resourceId
},
function (results) {
setSubscription(results[0]);
$scope.resolving = false;
},
function () {
setSubscription();
$scope.resolving = false;
}
);
}
);
}
/**
* When the user clicks on this control, activate/deactivate the
* subscription.
*/
$scope.toggleSubscribe = function () {
if ($scope.resolving) {
return;
}
$scope.resolving = true;
if (!!$scope.subscription) {
$scope.subscription.$delete(function () {
setSubscription();
$scope.resolving = false;
}, function () {
$scope.resolving = false;
});
} else {
cuPromise.then(
function (user) {
var sub = new Subscription({
user_id: user.id,
target_type: $scope.resource,
target_id: $scope.resourceId
});
sub.$create(function (result) {
setSubscription(result);
$scope.resolving = false;
}, function () {
$scope.resolving = false;
});
}
);
}
};
// On initialization, resolve.
resolveSubscription();
}
};
});

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* The StoryBoard subscription module. Adds directives and services necessary
* for a user to subscribe to resource changes in storyboard.
*
* @author Michael Krotscheck
*/
angular.module('sb.subscription', ['sb.notification']);

View File

@ -0,0 +1,20 @@
<span ng-if="!enabled && !resolving"
class="text-muted">
<i ng-if="subscribed"
class="fa fa-star"></i>
<i ng-if="!subscribed"
class="fa fa-star-o"></i>
</span>
<span ng-if="resolving"
class="text-muted">
<i class="fa fa-spin fa-refresh"></i>
</span>
<a ng-if="enabled && !resolving"
href=""
title="Subscribe"
ng-click="toggleSubscribe()">
<i ng-if="subscribed"
class="fa fa-star"></i>
<i ng-if="!subscribed"
class="fa fa-star-o"></i>
</a>