From 70402b42c940828af91e77421ba078bdfbdb6936 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Fri, 5 Jan 2024 22:14:42 -0800 Subject: [PATCH] Go to Comment endpoint + notification fixes * Add endpoint /go/comment?id= that finds the right page that a comment can be seen on for the current user and redirects there. * Resolves issues with link discrepancies in comment notifications, if the recipient sees different page numbers depending on blocklist status. * Supports copyable permalinks to any comment on the site reliably. --- pkg/controller/comment/goto_comment.go | 75 ++++++++++++++++++++++++++ pkg/controller/comment/post_comment.go | 4 +- pkg/models/comment.go | 46 ++++++++++++++++ pkg/router/router.go | 3 ++ web/templates/forum/newest.html | 4 +- web/templates/forum/thread.html | 9 ++++ web/templates/photo/permalink.html | 9 ++++ 7 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 pkg/controller/comment/goto_comment.go diff --git a/pkg/controller/comment/goto_comment.go b/pkg/controller/comment/goto_comment.go new file mode 100644 index 0000000..dc58702 --- /dev/null +++ b/pkg/controller/comment/goto_comment.go @@ -0,0 +1,75 @@ +package comment + +import ( + "fmt" + "net/http" + "strconv" + + "code.nonshy.com/nonshy/website/pkg/config" + "code.nonshy.com/nonshy/website/pkg/models" + "code.nonshy.com/nonshy/website/pkg/session" + "code.nonshy.com/nonshy/website/pkg/templates" +) + +// GoToComment finds the correct link to view a comment. +func GoToComment() http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Query params. + var ( + commentID uint64 + ) + + // Parse the ID param. + if idStr := r.FormValue("id"); idStr == "" { + session.FlashError(w, r, "Comment ID required.") + templates.Redirect(w, "/") + return + } else { + if idInt, err := strconv.Atoi(idStr); err != nil { + session.FlashError(w, r, "Comment ID invalid.") + templates.Redirect(w, "/") + return + } else { + commentID = uint64(idInt) + } + } + + // Get the current user. + currentUser, err := session.CurrentUser(r) + if err != nil { + session.FlashError(w, r, "Couldn't get current user: %s", err) + templates.Redirect(w, "/") + return + } + + // Locate this comment. + comment, err := models.GetComment(commentID) + if err != nil { + session.FlashError(w, r, "Couldn't find that comment: %s", err) + templates.Redirect(w, "/") + return + } + + // Where is this comment at? + switch comment.TableName { + case "threads": + // A forum thread, find what page it is on. + page, err := models.FindPageByComment(currentUser, comment, config.PageSizeThreadList) + if err != nil { + session.FlashError(w, r, "Couldn't find that comment, but bringing you to the forum thread instead.") + page = 1 + } + + templates.Redirect(w, fmt.Sprintf("/forum/thread/%d?page=%d#p%d", comment.TableID, page, comment.ID)) + return + case "photos": + // A photo comment thread, easy: only one page. + templates.Redirect(w, fmt.Sprintf("/photo/view?id=%d#p%d", comment.TableID, comment.ID)) + return + default: + session.FlashError(w, r, "Unknown type of comment.") + } + + templates.Redirect(w, "/") + }) +} diff --git a/pkg/controller/comment/post_comment.go b/pkg/controller/comment/post_comment.go index e1e6e0d..78e925d 100644 --- a/pkg/controller/comment/post_comment.go +++ b/pkg/controller/comment/post_comment.go @@ -177,7 +177,7 @@ func PostComment() http.HandlerFunc { TableName: comment.TableName, TableID: comment.TableID, Message: message, - Link: fmt.Sprintf("%s#p%d", fromURL, comment.ID), + Link: fmt.Sprintf("/go/comment?id=%d", comment.ID), } if err := models.CreateNotification(notif); err != nil { log.Error("Couldn't create Comment notification: %s", err) @@ -201,7 +201,7 @@ func PostComment() http.HandlerFunc { TableName: comment.TableName, TableID: comment.TableID, Message: message, - Link: fromURL, + Link: fmt.Sprintf("/go/comment?id=%d", comment.ID), } if err := models.CreateNotification(notif); err != nil { log.Error("Couldn't create Comment notification for subscriber %d: %s", userID, err) diff --git a/pkg/models/comment.go b/pkg/models/comment.go index 982a9c7..cf9caba 100644 --- a/pkg/models/comment.go +++ b/pkg/models/comment.go @@ -1,6 +1,8 @@ package models import ( + "errors" + "math" "strings" "time" @@ -108,6 +110,50 @@ func PaginateComments(user *User, tableName string, tableID uint64, pager *Pagin return cs, result.Error } +// FindPageByComment finds out what page a comment ID exists on for the current user, taking into +// account their block lists and comment visibility. +// +// Note: the comments are assumed ordered by created_at asc. +func FindPageByComment(user *User, comment *Comment, pageSize int) (int, error) { + var ( + allCommentIDs []uint64 + blockedUserIDs = BlockedUserIDs(user) + wheres = []string{} + placeholders = []interface{}{} + ) + + // Get the complete set of comment IDs that this comment is on a thread of. + wheres = append(wheres, "table_name = ? AND table_id = ?") + placeholders = append(placeholders, comment.TableName, comment.TableID) + + if len(blockedUserIDs) > 0 { + wheres = append(wheres, "user_id NOT IN ?") + placeholders = append(placeholders, blockedUserIDs) + } + + result := DB.Table( + "comments", + ).Select( + "id", + ).Where( + strings.Join(wheres, " AND "), + placeholders..., + ).Order("created_at asc").Scan(&allCommentIDs) + if result.Error != nil { + return 0, result.Error + } + + // Scan the comment thread to find it. + for i, cid := range allCommentIDs { + if cid == comment.ID { + var page = int(math.Ceil(float64(i) / float64(pageSize))) + return page, nil + } + } + + return -1, errors.New("comment not visible to current user") +} + // ListComments returns a complete set of comments without paging. func ListComments(user *User, tableName string, tableID uint64) ([]*Comment, error) { var ( diff --git a/pkg/router/router.go b/pkg/router/router.go index 9ae9618..d21c3f3 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -112,6 +112,9 @@ func New() http.Handler { mux.Handle("/v1/barertc/report", barertc.Report()) mux.Handle("/v1/barertc/profile", barertc.Profile()) + // Redirect endpoints. + mux.Handle("/go/comment", middleware.LoginRequired(comment.GoToComment())) + // Static files. mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(config.StaticPath)))) diff --git a/web/templates/forum/newest.html b/web/templates/forum/newest.html index ab85f02..9316698 100644 --- a/web/templates/forum/newest.html +++ b/web/templates/forum/newest.html @@ -105,7 +105,7 @@
- + {{TrimEllipses .Comment.Message 256}} diff --git a/web/templates/forum/thread.html b/web/templates/forum/thread.html index b1564b5..75b1aa5 100644 --- a/web/templates/forum/thread.html +++ b/web/templates/forum/thread.html @@ -298,6 +298,15 @@
{{end}} + + +
+ + + +
{{if $Root.CurrentUser.IsAdmin}} diff --git a/web/templates/photo/permalink.html b/web/templates/photo/permalink.html index 77d70de..fe3763f 100644 --- a/web/templates/photo/permalink.html +++ b/web/templates/photo/permalink.html @@ -289,6 +289,15 @@ {{end}} + + +
+ + + +