mirror of
https://github.com/tildeverse/lobsters
synced 2024-06-18 06:37:06 +00:00
Show story and comment replies, tracking unread ones
This commit is contained in:
parent
925bdc203c
commit
dd42cca880
|
@ -3,7 +3,9 @@ language: ruby
|
||||||
services:
|
services:
|
||||||
- mysql
|
- mysql
|
||||||
env:
|
env:
|
||||||
- DATABASE_URL=mysql2://root:@localhost/lobsters_test
|
global:
|
||||||
|
- DATABASE_URL=mysql2://root:@localhost/lobsters_dev
|
||||||
|
- RAILS_ENV=test
|
||||||
before_script:
|
before_script:
|
||||||
- ./bin/rails db:create
|
- ./bin/rails db:create
|
||||||
- ./bin/rails db:schema:load
|
- ./bin/rails db:schema:load
|
||||||
|
|
3
Gemfile
3
Gemfile
|
@ -10,6 +10,9 @@ gem "mysql2", ">= 0.3.14"
|
||||||
# uncomment to use PostgreSQL
|
# uncomment to use PostgreSQL
|
||||||
# gem "pg"
|
# gem "pg"
|
||||||
|
|
||||||
|
gem 'scenic'
|
||||||
|
gem 'scenic-mysql'
|
||||||
|
|
||||||
gem "uglifier", ">= 1.3.0"
|
gem "uglifier", ">= 1.3.0"
|
||||||
gem "jquery-rails", "~> 4.3"
|
gem "jquery-rails", "~> 4.3"
|
||||||
gem "dynamic_form"
|
gem "dynamic_form"
|
||||||
|
|
|
@ -132,6 +132,11 @@ GEM
|
||||||
rspec-support (3.6.0)
|
rspec-support (3.6.0)
|
||||||
ruby-enum (0.7.1)
|
ruby-enum (0.7.1)
|
||||||
i18n
|
i18n
|
||||||
|
scenic (1.4.0)
|
||||||
|
activerecord (>= 4.0.0)
|
||||||
|
railties (>= 4.0.0)
|
||||||
|
scenic-mysql (0.1.0)
|
||||||
|
scenic (>= 1.3)
|
||||||
sprockets (3.7.1)
|
sprockets (3.7.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
|
@ -175,6 +180,8 @@ DEPENDENCIES
|
||||||
rotp
|
rotp
|
||||||
rqrcode
|
rqrcode
|
||||||
rspec-rails (~> 3.6)
|
rspec-rails (~> 3.6)
|
||||||
|
scenic
|
||||||
|
scenic-mysql
|
||||||
sqlite3
|
sqlite3
|
||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
unicorn
|
unicorn
|
||||||
|
|
|
@ -922,6 +922,11 @@ div.comment_form_container textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.comment_unread {
|
||||||
|
color: red;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
/* trees */
|
/* trees */
|
||||||
|
|
||||||
.tree,
|
.tree,
|
||||||
|
|
54
app/controllers/replies_controller.rb
Normal file
54
app/controllers/replies_controller.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
class RepliesController < ApplicationController
|
||||||
|
REPLIES_PER_PAGE = 25
|
||||||
|
|
||||||
|
before_action :require_logged_in_user_or_400
|
||||||
|
after_action :update_read_ribbons
|
||||||
|
|
||||||
|
def show
|
||||||
|
@page = params[:page].to_i
|
||||||
|
if @page == 0
|
||||||
|
@page = 1
|
||||||
|
elsif @page < 0 || @page > (2 ** 32)
|
||||||
|
raise ActionController::RoutingError.new("page out of bounds")
|
||||||
|
end
|
||||||
|
|
||||||
|
@filter = params[:filter] || 'unread'
|
||||||
|
|
||||||
|
case @filter
|
||||||
|
when 'comments'
|
||||||
|
@heading = @title = "Your Comment Replies"
|
||||||
|
@replies = ReplyingComment
|
||||||
|
.comment_replies_for(@user.id)
|
||||||
|
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||||
|
.limit(REPLIES_PER_PAGE)
|
||||||
|
when 'stories'
|
||||||
|
@heading = @title = "Your Story Replies"
|
||||||
|
@replies = ReplyingComment
|
||||||
|
.story_replies_for(@user.id)
|
||||||
|
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||||
|
.limit(REPLIES_PER_PAGE)
|
||||||
|
when 'all'
|
||||||
|
@heading = @title = "All Your Replies"
|
||||||
|
@replies = ReplyingComment
|
||||||
|
.for_user(@user.id)
|
||||||
|
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||||
|
.limit(REPLIES_PER_PAGE)
|
||||||
|
else
|
||||||
|
@heading = @title = "Your Unread Replies"
|
||||||
|
@replies = ReplyingComment.unread_replies_for(@user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def update_read_ribbons
|
||||||
|
return unless @filter == 'unread'
|
||||||
|
stories = @replies.pluck(:story_id).uniq
|
||||||
|
|
||||||
|
stories.each do |story|
|
||||||
|
ribbon = ReadRibbon.find_by(user_id: @user.id, story_id: story)
|
||||||
|
ribbon.updated_at = Time.now
|
||||||
|
ribbon.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,6 +8,7 @@ class StoriesController < ApplicationController
|
||||||
before_action :find_user_story, :only => [ :destroy, :edit, :undelete,
|
before_action :find_user_story, :only => [ :destroy, :edit, :undelete,
|
||||||
:update ]
|
:update ]
|
||||||
before_action :find_story!, :only => [ :suggest, :submit_suggestions ]
|
before_action :find_story!, :only => [ :suggest, :submit_suggestions ]
|
||||||
|
around_action :track_story_reads, only: [ :show ], if: -> { @user.present? }
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@title = "Submit Story"
|
@title = "Submit Story"
|
||||||
|
@ -292,6 +293,7 @@ class StoriesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
HiddenStory.hide_story_for_user(story.id, @user.id)
|
HiddenStory.hide_story_for_user(story.id, @user.id)
|
||||||
|
ReadRibbon.hide_replies_for(story.id, @user.id)
|
||||||
|
|
||||||
render :plain => "ok"
|
render :plain => "ok"
|
||||||
end
|
end
|
||||||
|
@ -407,4 +409,12 @@ private
|
||||||
return redirect_to "/"
|
return redirect_to "/"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def track_story_reads
|
||||||
|
story = Story.where(short_id: params[:id]).first!
|
||||||
|
@ribbon = ReadRibbon.where(user_id: @user.id, story_id: story.id).first_or_create
|
||||||
|
yield
|
||||||
|
@ribbon.updated_at = Time.now
|
||||||
|
@ribbon.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,6 +79,17 @@ module ApplicationHelper
|
||||||
@right_header_links = {}
|
@right_header_links = {}
|
||||||
|
|
||||||
if @user
|
if @user
|
||||||
|
if (count = @user.unread_replies_count) > 0
|
||||||
|
@right_header_links.merge!({ "/replies" => {
|
||||||
|
:class => [ "new_messages" ],
|
||||||
|
:title => "Replies (#{count})",
|
||||||
|
} })
|
||||||
|
else
|
||||||
|
@right_header_links.merge!({
|
||||||
|
"/replies" => { :title => "Replies" }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
if (count = @user.unread_message_count) > 0
|
if (count = @user.unread_message_count) > 0
|
||||||
@right_header_links.merge!({ "/messages" => {
|
@right_header_links.merge!({ "/messages" => {
|
||||||
:class => [ "new_messages" ],
|
:class => [ "new_messages" ],
|
||||||
|
@ -170,6 +181,12 @@ module ApplicationHelper
|
||||||
ago = "#{years} year#{years == 1 ? "" : "s"} ago"
|
ago = "#{years} year#{years == 1 ? "" : "s"} ago"
|
||||||
end
|
end
|
||||||
|
|
||||||
raw(content_tag(:span, ago, :title => time.strftime("%F %T %z")))
|
span_class = ''
|
||||||
|
|
||||||
|
if options[:mark_unread]
|
||||||
|
span_class += 'comment_unread'
|
||||||
|
end
|
||||||
|
|
||||||
|
raw(content_tag(:span, ago, title: time.strftime("%F %T %z"), class: span_class))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
11
app/helpers/replies_helper.rb
Normal file
11
app/helpers/replies_helper.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module RepliesHelper
|
||||||
|
def link_to_filter(name)
|
||||||
|
title = name.titleize
|
||||||
|
|
||||||
|
if @filter != name
|
||||||
|
link_to(title, replies_path(filter: name))
|
||||||
|
else
|
||||||
|
title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,4 +16,12 @@ module StoriesHelper
|
||||||
|
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def is_unread?(comment)
|
||||||
|
if !@user || !@ribbon
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
(comment.created_at > @ribbon.updated_at) && (comment.user_id != @user.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
3
app/models/application_record.rb
Normal file
3
app/models/application_record.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
|
self.abstract_class = true
|
||||||
|
end
|
10
app/models/read_ribbon.rb
Normal file
10
app/models/read_ribbon.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class ReadRibbon < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :story
|
||||||
|
|
||||||
|
def self.hide_replies_for(story_id, user_id)
|
||||||
|
ribbon = find_by(user_id: user_id, story_id: story_id)
|
||||||
|
ribbon.is_following = false
|
||||||
|
ribbon.save!
|
||||||
|
end
|
||||||
|
end
|
16
app/models/replying_comment.rb
Normal file
16
app/models/replying_comment.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class ReplyingComment < ApplicationRecord
|
||||||
|
attribute :is_unread, :boolean
|
||||||
|
|
||||||
|
belongs_to :comment
|
||||||
|
|
||||||
|
scope :for_user, ->(user_id) { where(user_id: user_id).order(comment_created_at: :desc) }
|
||||||
|
scope :unread_replies_for, ->(user_id) { for_user(user_id).where(is_unread: true) }
|
||||||
|
scope :comment_replies_for, ->(user_id) { for_user(user_id).where('parent_comment_id is not null') }
|
||||||
|
scope :story_replies_for, ->(user_id) { for_user(user_id).where('parent_comment_id is null') }
|
||||||
|
|
||||||
|
protected
|
||||||
|
# This is a view, not a real table
|
||||||
|
def readonly?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
|
@ -505,6 +505,10 @@ class User < ActiveRecord::Base
|
||||||
self.received_messages.unread.count)
|
self.received_messages.unread.count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unread_replies_count
|
||||||
|
ReplyingComment.where(user_id: self.id, is_unread: true).count
|
||||||
|
end
|
||||||
|
|
||||||
def votes_for_others
|
def votes_for_others
|
||||||
self.votes.joins(:story, :comment).
|
self.votes.joins(:story, :comment).
|
||||||
where("comments.user_id <> votes.user_id AND " <<
|
where("comments.user_id <> votes.user_id AND " <<
|
||||||
|
|
|
@ -73,8 +73,11 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ?
|
||||||
<% elsif comment.is_from_email? %>
|
<% elsif comment.is_from_email? %>
|
||||||
e-mailed
|
e-mailed
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% if defined?(is_unread) && is_unread %>
|
||||||
|
<% mark_unread = true %>
|
||||||
|
<% end %>
|
||||||
<%= time_ago_in_words_label((comment.has_been_edited? ?
|
<%= time_ago_in_words_label((comment.has_been_edited? ?
|
||||||
comment.updated_at : comment.created_at)) %>
|
comment.updated_at : comment.created_at), strip_about: true, mark_unread: mark_unread) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if !comment.previewing %>
|
<% if !comment.previewing %>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<% @threads.each do |thread| %>
|
<% @threads.each do |thread| %>
|
||||||
<ol class="comments comments1">
|
<ol class="comments comments1">
|
||||||
<% comments_by_parent = thread.group_by(&:parent_comment_id) %>
|
<% comments_by_parent = thread.group_by(&:parent_comment_id) %>
|
||||||
|
|
44
app/views/replies/show.html.erb
Normal file
44
app/views/replies/show.html.erb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<div class="box wide">
|
||||||
|
<div class="legend right">
|
||||||
|
<%= link_to_filter('unread') %> |
|
||||||
|
<%= link_to_filter('all') %> |
|
||||||
|
<%= link_to_filter('comments') %> |
|
||||||
|
<%= link_to_filter('stories') %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="legend">
|
||||||
|
<%= @heading %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ol class="comments comments1">
|
||||||
|
<% @replies.each do |reply| %>
|
||||||
|
<li class="comments_subtree">
|
||||||
|
<%= render "comments/comment",
|
||||||
|
comment: reply.comment,
|
||||||
|
show_story: true,
|
||||||
|
is_unread: reply.is_unread,
|
||||||
|
show_tree_lines: false %>
|
||||||
|
<ol class="comments"></ol>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<% if @replies.count > RepliesController::REPLIES_PER_PAGE && @filter != 'unread'%>
|
||||||
|
<div class="morelink">
|
||||||
|
<% if @page && @page > 1 %>
|
||||||
|
<a href="/replies<%= @page == 2 ? "" : "/page/#{@page - 1}" %>"><<
|
||||||
|
Page <%= @page - 1 %></a>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if @replies.any? %>
|
||||||
|
<% if @page && @page > 1 %>
|
||||||
|
|
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<a href="/replies/page/<%= @page + 1 %>">Page
|
||||||
|
<%= @page + 1 %> >></a>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
|
@ -56,7 +56,8 @@
|
||||||
:show_story => (comment.story_id != @story.id),
|
:show_story => (comment.story_id != @story.id),
|
||||||
:show_tree_lines => true,
|
:show_tree_lines => true,
|
||||||
:was_merged => (comment.story_id != @story.id),
|
:was_merged => (comment.story_id != @story.id),
|
||||||
:children => children %>
|
:children => children,
|
||||||
|
:is_unread => is_unread?(comment) %>
|
||||||
|
|
||||||
<% if ancestors.present? %>
|
<% if ancestors.present? %>
|
||||||
<div class="comment_siblings_tree_line"></div>
|
<div class="comment_siblings_tree_line"></div>
|
||||||
|
|
3
config/initializers/senic.rb
Normal file
3
config/initializers/senic.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Scenic.configure do |config|
|
||||||
|
config.database = Scenic::Adapters::Mysql.new
|
||||||
|
end
|
|
@ -33,6 +33,9 @@ Lobsters::Application.routes.draw do
|
||||||
get "/threads" => "comments#threads"
|
get "/threads" => "comments#threads"
|
||||||
get "/threads/:user" => "comments#threads"
|
get "/threads/:user" => "comments#threads"
|
||||||
|
|
||||||
|
get "/replies" => "replies#show"
|
||||||
|
get "/replies/page/:page" => "replies#show"
|
||||||
|
|
||||||
get "/login" => "login#index"
|
get "/login" => "login#index"
|
||||||
post "/login" => "login#login"
|
post "/login" => "login#login"
|
||||||
post "/logout" => "login#logout"
|
post "/logout" => "login#logout"
|
||||||
|
|
13
db/migrate/20180130235553_add_read_notification_support.rb
Normal file
13
db/migrate/20180130235553_add_read_notification_support.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
class AddReadNotificationSupport < ActiveRecord::Migration[5.1]
|
||||||
|
def change
|
||||||
|
create_table :read_ribbons do |t|
|
||||||
|
t.boolean :is_following, default: true
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_reference :read_ribbons, :user, index: true
|
||||||
|
add_reference :read_ribbons, :story, index: true
|
||||||
|
|
||||||
|
create_view :replying_comments
|
||||||
|
end
|
||||||
|
end
|
17
db/schema.rb
17
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20180124143340) do
|
ActiveRecord::Schema.define(version: 20180130235553) do
|
||||||
|
|
||||||
create_table "comments", id: :integer, unsigned: true, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
|
create_table "comments", id: :integer, unsigned: true, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
|
@ -115,6 +115,16 @@ ActiveRecord::Schema.define(version: 20180124143340) do
|
||||||
t.index ["created_at"], name: "index_moderations_on_created_at"
|
t.index ["created_at"], name: "index_moderations_on_created_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "read_ribbons", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
|
t.boolean "is_following", default: true
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.bigint "user_id"
|
||||||
|
t.bigint "story_id"
|
||||||
|
t.index ["story_id"], name: "index_read_ribbons_on_story_id"
|
||||||
|
t.index ["user_id"], name: "index_read_ribbons_on_user_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "saved_stories", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
create_table "saved_stories", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
@ -236,4 +246,9 @@ ActiveRecord::Schema.define(version: 20180124143340) do
|
||||||
t.index ["user_id", "story_id"], name: "user_id_story_id"
|
t.index ["user_id", "story_id"], name: "user_id_story_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
create_view "replying_comments", sql_definition: <<-SQL
|
||||||
|
select `lobsters_dev`.`read_ribbons`.`user_id` AS `user_id`,`lobsters_dev`.`comments`.`id` AS `comment_id`,`lobsters_dev`.`read_ribbons`.`story_id` AS `story_id`,`lobsters_dev`.`comments`.`parent_comment_id` AS `parent_comment_id`,`lobsters_dev`.`comments`.`created_at` AS `comment_created_at`,`parent_comments`.`user_id` AS `parent_comment_author_id`,`lobsters_dev`.`comments`.`user_id` AS `comment_author_id`,`lobsters_dev`.`stories`.`user_id` AS `story_author_id`,(`lobsters_dev`.`read_ribbons`.`updated_at` < `lobsters_dev`.`comments`.`created_at`) AS `is_unread` from (((`lobsters_dev`.`read_ribbons` join `lobsters_dev`.`comments` on((`lobsters_dev`.`comments`.`story_id` = `lobsters_dev`.`read_ribbons`.`story_id`))) join `lobsters_dev`.`stories` on((`lobsters_dev`.`stories`.`id` = `lobsters_dev`.`comments`.`story_id`))) left join `lobsters_dev`.`comments` `parent_comments` on((`parent_comments`.`id` = `lobsters_dev`.`comments`.`parent_comment_id`))) where ((`lobsters_dev`.`read_ribbons`.`is_following` = 1) and (`lobsters_dev`.`comments`.`user_id` <> `lobsters_dev`.`read_ribbons`.`user_id`) and ((`parent_comments`.`user_id` = `lobsters_dev`.`read_ribbons`.`user_id`) or (isnull(`parent_comments`.`user_id`) and (`lobsters_dev`.`stories`.`user_id` = `lobsters_dev`.`read_ribbons`.`user_id`))) and ((`lobsters_dev`.`comments`.`upvotes` - `lobsters_dev`.`comments`.`downvotes`) < 0) and ((`parent_comments`.`upvotes` - `parent_comments`.`downvotes`) < 0))
|
||||||
|
SQL
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
27
db/views/replying_comments_v01.sql
Normal file
27
db/views/replying_comments_v01.sql
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
SELECT
|
||||||
|
read_ribbons.user_id,
|
||||||
|
comments.id as comment_id,
|
||||||
|
read_ribbons.story_id as story_id,
|
||||||
|
comments.parent_comment_id,
|
||||||
|
comments.created_at as comment_created_at,
|
||||||
|
parent_comments.user_id as parent_comment_author_id,
|
||||||
|
comments.user_id as comment_author_id,
|
||||||
|
stories.user_id as story_author_id,
|
||||||
|
(read_ribbons.updated_at < comments.created_at) as is_unread
|
||||||
|
FROM
|
||||||
|
read_ribbons
|
||||||
|
JOIN
|
||||||
|
comments ON comments.story_id = read_ribbons.story_id
|
||||||
|
JOIN
|
||||||
|
stories ON stories.id = comments.story_id
|
||||||
|
LEFT JOIN
|
||||||
|
comments parent_comments ON parent_comments.id = comments.parent_comment_id
|
||||||
|
WHERE
|
||||||
|
read_ribbons.is_following = 1
|
||||||
|
AND comments.user_id != read_ribbons.user_id
|
||||||
|
AND
|
||||||
|
(parent_comments.user_id = read_ribbons.user_id
|
||||||
|
OR (parent_comments.user_id IS NULL
|
||||||
|
AND stories.user_id = read_ribbons.user_id))
|
||||||
|
AND (comments.upvotes - comments.downvotes) < 0
|
||||||
|
AND (parent_comments.upvotes - parent_comments.downvotes) < 0;
|
Loading…
Reference in New Issue
Block a user