From 80ceb57c76fd3a4852b28ccab08fc9f143514482 Mon Sep 17 00:00:00 2001
From: Sam <sam.saffron@gmail.com>
Date: Tue, 13 Nov 2018 16:07:48 +1100
Subject: [PATCH] DEV: add API endpoint to destroy_timings only of last post

Previously API only allowed you to nuke all timings from a topic,
new API is less punishing and allows you just to remove 1 post.
---
 app/controllers/topics_controller.rb    |  7 ++++-
 app/models/post_timing.rb               | 19 +++++++++++++
 spec/requests/topics_controller_spec.rb | 38 +++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb
index fd93edbb7bd..002f2aa0103 100644
--- a/app/controllers/topics_controller.rb
+++ b/app/controllers/topics_controller.rb
@@ -250,7 +250,12 @@ class TopicsController < ApplicationController
   end
 
   def destroy_timings
-    PostTiming.destroy_for(current_user.id, [params[:topic_id].to_i])
+    if params[:last].to_s == "1"
+      PostTiming.destroy_last_for(current_user, params[:topic_id])
+    else
+      PostTiming.destroy_for(current_user.id, [params[:topic_id].to_i])
+    end
+
     render body: nil
   end
 
diff --git a/app/models/post_timing.rb b/app/models/post_timing.rb
index 23d224c5e64..82f7a024812 100644
--- a/app/models/post_timing.rb
+++ b/app/models/post_timing.rb
@@ -64,6 +64,25 @@ class PostTiming < ActiveRecord::Base
     record_new_timing(args) if rows == 0
   end
 
+  def self.destroy_last_for(user, topic_id)
+    topic = Topic.find(topic_id)
+    post_number = user.staff? ? topic.highest_staff_post_number : topic.highest_post_number
+
+    last_read = post_number - 1
+
+    PostTiming.transaction do
+      PostTiming.where("topic_id = ? AND user_id = ? AND post_number > ?", topic.id, user.id, last_read).delete_all
+      if last_read < 1
+        last_read = nil
+      end
+
+      TopicUser.where(user_id: user.id, topic_id: topic.id).update_all(
+        highest_seen_post_number: last_read,
+        last_read_post_number: last_read
+      )
+    end
+  end
+
   def self.destroy_for(user_id, topic_ids)
     PostTiming.transaction do
       PostTiming
diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb
index c15c9770eb1..0c6b6f3d668 100644
--- a/spec/requests/topics_controller_spec.rb
+++ b/spec/requests/topics_controller_spec.rb
@@ -529,6 +529,44 @@ RSpec.describe TopicsController do
       end
     end
 
+    context 'for last post only' do
+
+      it 'should allow you to retain topic timing but remove last post only' do
+        post1 = create_post
+        topic = post1.topic
+
+        post2 = create_post(topic_id: topic.id)
+
+        PostTiming.create!(topic: topic, user: user, post_number: 1, msecs: 100)
+        PostTiming.create!(topic: topic, user: user, post_number: 2, msecs: 100)
+
+        TopicUser.create!(
+          topic: topic,
+          user: user,
+          last_read_post_number: 2,
+          highest_seen_post_number: 2
+        )
+
+        sign_in(user)
+
+        delete "/t/#{topic.id}/timings.json?last=1"
+
+        expect(PostTiming.where(topic: topic, user: user, post_number: 2).exists?).to eq(false)
+        expect(PostTiming.where(topic: topic, user: user, post_number: 1).exists?).to eq(true)
+
+        expect(TopicUser.where(topic: topic, user: user, last_read_post_number: 1, highest_seen_post_number: 1).exists?).to eq(true)
+
+        PostDestroyer.new(Fabricate(:admin), post2).destroy
+
+        delete "/t/#{topic.id}/timings.json?last=1"
+
+        expect(PostTiming.where(topic: topic, user: user, post_number: 1).exists?).to eq(false)
+        expect(TopicUser.where(topic: topic, user: user, last_read_post_number: nil, highest_seen_post_number: nil).exists?).to eq(true)
+
+      end
+
+    end
+
     context 'when logged in' do
       before do
         @user = sign_in(Fabricate(:user))