Feature-comment-smiles complete (fix Retrospring/bugs#42)

This commit is contained in:
Yuki 2015-05-04 07:09:41 +05:30
parent 1c80c70dd9
commit 2204227273
21 changed files with 254 additions and 6 deletions

View File

@ -0,0 +1,41 @@
$(document).on "click", "button[name=ab-smile-comment]", ->
btn = $(this)
cid = btn[0].dataset.cId
action = btn[0].dataset.action
count = Number $("span#ab-comment-smile-count-#{cid}").html()
btn[0].dataset.loadingText = "<i class=\"fa fa-meh-o fa-spin\"></i> <span id=\"ab-smile-count-#{cid}\">#{count}</span>"
btn.button "loading"
target_url = switch action
when 'smile'
count++
'/ajax/create_comment_smile'
when 'unsmile'
count--
'/ajax/destroy_comment_smile'
success = false
$.ajax
url: target_url
type: 'POST'
data:
id: cid
success: (data, status, jqxhr) ->
success = data.success
if success
$("span#ab-comment-smile-count-#{cid}").html(count)
showNotification data.message, data.success
error: (jqxhr, status, error) ->
console.log jqxhr, status, error
showNotification "An error occurred, a developer should check the console for details", false
complete: (jqxhr, status) ->
btn.button "reset"
if success
switch action
when 'smile'
btn[0].dataset.action = 'unsmile'
btn.html "<i class=\"fa fa-frown-o\"></i> <span id=\"ab-comment-smile-count-#{cid}\">#{count}</span>"
when 'unsmile'
btn[0].dataset.action = 'smile'
btn.html "<i class=\"fa fa-smile-o\"></i> <span id=\"ab-comment-smile-count-#{cid}\">#{count}</span>"

View File

@ -6,6 +6,7 @@ body {
background-color: #fafafa;
}
@import "scss/generic";
@import "scss/answerbox";
@import "scss/comments";
@import "scss/entry";

View File

@ -41,3 +41,19 @@
font-size: 12px;
line-height: 1.3em;
}
.answerbox [name="ab-smile"], .answerbox [name="ab-smile-comment"] {
padding: 6px 11px;
padding-left: 21px;
position: relative;
overflow: hidden;
border: none;
i.fa.fa-smile-o, i.fa.fa-frown-o, i.fa.fa-meh-o {
position: absolute;
font-size: 3em;
left: -13px;
top: -10px;
display: block;
}
}

View File

@ -0,0 +1,19 @@
.user-list {
margin: 0;
padding: 0;
}
.user-list-entry-smiles {
* {
display: inline-block;
vertical-align: middle;
}
img {
height: 64px
}
span {
margin-left: 5px;
}
}

View File

@ -36,4 +36,42 @@ class Ajax::SmileController < ApplicationController
@message = "Successfully unsmiled answer."
@success = true
end
def create_comment
params.require :id
comment = Comment.find(params[:id])
begin
current_user.smile_comment comment
rescue
@status = :fail
@message = "You have already smiled that comment."
@success = false
return
end
@status = :okay
@message = "Successfully smiled comment."
@success = true
end
def destroy_comment
params.require :id
comment = Comment.find(params[:id])
begin
current_user.unsmile_comment comment
rescue
@status = :fail
@message = "You have not smiled that comment."
@success = false
return
end
@status = :okay
@message = "Successfully unsmiled comment."
@success = true
end
end

View File

@ -3,6 +3,7 @@ class Comment < ActiveRecord::Base
belongs_to :answer
validates :user_id, presence: true
validates :answer_id, presence: true
has_many :smiles, class_name: "CommentSmile", foreign_key: :comment_id, dependent: :destroy
validates :content, length: { maximum: 160 }

View File

@ -0,0 +1,22 @@
class CommentSmile < ActiveRecord::Base
belongs_to :user
belongs_to :comment
validates :user_id, presence: true, uniqueness: { scope: :comment_id, message: "already smiled comment" }
validates :comment_id, presence: true
after_create do
Notification.notify comment.user, self unless comment.user == user
user.increment! :comment_smiled_count
comment.increment! :smile_count
end
before_destroy do
Notification.denotify comment.user, self unless comment.user == user
user.decrement! :comment_smiled_count
comment.decrement! :smile_count
end
def notification_type(*_args)
Notifications::CommentSmiled
end
end

