diff --git a/app/assets/javascripts/angular-new/config.js.erb b/app/assets/javascripts/angular-new/config.js.erb index 853f9441a..428f238a3 100644 --- a/app/assets/javascripts/angular-new/config.js.erb +++ b/app/assets/javascripts/angular-new/config.js.erb @@ -1,6 +1,6 @@ //var RosaABF = angular.module('RosaABF', ['ngResource', 'ng-rails-csrf', 'angular-i18n', 'angularMoment']); var RosaABF = angular.module('RosaABF', ['ui.bootstrap', 'angular-i18n', 'angularMoment', - 'chieffancypants.loadingBar', 'ngAnimate']); + 'chieffancypants.loadingBar', 'ngSanitize']); var LocalesHelper = function($locale) { var locales = { @@ -13,4 +13,9 @@ var LocalesHelper = function($locale) { } } } + RosaABF.factory("LocalesHelper", ['$locale', LocalesHelper]); + +RosaABF.config(function(cfpLoadingBarProvider) { + cfpLoadingBarProvider.includeSpinner = false; +}); \ No newline at end of file diff --git a/app/assets/javascripts/angular-new/controllers/activity_controller.js b/app/assets/javascripts/angular-new/controllers/activity_controller.js index 5903eb5cd..3dc457d5a 100644 --- a/app/assets/javascripts/angular-new/controllers/activity_controller.js +++ b/app/assets/javascripts/angular-new/controllers/activity_controller.js @@ -1,13 +1,15 @@ -RosaABF.controller('ActivityCtrl', ['$scope', '$http', '$timeout', '$q', - function($scope, $http, $timeout, $q) { +RosaABF.controller('ActivityCtrl', ['$scope', '$http', '$timeout', '$q', '$filter', + function($scope, $http, $timeout, $q, $filter) { $scope.activity_tab = { title: 'activity_menu.activity_feed', content: [] , isLoaded: false , active: true }; $scope.tracker_tab = { title: 'activity_menu.tracker', content: [] , isLoaded: false , active: false }; $scope.pull_requests_tab = { title: 'activity_menu.pull_requests', content: [] , isLoaded: false , active: false }; var today = moment().startOf('day'); - $scope.activity_tab.content = [{date: today, test:'adf'}, {date: today.add('days', 1).calendar, test:'fd'}]; + // $scope.activity_tab.content = [{date: today, kind:'new_comment_notification'}, + // {date: today, kind:'git_new_push_notification'}, + // {date: moment().add('days', 1), kind:'build_list_notification'}]; - $scope.getContent=function(tab){ + $scope.getContent=function(tab){ var cur_tab = $scope.$eval(tab+'_tab'); /* see if we have data already */ if(cur_tab.isLoaded){ @@ -15,27 +17,42 @@ RosaABF.controller('ActivityCtrl', ['$scope', '$http', '$timeout', '$q', } /* or make request for data */ var path = Routes.root_path({ filter: cur_tab.filter, format: 'json' }); - $http.get(path, { tracker: 'activity' }).then(function(res){ - //cur_tab.content=res.data; + $http.get(path).then(function(res){ + cur_tab.content=res.data; cur_tab.isLoaded=true; }); } - $scope.getTimeLinefaClass = function(contentType) { - var template = 'bg-warning fa-question'; + $scope.getTimeLinefaClass = function(content) { + var template = 'btn-warning fa-question'; - switch(contentType) { + switch(content.kind) { case 'build_list_notification': - template = 'bg-danger fa-gear'; - break; + template = 'btn-success fa-gear'; + break; case 'new_comment_notification': - template = 'bg-primary fa-comment'; - break; + case 'new_comment_commit_notification': + template = 'btn-warning fa-comment'; + break; case 'git_new_push_notification': - template = 'bg-success fa-sign-in'; - break; + template = 'bg-primary fa-sign-in'; + break; + case 'new_issue_notification': + template = 'btn-warning fa-check-square-o'; + break; } return template; } + + $scope.needShowTimeLabel = function(index) { + var cur_date = $filter('amDateFormat')($scope.activity_tab.content[index].date, 'll'); + var prev_date = index == 0 || $filter('amDateFormat')($scope.activity_tab.content[index-1].date, 'll'); + return cur_date !== prev_date; + }; + + $scope.isComment = function(content) { + return content.kind === 'new_comment_notification' || + content.kind === 'new_comment_commit_notification'; + }; }]); diff --git a/app/assets/javascripts/new_application.js b/app/assets/javascripts/new_application.js index b69cbe8e0..600bf831e 100644 --- a/app/assets/javascripts/new_application.js +++ b/app/assets/javascripts/new_application.js @@ -3,13 +3,14 @@ // Loads all Bootstrap javascripts //= require bootstrap -//= require unstable/angular -// require angular -//= require angular-ui-bootstrap-tpls +// require unstable/angular +//= require angular +//= require angular-sanitize +//= require angular-ui-bootstrap-tpls //= require angular-i18n -//= require angular-animate //= require angularjs/locales + //= require moment //= require angularjs/angular-moment diff --git a/app/assets/stylesheets/custom_bootstrap.scss b/app/assets/stylesheets/custom_bootstrap.scss index 36938cedd..12e38e532 100644 --- a/app/assets/stylesheets/custom_bootstrap.scss +++ b/app/assets/stylesheets/custom_bootstrap.scss @@ -43,6 +43,7 @@ footer { } .offset20 { margin-top: 20px; } +.boffset20 { margin-bottom: 20px; } [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 889066b4b..4c07325ba 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -13,6 +13,7 @@ class HomeController < ApplicationController @activity_feeds = @activity_feeds.paginate page: params[:page] respond_to do |format| format.html { request.xhr? ? render('_list', layout: false) : render('activity') } + format.json {} format.atom end end diff --git a/app/helpers/activity_feeds_helper.rb b/app/helpers/activity_feeds_helper.rb index ac4fd1e23..95b8f1643 100644 --- a/app/helpers/activity_feeds_helper.rb +++ b/app/helpers/activity_feeds_helper.rb @@ -10,7 +10,38 @@ module ActivityFeedsHelper feed_title = feed_title.gsub(/\s{2,}/, ' ').strip end + def get_user_from_activity_item(item) + email = item.data[:user_email] + User.where(email: email).first || User.new(email: email) if email.present? + end + def user_link(user, user_name, full_url = false) user.persisted? ? link_to(user_name, full_url ? user_url(user) : user_path(user)) : user_name end + + def get_title_from_activity_item(item, opts = {}) + case item.kind + when 'new_comment_notification' + res = t('notifications.bodies.new_comment_notification.title', { user_link: nil }) + res << ' ' << t('notifications.bodies.new_comment_notification.content', + { issue_link: link_to(item.data[:issue_title], opts[:path]) }) + when 'git_new_push_notification' + res = t("notifications.bodies.#{item.data[:change_type]}_branch", + { branch_name: item.data[:branch_name], user_link: nil }) + res << ' ' << t('notifications.bodies.project', project_link: link_to(opts[:project_name_with_owner], opts[:path])) + else nil + end + raw res + end + + def get_path_from_activity_item(item, opts = {}) + case item.kind + when 'new_comment_notification' + project_issue_path(opts[:project_name_with_owner], item.data[:issue_serial_id]) + when 'git_new_push_notification' + project_path(opts[:project_name_with_owner]) + else + '?' + end + end end diff --git a/app/models/activity_feed.rb b/app/models/activity_feed.rb index 8e4b39ccc..0cc809782 100644 --- a/app/models/activity_feed.rb +++ b/app/models/activity_feed.rb @@ -13,7 +13,7 @@ class ActivityFeed < ActiveRecord::Base default_scope { order created_at: :desc } scope :outdated, -> { offset(100) } - self.per_page = 4 + self.per_page = 10 def partial 'home/partials/' + self.kind diff --git a/app/views/home/_commits_list.json.haml b/app/views/home/_commits_list.json.haml new file mode 100644 index 000000000..793adb187 --- /dev/null +++ b/app/views/home/_commits_list.json.haml @@ -0,0 +1,11 @@ +.row + - item.data[:last_commits].each do |commit| + .col-sm-3.col-md-2= link_to shortest_hash_id(commit[0]), commit_path(project_name_with_owner, commit[0]) + .col-sm-8.col-md-9= truncate(commit[1], length: 70, omission: '…') + .clearfix +-#%br +- if item.data[:other_commits].present? + %br + =link_to t('notifications.bodies.more_commits', count: item.data[:other_commits_count], + commits: commits_pluralize(item.data[:other_commits_count])), + diff_path(project_name_with_owner, diff: item.data[:other_commits]) diff --git a/app/views/home/activity.html.haml b/app/views/home/activity.html.haml index f22527864..45d1425be 100644 --- a/app/views/home/activity.html.haml +++ b/app/views/home/activity.html.haml @@ -8,12 +8,12 @@ / - render 'top_menu' %div{ 'ng-controller' => 'ActivityCtrl', 'cg-busy' => "'activity'" } - %tabset + %tabset.offset20 %tab{ 'heading' => "{{activity_tab.title | i18n}}", 'active' => "activity_tab.active", 'select' => "getContent('activity')" } .row - .col-md-3.offset20 + .col-md-3.col-sm-4.offset20 %p = link_to t('layout.activity_feed.new_project'), new_project_path, class: 'btn btn-primary btn-small', role: 'button' @@ -24,44 +24,47 @@ - current_user.projects.order(updated_at: :desc).limit(5).each do |project| %li = link_to project_path(project) do - - image, color = project.public? ? ['unlock-alt', 'text-success'] : ['lock', 'text-danger'] + - image, color = project.public? ? ['unlock-alt', 'text-success fa-fw'] : ['lock', 'text-danger fa-fw'] = fa_icon(image, class: color) = project.name_with_owner %li = link_to t('layout.activity_feed.all_my_projects'), projects_path - .col-md-9 + .col-md-9.col-sm-8 %h3 = t("layout.activity_feed.header") = link_to image_tag("rss.ico", width: '15px', height: '15px', class: 'atom_icon'), atom_activity_feeds_path(format: 'atom', token: current_user.authentication_token) / The time line .row - .col-md-12 + .col-md-12.col-sm-12 %ul.timeline - .timeline-element{ 'ng-repeat' => 'item in activity_tab.content' } - / timeline time label - %li.time-label - %span{ 'ng-show' => "(item.date | amDateFormat:'ll') != (item[$index-1].date | amDateFormat:'ll')" } - {{item.date | amDateFormat:'ll'}} - / timeline item - %li - %i.img-circle.bg-primary.fa.fa-comment - .timeline-item - %span.time + / timeline time label + %li.time-label{ 'ng-repeat-start' => 'item in activity_tab.content' } + %span{ 'ng-show' => "needShowTimeLabel($index)", 'ng-cloak' => true } + {{item.date | amDateFormat:'ll'}} + / timeline item + %li + %i.img-circle.fa{ 'ng-class' => "getTimeLinefaClass(item)" } + .timeline-item + %h3.timeline-header + %a{ 'ng-href' => "{{item.user.link}}" } + %img{ 'ng-src' => "{{item.user.image}}" } + {{item.user.uname}} + %span.time{ 'ng-cloak' => true, popover: "{{item.date | amDateFormat:'ddd, LLL'}}", + "popover-trigger" => "mouseenter", 'popover-append-to-body' => 'true' } %span.glyphicon.glyphicon-time - 12:05 - %h3.timeline-header - %a{href: "#"} Support Team - sent you and email - .timeline-body - Etsy doostang zoodles disqus groupon greplin oooj voxy zoodles, - weebly ning heekya handango imeem plugg dopplr jibjab, movity - jajah plickers sifteo edmodo ifttt zimbra. Babblely odeo kaboodle - quora plaxo ideeli hulu weebly balihoo... - .timeline-footer - %a.btn.btn-primary.btn-xs Read more - %a.btn.btn-danger.btn-xs Delete + %span {{item.date | amDateFormat:'HH:mm'}} + .clearfix + .timeline-body + -#%p{ 'ng-bind-html' => "item.title" } + %p{ 'ng-bind-html' => "item.title" } + %blockquote{ 'ng-if' => "isComment(item)" } + {{item.body}} + %p{ 'ng-if' => "!isComment(item)", 'ng-bind-html' => "item.body" } - %li - %i.fa.fa-clock-o - / /.col \ No newline at end of file + .timeline-footer + %a.btn.btn-primary.btn-xs{ 'ng-href' => "{{item.read_more}}", 'ng-if' => "isComment(item)" } + = t('layout.read_more') + .hide{ 'ng-repeat-end' => true } + %li + %i.img-circle.bg-primary.fa.fa-clock-o diff --git a/app/views/home/activity.json.jbuilder b/app/views/home/activity.json.jbuilder new file mode 100644 index 000000000..977d363d8 --- /dev/null +++ b/app/views/home/activity.json.jbuilder @@ -0,0 +1,25 @@ +json.array!(@activity_feeds) do |item| + json.cache! item do + json.date item.created_at + json.kind item.kind + user = get_user_from_activity_item(item) + json.user do + json.link user_path(user) if user.persisted? + json.image avatar_url(user, :small) if user.persisted? + json.uname (user.fullname || user.email) + end if user + project_name_with_owner = "#{item.data[:project_owner]}/#{item.data[:project_name]}" + json.project_name_with_owner project_name_with_owner + path = get_path_from_activity_item(item, project_name_with_owner: project_name_with_owner) + json.title get_title_from_activity_item(item, user: user, project_name_with_owner: project_name_with_owner, path: path) + + case item.kind + when 'new_comment_notification' + json.read_more path + "#comment#{item.data[:comment_id]}" + json.body short_message(item.data[:comment_body], 1000) + when 'git_new_push_notification' + json.body render('commits_list', item: item, project_name_with_owner: project_name_with_owner).html_safe + end + json.id item.id + end +end \ No newline at end of file diff --git a/app/views/layouts/menu/_new_bottom.html.haml b/app/views/layouts/menu/_new_bottom.html.haml index 4981f3249..71f9ea99d 100644 --- a/app/views/layouts/menu/_new_bottom.html.haml +++ b/app/views/layouts/menu/_new_bottom.html.haml @@ -1,4 +1,4 @@ -%footer +%footer.offset20 %ul %li= t('bottom_menu.copyright', year: Date.today.year) %li · diff --git a/vendor/assets/stylesheets/loading-bar.scss b/vendor/assets/stylesheets/loading-bar.scss index be0ad816f..ca9ed85b7 100644 --- a/vendor/assets/stylesheets/loading-bar.scss +++ b/vendor/assets/stylesheets/loading-bar.scss @@ -48,7 +48,7 @@ $loading-bar-bg: #254561; // #dff0d8; top: 0; left: 0; width: 100%; - height: 4px; + height: 3px; border-bottom-right-radius: 1px; border-top-right-radius: 1px; } @@ -75,7 +75,7 @@ $loading-bar-bg: #254561; // #dff0d8; position: fixed; z-index: 2000; top: 10px; - left: 17px; + left: 18px; font-size: 31px; color: $loading-bar-bg; } diff --git a/vendor/assets/stylesheets/timeline.scss b/vendor/assets/stylesheets/timeline.scss index 7971ef03e..4eecaae0b 100644 --- a/vendor/assets/stylesheets/timeline.scss +++ b/vendor/assets/stylesheets/timeline.scss @@ -47,63 +47,67 @@ Component: timeline -moz-border-radius: 2px; border-radius: 2px; } -.timeline > .timeline-element > li { +.timeline > li { position: relative; margin-right: 10px; margin-bottom: 15px; } -.timeline > .timeline-element > li:before, -.timeline > .timeline-element > li:after { +.timeline > li:before, +.timeline > li:after { display: table; content: " "; } -.timeline > .timeline-element > li:after { +.timeline > li:after { clear: both; } -.timeline > .timeline-element > li > .timeline-item { +.timeline > li > .timeline-item { margin-top: 10px; - border: 0px solid #dfdfdf; + border: 0px solid #b2b2b2; background: #fff; color: #555; margin-left: 60px; margin-right: 15px; padding: 5px; position: relative; - box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2); } -.timeline > .timeline-element > li > .timeline-item > .time { +.timeline > li > .timeline-item .time { color: #999; float: right; - margin: 2px 0 0 0; + margin: 2px 0 2px 0; } -.timeline > .timeline-element > li > .timeline-item > .timeline-header { +.timeline > li > .timeline-item > .timeline-header { margin: 0; color: #555; - border-bottom: 1px solid #f4f4f4; - padding: 5px; + //border-bottom: 1px solid #dcdcdc; + padding: 2px; font-size: 16px; line-height: 1.1; } -.timeline > .timeline-element > li > .timeline-item > .timeline-header > a { +.timeline > li > .timeline-item > .timeline-header > a { font-weight: 600; } -.timeline > .timeline-element > li > .timeline-item > .timeline-body, -.timeline > .timeline-element > li > .timeline-item > .timeline-footer { - padding: 10px; +.timeline > li > .timeline-item > .timeline-header > a > img { + margin: 0 10px 0 10px; } -.timeline > .timeline-element > li.time-label > span { +.timeline > li > .timeline-item > .timeline-body, +.timeline > li > .timeline-item > .timeline-footer { + padding: 5px 10px 5px 10px; +} +.timeline > li.time-label > span { font-weight: 600; padding: 5px; display: inline-block; - background-color: #fff; + background-color: #AA53AA; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; + color: #f9f9f9; } -.timeline > .timeline-element > li > .fa, -.timeline > .timeline-element > li > .glyphicon, -.timeline > .timeline-element > li > .ion { +.timeline > li > .fa, +.timeline > li > .glyphicon, +.timeline > li > .ion { box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); width: 30px; height: 30px;