From 8cc04a7f58adc520d26c5110913a60eededdd339 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Tue, 14 Apr 2015 12:25:20 +0200 Subject: [PATCH] timeline items header --- .../common/compile-html.directive.coffee | 13 + .../profile/profileTimeline.controller.coffee | 21 +- .../timeline-attachment.directive.coffee | 2 + .../profile/timeline-item.directive.coffee | 252 ++++++++++++++++-- app/locales/locale-en.json | 32 ++- .../modules/profile/profile-timeline.jade | 2 +- .../profile/timeline/comment-timeline.jade | 12 - .../profile/timeline/timeline-attachment.jade | 15 +- .../profile/timeline/timeline-item.jade | 11 + 9 files changed, 303 insertions(+), 57 deletions(-) create mode 100644 app/coffee/modules/common/compile-html.directive.coffee delete mode 100644 app/partials/profile/timeline/comment-timeline.jade create mode 100644 app/partials/profile/timeline/timeline-item.jade diff --git a/app/coffee/modules/common/compile-html.directive.coffee b/app/coffee/modules/common/compile-html.directive.coffee new file mode 100644 index 00000000..195d6a2c --- /dev/null +++ b/app/coffee/modules/common/compile-html.directive.coffee @@ -0,0 +1,13 @@ +CompileHtmlDirective = ($compile) -> + link = (scope, element, attrs) -> + scope.$watch attrs.tgCompileHtml, (newValue, oldValue) -> + element.html(newValue) + $compile(element.contents())(scope) + + return { + link: link + } + +CompileHtmlDirective.$inject = ["$compile"] + +angular.module("taigaCommon").directive("tgCompileHtml", CompileHtmlDirective) diff --git a/app/coffee/modules/profile/profileTimeline.controller.coffee b/app/coffee/modules/profile/profileTimeline.controller.coffee index ca8c20f0..a8656c55 100644 --- a/app/coffee/modules/profile/profileTimeline.controller.coffee +++ b/app/coffee/modules/profile/profileTimeline.controller.coffee @@ -30,16 +30,33 @@ class ProfileTimelineController extends mixOf(taiga.Controller, taiga.PageMixin, "$tgAuth" ] + valid_fields: ['status', 'subject', 'description', 'assigned_to', 'points', 'severity', 'priority', 'type', 'attachments', 'milestone', 'is_blocked', 'is_iocaine', 'content_diff', 'name', 'estimated_finish', 'estimated_start'] + constructor: (@scope, @rs, @auth) -> promise = @.loadTimeline() promise.then null, @.onInitialDataError.bind(@) + isValid: (values) => + return _.some values, (value) => @valid_fields.indexOf(value) != -1 + + filterValidTimelineItems: (timeline) => + if timeline.data.values_diff + values = Object.keys(timeline.data.values_diff) + + if values && values.length + if !@isValid(values) + return false + else if values[0] == 'attachments' && timeline.data.values_diff.attachments.new.length == 0 + return false + + return true + loadTimeline: () -> user = @auth.getUser() return @rs.timeline.profile(user.id).then (result) => - @scope.result = result - console.log @scope.result.data + console.log result.data + @scope.timelineList = _.filter result.data, @filterValidTimelineItems angular.module("taigaProfile") .controller("ProfileTimeline", ProfileTimelineController) diff --git a/app/coffee/modules/profile/timeline-attachment.directive.coffee b/app/coffee/modules/profile/timeline-attachment.directive.coffee index 7bbe12fa..88669bdd 100644 --- a/app/coffee/modules/profile/timeline-attachment.directive.coffee +++ b/app/coffee/modules/profile/timeline-attachment.directive.coffee @@ -12,6 +12,8 @@ TimelineAttachmentDirective = ($tgTemplate, $compile) -> if is_image template = $tgTemplate.get("profile/timeline/timeline-attachment-image.html") + else + template = $tgTemplate.get("profile/timeline/timeline-attachment.html") $el.html(template) $compile($el.contents())($scope) diff --git a/app/coffee/modules/profile/timeline-item.directive.coffee b/app/coffee/modules/profile/timeline-item.directive.coffee index 6ac4726f..4a3a69b8 100644 --- a/app/coffee/modules/profile/timeline-item.directive.coffee +++ b/app/coffee/modules/profile/timeline-item.directive.coffee @@ -1,4 +1,151 @@ -TimelineItemDirective = ($tgTemplate, $compile, $navUrls) -> +timelineTitle = (timeline, event) -> + title = [ + { # NewMember + check: (timeline, event) -> + return event.obj == 'membership' + key: 'TIMELINE.NEW_MEMBER', + translate_params: ['project_name'] + }, + { # NewProject + check: (timeline, event) -> + return event.obj == 'project' && event.type == 'create' + key: 'TIMELINE.NEW_PROJECT', + translate_params: ['username', 'project_name'] + }, + { # NewAttachment + check: (timeline, event) -> + return event.type == 'change' && timeline.data.values_diff.attachments + key: 'TIMELINE.UPLOAD_ATTACHMENT', + translate_params: ['username', 'obj_name'] + }, + { # NewUs + check: (timeline, event) -> + return event.obj == 'userstory' && event.type == 'create' + key: 'TIMELINE.US_CREATED', + translate_params: ['username', 'project_name', 'obj_name'] + }, + { # NewIssue + check: (timeline, event) -> + return event.obj == 'issue' && event.type == 'create' + key: 'TIMELINE.ISSUE_CREATED', + translate_params: ['username', 'project_name', 'obj_name'] + }, + { # NewWiki + check: (timeline, event) -> + return event.obj == 'wikipage' && event.type == 'create' + key: 'TIMELINE.WIKI_CREATED', + translate_params: ['username', 'project_name', 'obj_name'] + }, + { # NewTask + check: (timeline, event) -> + return event.obj == 'task' && event.type == 'create' + key: 'TIMELINE.TASK_CREATED', + translate_params: ['username', 'project_name', 'obj_name'] + }, + { # NewUsComment + check: (timeline, event) -> + return timeline.data.comment && event.obj == 'userstory' + key: 'TIMELINE.NEW_COMMENT_US', + translate_params: ['username', 'obj_name'] + }, + { # NewIssueComment + check: (timeline, event) -> + return timeline.data.comment && event.obj == 'issue' + key: 'TIMELINE.NEW_COMMENT_ISSUE', + translate_params: ['username', 'obj_name'] + }, + { # NewTask + check: (timeline, event) -> + return timeline.data.comment && event.obj == 'task' + key: 'TIMELINE.NEW_COMMENT_TASK' + translate_params: ['username', 'obj_name'] + }, + { # UsToMilestone + check: (timeline, event, field_name) -> + if field_name == 'milestone' && event.type == 'change' + return timeline.data.values_diff.milestone[0] == null + + return false + key: 'TIMELINE.US_ADDED_MILESTONE', + translate_params: ['username', 'obj_name', 'sprint_name'] + }, + { # UsToBacklog + check: (timeline, event, field_name) -> + if field_name == 'milestone' && event.type == 'change' + return timeline.data.values_diff.milestone[1] == null + + return false + key: 'TIMELINE.US_REMOVED_FROM_MILESTONE', + translate_params: ['username', 'obj_name'] + }, + { # Blocked + check: (timeline, event) -> + if event.type == 'change' && timeline.data.values_diff.is_blocked + return timeline.data.values_diff.is_blocked[1] == true + + return false + key: 'TIMELINE.BLOCKED', + translate_params: ['username', 'obj_name'] + }, + { # UnBlocked + check: (timeline, event) -> + if event.type == 'change' && timeline.data.values_diff.is_blocked + return timeline.data.values_diff.is_blocked[1] == false + + return false + key: 'TIMELINE.UNBLOCKED', + translate_params: ['username', 'obj_name'] + }, + { # MilestoneUpdated + check: (timeline, event) -> + return event.obj == 'milestone' && event.type == 'change' + key: 'TIMELINE.MILESTONE_UPDATED', + translate_params: ['username', 'obj_name'] + }, + { # WikiUpdated + check: (timeline, event) -> + return event.obj == 'wikipage' && event.type == 'change' + key: 'TIMELINE.WIKI_UPDATED', + translate_params: ['username', 'obj_name'] + }, + { # UsUpdated + check: (timeline, event) -> + return event.obj == 'userstory' && event.type == 'change' + key: 'TIMELINE.US_UPDATED', + translate_params: ['username', 'field_name', 'obj_name'] + }, + { # IssueUpdated + check: (timeline, event) -> + return event.obj == 'issue' && event.type == 'change' + key: 'TIMELINE.ISSUE_UPDATED', + translate_params: ['username', 'field_name', 'obj_name'] + }, + { # TaskUpdated + check: (timeline, event) -> + return event.obj == 'task' && event.type == 'change' + key: 'TIMELINE.TASK_UPDATED', + translate_params: ['username', 'field_name', 'obj_name'] + } + ] + + if timeline.data.values_diff + field_name = Object.keys(timeline.data.values_diff)[0] + + return _.find title, (obj) -> + return obj.check(timeline, event, field_name) + +TimelineItemDirective = ($tgTemplate, $compile, $navUrls, $translate, $sce) -> + fieldTranslationKey = { + 'status': 'COMMON.FIELDS.STATUS', + 'subject': 'COMMON.FIELDS.SUBJECT', + 'description': 'COMMON.FIELDS.DESCRIPTION', + 'points': 'COMMON.FIELDS.POINTS', + 'severity': 'ISSUES.FIELDS.SEVERITY', + 'priority': 'ISSUES.FIELDS.PRIORITY', + 'type': 'ISSUES.FIELDS.TYPE', + 'is_iocaine': 'TASK.FIELDS.IS_IOCAINE' + } + parseEventType = (event_type) -> event_type = event_type.split(".") @@ -8,53 +155,100 @@ TimelineItemDirective = ($tgTemplate, $compile, $navUrls) -> type: event_type[2] } - getUrl = (timeline, event) -> + getDetailObjUrl = (event) -> url = { - "issue": "project-issues-detail", - "wiki": "project-wiki-page", - "task": "project-tasks-detail", - "userstories": "project-userstories-detail" + "issue": ["project-issues-detail", ":project=activity.project.slug,ref=activity.obj.ref"], + "wikipage": ["project-wiki-page", ":project=activity.project.slug,slug=activity.obj.slug"], + "task": ["project-tasks-detail", ":project=activity.project.slug,ref=activity.obj.ref"], + "userstory": ["project-userstories-detail", ":project=activity.project.slug,ref=activity.obj.ref"], + "milestone": ["project-taskboard", ":project=activity.project.slug,sprint=activity.obj.slug"] } - params = {project: timeline.data.project.slug, ref: timeline.data[event.obj].ref} + return url[event.obj][0] + url[event.obj][1] - return $navUrls.resolve(url[event.obj], params) + getLink = (url, text, title) -> + title = title || text - getTemplate = (timeline, event) -> - template = "" + return $('') + .attr('tg-nav', url) + .text(text) + .attr('title', title) + .prop('outerHTML') - if event.type == 'change' - if timeline.data.comment.length - template = "profile/timeline/comment-timeline.html" - else if timeline.data.values_diff.attachments - template = "profile/timeline/attachment-timeline.html" + translate_params = { + username: (timeline) -> + user = timeline.data.user + title_attr = $translate.instant('COMMON.SEE_USER_PROFILE', {username: user.username}) + url = 'user-profile:username=activity.user.username' + return getLink(url, user.username, title_attr) - return $tgTemplate.get(template) + field_name: (timeline) -> + field_name = Object.keys(timeline.data.values_diff)[0] + + return $translate.instant(fieldTranslationKey[field_name]) + + project_name: (timeline) -> + url = 'project:project=activity.project.slug' + + return getLink(url, timeline.data.project.name) + + sprint_name: (timeline) -> + url = 'project-taskboard:project=activity.project.slug,sprint=activity.sprint.slug' + + return getLink(url, timeline.data.milestone.name) + + obj_name: (timeline, event) -> + obj = getTimelineObj(timeline, event) + url = getDetailObjUrl(event) + + if event.obj == 'wikipage' + text = obj.slug + else if event.obj == 'milestone' + text = obj.name + else + text = '#' + obj.ref + ' ' + obj.subject + + return getLink(url, text) + } + + getTimelineObj = (timeline, event) -> + return timeline.data[event.obj] + + getParams = (timeline, event, timeline_type) -> + params = {} + + timeline_type.translate_params.forEach (param) -> + params[param] = translate_params[param](timeline, event) + + return params + + getTitle = (timeline, event) -> + type = timelineTitle(timeline, event) + + return $translate.instant(type.key, getParams(timeline, event, type)) link = ($scope, $el, $attrs) -> event = parseEventType($scope.timeline.event_type) - template = getTemplate($scope.timeline, event) - if !template - return "" + $scope.activity = {} - obj = $scope.timeline.data[event.obj] + $scope.activity.obj = getTimelineObj($scope.timeline, event) + $scope.activity.user = $scope.timeline.data.user + $scope.activity.project = $scope.timeline.data.project + $scope.activity.sprint = $scope.timeline.data.milestone + $scope.activity.title = getTitle($scope.timeline, event) + $scope.activity.created_formated = moment($scope.timeline.created).fromNow() - $scope.timeline.subject = obj.subject - $scope.timeline.ref = obj.ref - $scope.timeline.type = event.obj - $scope.timeline.created_formated = moment($scope.timeline.created).fromNow() - $scope.timeline.detail_url = getUrl($scope.timeline, event) - - $el.html(template) - $compile($el.contents())($scope) + if $scope.timeline.data.values_diff?.attachments + $scope.activity.attachments = $scope.timeline.data.values_diff.attachments.new return { link: link + templateUrl: "profile/timeline/timeline-item.html" scope: { timeline: "=tgTimelineItem" } } angular.module("taigaProfile") - .directive("tgTimelineItem", ["$tgTemplate", "$compile", "$tgNavUrls", TimelineItemDirective]) + .directive("tgTimelineItem", ["$tgTemplate", "$compile", "$tgNavUrls", "$translate", "$sce", TimelineItemDirective]) diff --git a/app/locales/locale-en.json b/app/locales/locale-en.json index 0a91ff9c..1f2634d5 100644 --- a/app/locales/locale-en.json +++ b/app/locales/locale-en.json @@ -100,6 +100,7 @@ "SAT": "Sat" } }, + "SEE_USER_PROFILE": "See {{username }} profile", "TAGS": { "PLACEHOLDER": "I'm it! Tag me...", "DELETE": "Delete tag", @@ -383,7 +384,7 @@ "TITLE": "Types", "SUBTITLE": "Specify the types your issues could be", "ISSUE_TITLE": "Issues types", - "ACTION_ADD": "Add new type" + "ACTION_ADD": "Add new type" }, "ROLES": { "SECTION_NAME": "Roles - {{projectName}}", @@ -924,6 +925,11 @@ "TITLE_NEXT_ISSUE": "next issue", "ACTION_DELETE": "Delete issue", "LIGHTBOX_TITLE_BLOKING_ISSUE": "Blocking issue", + "FIELDS": { + "PRIORITY": "Priority", + "SEVERITY": "Severity", + "TYPE": "Type" + }, "CONFIRM_PROMOTE": { "TITLE": "Promote this issue to a new user story", "MESSAGE": "Are you sure you want to create a new US from this Issue?" @@ -1090,5 +1096,29 @@ "LAST_EDIT": "last
edit", "LAST_MODIFICATION": "last modification" } + }, + "TIMELINE": { + "UPLOAD_ATTACHMENT": "{{username}} has uploaded a new attachment in {{obj_name}}", + "US_CREATED": "{{username}} has created a new US in {{project_name}} {{obj_name}}", + "ISSUE_CREATED": "{{username}} has created a new Issue in {{project_name}} {{obj_name}}", + "TASK_CREATED": "{{username}} has created a new Task in {{project_name}} {{obj_name}}", + "WIKI_CREATED": "{{username}} has created a new Wiki page in {{project_name}} {{obj_name}}", + "NEW_PROJECT": "{{username}} has a new project {{project_name}}", + "MILESTONE_UPDATED": "{{username}} has updated the milestone {{obj_name}}", + "US_UPDATED": "{{username}} has updated the field \"{{field_name}}\" of the US {{obj_name}}", + "ISSUE_UPDATED": "{{username}} has updated the field \"{{field_name}}\" of the Issue {{obj_name}}", + "TASK_UPDATED": "{{username}} has updated the field \"{{field_name}}\" of the Task {{obj_name}}", + "WIKI_UPDATED": "{{username}} has update the Wiki page {{obj_name}}", + "NEW_COMMENT_US": "{{username}} has commented in the US {{obj_name}}", + "NEW_COMMENT_ISSUE": "{{username}} has commented in the Issue {{obj_name}}", + "NEW_COMMENT_TASK": "{{username}} has commented in the Task {{obj_name}}", + "NEW_MEMBER": "{{project_name}} has a new member", + "US_ADDED_MILESTONE": "{{username}} has added the US {{obj_name}} to {{sprint_name}}", + "US_REMOVED_FROM_MILESTONE": "{{username}} has added the US {{obj_name}} to the Backlog", + "BLOCKED": "{{username}} has blocked {{obj_name}}", + "UNBLOCKED": "{{username}} has unblocked {{obj_name}}" + }, + "LANGUAGES": { + "ENGLISH": "English" } } diff --git a/app/partials/includes/modules/profile/profile-timeline.jade b/app/partials/includes/modules/profile/profile-timeline.jade index 4580cc0b..09ac8855 100644 --- a/app/partials/includes/modules/profile/profile-timeline.jade +++ b/app/partials/includes/modules/profile/profile-timeline.jade @@ -1,5 +1,5 @@ section.profile-timeline(ng-controller="ProfileTimeline as ctrl") - div(ng-repeat="timeline in result.data", tg-timeline-item="timeline") + div(ng-repeat="timeline in timelineList", tg-timeline-item="timeline") - for (var x = 0; x < 3; x++) // Simple message for favorites, updates, etc. div.activity-simple diff --git a/app/partials/profile/timeline/comment-timeline.jade b/app/partials/profile/timeline/comment-timeline.jade deleted file mode 100644 index 13b8dc76..00000000 --- a/app/partials/profile/timeline/comment-timeline.jade +++ /dev/null @@ -1,12 +0,0 @@ -div.activity-comment - span.activity-date {{::timeline.created_formated}} - div.activity-info - div.profile-contact-picture - a(tg-nav="user-profile:username=timeline.data.user.username", title="{{::timeline.data.user.name }}") - img(ng-src="{{::timeline.data.user.photo}}", alt="{{::timeline.data.user.name}}") - p - a(tg-nav="user-profile:username=timeline.data.user.username", title="See {{::timeline.data.user.name }} profile") {{::timeline.data.user.name}} - span has commented in the {{::timeline.type }} - a(href="{{::timeline.detail_url}}", title="See #{{::timeline.ref}} {{::timeline.subject}}") \#{{::timeline.ref}} {{::timeline.subject}} - div.activity-comment-quote - p(ng-if="timeline.data.comment") "{{::timeline.data.comment}}" diff --git a/app/partials/profile/timeline/timeline-attachment.jade b/app/partials/profile/timeline/timeline-attachment.jade index 94f92d5a..22edecd8 100644 --- a/app/partials/profile/timeline/timeline-attachment.jade +++ b/app/partials/profile/timeline/timeline-attachment.jade @@ -1,12 +1,3 @@ -div.activity-image - span.activity-date {{::timeline.created_formated}} - div.activity-info - div.profile-contact-picture - a(tg-nav="user-profile:username=timeline.data.user.username", title="{{::timeline.data.user.name }}") - img(ng-src="{{::timeline.data.user.photo}}", alt="{{::timeline.data.user.name}}") - p - a(tg-nav="user-profile:username=timeline.data.user.username", title="See {{::timeline.data.user.name }} profile") {{::timeline.data.user.name}} - span has uploaded an image in the {{::timeline.type }} - a(href="{{::timeline.detail_url}}", title="See #{{::timeline.ref}} {{::timeline.subject}}") \#{{::timeline.ref}} {{::timeline.subject}} - div(ng-repeat="attachment in timeline.data.values_diff.attachments.new") - div(tg-timeline-attachment="attachment") \ No newline at end of file +p TODO: ATTACHMENT +p + a(ng-href="attachment.url"){{attachment.filename}} \ No newline at end of file diff --git a/app/partials/profile/timeline/timeline-item.jade b/app/partials/profile/timeline/timeline-item.jade new file mode 100644 index 00000000..da03224a --- /dev/null +++ b/app/partials/profile/timeline/timeline-item.jade @@ -0,0 +1,11 @@ +div.activity-image + span.activity-date {{::activity.created_formated}} + div.activity-info + div.profile-contact-picture + a(tg-nav="user-profile:username=activity.user.username", title="{{::activity.user.name }}") + img(ng-src="{{::activity.user.photo}}", alt="{{::activity.user.name}}") + + p(tg-compile-html="activity.title") + + div(ng-repeat="attachment in activity.attachments") + div(tg-timeline-attachment="attachment") \ No newline at end of file