View File

@ -0,0 +1,2 @@
class Notifications::CommentSmiled < Notification
end

View File

@ -20,6 +20,7 @@ class User < ActiveRecord::Base
has_many :friends, through: :active_relationships, source: :target
has_many :followers, through: :passive_relationships, source: :source
has_many :smiles, dependent: :destroy
has_many :comment_smiles, dependent: :destroy
has_many :services, dependent: :destroy
has_many :notifications, foreign_key: :recipient_id, dependent: :destroy
has_many :reports, dependent: :destroy
@ -140,10 +141,26 @@ class User < ActiveRecord::Base
Smile.find_by(user: self, answer: answer).destroy
end
# smiles a comment
# @param comment [Comment] the comment to smile
def smile_comment(comment)
CommentSmile.create!(user: self, comment: comment)
end
# unsmile an comment
# @param comment [Comment] the comment to unsmile
def unsmile_comment(comment)
CommentSmile.find_by(user: self, comment: comment).destroy
end
def smiled?(answer)
answer.smiles.pluck(:user_id).include? self.id
end
def smiled_comment?(comment)
comment.smiles.pluck(:user_id).include? self.id
end
def display_website
website.match(/https?:\/\/([A-Za-z.\-0-9]+)\/?(?:.*)/i)[1]
rescue NoMethodError

BIN
app/views/ajax/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
json.partial! 'ajax/shared/status'

View File

@ -0,0 +1 @@
json.partial! 'ajax/shared/status'

View File

@ -50,6 +50,18 @@
your answer
= time_ago_in_words notification.target.created_at
ago
- when "CommentSmile"
.pull-left
%img.img-rounded.notification--dropdown-img{src: gravatar_url(notification.target.user)}
.media-body
%h6.media-heading.notification--dropdown-user
= user_screen_name notification.target.user
.notification--dropdown-text
smiled at
%a{href: show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.comment.answer.id), title: "#{notification.target.comment.content[0..40]}...", data: { toggle: :tooltip, placement: :top }}
your comment
= time_ago_in_words notification.target.created_at
ago
- when "Comment"
.pull-left
%img.img-rounded.notification--dropdown-img{src: gravatar_url(notification.target.user)}

View File

@ -44,6 +44,21 @@
ago
.notification--icon
%i.fa.fa-smile-o
- when "CommentSmile"
.pull-left
%img.img-rounded.notification--img{src: gravatar_url(notification.target.user)}
.media-body
%h6.media-heading.notification--user
= user_screen_name notification.target.user
%p.notification--text
smiled at
%a{href: show_user_answer_path(username: notification.target.user.screen_name, id: notification.target.comment.answer.id), title: "#{notification.target.comment.content[0..40]}...", data: { toggle: :tooltip, placement: :top }}
your comment
%span{title: notification.target.created_at, data: { toggle: :tooltip, placement: :bottom }}
= time_ago_in_words notification.target.created_at
ago
.notification--icon
%i.fa.fa-smile-o
- when "Comment"
.pull-left
%img.img-rounded.notification--img{src: gravatar_url(notification.target.user)}

View File

@ -1,8 +1,9 @@
%span.hidden-xs.text-muted
- unless user_signed_in?
- if a.smile_count > 0
%button.btn.btn-info.btn-sm{name: 'ab-smile', disabled: true}
%i.fa.fa-smile-o
%span{id: "ab-smile-count-#{a.id}"}= a.smile_count
users smiled this
- if user_signed_in?
- if current_user.smiled? a
%button.btn.btn-info.btn-sm{type: :button, name: 'ab-smile', data: { a_id: a.id, action: :unsmile }}

View File

