add an inbox view
This commit is contained in:
		
							
								
								
									
										9
									
								
								app.lua
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								app.lua
									
									
									
									
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
local lapis = require("lapis")
 | 
			
		||||
local date = require("date")
 | 
			
		||||
local models = require("models")
 | 
			
		||||
local app = lapis.Application()
 | 
			
		||||
local constants = require("constants")
 | 
			
		||||
local babycode = require("lib.babycode")
 | 
			
		||||
@@ -47,6 +48,14 @@ local function inject_methods(req)
 | 
			
		||||
  req.babycode_to_html = function (_, bb)
 | 
			
		||||
    return babycode.to_html(bb, html_escape)
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  req.get_thread_by_id = function(_, id)
 | 
			
		||||
    return models.Threads:find({id = id})
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  req.get_post_url = function(_, id)
 | 
			
		||||
    return util.get_post_url(_, id)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  util.pop_infobox(req)
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -238,13 +238,31 @@ app:post("thread_subscribe", "/:slug/subscribe", function(self)
 | 
			
		||||
      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})}
 | 
			
		||||
    if self.params.last_visible_post then
 | 
			
		||||
      return {redirect_to = self:url_for("thread", {slug = thread.slug}, {after = self.params.last_visible_post})}
 | 
			
		||||
    else
 | 
			
		||||
      return {redirect_to = self:url_for("user_inbox", {username = user.username})}
 | 
			
		||||
    end
 | 
			
		||||
  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})}
 | 
			
		||||
    if self.params.last_visible_post then
 | 
			
		||||
      return {redirect_to = self:url_for("thread", {slug = thread.slug}, {after = self.params.last_visible_post})}
 | 
			
		||||
    else
 | 
			
		||||
      return {redirect_to = self:url_for("user_inbox", {username = user.username})}
 | 
			
		||||
    end
 | 
			
		||||
  elseif self.params.subscribe == "read" then
 | 
			
		||||
    if not subscription then
 | 
			
		||||
      return {status = 404}
 | 
			
		||||
    end
 | 
			
		||||
    subscription:update({last_seen = os.time()})
 | 
			
		||||
    if self.params.last_visible_post then
 | 
			
		||||
      return {redirect_to = self:url_for("thread", {slug = thread.slug}, {after = self.params.last_visible_post})}
 | 
			
		||||
    else
 | 
			
		||||
      return {redirect_to = self:url_for("user_inbox", {username = user.username})}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  return {status = 400}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ local models = require("models")
 | 
			
		||||
local Users = models.Users
 | 
			
		||||
local Sessions = models.Sessions
 | 
			
		||||
local Avatars = models.Avatars
 | 
			
		||||
local Subscriptions = models.Subscriptions
 | 
			
		||||
 | 
			
		||||
local function authenticate_user(user, password)
 | 
			
		||||
  return auth.verify(password, user.password_hash)
 | 
			
		||||
