{ post.Body }
+{ thread.Subject } -
+ + { posts[0].CreatedAt.Format("01/02/2006") } + { posts[0].CreatedAt.Format("15:04") } + No. { strconv.Itoa(posts[0].Id) } +{ posts[0].Body }
+diff --git a/internal/database/handlers.go b/internal/database/handlers.go index c35cfde..6cce218 100644 --- a/internal/database/handlers.go +++ b/internal/database/handlers.go @@ -89,7 +89,7 @@ func GetThreadPosts(db *sql.DB, threadId int) ([]Post, error) { return result, rows.Err() } -func PutBoardThread(db *sql.DB, boardSlug string, subject string, body string) error { +func PutThread(db *sql.DB, boardSlug string, subject string, body string) error { tx, err := db.Begin() if err != nil { return err @@ -117,3 +117,12 @@ func PutBoardThread(db *sql.DB, boardSlug string, subject string, body string) e return nil } + +func PutPost(db *sql.DB, threadId int, body string) error { + _, err := db.Exec(`INSERT INTO posts (thread_id, body) VALUES (?, ?)`, threadId, body) + if err != nil { + return err + } + + return nil +} diff --git a/internal/database/seed.sql b/internal/database/seed.sql index 7565f53..8ca9818 100644 --- a/internal/database/seed.sql +++ b/internal/database/seed.sql @@ -50,3 +50,7 @@ INSERT INTO boards (slug, name, tag) VALUES (SELECT id FROM threads WHERE subject LIKE 'Welcome to /comfy/.%'), 'Post comfy. Be nice.' ); + INSERT INTO posts (thread_id, body) VALUES ( + (SELECT id FROM threads WHERE subject LIKE 'Welcome to /comfy/.%'), + 'Henlo fren.' + ); diff --git a/web/main.go b/web/main.go index c6bb851..5ff750b 100644 --- a/web/main.go +++ b/web/main.go @@ -100,21 +100,57 @@ func main() { return } + if len(posts) == 0 { + http.Error(w, "Malformed thread", http.StatusInternalServerError) + log.Printf("Thread %d has no posts", thread.Id) + return + } + views.Thread(board, thread, posts).Render(r.Context(), w) }) + r.Post("/{slug}/threads/{threadId}", func(w http.ResponseWriter, r *http.Request) { + // slug := chi.URLParam(r, "slug") + // TODO: use relative thread_nums (see issue #1) + threadIdStr := chi.URLParam(r, "threadId") + threadId, err := strconv.Atoi(threadIdStr) + if err != nil { + http.Error(w, "Invalid thread id", http.StatusBadRequest) + return + } + + if err := r.ParseForm(); err != nil { + http.Error(w, "Bad form data", http.StatusBadRequest) + log.Printf("ParseForm: %v", err) + return + } + + body := r.FormValue("body") + + if err := database.PutPost(db, threadId, body); err != nil { + http.Error(w, "Failed to create post", http.StatusInternalServerError) + log.Printf("PutPost: %v", err) + return + } + }) + r.Post("/{slug}/threads", func(w http.ResponseWriter, r *http.Request) { slug := chi.URLParam(r, "slug") if err := r.ParseForm(); err != nil { http.Error(w, "Bad form data", http.StatusBadRequest) + log.Printf("ParseForm: %v", err) return } subject := r.FormValue("subject") body := r.FormValue("body") - database.PutBoardThread(db, slug, subject, body) + if err := database.PutThread(db, slug, subject, body); err != nil { + http.Error(w, "Failed to create thread", http.StatusInternalServerError) + log.Printf("PutThread: %v", err) + return + } }) // ----------------- @@ -143,7 +179,7 @@ func main() { if len(posts) == 0 { http.Error(w, "Malformed thread", http.StatusInternalServerError) - log.Printf("GetThreadPosts: %v", err) + log.Printf("Thread %d has no posts", thread.Id) return } diff --git a/web/static/index.css b/web/static/index.css index ff2129a..702df29 100644 --- a/web/static/index.css +++ b/web/static/index.css @@ -11,7 +11,6 @@ body { margin-bottom: 50px; } - /* BOARD LIST */ #boardList { @@ -28,7 +27,7 @@ body { color: #ff0000; } -/* BOARDS */ +/* BOARD */ .board-header { text-align: center; @@ -51,11 +50,7 @@ body { width: 300px; } -.new-thread-container { - width: max-content; - margin: auto; - margin-top: 16px; -} +/* THREAD CATALOG */ #catalog { display: grid; @@ -87,6 +82,9 @@ body { cursor: pointer; } + +/* NEW THREAD BUTTON/FORM */ + #newThreadBtn { margin-top: 12px; background: none; @@ -102,11 +100,18 @@ body { color: #ff0000 } +#newPostForm button, #newThreadForm button { display: block; margin-left: auto; } +.new-thread-container { + width: max-content; + margin: auto; + margin-top: 16px; +} + .new-thread-form-field th { border: 1px solid black; text-align: left; @@ -122,6 +127,43 @@ body { margin: 2px; } + +/* THREAD/POSTS */ + +.thread { + margin: 2rem; +} + +.thread-subject { + font-weight: bold; + color: #b294bb; + margin-right: 5px; +} + +.post-header { + display: flex; + margin-bottom: 10px; +} + +.post-header span { + margin-right: 5px; +} + +.post-author { + color: #13A82A; +} + +.op-post { + margin-bottom: 10px; +} + +.post { + padding: 10px; + background-color: #D6DAF0; + border: 1px solid #282a2e; + margin-bottom: 10px; +} + /* GENERAL LAYOUT */ .container { diff --git a/web/views/thread.templ b/web/views/thread.templ index 35ec88c..015f571 100644 --- a/web/views/thread.templ +++ b/web/views/thread.templ @@ -1,12 +1,54 @@ package views import ( +"fmt" "github.com/dominicf2001/comfychan/internal/database" "github.com/dominicf2001/comfychan/web/views/shared" +"strconv" ) templ Thread(board database.Board, thread database.Thread, posts []database.Post) { @shared.Layout() { @BoardHeader(board) +
{ posts[0].Body }
+{ post.Body }
+