@ -0,0 +1,18 @@
.modal.fade{"id" => "modal-view-comment#{comment.id}-smiles","aria-hidden" => "true", "aria-labelledby" => "modal-view-comment#{comment.id}-smiles-label", :role => "dialog", :tabindex => "-1"}
.modal-dialog
.modal-content
.modal-header
%button.close{"data-dismiss" => "modal", :type => "button"}
%span{"aria-hidden" => "true"} ×
%span.sr-only Close
%h4#modal-ask-followers-label.modal-title People who smiled this comment
.modal-body
- if comment.smiles.all.count == 0
No one smiled this, yet.
- else
%ul.user-list.user-list-smiles
- comment.smiles.all.each do |smile|
%li.user-list-entry.user-list-entry-smiles
%a{href: show_user_profile_path(smile.user.screen_name)}
%img.img-rounded{src: gravatar_url(smile.user), alt: user_screen_name(smile.user, false, false)}
%span= user_screen_name(smile.user, false, false)

View File

@ -4,17 +4,36 @@
%ul.comments
- a.comments.order(:created_at).each do |comment|
%li{data: { comment_id: comment.id }}
%div{class: "ab-comment-smile-list", style: "height: 0; width: 0"}= render "shared/comment_smiles", comment: comment
.media.comments--media
.pull-left
%img.img-rounded.answerbox--img{src: gravatar_url(comment.user)}
.media-body.comments--body
%h6.media-heading.answerbox--question-user= user_screen_name comment.user
- if user_signed_in?
.pull-right
%span.hidden-xs.text-muted
- unless user_signed_in?
- if comment.smile_count > 0
%button.btn.btn-info.btn-sm{name: 'ab-smile-comment', disabled: true}
%i.fa.fa-smile-o
%span{id: "ab-comment-smile-count-#{comment.id}"}= comment.smile_count
- if user_signed_in?
- if current_user.smiled_comment? comment
%button.btn.btn-info.btn-sm{type: :button, name: 'ab-smile-comment', data: { c_id: comment.id, action: :unsmile }}
%i.fa.fa-frown-o
%span{id: "ab-comment-smile-count-#{comment.id}"}= comment.smile_count
- else
%button.btn.btn-info.btn-sm{type: :button, name: 'ab-smile-comment', data: { c_id: comment.id, action: :smile }}
%i.fa.fa-smile-o
%span{id: "ab-comment-smile-count-#{comment.id}"}= comment.smile_count
.btn-group
%button.btn.btn-link.btn-sm.dropdown-toggle{data: { toggle: :dropdown }, aria: { expanded: :false }}
%span.caret
%ul.dropdown-menu.dropdown-menu-right{role: :menu}
%li
%a{href: '#', type: :button, data: { target: "#modal-view-comment#{comment.id}-smiles", toggle: :modal}}
%i.fa.fa-smile-o
View comment smiles
- if privileged?(comment.user) or privileged?(a.user)
%li.text-danger
%a{href: '#', data: { action: 'ab-comment-destroy', c_id: comment.id }}

View File

@ -76,6 +76,8 @@ Rails.application.routes.draw do
match '/destroy_friend', to: 'friend#destroy', via: :post, as: :destroy_friend
match '/create_smile', to: 'smile#create', via: :post, as: :create_smile
match '/destroy_smile', to: 'smile#destroy', via: :post, as: :destroy_smile
match '/create_comment_smile', to: 'smile#create_comment', via: :post, as: :create_comment_smile
match '/destroy_comment_smile', to: 'smile#destroy_comment', via: :post, as: :destroy_comment_smile
match '/create_comment', to: 'comment#create', via: :post, as: :create_comment
match '/destroy_comment', to: 'comment#destroy', via: :post, as: :destroy_comment
match '/report', to: 'report#create', via: :post, as: :report

View File

@ -0,0 +1,17 @@
class CreateCommentSmiles < ActiveRecord::Migration
def change
create_table :comment_smiles do |t|
t.integer :user_id
t.integer :comment_id
t.timestamps null: false
end
add_index :comment_smiles, :user_id
add_index :comment_smiles, :comment_id
add_index :comment_smiles, [:user_id, :comment_id], unique: true
add_column :users, :comment_smiled_count, :integer, default: 0, null: false
add_column :comments, :smile_count, :integer, default: 0, null: false
end
end

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe CommentSmile, :type => :model do
pending "add some examples to (or delete) #{__FILE__}"
end