Inbox
+<% 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}) %> + > +
diff --git a/app.lua b/app.lua index 76e88af..b83b7ca 100644 --- a/app.lua +++ b/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 diff --git a/apps/threads.lua b/apps/threads.lua index 74222a0..611b1b6 100644 --- a/apps/threads.lua +++ b/apps/threads.lua @@ -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} diff --git a/apps/users.lua b/apps/users.lua index 12307c2..451511a 100644 --- a/apps/users.lua +++ b/apps/users.lua @@ -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) diff --git a/data/static/style.css b/data/static/style.css index b28c004..e7431dd 100644 --- a/data/static/style.css +++ b/data/static/style.css @@ -679,3 +679,7 @@ ul, ol { .accordion-content.hidden { display: none; } + +.inbox-container { + padding: 10px; +} diff --git a/sass/style.scss b/sass/style.scss index ff8ccb0..9740cf5 100644 --- a/sass/style.scss +++ b/sass/style.scss @@ -689,3 +689,7 @@ ul, ol { .accordion-content.hidden { display: none; } + +.inbox-container { + padding: 10px; +} diff --git a/views/common/babycode-editor.etlua b/views/common/babycode-editor.etlua index 21ba28c..43c8814 100644 --- a/views/common/babycode-editor.etlua +++ b/views/common/babycode-editor.etlua @@ -8,7 +8,7 @@ <% render ("views.common.babycode-editor-component", {ta_name = ta_name, prefill = prefill}) %> <% if not cancel_url then %> - + > <% end %> diff --git a/views/common/topnav.etlua b/views/common/topnav.etlua index 1a77e4d..7d24ba3 100644 --- a/views/common/topnav.etlua +++ b/views/common/topnav.etlua @@ -9,6 +9,8 @@ Welcome, "><%= me.username %> • ">Settings + • + ">Inbox <% if me:is_mod() then %> • ">User list diff --git a/views/threads/post.etlua b/views/threads/post.etlua index 1888f9a..65aace9 100644 --- a/views/threads/post.etlua +++ b/views/threads/post.etlua @@ -18,7 +18,8 @@