From 8085e092bc3e516a4f9c1936ba2a1358d632f47a Mon Sep 17 00:00:00 2001 From: Noah Date: Sat, 10 Sep 2022 12:09:46 -0700 Subject: [PATCH] Forum Reply Enhancements + Better Pagers * Enhance user experience replying to a forum thread. An inline reply textarea is added to page footers, "Quote" buttons on posts will quote the markdown source and focus the reply textarea, and "Reply" buttons will put an "@ mention" and focus the reply textarea. Users with scripts disabled will still be sent to the regular reply page as before. * Improve all pagers by adding a "QueryPlus" template function that merges the page number with other current query parameters. * Fix private profile picture avatars not displaying in your Notifications for profile pics you're allowed to see. --- pkg/controller/account/dashboard.go | 1 + pkg/models/user_relationship.go | 17 +++ pkg/templates/template_funcs.go | 164 +++++++++++++++---------- web/templates/account/block_list.html | 6 +- web/templates/account/dashboard.html | 2 +- web/templates/admin/certification.html | 6 +- web/templates/admin/feedback.html | 6 +- web/templates/forum/admin.html | 6 +- web/templates/forum/board_index.html | 6 +- web/templates/forum/newest.html | 6 +- web/templates/forum/thread.html | 96 +++++++++++++-- web/templates/friend/friends.html | 6 +- web/templates/inbox/inbox.html | 8 +- web/templates/photo/gallery.html | 6 +- web/templates/photo/private.html | 6 +- 15 files changed, 238 insertions(+), 104 deletions(-) diff --git a/pkg/controller/account/dashboard.go b/pkg/controller/account/dashboard.go index b6d5675..c3eefd4 100644 --- a/pkg/controller/account/dashboard.go +++ b/pkg/controller/account/dashboard.go @@ -41,6 +41,7 @@ func Dashboard() http.HandlerFunc { // Map our notifications. notifMap := models.MapNotifications(notifs) + models.SetUserRelationshipsInNotifications(currentUser, notifs) var vars = map[string]interface{}{ "Notifications": notifs, diff --git a/pkg/models/user_relationship.go b/pkg/models/user_relationship.go index 359974c..0ff49d6 100644 --- a/pkg/models/user_relationship.go +++ b/pkg/models/user_relationship.go @@ -85,3 +85,20 @@ func SetUserRelationshipsInThreads(user *User, threads []*Thread) { // Inject relationships into those comments' users. SetUserRelationshipsInComments(user, comments) } + +// SetUserRelationshipsInNotifications takes a set of Notifications and sets relationship booleans on their AboutUsers. +func SetUserRelationshipsInNotifications(user *User, notifications []*Notification) { + // Gather and map the users. + var ( + users = []*User{} + userMap = map[uint64]*User{} + ) + + for _, n := range notifications { + users = append(users, &n.AboutUser) + userMap[n.AboutUser.ID] = &n.AboutUser + } + + // Inject relationships. + SetUserRelationships(user, users) +} diff --git a/pkg/templates/template_funcs.go b/pkg/templates/template_funcs.go index a91bddc..9fad04a 100644 --- a/pkg/templates/template_funcs.go +++ b/pkg/templates/template_funcs.go @@ -15,6 +15,11 @@ import ( "code.nonshy.com/nonshy/website/pkg/utility" ) +// Generics +type Number interface { + int | int64 | uint64 | float32 | float64 +} + // TemplateFuncs available to all pages. func TemplateFuncs(r *http.Request) template.FuncMap { return template.FuncMap{ @@ -31,68 +36,15 @@ func TemplateFuncs(r *http.Request) template.FuncMap { `shy`, )) }, - "Pluralize64": func(count int64, labels ...string) string { - if len(labels) < 2 { - labels = []string{"", "s"} - } - - if count == 1 { - return labels[0] - } else { - return labels[1] - } - }, - "PluralizeU64": func(count uint64, labels ...string) string { - if len(labels) < 2 { - labels = []string{"", "s"} - } - - if count == 1 { - return labels[0] - } else { - return labels[1] - } - }, - "Pluralize": func(count int, labels ...string) string { - if len(labels) < 2 { - labels = []string{"", "s"} - } - - if count == 1 { - return labels[0] - } else { - return labels[1] - } - }, - "Substring": func(value string, n int) string { - if n > len(value) { - return value - } - return value[:n] - }, - "TrimEllipses": func(value string, n int) string { - if n > len(value) { - return value - } - return value[:n] + "…" - }, - "IterRange": func(start, n int) []int { - var result = []int{} - for i := start; i <= n; i++ { - result = append(result, i) - } - return result - }, - "SubtractInt": func(a, b int) int { - return a - b - }, - "UrlEncode": func(values ...interface{}) string { - var result string - for _, value := range values { - result += url.QueryEscape(fmt.Sprintf("%v", value)) - } - return result - }, + "Pluralize": Pluralize[int], + "Pluralize64": Pluralize[int64], + "PluralizeU64": Pluralize[uint64], + "Substring": Substring, + "TrimEllipses": TrimEllipses, + "IterRange": IterRange, + "SubtractInt": SubtractInt, + "UrlEncode": UrlEncode, + "QueryPlus": QueryPlus(r), } } @@ -124,3 +76,91 @@ func SincePrettyCoarse() func(time.Time) template.HTML { func ToMarkdown(input string) template.HTML { return template.HTML(markdown.Render(input)) } + +// Pluralize text based on a quantity number. Provide up to 2 labels for the +// singular and plural cases, or the defaults are "", "s" +func Pluralize[V Number](count V, labels ...string) string { + if len(labels) < 2 { + labels = []string{"", "s"} + } + + if count == 1 { + return labels[0] + } + return labels[1] +} + +// Substring safely returns the first N characters of a string. +func Substring(value string, n int) string { + if n > len(value) { + return value + } + return value[:n] +} + +// TrimEllipses is like Substring but will add an ellipses if truncated. +func TrimEllipses(value string, n int) string { + if n > len(value) { + return value + } + return value[:n] + "…" +} + +// IterRange returns a list of integers useful for pagination. +func IterRange(start, n int) []int { + var result = []int{} + for i := start; i <= n; i++ { + result = append(result, i) + } + return result +} + +// SubtractInt subtracts two numbers. +func SubtractInt(a, b int) int { + return a - b +} + +// UrlEncode escapes a series of values (joined with no delimiter) +func UrlEncode(values ...interface{}) string { + var result string + for _, value := range values { + result += url.QueryEscape(fmt.Sprintf("%v", value)) + } + return result +} + +// QueryPlus takes the current request's query parameters and upserts them with new values. +// +// Use it like: {{QueryPlus "page" .NextPage}} +// +// Returns the query string sans the ? prefix, like "key1=value1&key2=value2" +func QueryPlus(r *http.Request) func(...interface{}) template.URL { + return func(upsert ...interface{}) template.URL { + // Get current parameters. + var params = r.Form + + // Mix in the incoming fields. + for i := 0; i < len(upsert); i += 2 { + var ( + key = fmt.Sprintf("%v", upsert[i]) + value interface{} + ) + if len(upsert) > i { + value = upsert[i+1] + } + + params[key] = []string{fmt.Sprintf("%v", value)} + } + + // Assemble and return the query string. + var parts = []string{} + for k, vs := range params { + for _, v := range vs { + parts = append(parts, + fmt.Sprintf("%s=%s", url.QueryEscape(k), url.QueryEscape(v)), + ) + } + } + return template.URL(strings.Join(parts, "&")) + } +} diff --git a/web/templates/account/block_list.html b/web/templates/account/block_list.html index 8555009..34f3485 100644 --- a/web/templates/account/block_list.html +++ b/web/templates/account/block_list.html @@ -20,16 +20,16 @@
diff --git a/web/templates/admin/certification.html b/web/templates/admin/certification.html index d3604b1..fe02f78 100644 --- a/web/templates/admin/certification.html +++ b/web/templates/admin/certification.html @@ -59,15 +59,15 @@ {{if .Pager}}