From f5ba3120323d239bd4c4e14034f1b67ad193d6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lera=20Elvo=C3=A9?= Date: Wed, 21 May 2025 17:34:24 +0300 Subject: [PATCH] add topic/thread list view --- apps/topics.lua | 52 +++++++++++++++++++++-- sass/style.scss | 56 ++++++++++++++++++++++++ static/style.css | 55 ++++++++++++++++++++++++ svg-icons/sticky.etlua | 5 +++ views/topics/topic.etlua | 92 ++++++++++++++++++++++++++++------------ 5 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 svg-icons/sticky.etlua diff --git a/apps/topics.lua b/apps/topics.lua index 73cfaf9..bab6961 100644 --- a/apps/topics.lua +++ b/apps/topics.lua @@ -12,6 +12,8 @@ local Avatars = models.Avatars local Topics = models.Topics local Threads = models.Threads +local THREADS_PER_PAGE = 10 + local ThreadCreateError = { OK = 0, GUEST = 1, @@ -53,8 +55,10 @@ app:post("topic_create", "/create", function(self) description = topic_description, slug = slug, }) + + util.inject_infobox(self, "Topic created.") - return {redirect_to = self:url_for("all_topics")} + return {redirect_to = self:url_for("topic", {slug = topic.slug})} end) app:get("topic", "/:slug", function(self) @@ -64,11 +68,51 @@ app:get("topic", "/:slug", function(self) if not topic then return {status = 404} end + local threads_count = Threads:count(db.clause({ + topic_id = topic.id + })) self.topic = topic - self.threads_list = db.query("SELECT * FROM threads WHERE topic_id = ? ORDER BY is_stickied DESC, created_at DESC", topic.id) + + self.pages = math.ceil(threads_count / THREADS_PER_PAGE) + self.page = math.max(1, math.min(tonumber(self.params.page) or 1, self.pages)) + -- self.threads_list = db.query("SELECT * FROM threads WHERE topic_id = ? ORDER BY is_stickied DESC, created_at DESC", topic.id) + self.threads_list = db.query([[ + SELECT + threads.title, threads.slug, threads.created_at, threads.is_locked, threads.is_stickied, + users.username AS started_by, + u.username AS latest_post_username, + ph.content AS latest_post_content, + posts.created_at AS latest_post_created_at, + posts.id AS latest_post_id + FROM + threads + JOIN users ON users.id = threads.user_id + JOIN ( + SELECT + posts.thread_id, + posts.id, + posts.user_id, + posts.created_at, + posts.current_revision_id, + ROW_NUMBER() OVER (PARTITION BY posts.thread_id ORDER BY posts.created_at DESC) AS rn + FROM + posts + ) posts ON posts.thread_id = threads.id AND posts.rn = 1 + JOIN + post_history ph ON ph.id = posts.current_revision_id + JOIN + users u ON u.id = posts.user_id + WHERE + threads.topic_id = ? + ORDER BY + threads.is_stickied DESC, + threads.created_at DESC + LIMIT ? OFFSET ? + ]], topic.id, THREADS_PER_PAGE, (self.page - 1) * THREADS_PER_PAGE) + local user = util.get_logged_in_user_or_transient(self) - print(topic.is_locked, type(topic.is_locked)) self.me = user + self.ThreadCreateError = ThreadCreateError self.thread_create_error = ThreadCreateError.OK if user:is_logged_in_guest() then @@ -79,7 +123,7 @@ app:get("topic", "/:slug", function(self) self.thread_create_error = ThreadCreateError.TOPIC_LOCKED end - self.page_title = "all threads in " .. topic.name + self.page_title = "browsing topic " .. topic.name return {render = "topics.topic"} end) diff --git a/sass/style.scss b/sass/style.scss index 05aaae3..d9d9636 100644 --- a/sass/style.scss +++ b/sass/style.scss @@ -86,6 +86,8 @@ body { .thread-title { margin: 0; + font-size: 1.5rem; + font-weight: bold; } .post { @@ -187,6 +189,7 @@ body { } button, input[type="submit"], .linkbutton { + display: inline-block; @include button($button_color); &.critical { @@ -205,6 +208,10 @@ input[type="file"]::file-selector-button { margin: 10px 10px; } +p { + margin: 15px 0; +} + .pagebutton { @include button($button_color); padding: 5px 5px; @@ -277,3 +284,52 @@ input[type="text"], input[type="password"] { min-width: 60px; padding-right: 15px; } + +.thread { + display: grid; + grid-template-columns: 96px 1.6fr 96px; + grid-template-rows: 1fr; + gap: 0px 0px; + grid-auto-flow: row; + min-height: 96px; + grid-template-areas: + "thread-sticky-container thread-info-container thread-locked-container"; +} + +.thread-sticky-container { + grid-area: thread-sticky-container; + border: 2px outset $light; +} + +.thread-locked-container { + grid-area: thread-locked-container; + border: 2px outset $light; +} + +.contain-svg { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.contain-svg > svg { + height: 50%; + width: 50%; +} + +.thread-info-container { + grid-area: thread-info-container; + background-color: $accent_color; + padding: 5px 20px; + border-top: 1px solid black; + border-bottom: 1px solid black; + display: flex; + flex-direction: column; +} + +.thread-info-post-preview { + overflow: hidden; + text-overflow: ellipsis; + display: inline; +} diff --git a/static/style.css b/static/style.css index f13b69a..44f4396 100644 --- a/static/style.css +++ b/static/style.css @@ -58,6 +58,8 @@ body { .thread-title { margin: 0; + font-size: 1.5rem; + font-weight: bold; } .post { @@ -153,6 +155,7 @@ body { } button, input[type=submit], .linkbutton { + display: inline-block; background-color: rgb(177, 206, 204.5); } button:hover, input[type=submit]:hover, .linkbutton:hover { @@ -192,6 +195,10 @@ input[type=file]::file-selector-button:active { background-color: rgb(166.6881496063, 178.0118503937, 177.4261417323); } +p { + margin: 15px 0; +} + .pagebutton { background-color: rgb(177, 206, 204.5); padding: 5px 5px; @@ -267,3 +274,51 @@ input[type=text], input[type=password] { min-width: 60px; padding-right: 15px; } + +.thread { + display: grid; + grid-template-columns: 96px 1.6fr 96px; + grid-template-rows: 1fr; + gap: 0px 0px; + grid-auto-flow: row; + min-height: 96px; + grid-template-areas: "thread-sticky-container thread-info-container thread-locked-container"; +} + +.thread-sticky-container { + grid-area: thread-sticky-container; + border: 2px outset rgb(217.26, 220.38, 213.42); +} + +.thread-locked-container { + grid-area: thread-locked-container; + border: 2px outset rgb(217.26, 220.38, 213.42); +} + +.contain-svg { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.contain-svg > svg { + height: 50%; + width: 50%; +} + +.thread-info-container { + grid-area: thread-info-container; + background-color: #c1ceb1; + padding: 5px 20px; + border-top: 1px solid black; + border-bottom: 1px solid black; + display: flex; + flex-direction: column; +} + +.thread-info-post-preview { + overflow: hidden; + text-overflow: ellipsis; + display: inline; +} diff --git a/svg-icons/sticky.etlua b/svg-icons/sticky.etlua new file mode 100644 index 0000000..26009f9 --- /dev/null +++ b/svg-icons/sticky.etlua @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/views/topics/topic.etlua b/views/topics/topic.etlua index f1c6e78..be3cd07 100644 --- a/views/topics/topic.etlua +++ b/views/topics/topic.etlua @@ -1,33 +1,69 @@ -

<%= topic.name %>

-

<%= topic.description %>

+<% render("views.common.topnav") -%> +<% if infobox then %> + <% render("views.common.infobox", infobox) %> +<% end %> + + + <% if #threads_list == 0 then %>

There are no threads in this topic.

<% else %> - + <% for _, thread in ipairs(threads_list) do %> + <% local is_stickied = ntob(thread.is_stickied) %> + <% local is_locked = ntob(thread.is_locked) %> +
+
+ <% if is_stickied then -%> + <% render("svg-icons.sticky") %> + Stickied + <% end -%> +
+
+ + "><%= thread.title %> + • + Started by ><%= thread.started_by %> + on <%= os.date("%c", thread.created_at) %> + + + Latest post by "><%= thread.latest_post_username %> + ">on <%= os.date("%c", thread.latest_post_created_at) %>: + + + <%- thread.latest_post_content %> + +
+
+ <% if is_locked then -%> + <% render("svg-icons.lock") %> + Locked + <% end -%> +
+
+ <% end %> <% end %> -<% if thread_create_error == ThreadCreateError.OK then %> ->New thread -<% elseif thread_create_error == ThreadCreateError.GUEST then %> -

Your account is still pending confirmation by a moderator. You are not able to create a new thread or post at this time.

-<% elseif thread_create_error == ThreadCreateError.LOGGED_OUT then %> -

Only logged in users can create threads. ">Sign up or ">log in to create a thread.

-<% else %> -

This topic is locked.

-<% end %> - -<% if me:is_mod() then %> -
- ">Edit topic -
"> - -

<%= "This topic is " .. (ntob(topic.is_locked) and "" or "un") .. "locked." %>

- "> -
-<% end %> +