[#369] add new comment & new push notifications

This commit is contained in:
Alexander Machehin 2014-04-08 19:11:34 +06:00
parent 5cf413139d
commit 09b75e4110
13 changed files with 174 additions and 75 deletions

View File

@ -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;
});

View File

@ -1,11 +1,13 @@
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){
var cur_tab = $scope.$eval(tab+'_tab');
@ -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';
template = 'btn-success fa-gear';
break;
case 'new_comment_notification':
template = 'bg-primary fa-comment';
case 'new_comment_commit_notification':
template = 'btn-warning fa-comment';
break;
case 'git_new_push_notification':
template = 'bg-success fa-sign-in';
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';
};
}]);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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])

View File

@ -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')" }
%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.bg-primary.fa.fa-comment
%i.img-circle.fa{ 'ng-class' => "getTimeLinefaClass(item)" }
.timeline-item
%span.time
%span.glyphicon.glyphicon-time
12:05
%h3.timeline-header
%a{href: "#"} Support Team
sent you and email
%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
%span {{item.date | amDateFormat:'HH:mm'}}
.clearfix
.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
-#%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" }
.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.fa.fa-clock-o
/ /.col
%i.img-circle.bg-primary.fa.fa-clock-o

View File

@ -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

View File

@ -1,4 +1,4 @@
%footer
%footer.offset20
%ul
%li= t('bottom_menu.copyright', year: Date.today.year)
%li ·

View File

@ -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;
}

View File

@ -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;