forum threads

This commit is contained in:
Sky Johnson 2025-08-22 17:52:16 -05:00
parent c147df63dc
commit 340d4cf6e8
7 changed files with 179 additions and 10 deletions

View File

@ -272,6 +272,33 @@ form.standard {
background-color: rgba(0, 0, 0, 0.6);
border-color: black;
}
&::placeholder {
color: rgba(255, 255, 255, 0.6);
}
}
textarea.forum-text {
appearance: none;
outline: none;
border: 1px solid transparent;
padding: 0.25rem 0.25rem;
background-color: rgba(0, 0, 0, 0.4);
color: white;
min-height: 200px;
&:hover {
background-color: rgba(0, 0, 0, 0.5);
}
&:focus {
background-color: rgba(0, 0, 0, 0.6);
border-color: black;
}
&::placeholder {
color: rgba(255, 255, 255, 0.6);
}
}
}
@ -287,6 +314,10 @@ form.standard {
margin-bottom: 0.25rem;
}
.w-full {
width: 100%;
}
div.town {
& > section:not(:last-child) {
margin-bottom: 2rem;
@ -494,3 +525,9 @@ div#battle-window {
color: white;
}
}
div.thread-title {
display: flex;
align-items: center;
justify-content: space-between;
}

Binary file not shown.

View File

@ -83,13 +83,13 @@ func Find(id int) (*Forum, error) {
func All() ([]*Forum, error) {
var forums []*Forum
err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC")
err := database.Select(&forums, "SELECT * FROM forum ORDER BY last_post DESC, id DESC")
return forums, err
}
func Threads() ([]*Forum, error) {
var forums []*Forum
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY newpostdate DESC, id DESC")
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY last_post DESC, id DESC")
return forums, err
}
@ -97,37 +97,37 @@ func ByParent(parentID int) ([]*Forum, error) {
var forums []*Forum
if parentID > 0 {
// Replies sorted chronologically
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY postdate ASC, id ASC", parentID)
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY posted ASC, id ASC", parentID)
return forums, err
} else {
// Threads sorted by last activity
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY newpostdate DESC, id DESC", parentID)
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY last_post DESC, id DESC", parentID)
return forums, err
}
}
func ByAuthor(authorID int) ([]*Forum, error) {
var forums []*Forum
err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY postdate DESC, id DESC", authorID)
err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY posted DESC, id DESC", authorID)
return forums, err
}
func Recent(limit int) ([]*Forum, error) {
var forums []*Forum
err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC LIMIT %d", limit)
err := database.Select(&forums, "SELECT * FROM forum ORDER BY last_post DESC, id DESC LIMIT %d", limit)
return forums, err
}
func Search(term string) ([]*Forum, error) {
var forums []*Forum
searchTerm := "%" + term + "%"
err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY newpostdate DESC, id DESC", searchTerm, searchTerm)
err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY last_post DESC, id DESC", searchTerm, searchTerm)
return forums, err
}
func Since(since int64) ([]*Forum, error) {
var forums []*Forum
err := database.Select(&forums, "SELECT * FROM forum WHERE newpostdate >= %d ORDER BY newpostdate DESC, id DESC", since)
err := database.Select(&forums, "SELECT * FROM forum WHERE last_post >= %d ORDER BY last_post DESC, id DESC", since)
return forums, err
}

View File

@ -2,6 +2,11 @@ package routes
import (
"dk/internal/components"
"dk/internal/database"
"dk/internal/models/forum"
"dk/internal/models/users"
"fmt"
"strings"
sushi "git.sharkk.net/Sharkk/Sushi"
"git.sharkk.net/Sharkk/Sushi/auth"
@ -11,8 +16,77 @@ func RegisterForumRoutes(app *sushi.App) {
authed := app.Group("/forum")
authed.Use(auth.RequireAuth())
authed.Get("/", index)
authed.Get("/new", showNew)
authed.Post("/new", new)
authed.Get("/:id", showThread)
}
func index(ctx sushi.Ctx) {
components.RenderPage(ctx, "Forum", "forum/index.html", map[string]any{})
threads, err := forum.Threads()
if err != nil {
threads = make([]*forum.Forum, 0)
}
fmt.Printf("\nFound %d threads\n", len(threads))
components.RenderPage(ctx, "Forum", "forum/index.html", map[string]any{
"threads": threads,
})
}
func showNew(ctx sushi.Ctx) {
components.RenderPage(ctx, "New Forum Thread", "forum/new.html", map[string]any{})
}
func new(ctx sushi.Ctx) {
sess := ctx.GetCurrentSession()
title := strings.TrimSpace(ctx.Form("title").String())
content := strings.TrimSpace(ctx.Form("content").String())
if title == "" {
sess.SetFlash("error", "Thread title cannot be empty")
ctx.Redirect("/forum/new")
return
}
if content == "" {
sess.SetFlash("error", "Thread content cannot be empty")
ctx.Redirect("/forum/new")
return
}
user := ctx.GetCurrentUser().(*users.User)
thread := forum.New()
thread.Author = user.ID
thread.Title = title
thread.Content = content
database.Transaction(func() error {
return thread.Insert()
})
ctx.Redirect(fmt.Sprintf("/forum/%d", thread.ID))
}
func showThread(ctx sushi.Ctx) {
sess := ctx.GetCurrentSession()
id := ctx.Param("id").Int()
thread, err := forum.Find(id)
if err != nil {
sess.SetFlash("error", fmt.Sprintf("Forum thread %d not found", id))
ctx.Redirect("/forum")
return
}
if thread.Parent != 0 {
sess.SetFlash("error", fmt.Sprintf("Forum post %d is not a thread", id))
ctx.Redirect("/forum")
return
}
components.RenderPage(ctx, thread.Title, "forum/thread.html", map[string]any{
"thread": thread,
})
}

View File

@ -1,5 +1,27 @@
{include "layout.html"}
{block "content"}
FORUM PAGE
<div class="thread-title mb-05">
<h2>Forum</h2>
<div>
<a href="/forum/new"><button class="btn btn-primary">New Thread</button></a>
</div>
</div>
{if #threads > 0}
<table>
<tbody>
{for thread in threads}
<tr>
<td><a href="/forum/{thread.ID}">{thread.Title}</a></td>
<td>{thread.Replies} Replies</td>
</tr>
{/for}
</tbody>
</table>
{else}
No threads!
{/if}
{/block}

19
templates/forum/new.html Normal file
View File

@ -0,0 +1,19 @@
{include "layout.html"}
{block "content"}
<h2 class="mb-1">Forum</h2>
<form action="/forum/new" method="post" class="standard">
{csrf}
<div class="mb-1">
<div class="mb-025"><input type="text" class="text w-full" placeholder="Thread title" name="title"></div>
<textarea class="forum-text w-full" placeholder="Type your message here!" name="content"></textarea>
</div>
<div>
<button type="submit" class="btn btn-primary">Post</button>
<a href="/forum"><button class="btn">Back to Forums</button></a>
</div>
</form>
{/block}

View File

@ -0,0 +1,17 @@
{include "layout.html"}
{block "content"}
<div class="thread-title mb-05">
<h2>{thread.Title}</h2>
<div>
<a href="/forum"><button class="btn">Back</button></a>
<a href="/forum/{thread.ID}/reply"><button class="btn btn-primary">Reply</button></a>
</div>
</div>
<p>
{thread.Content}
</p>
{/block}