mirror of
https://github.com/discourse/discourse.git
synced 2024-12-16 01:26:29 +08:00
118 lines
4.1 KiB
Ruby
118 lines
4.1 KiB
Ruby
class PostTiming < ActiveRecord::Base
|
|
belongs_to :topic
|
|
belongs_to :user
|
|
|
|
validates_presence_of :post_number
|
|
validates_presence_of :msecs
|
|
|
|
|
|
def self.pretend_read(topic_id, actual_read_post_number, pretend_read_post_number)
|
|
# This is done in SQL cause the logic is quite tricky and we want to do this in one db hit
|
|
#
|
|
exec_sql("INSERT INTO post_timings(topic_id, user_id, post_number, msecs)
|
|
SELECT :topic_id, user_id, :pretend_read_post_number, 1
|
|
FROM post_timings pt
|
|
WHERE topic_id = :topic_id AND
|
|
post_number = :actual_read_post_number AND
|
|
NOT EXISTS (
|
|
SELECT 1 FROM post_timings pt1
|
|
WHERE pt1.topic_id = pt.topic_id AND
|
|
pt1.post_number = :pretend_read_post_number AND
|
|
pt1.user_id = pt.user_id
|
|
)
|
|
",
|
|
pretend_read_post_number: pretend_read_post_number,
|
|
topic_id: topic_id,
|
|
actual_read_post_number: actual_read_post_number
|
|
)
|
|
|
|
TopicUser.ensure_consistency!(topic_id)
|
|
end
|
|
|
|
# Increases a timer if a row exists, otherwise create it
|
|
def self.record_timing(args)
|
|
rows = exec_sql_row_count("UPDATE post_timings
|
|
SET msecs = msecs + :msecs
|
|
WHERE topic_id = :topic_id
|
|
AND user_id = :user_id
|
|
AND post_number = :post_number",
|
|
args)
|
|
|
|
if rows == 0
|
|
|
|
begin
|
|
exec_sql("INSERT INTO post_timings (topic_id, user_id, post_number, msecs)
|
|
SELECT :topic_id, :user_id, :post_number, :msecs
|
|
WHERE NOT EXISTS(SELECT 1 FROM post_timings
|
|
WHERE topic_id = :topic_id
|
|
AND user_id = :user_id
|
|
AND post_number = :post_number)",
|
|
args)
|
|
rescue PG::UniqueViolation
|
|
# concurrency is hard, we are not running serialized so this can possibly
|
|
# still happen, if it happens we just don't care, its an invalid record anyway
|
|
return
|
|
end
|
|
|
|
Post.where(['topic_id = :topic_id and post_number = :post_number', args]).update_all 'reads = reads + 1'
|
|
UserStat.where(user_id: args[:user_id]).update_all 'posts_read_count = posts_read_count + 1'
|
|
end
|
|
end
|
|
|
|
|
|
def self.destroy_for(user_id, topic_ids)
|
|
PostTiming.transaction do
|
|
PostTiming.delete_all(['user_id = ? and topic_id in (?)', user_id, topic_ids])
|
|
TopicUser.delete_all(['user_id = ? and topic_id in (?)', user_id, topic_ids])
|
|
end
|
|
end
|
|
|
|
|
|
def self.process_timings(current_user, topic_id, topic_time, timings)
|
|
current_user.user_stat.update_time_read!
|
|
|
|
account_age_msecs = ((Time.now - current_user.created_at) * 1000.0)
|
|
|
|
highest_seen = 1
|
|
timings.each do |post_number, time|
|
|
if post_number >= 0 && time < account_age_msecs
|
|
PostTiming.record_timing(topic_id: topic_id,
|
|
post_number: post_number,
|
|
user_id: current_user.id,
|
|
msecs: time)
|
|
|
|
highest_seen = post_number.to_i > highest_seen ?
|
|
post_number.to_i : highest_seen
|
|
end
|
|
end
|
|
|
|
total_changed = 0
|
|
if timings.length > 0
|
|
total_changed = Notification.mark_posts_read(current_user, topic_id, timings.map{|t| t[0]})
|
|
end
|
|
|
|
TopicUser.update_last_read(current_user, topic_id, highest_seen, topic_time)
|
|
|
|
if total_changed > 0
|
|
current_user.reload
|
|
current_user.publish_notifications_state
|
|
end
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: post_timings
|
|
#
|
|
# topic_id :integer not null
|
|
# post_number :integer not null
|
|
# user_id :integer not null
|
|
# msecs :integer not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_post_timings_on_user_id (user_id)
|
|
# post_timings_summary (topic_id,post_number)
|
|
# post_timings_unique (topic_id,post_number,user_id) UNIQUE
|
|
#
|