@@ -267,6 +268,11 @@ app:post("user_settings", "/:username/settings", function(self)
 | 
			
		||||
  local status = self.params.status:sub(1, 100)
 | 
			
		||||
  local original_sig = self.params.signature or ""
 | 
			
		||||
  local rendered_sig = babycode.to_html(original_sig, html_escape)
 | 
			
		||||
  if self.params.subscribe_by_default == "on" then
 | 
			
		||||
    self.session.subscribe_by_default = true
 | 
			
		||||
  else
 | 
			
		||||
    self.session.subscribe_by_default = false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  target_user:update({
 | 
			
		||||
    status = status,
 | 
			
		||||
@@ -277,6 +283,97 @@ app:post("user_settings", "/:username/settings", function(self)
 | 
			
		||||
  return {redirect_to = self:url_for("user_settings", {username = self.params.username})}
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
app:get("user_inbox", "/:username/inbox", function(self)
 | 
			
		||||
  local me = util.get_logged_in_user(self)
 | 
			
		||||
  if me == nil then
 | 
			
		||||
    util.inject_err_infobox(self, "You must be logged in to perform this action.")
 | 
			
		||||
    return {redirect_to = self:url_for("user_login")}
 | 
			
		||||
  end
 | 
			
		||||
  local target_user = Users:find({username = self.params.username})
 | 
			
		||||
  if me.id ~= target_user.id then
 | 
			
		||||
    return {redirect_to = self:url_for("user_inbox", {username = me.username})}
 | 
			
		||||
  end
 | 
			
		||||
  self.me = target_user
 | 
			
		||||
  self.page_title = "inbox"
 | 
			
		||||
  self.new_posts = {}
 | 
			
		||||
  local subscription = Subscriptions:find({user_id = me.id})
 | 
			
		||||
  if subscription then
 | 
			
		||||
    local q = [[
 | 
			
		||||
    WITH thread_metadata AS (
 | 
			
		||||
      SELECT
 | 
			
		||||
        posts.thread_id, threads.slug AS thread_slug, threads.title AS thread_title, COUNT(*) AS unread_count, MAX(posts.created_at) AS newest_post_time
 | 
			
		||||
      FROM
 | 
			
		||||
        posts
 | 
			
		||||
      LEFT JOIN
 | 
			
		||||
        threads ON threads.id = posts.thread_id
 | 
			
		||||
      LEFT JOIN
 | 
			
		||||
        subscriptions ON subscriptions.thread_id = posts.thread_id
 | 
			
		||||
      WHERE subscriptions.user_id = ? AND posts.created_at > subscriptions.last_seen
 | 
			
		||||
      GROUP BY posts.thread_id
 | 
			
		||||
    )
 | 
			
		||||
    
 | 
			
		||||
    SELECT
 | 
			
		||||
      tm.thread_id, tm.thread_slug, tm.thread_title, tm.unread_count, tm.newest_post_time,
 | 
			
		||||
    
 | 
			
		||||
      posts.id, posts.created_at, post_history.content, post_history.edited_at, users.username, users.status, avatars.file_path AS avatar_path, posts.thread_id, users.id AS user_id, post_history.original_markup, users.signature_rendered
 | 
			
		||||
    FROM
 | 
			
		||||
      thread_metadata tm
 | 
			
		||||
    JOIN
 | 
			
		||||
      posts ON posts.thread_id = tm.thread_id
 | 
			
		||||
    JOIN
 | 
			
		||||
      post_history ON posts.current_revision_id = post_history.id
 | 
			
		||||
    JOIN
 | 
			
		||||
      users ON posts.user_id = users.id
 | 
			
		||||
    LEFT JOIN
 | 
			
		||||
      threads ON threads.id = posts.thread_id
 | 
			
		||||
    LEFT JOIN
 | 
			
		||||
      avatars ON users.avatar_id = avatars.id
 | 
			
		||||
    LEFT JOIN
 | 
			
		||||
      subscriptions ON subscriptions.thread_id = posts.thread_id
 | 
			
		||||
    WHERE
 | 
			
		||||
      subscriptions.user_id = ? AND posts.created_at > subscriptions.last_seen
 | 
			
		||||
    ORDER BY
 | 
			
		||||
      tm.newest_post_time DESC, posts.created_at ASC]]
 | 
			
		||||
    local new_posts_raw = db.query(q, me.id, me.id)
 | 
			
		||||
    local threads = {}
 | 
			
		||||
    local current_thread_id = nil
 | 
			
		||||
    local current_thread_group = nil
 | 
			
		||||
    self.total_unreads_count = 0
 | 
			
		||||
    for _, row in ipairs(new_posts_raw) do
 | 
			
		||||
      if row.thread_id ~= current_thread_id then
 | 
			
		||||
        current_thread_group = {
 | 
			
		||||
          thread_id = row.thread_id,
 | 
			
		||||
          thread_title = row.thread_title,
 | 
			
		||||
          unread_count = row.unread_count,
 | 
			
		||||
          thread_slug = row.thread_slug,
 | 
			
		||||
          newest_post_time = row.newest_post_time,
 | 
			
		||||
          posts = {}
 | 
			
		||||
        }
 | 
			
		||||
        self.total_unreads_count = self.total_unreads_count + row.unread_count
 | 
			
		||||
        table.insert(threads, current_thread_group)
 | 
			
		||||
        current_thread_id = row.thread_id
 | 
			
		||||
      end
 | 
			
		||||
---@diagnostic disable-next-line: need-check-nil
 | 
			
		||||
      table.insert(current_thread_group.posts, {
 | 
			
		||||
        id = row.id,
 | 
			
		||||
        created_at = row.created_at,
 | 
			
		||||
        content = row.content,
 | 
			
		||||
        edited_at = row.edited_at,
 | 
			
		||||
        username = row.username,
 | 
			
		||||
        status = row.status,
 | 
			
		||||
        avatar_path = row.avatar_path,
 | 
			
		||||
        thread_id = row.thread_id,
 | 
			
		||||
        user_id = row.user_id,
 | 
			
		||||
        original_markup = row.original_markup,
 | 
			
		||||
        signature_rendered = row.signature_rendered,
 | 
			
		||||
      })
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    self.new_posts = threads
 | 
			
		||||
  end
 | 
			
		||||
  return {render = "user.inbox"}
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
app:get("user_login", "/login", function(self)
 | 
			
		||||
  if self.session.session_key then
 | 
			
		||||
    local user = util.get_logged_in_user(self)
 | 
			
		||||
 
 | 
			
		||||
@@ -679,3 +679,7 @@ ul, ol {
 | 
			
		||||
.accordion-content.hidden {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.inbox-container {
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -689,3 +689,7 @@ ul, ol {
 | 
			
		||||
.accordion-content.hidden {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.inbox-container {
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
  <% 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>
 | 
			
		||||
      <input type="checkbox" id="subscribe" name="subscribe" <%= session.subscribe_by_default and "checked" or "" %>>
 | 
			
		||||
      <label for="subscribe">Subscribe to thread</label>
 | 
			
		||||
    </span>
 | 
			
		||||
  <% end %>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,8 @@
 | 
			
		||||
      Welcome, <a href="<%= url_for("user", {username = me.username}) %>"><%= me.username %></a>
 | 
			
		||||
      •
 | 
			
		||||
      <a href="<%= url_for("user_settings", {username = me.username}) %>">Settings</a>
 | 
			
		||||
      •
 | 
			
		||||
      <a href="<%= url_for("user_inbox", {username = me.username}) %>">Inbox</a>
 | 
			
		||||
      <% if me:is_mod() then %>
 | 
			
		||||
        •
 | 
			
		||||
        <a href="<%= url_for("user_list") %>">User list</a>
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,8 @@
 | 
			
		||||
  <div class="post-content-container"<%= is_latest and 'id=latest-post' or "" %>>
 | 
			
		||||
    <div class="post-info">
 | 
			
		||||
      <%
 | 
			
		||||
        local post_url = url_for("thread", {slug = thread.slug}, {page = page}) .. "#post-" .. post.id
 | 
			
		||||
        --local post_url = url_for("thread", {slug = thread.slug}, {page = page}) .. "#post-" .. post.id
 | 
			
		||||
        local post_url = get_post_url(post.id)
 | 
			
		||||
      %>
 | 
			
		||||
      <a href="<%= post_url %>" title="Permalink"><i>
 | 
			
		||||
        <% if tonumber(post.edited_at) > tonumber(post.created_at) then -%>
 | 
			
		||||
@@ -55,7 +56,7 @@
 | 
			
		||||
          <button value="<%= reply_text %>" class="reply-button">Quote</button>
 | 
			
		||||
      <% end %>
 | 
			
		||||
      <%
 | 
			
		||||
        local show_delete = (post.user_id == me.id and not ntob(thread.is_locked)) or me:is_mod()
 | 
			
		||||
        local show_delete = ((post.user_id == me.id and not ntob(thread.is_locked)) or me:is_mod()) and not no_reply
 | 
			
		||||
        if show_delete then
 | 
			
		||||
      %>
 | 
			
		||||
          <button class="critical post-delete-button" value="<%= post.id %>">Delete</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
    <div>
 | 
			
		||||
      <% 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="last_visible_post" value=<%= posts[#posts].id %>>
 | 
			
		||||
          <input type="hidden" name="subscribe" value=<%= is_subscribed and "unsubscribe" or "subscribe" %>>
 | 
			
		||||
          <input type="submit" value="<%= is_subscribed and "Unsubscribe" or "Subscribe" %>">
 | 
			
		||||
        </form>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								views/user/inbox.etlua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								views/user/inbox.etlua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<div class="darkbg">
 | 
			
		||||
  <h1 class="thread-title">Inbox</h1>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="inbox-container">
 | 
			
		||||
  <% if #new_posts == 0 then %>
 | 
			
		||||
  You have no unread posts.
 | 
			
		||||
  <% else %>
 | 
			
		||||
    You have <%= total_unreads_count %> unread post<%= total_unreads_count > 1 and "s" or "" %>:
 | 
			
		||||
    <% for _, thread in ipairs(new_posts) do %>
 | 
			
		||||
      <div class="accordion">
 | 
			
		||||
        <div class="accordion-header">
 | 
			
		||||
          <button type="button" class="accordion-toggle">▼</button>
 | 
			
		||||
          <% local latest_post_id = thread.posts[#thread.posts].id %>
 | 
			
		||||
          <%
 | 
			
		||||
            local unread_posts_text = " (" .. thread.unread_count .. " unread post" .. (thread.unread_count > 1 and "s" or "")-- .. ")"
 | 
			
		||||
          %>
 | 
			
		||||
          <a class="accordion-title" href="<%= url_for("thread", {slug = thread.thread_slug}, {after = latest_post_id}) .. "#post-" .. latest_post_id %>" title="Jump to latest post"><%= thread.thread_title .. unread_posts_text %>, latest at <span class="timestamp" data-utc=<%= thread.newest_post_time %>><%= os.date("%c", thread.newest_post_time) %></span>)</a>
 | 
			
		||||
          <form action="<%= url_for("thread_subscribe", {slug = thread.thread_slug}) %>" method="post">
 | 
			
		||||
            <input type="hidden" name="subscribe" value="read">
 | 
			
		||||
            <input type="submit" value="Mark Thread as Read">
 | 
			
		||||
          </form>
 | 
			
		||||
          <form action="<%= url_for("thread_subscribe", {slug = thread.thread_slug}) %>" method="post">
 | 
			
		||||
            <input type="hidden" name="subscribe" value="unsubscribe">
 | 
			
		||||
            <input class="warn" type="submit" value="Unsubscribe">
 | 
			
		||||
          </form>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="accordion-content">
 | 
			
		||||
          <% for _, post in ipairs(thread.posts) do %>
 | 
			
		||||
            <% render("views.threads.post", {post = post, edit = false, is_latest = false, no_reply = true, thread = get_thread_by_id(thread.thread_id)}) %>
 | 
			
		||||
          <% end %>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    <% end %>
 | 
			
		||||
  <% end %>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -24,6 +24,8 @@
 | 
			
		||||
    <input type="text" id="status" name="status" value="<%= me.status %>" maxlength="70" placeholder="Will be shown under your username. Max 70 characters">
 | 
			
		||||
    <label for="babycode-content">Signature</label><br>
 | 
			
		||||
    <% render("views.common.babycode-editor-component", {ta_name = "signature", prefill = me.signature_original_markup, ta_placeholder = "Will be shown under each of your posts", optional = true}) %>
 | 
			
		||||
    <input autocomplete="off" type="checkbox" id="subscribe_by_default" name="subscribe_by_default" <%= session.subscribe_by_default and "checked" or "" %>>
 | 
			
		||||
    <label for="subscribe_by_default">Subscribe to thread by default when responding</label><br>
 | 
			
		||||
    <input type="submit" value="Save settings">
 | 
			
		||||
  </form>
 | 
			
		||||
  <form method="post" action="<%= url_for("user_change_password", {username = me.username}) %>">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user