add subscribing and unsubscribing to threads
This commit is contained in:
		@@ -9,6 +9,7 @@ local models = require("models")
 | 
			
		||||
local Topics = models.Topics
 | 
			
		||||
local Threads = models.Threads
 | 
			
		||||
local Posts = models.Posts
 | 
			
		||||
local Subscriptions = models.Subscriptions
 | 
			
		||||
 | 
			
		||||
local POSTS_PER_PAGE = 10
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +98,18 @@ app:get("thread", "/:slug", function(self)
 | 
			
		||||
  self.other_topics = db.query("SELECT topics.id, topics.name FROM topics")
 | 
			
		||||
  self.me = util.get_logged_in_user_or_transient(self)
 | 
			
		||||
  self.posts = posts
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
  if self.me:is_logged_in() then
 | 
			
		||||
    self.is_subscribed = false
 | 
			
		||||
    local subscription = Subscriptions:find({user_id = self.me.id, thread_id = thread.id})
 | 
			
		||||
    if subscription then
 | 
			
		||||
      self.is_subscribed = true
 | 
			
		||||
      if posts[#posts].created_at > subscription.last_seen then
 | 
			
		||||
        subscription:update({last_seen = os.time()})
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  self.page_title = thread.title
 | 
			
		||||
 | 
			
		||||
  return {render = "threads.thread"}
 | 
			
		||||
@@ -133,6 +145,10 @@ app:post("thread", "/:slug", function(self)
 | 
			
		||||
    return {redirect_to = self:url_for("thread", {slug = thread.slug}, {page = last_page}) .. "#latest-post"}
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  if self.params.subscribe == "on" then
 | 
			
		||||
    Subscriptions:create({user_id = user.id, thread_id = thread.id, last_seen = os.time()})
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  return {redirect_to = self:url_for("thread", {slug = thread.slug}, {page = last_page}) .. "#latest-post"}
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
@@ -206,4 +222,32 @@ app:post("thread_move", "/:slug/move", function(self)
 | 
			
		||||
  return {redirect_to = self:url_for("thread", {slug = self.params.slug})}
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
app:post("thread_subscribe", "/:slug/subscribe", function(self)
 | 
			
		||||
  local user = util.get_logged_in_user(self)
 | 
			
		||||
  if not user then
 | 
			
		||||
    return {status = 403}
 | 
			
		||||
  end
 | 
			
		||||
  local thread = Threads:find({slug = self.params.slug})
 | 
			
		||||
  if not thread then
 | 
			
		||||
    return {status = 404}
 | 
			
		||||
  end
 | 
			
		||||
  local subscription = Subscriptions:find({user_id = user.id, thread_id = thread.id})
 | 
			
		||||
  if self.params.subscribe == "subscribe" then
 | 
			
		||||
    local now = os.time()
 | 
			
		||||
    if subscription then
 | 
			
		||||
      subscription:delete()
 | 
			
		||||
    end
 | 
			
		||||
    Subscriptions:create({user_id = user.id, thread_id = thread.id, last_seen = now})
 | 
			
		||||
    return {redirect_to = self:url_for("thread", {slug = thread.slug}, {after = self.params.first_visible_post})}
 | 
			
		||||
  elseif self.params.subscribe == "unsubscribe" then
 | 
			
		||||
    if not subscription then
 | 
			
		||||
      return {status = 404}
 | 
			
		||||
    end
 | 
			
		||||
    subscription:delete()
 | 
			
		||||
    return {redirect_to = self:url_for("thread", {slug = thread.slug}, {after = self.params.first_visible_post})}
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  return {status = 400}
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
return app
 | 
			
		||||
 
 | 
			
		||||
@@ -102,4 +102,15 @@ return {
 | 
			
		||||
    
 | 
			
		||||
    db.query("CREATE INDEX idx_rate_limit_user_method ON api_rate_limits (user_id, method)")
 | 
			
		||||
  end,
 | 
			
		||||
  
 | 
			
		||||
  [13] = function ()
 | 
			
		||||
    schema.create_table("subscriptions", {
 | 
			
		||||
      {"id", types.integer{primary_key = true}},
 | 
			
		||||
      {"user_id", "INTEGER REFERENCES users(id) ON DELETE CASCADE"},
 | 
			
		||||
      {"thread_id", "INTEGER REFERENCES threads(id) ON DELETE CASCADE"},
 | 
			
		||||
      {"last_seen", "INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)) NOT NULL"},
 | 
			
		||||
    })
 | 
			
		||||
    
 | 
			
		||||
    db.query("CREATE INDEX idx_subscription_user_thread ON subscriptions (user_id, thread_id)")
 | 
			
		||||
  end,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ local ret = {
 | 
			
		||||
  PostHistory = Model:extend("post_history"),
 | 
			
		||||
  Sessions = Model:extend("sessions"),
 | 
			
		||||
  Avatars = Model:extend("avatars"),
 | 
			
		||||
  Subscriptions = Model:extend("subscriptions"),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
return ret
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								util.lua
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								util.lua
									
									
									
									
									
								
							@@ -105,13 +105,16 @@ function util.split_sentences(sentences, max_sentences)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
---@return string
 | 
			
		||||
function util.get_post_url(req, post_id)
 | 
			
		||||
function util.get_post_url(req, post_id, hash)
 | 
			
		||||
  hash = hash ~= false
 | 
			
		||||
  local post = Posts:find({id = post_id})
 | 
			
		||||
  if not post then return "" end
 | 
			
		||||
  local thread = Threads:find({id = post.thread_id})
 | 
			
		||||
  if not thread then return "" end
 | 
			
		||||
  
 | 
			
		||||
  return req:url_for("thread", {slug = thread.slug}, {after = post_id}) .. "#post-" .. post_id
 | 
			
		||||
  local url = req:url_for("thread", {slug = thread.slug}, {after = post_id})
 | 
			
		||||
  if not hash then return url end
 | 
			
		||||
  return url .. "#post-" .. post_id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function util.infobox_message(msg)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,12 @@
 | 
			
		||||
%>
 | 
			
		||||
<form class="post-edit-form" method="post" action="<%= url or "" %>">
 | 
			
		||||
  <% render ("views.common.babycode-editor-component", {ta_name = ta_name, prefill = prefill}) %>
 | 
			
		||||
  <% if not cancel_url then %>
 | 
			
		||||
    <span>
 | 
			
		||||
      <input type="checkbox" id="subscribe" name="subscribe" checked>
 | 
			
		||||
      <label for="subscribe">Subscribe to thread</label>
 | 
			
		||||
    </span>
 | 
			
		||||
  <% end %>
 | 
			
		||||
  <span>
 | 
			
		||||
  <input type=submit value="<%= save_button_text %>">
 | 
			
		||||
    <% if cancel_url then %>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,29 +14,36 @@
 | 
			
		||||
    <% if is_stickied then %> • <i>stickied, so it's probably important</i>
 | 
			
		||||
    <% end %>
 | 
			
		||||
    </span>
 | 
			
		||||
    <% if can_lock then %>
 | 
			
		||||
    <div>
 | 
			
		||||
      <form class="modform" action="<%= url_for("thread_lock", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
        <input type=hidden value="<%= not is_locked %>" name="target_op">
 | 
			
		||||
        <input class="warn" type="submit" value="<%= is_locked and "Unlock thread" or "Lock thread" %>">
 | 
			
		||||
      </form>
 | 
			
		||||
      <% if me:is_mod() then %>
 | 
			
		||||
        <form class="modform" action="<%= url_for("thread_sticky", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
          <input type=hidden value="<%= not is_stickied %>" name="target_op">
 | 
			
		||||
          <input class="warn" type="submit" value="<%= is_stickied and "Unsticky thread" or "Sticky thread" %>">
 | 
			
		||||
        </form>
 | 
			
		||||
        <form class="modform" action="<%= url_for("thread_move", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
          <label for="new_topic_id">Move to topic:</label>
 | 
			
		||||
          <select style="width:200px;" id="new_topic_id" name="new_topic_id" autocomplete="off">
 | 
			
		||||
            <% for _, topic in ipairs(other_topics) do %>
 | 
			
		||||
              <option value="<%= topic.id %>" <%- thread.topic_id == topic.id and "selected disabled" or "" %>><%= topic.name %></option>
 | 
			
		||||
            <% end %>
 | 
			
		||||
          </select>
 | 
			
		||||
          <input class="warn" type="submit" value="Move thread">
 | 
			
		||||
      <% if me:is_logged_in() then %>
 | 
			
		||||
        <form class="modform" action="<%= url_for("thread_subscribe", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
          <input type="hidden" name="first_visible_post" value=<%= posts[1].id %>>
 | 
			
		||||
          <input type="hidden" name="subscribe" value=<%= is_subscribed and "unsubscribe" or "subscribe" %>>
 | 
			
		||||
          <input type="submit" value="<%= is_subscribed and "Unsubscribe" or "Subscribe" %>">
 | 
			
		||||
        </form>
 | 
			
		||||
      <% end %>
 | 
			
		||||
      <% if can_lock then %>
 | 
			
		||||
        <form class="modform" action="<%= url_for("thread_lock", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
          <input type=hidden value="<%= not is_locked %>" name="target_op">
 | 
			
		||||
          <input class="warn" type="submit" value="<%= is_locked and "Unlock thread" or "Lock thread" %>">
 | 
			
		||||
        </form>
 | 
			
		||||
        <% if me:is_mod() then %>
 | 
			
		||||
          <form class="modform" action="<%= url_for("thread_sticky", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
            <input type=hidden value="<%= not is_stickied %>" name="target_op">
 | 
			
		||||
            <input class="warn" type="submit" value="<%= is_stickied and "Unsticky thread" or "Sticky thread" %>">
 | 
			
		||||
          </form>
 | 
			
		||||
          <form class="modform" action="<%= url_for("thread_move", {slug = thread.slug}) %>" method="post">
 | 
			
		||||
            <label for="new_topic_id">Move to topic:</label>
 | 
			
		||||
            <select style="width:200px;" id="new_topic_id" name="new_topic_id" autocomplete="off">
 | 
			
		||||
              <% for _, topic in ipairs(other_topics) do %>
 | 
			
		||||
                <option value="<%= topic.id %>" <%- thread.topic_id == topic.id and "selected disabled" or "" %>><%= topic.name %></option>
 | 
			
		||||
              <% end %>
 | 
			
		||||
            </select>
 | 
			
		||||
            <input class="warn" type="submit" value="Move thread">
 | 
			
		||||
          </form>
 | 
			
		||||
        <% end %>
 | 
			
		||||
      <% end %>
 | 
			
		||||
    </div>
 | 
			
		||||
    <% end %>
 | 
			
		||||
  </nav>
 | 
			
		||||
  <% for i, post in ipairs(posts) do %>
 | 
			
		||||
    <% render("views.threads.post", {post = post, render_sig = true, is_latest = i == #posts}) %>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user