Taiga-events integration (realtime taiga)
parent
56954aa1f0
commit
38a6f73b9f
|
@ -21,7 +21,21 @@
|
|||
|
||||
@taiga = taiga = {}
|
||||
|
||||
configure = ($routeProvider, $locationProvider, $httpProvider, $provide, tgLoaderProvider) ->
|
||||
# Generic function for generate hash from a arbitrary length
|
||||
# collection of parameters.
|
||||
taiga.generateHash = (components=[]) ->
|
||||
components = _.map(components, (x) -> JSON.stringify(x))
|
||||
return hex_sha1(components.join(":"))
|
||||
|
||||
taiga.generateUniqueSessionIdentifier = ->
|
||||
date = (new Date()).getTime()
|
||||
randomNumber = Math.floor(Math.random() * 0x9000000)
|
||||
return taiga.generateHash([date, randomNumber])
|
||||
|
||||
taiga.sessionId = taiga.generateUniqueSessionIdentifier()
|
||||
|
||||
|
||||
configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider) ->
|
||||
$routeProvider.when("/",
|
||||
{templateUrl: "/partials/projects.html", resolve: {loader: tgLoaderProvider.add()}})
|
||||
$routeProvider.when("/project/:pslug/",
|
||||
|
@ -127,13 +141,18 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, tgLoade
|
|||
defaultHeaders = {
|
||||
"Content-Type": "application/json"
|
||||
"Accept-Language": "en"
|
||||
"X-Session-Id": taiga.sessionId
|
||||
}
|
||||
|
||||
$httpProvider.defaults.headers.delete = defaultHeaders
|
||||
$httpProvider.defaults.headers.patch = defaultHeaders
|
||||
$httpProvider.defaults.headers.post = defaultHeaders
|
||||
$httpProvider.defaults.headers.put = defaultHeaders
|
||||
$httpProvider.defaults.headers.get = {}
|
||||
$httpProvider.defaults.headers.get = {
|
||||
"X-Session-Id": taiga.sessionId
|
||||
}
|
||||
|
||||
$tgEventsProvider.setSessionId(taiga.sessionId)
|
||||
|
||||
# Add next param when user try to access to a secction need auth permissions.
|
||||
authHttpIntercept = ($q, $location, $confirm, $navUrls, $lightboxService) ->
|
||||
|
@ -148,7 +167,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, tgLoade
|
|||
$location.url($navUrls.resolve("login")).search("next=#{nextPath}")
|
||||
return $q.reject(response)
|
||||
|
||||
$provide.factory("authHttpIntercept", ["$q", "$location", "$tgConfirm", "$tgNavUrls", "lightboxService", authHttpIntercept])
|
||||
$provide.factory("authHttpIntercept", ["$q", "$location", "$tgConfirm", "$tgNavUrls",
|
||||
"lightboxService", authHttpIntercept])
|
||||
$httpProvider.responseInterceptors.push('authHttpIntercept')
|
||||
$httpProvider.interceptors.push('loaderInterceptor')
|
||||
|
||||
|
@ -166,10 +186,13 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, tgLoade
|
|||
linewidth: "The subject must have a maximum size of %s"
|
||||
})
|
||||
|
||||
init = ($log, $i18n, $config, $rootscope) ->
|
||||
init = ($log, $i18n, $config, $rootscope, $auth, $events) ->
|
||||
$i18n.initialize($config.get("defaultLanguage"))
|
||||
$log.debug("Initialize application")
|
||||
|
||||
if $auth.isAuthenticated()
|
||||
$events.setupConnection()
|
||||
|
||||
# Default Value for taiga local config module.
|
||||
angular.module("taigaLocalConfig", []).value("localconfig", {})
|
||||
|
||||
|
@ -181,6 +204,7 @@ modules = [
|
|||
"taigaResources",
|
||||
"taigaLocales",
|
||||
"taigaAuth",
|
||||
"taigaEvents",
|
||||
|
||||
# Specific Modules
|
||||
"taigaRelatedTasks",
|
||||
|
@ -211,6 +235,7 @@ module.config([
|
|||
"$locationProvider",
|
||||
"$httpProvider",
|
||||
"$provide",
|
||||
"$tgEventsProvider",
|
||||
"tgLoaderProvider",
|
||||
configure
|
||||
])
|
||||
|
@ -220,5 +245,7 @@ module.run([
|
|||
"$tgI18n",
|
||||
"$tgConfig",
|
||||
"$rootScope",
|
||||
"$tgAuth",
|
||||
"$tgEvents",
|
||||
init
|
||||
])
|
||||
|
|
|
@ -171,7 +171,7 @@ PublicRegisterMessageDirective = ($config, $navUrls) ->
|
|||
module.directive("tgPublicRegisterMessage", ["$tgConfig", "$tgNavUrls", PublicRegisterMessageDirective])
|
||||
|
||||
|
||||
LoginDirective = ($auth, $confirm, $location, $routeParams, $navUrls) ->
|
||||
LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $events) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$scope.data = {}
|
||||
|
||||
|
@ -181,6 +181,7 @@ LoginDirective = ($auth, $confirm, $location, $routeParams, $navUrls) ->
|
|||
else
|
||||
nextUrl = $navUrls.resolve("home")
|
||||
|
||||
$events.setupConnection()
|
||||
$location.path(nextUrl)
|
||||
|
||||
onErrorSubmit = (response) ->
|
||||
|
@ -204,8 +205,8 @@ LoginDirective = ($auth, $confirm, $location, $routeParams, $navUrls) ->
|
|||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgLogin", ["$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams", "$tgNavUrls",
|
||||
LoginDirective])
|
||||
module.directive("tgLogin", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgConfig", "$routeParams",
|
||||
"$tgNavUrls", "$tgEvents", LoginDirective])
|
||||
|
||||
#############################################################################
|
||||
## Register Directive
|
||||
|
|
|
@ -46,11 +46,12 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
"$tgLocation",
|
||||
"$appTitle",
|
||||
"$tgNavUrls",
|
||||
"$tgEvents",
|
||||
"tgLoader"
|
||||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @appTitle, @navUrls,
|
||||
tgLoader) ->
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q,
|
||||
@location, @appTitle, @navUrls, @events, tgLoader) ->
|
||||
_.bindAll(@)
|
||||
|
||||
@scope.sectionName = "Backlog"
|
||||
|
@ -94,8 +95,18 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@scope.$on("sprint:us:moved", @.loadSprints)
|
||||
@scope.$on("sprint:us:moved", @.loadProjectStats)
|
||||
|
||||
initializeSubscription: ->
|
||||
routingKey1 = "changes.project.#{@scope.projectId}.userstories"
|
||||
@events.subscribe @scope, routingKey1, (message) =>
|
||||
@.loadUserstories()
|
||||
@.loadSprints()
|
||||
|
||||
routingKey2 = "changes.project.#{@scope.projectId}.milestones"
|
||||
@events.subscribe @scope, routingKey2, (message) =>
|
||||
@.loadSprints()
|
||||
|
||||
toggleShowTags: ->
|
||||
@scope.$apply () =>
|
||||
@scope.$apply =>
|
||||
@showTags = !@showTags
|
||||
@rs.userstories.storeShowTags(@scope.projectId, @showTags)
|
||||
|
||||
|
@ -186,6 +197,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
# Resolve project slug
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
@.initializeSubscription()
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
|
|
|
@ -333,15 +333,20 @@ ListItemPriorityDirective = ->
|
|||
"""
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
issue = $scope.$eval($attrs.tgListitemPriority)
|
||||
bindOnce $scope, "priorityById", (priorityById) ->
|
||||
render = (priorityById, issue) ->
|
||||
priority = priorityById[issue.priority]
|
||||
|
||||
domNode = $el.find("div.level")
|
||||
domNode = $el.find(".level")
|
||||
domNode.css("background-color", priority.color)
|
||||
domNode.addClass(priority.name.toLowerCase())
|
||||
domNode.attr("title", priority.name)
|
||||
|
||||
bindOnce $scope, "priorityById", (priorityById) ->
|
||||
issue = $scope.$eval($attrs.tgListitemPriority)
|
||||
render(priorityById, issue)
|
||||
|
||||
$scope.$watch $attrs.tgListitemPriority, (issue) ->
|
||||
render($scope.priorityById, issue)
|
||||
|
||||
return {
|
||||
link: link
|
||||
template: template
|
||||
|
@ -354,15 +359,20 @@ ListItemSeverityDirective = ->
|
|||
"""
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
issue = $scope.$eval($attrs.tgListitemSeverity)
|
||||
bindOnce $scope, "severityById", (severityById) ->
|
||||
render = (severityById, issue) ->
|
||||
severity = severityById[issue.severity]
|
||||
|
||||
domNode = $el.find("div.level")
|
||||
domNode = $el.find(".level")
|
||||
domNode.css("background-color", severity.color)
|
||||
domNode.addClass(severity.name.toLowerCase())
|
||||
domNode.attr("title", severity.name)
|
||||
|
||||
bindOnce $scope, "severityById", (severityById) ->
|
||||
issue = $scope.$eval($attrs.tgListitemSeverity)
|
||||
render(severityById, issue)
|
||||
|
||||
$scope.$watch $attrs.tgListitemSeverity, (issue) ->
|
||||
render($scope.severityById, issue)
|
||||
|
||||
return {
|
||||
link: link
|
||||
template: template
|
||||
|
@ -374,16 +384,20 @@ ListItemTypeDirective = ->
|
|||
"""
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
issue = $scope.$eval($attrs.tgListitemType)
|
||||
|
||||
bindOnce $scope, "issueTypeById", (issueTypeById) ->
|
||||
render = (issueTypeById, issue) ->
|
||||
type = issueTypeById[issue.type]
|
||||
|
||||
domNode = $el.find("div.level")
|
||||
domNode = $el.find(".level")
|
||||
domNode.css("background-color", type.color)
|
||||
domNode.addClass(type.name.toLowerCase())
|
||||
domNode.attr("title", type.name)
|
||||
|
||||
bindOnce $scope, "issueTypeById", (issueTypeById) ->
|
||||
issue = $scope.$eval($attrs.tgListitemType)
|
||||
render(issueTypeById, issue)
|
||||
|
||||
$scope.$watch $attrs.tgListitemType, (issue) ->
|
||||
render($scope.issueTypeById, issue)
|
||||
|
||||
return {
|
||||
link: link
|
||||
template: template
|
||||
|
|
|
@ -42,9 +42,9 @@ UsStatusDirective = ($repo, popoverService) ->
|
|||
|
||||
NOTE: This directive need 'usStatusById' and 'project'.
|
||||
###
|
||||
selectionTemplate = _.template("""
|
||||
template = _.template("""
|
||||
<ul class="popover pop-status">
|
||||
<% _.forEach(statuses, function(status) { %>
|
||||
<% _.each(statuses, function(status) { %>
|
||||
<li>
|
||||
<a href="" class="status" title="<%- status.name %>" data-status-id="<%- status.id %>">
|
||||
<%- status.name %>
|
||||
|
@ -53,51 +53,56 @@ UsStatusDirective = ($repo, popoverService) ->
|
|||
<% }); %>
|
||||
</ul>""")
|
||||
|
||||
updateUsStatus = ($el, us, usStatusById) ->
|
||||
usStatusDomParent = $el.find(".us-status")
|
||||
usStatusDom = $el.find(".us-status .us-status-bind")
|
||||
|
||||
if usStatusById[us.status]
|
||||
usStatusDom.text(usStatusById[us.status].name)
|
||||
usStatusDomParent.prop("title", usStatusById[us.status].name)
|
||||
usStatusDomParent.css('color', usStatusById[us.status].color)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$ctrl = $el.controller()
|
||||
us = $scope.$eval($attrs.tgUsStatus)
|
||||
|
||||
render = (us) ->
|
||||
usStatusDomParent = $el.find(".us-status")
|
||||
usStatusDom = $el.find(".us-status .us-status-bind")
|
||||
usStatusById = $scope.usStatusById
|
||||
|
||||
if usStatusById[us.status]
|
||||
usStatusDom.text(usStatusById[us.status].name)
|
||||
usStatusDomParent.css("color", usStatusById[us.status].color)
|
||||
|
||||
$el.on "click", ".us-status", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
$el.find(".pop-status").popover().open()
|
||||
|
||||
# pop = $el.find(".pop-status")
|
||||
# popoverService.open(pop)
|
||||
|
||||
$el.on "click", ".status", debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
us = $scope.$eval($attrs.tgUsStatus)
|
||||
us.status = target.data("status-id")
|
||||
render(us)
|
||||
|
||||
$el.find(".pop-status").popover().close()
|
||||
updateUsStatus($el, us, $scope.usStatusById)
|
||||
|
||||
$scope.$apply () ->
|
||||
$repo.save(us).then ->
|
||||
$scope.$eval($attrs.onUpdate)
|
||||
|
||||
taiga.bindOnce $scope, "project", (project) ->
|
||||
$el.append(selectionTemplate({ 'statuses': project.us_statuses }))
|
||||
updateUsStatus($el, us, $scope.usStatusById)
|
||||
|
||||
$scope.$on("userstories:loaded", -> render($scope.$eval($attrs.tgUsStatus)))
|
||||
$scope.$on("$destroy", -> $el.off())
|
||||
|
||||
# Bootstrap
|
||||
us = $scope.$eval($attrs.tgUsStatus)
|
||||
render(us)
|
||||
|
||||
bindOnce $scope, "project", (project) ->
|
||||
html = template({"statuses": project.us_statuses})
|
||||
$el.append(html)
|
||||
|
||||
# If the user has not enough permissions the click events are unbinded
|
||||
if project.my_permissions.indexOf("modify_us") == -1
|
||||
if $scope.project.my_permissions.indexOf("modify_us") == -1
|
||||
$el.unbind("click")
|
||||
$el.find("a").addClass("not-clickable")
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {link: link}
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: modules/events.coffee
|
||||
###
|
||||
|
||||
taiga = @.taiga
|
||||
|
||||
module = angular.module("taigaEvents", [])
|
||||
|
||||
|
||||
class EventsService
|
||||
constructor: (@win, @log, @config, @auth) ->
|
||||
_.bindAll(@)
|
||||
|
||||
initialize: (sessionId) ->
|
||||
@.sessionId = sessionId
|
||||
@.subscriptions = {}
|
||||
|
||||
if @win.WebSocket is undefined
|
||||
@log.debug "WebSockets not supported on your browser"
|
||||
|
||||
setupConnection: ->
|
||||
@.stopExistingConnection()
|
||||
|
||||
wshost = @config.get("eventsHost", "localhost:8888")
|
||||
wsscheme = @config.get("eventsScheme", "ws")
|
||||
url = "#{wsscheme}://#{wshost}/events"
|
||||
|
||||
@.ws = new @win.WebSocket(url)
|
||||
@.ws.addEventListener("open", @.onOpen)
|
||||
@.ws.addEventListener("message", @.onMessage)
|
||||
@.ws.addEventListener("error", @.onError)
|
||||
@.ws.addEventListener("close", @.onClose)
|
||||
|
||||
stopExistingConnection: ->
|
||||
if @.ws is undefined
|
||||
return
|
||||
|
||||
@.ws.close()
|
||||
@.ws.removeEventListener("open", @.onOpen)
|
||||
@.ws.removeEventListener("close", @.onClose)
|
||||
@.ws.removeEventListener("error", @.onError)
|
||||
@.ws.removeEventListener("message", @.onMessage)
|
||||
|
||||
delete @.ws
|
||||
|
||||
onOpen: ->
|
||||
@log.debug("WebSocket connection opened")
|
||||
token = @auth.getToken()
|
||||
|
||||
message = {
|
||||
cmd: "auth"
|
||||
data: {token: token, sessionId: @.sessionId}
|
||||
}
|
||||
|
||||
@.ws.send(JSON.stringify(message))
|
||||
|
||||
onMessage: (event) ->
|
||||
@.log.debug "WebSocket message received: #{event.data}"
|
||||
|
||||
data = JSON.parse(event.data)
|
||||
routingKey = data.routing_key
|
||||
|
||||
if not @.subscriptions[routingKey]?
|
||||
return
|
||||
|
||||
subscription = @.subscriptions[routingKey]
|
||||
subscription.scope.$apply ->
|
||||
subscription.callback(data.data)
|
||||
|
||||
onError: (error) ->
|
||||
@log.error("WebSocket error: #{error}")
|
||||
|
||||
onClose: ->
|
||||
@log.debug("WebSocket closed.")
|
||||
|
||||
subscribe: (scope, routingKey, callback) ->
|
||||
subscription = {
|
||||
scope: scope,
|
||||
routingKey: routingKey,
|
||||
callback: callback
|
||||
}
|
||||
|
||||
message = {
|
||||
"cmd": "subscribe",
|
||||
"routing_key": routingKey
|
||||
}
|
||||
|
||||
@.subscriptions[routingKey] = subscription
|
||||
@.ws.send(JSON.stringify(message))
|
||||
scope.$on("$destroy", => @.unsubscribe(routingKey))
|
||||
|
||||
unsubscribe: (routingKey) ->
|
||||
message = {
|
||||
"cmd": "unsubscribe",
|
||||
"routing_key": routingKey
|
||||
}
|
||||
|
||||
@.ws.send(JSON.stringify(message))
|
||||
|
||||
|
||||
class EventsProvider
|
||||
setSessionId: (sessionId) ->
|
||||
@.sessionId = sessionId
|
||||
|
||||
$get: ($win, $log, $conf, $auth) ->
|
||||
service = new EventsService($win, $log, $conf, $auth)
|
||||
service.initialize(@.sessionId)
|
||||
return service
|
||||
|
||||
@.prototype.$get.$inject = ["$window", "$log", "$tgConfig", "$tgAuth"]
|
||||
|
||||
module.provider("$tgEvents", EventsProvider)
|
|
@ -49,11 +49,12 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
"$tgLocation",
|
||||
"$appTitle",
|
||||
"$tgNavUrls",
|
||||
"$tgEvents",
|
||||
"tgLoader"
|
||||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @urls, @params, @q, @location, @appTitle,
|
||||
@navUrls, tgLoader) ->
|
||||
@navUrls, @events, tgLoader) ->
|
||||
@scope.sectionName = "Issues"
|
||||
@scope.filters = {}
|
||||
|
||||
|
@ -82,6 +83,11 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@.loadIssues()
|
||||
@.loadFilters()
|
||||
|
||||
initializeSubscription: ->
|
||||
routingKey = "changes.project.#{@scope.projectId}.issues"
|
||||
@events.subscribe @scope, routingKey, (message) =>
|
||||
@.loadIssues()
|
||||
|
||||
storeFilters: ->
|
||||
@rs.issues.storeFilters(@params.pslug, @location.search())
|
||||
|
||||
|
@ -256,6 +262,7 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
loadInitialData: ->
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
@.initializeSubscription()
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
|
@ -755,6 +762,9 @@ IssueStatusInlineEditionDirective = ($repo, popoverService) ->
|
|||
$el.unbind("click")
|
||||
$el.find("a").addClass("not-clickable")
|
||||
|
||||
$scope.$watch $attrs.tgIssueStatusInlineEdition, (val) =>
|
||||
updateIssueStatus($el, val, $scope.issueStatusById)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
@ -803,6 +813,9 @@ IssueAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService) ->
|
|||
$repo.save(updatedIssue)
|
||||
updateIssue(updatedIssue)
|
||||
|
||||
$scope.$watch $attrs.tgIssueAssignedToInlineEdition, (val) =>
|
||||
updateIssue(val)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
|
|
@ -59,11 +59,12 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
"$tgLocation",
|
||||
"$appTitle",
|
||||
"$tgNavUrls",
|
||||
"$tgEvents",
|
||||
"tgLoader"
|
||||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @appTitle, @navUrls,
|
||||
tgLoader) ->
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||
@appTitle, @navUrls, @events, tgLoader) ->
|
||||
_.bindAll(@)
|
||||
@scope.sectionName = "Kanban"
|
||||
@scope.statusViewModes = {}
|
||||
|
@ -162,10 +163,16 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@scope.$emit("project:loaded", project)
|
||||
return project
|
||||
|
||||
initializeSubscription: ->
|
||||
routingKey1 = "changes.project.#{@scope.projectId}.userstories"
|
||||
@events.subscribe @scope, routingKey1, (message) =>
|
||||
@.loadUserstories()
|
||||
|
||||
loadInitialData: ->
|
||||
# Resolve project slug
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
@.initializeSubscription()
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
|
|
|
@ -46,11 +46,12 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
"$appTitle",
|
||||
"$tgLocation",
|
||||
"$tgNavUrls"
|
||||
"$tgEvents"
|
||||
"tgLoader"
|
||||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appTitle, @location, @navUrls,
|
||||
tgLoader) ->
|
||||
@events, tgLoader) ->
|
||||
_.bindAll(@)
|
||||
|
||||
@scope.sectionName = "Taskboard"
|
||||
|
@ -82,6 +83,11 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
promise.then null, ->
|
||||
console.log "FAIL" # TODO
|
||||
|
||||
initializeSubscription: ->
|
||||
routingKey = "changes.project.#{@scope.projectId}.tasks"
|
||||
@events.subscribe @scope, routingKey, (message) =>
|
||||
@.loadTaskboard()
|
||||
|
||||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
|
@ -157,6 +163,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
promise = @repo.resolve(params).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
@scope.sprintId = data.milestone
|
||||
@.initializeSubscription()
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
|
|
|
@ -125,15 +125,6 @@ sizeFormat = (input, precision=1) ->
|
|||
return "#{size} #{units[number]}"
|
||||
|
||||
|
||||
typeIsArray = Array.isArray || ( value ) -> return {}.toString.call( value ) is '[object Array]'
|
||||
|
||||
|
||||
# Generic method for generate hash from a arbitrary length
|
||||
# collection of parameters.
|
||||
generateHash = (components=[]) ->
|
||||
components = _.map(components, (x) -> JSON.stringify(x))
|
||||
return hex_sha1(components.join(":"))
|
||||
|
||||
taiga = @.taiga
|
||||
taiga.nl2br = nl2br
|
||||
taiga.bindOnce = bindOnce
|
||||
|
@ -151,5 +142,3 @@ taiga.joinStr = joinStr
|
|||
taiga.debounce = debounce
|
||||
taiga.startswith = startswith
|
||||
taiga.sizeFormat = sizeFormat
|
||||
taiga.typeIsArray = typeIsArray
|
||||
taiga.generateHash = generateHash
|
||||
|
|
|
@ -14,7 +14,7 @@ section.issues-table.basic-table(ng-class="{empty: !issues.length}")
|
|||
div.subject
|
||||
a(href="", tg-nav="project-issues-detail:project=project.slug,ref=issue.ref")
|
||||
span(tg-bo-ref="issue.ref")
|
||||
span(tg-bo-bind="issue.subject")
|
||||
span(ng-bind="issue.subject")
|
||||
|
||||
div.issue-field(tg-issue-status-inline-edition="issue")
|
||||
a.issue-status(href="", title="Change status")
|
||||
|
|
Loading…
Reference in New Issue