From c83e96b8a740a44f565068d06c4f12eef486b7e5 Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:26:16 +0200 Subject: [PATCH] [chore/frontend] Tweak threading a bit, inform about hidden replies (#3097) * [chore/frontend] Tweak threading a bit, inform about hidden replies * whoops * round off bottom of replies col-header if no replies visible --- internal/api/model/status.go | 13 ++- internal/api/model/statuscontext.go | 17 +-- internal/processing/status/context.go | 106 ++++++++---------- internal/typeutils/internaltofrontend_test.go | 2 + web/source/css/thread.css | 9 ++ web/template/thread.tmpl | 45 ++------ 6 files changed, 90 insertions(+), 102 deletions(-) diff --git a/internal/api/model/status.go b/internal/api/model/status.go index 00be868f1..a9c668565 100644 --- a/internal/api/model/status.go +++ b/internal/api/model/status.go @@ -126,8 +126,17 @@ type WebStatus struct { // display this status in the web view. Indent int - // This status is the first status after - // the "main" thread, so it and everything + // This status is the last visible status + // in the main thread, so everything below + // can be considered "replies". + ThreadLastMain bool + + // This status is the one around which + // the thread context was constructed. + ThreadContextStatus bool + + // This status is the first visibile status + // after the "main" thread, so it and everything // below it can be considered "replies". ThreadFirstReply bool } diff --git a/internal/api/model/statuscontext.go b/internal/api/model/statuscontext.go index 205672dc8..f2519c0a3 100644 --- a/internal/api/model/statuscontext.go +++ b/internal/api/model/statuscontext.go @@ -29,15 +29,16 @@ type ThreadContext struct { } type WebThreadContext struct { - // Parents in the thread. - Ancestors []*WebStatus `json:"ancestors"` + // Status around which this + // thread ctx was constructed. + Status *WebStatus - // Children in the thread. - Descendants []*WebStatus `json:"descendants"` - - // The status around which the ancestors - // + descendants context was constructed. - Status *WebStatus `json:"-"` + // Ordered slice of statuses + // for rendering in template. + // + // Includes ancestors, target + // status, and descendants. + Statuses []*WebStatus // Total length of // the main thread. diff --git a/internal/processing/status/context.go b/internal/processing/status/context.go index 4271bd233..013cf4827 100644 --- a/internal/processing/status/context.go +++ b/internal/processing/status/context.go @@ -400,8 +400,7 @@ func (p *Processor) WebContextGet( // Start preparing web context. wCtx := &apimodel.WebThreadContext{ - Ancestors: make([]*apimodel.WebStatus, 0, len(iCtx.ancestors)), - Descendants: make([]*apimodel.WebStatus, 0, len(iCtx.descendants)), + Statuses: make([]*apimodel.WebStatus, 0, len(wholeThread)), } var ( @@ -415,72 +414,70 @@ func (p *Processor) WebContextGet( // ie., who created first post in the thread. contextAcctID = wholeThread[0].AccountID - // Position of target status in wholeThread, - // we put it on top of ancestors. - targetStatusIdx = len(iCtx.ancestors) - - // Position from which we should add - // to descendants and not to ancestors. - descendantsIdx = targetStatusIdx + 1 - // Whether we've reached end of "main" // thread and are now looking at replies. inReplies bool - // Index in wholeThread where - // the "main" thread ends. + // Index in wholeThread + // where replies begin. firstReplyIdx int // We should mark the next **VISIBLE** // reply as the first reply. - markNextVisibleAsReply bool + markNextVisibleAsFirstReply bool ) for idx, status := range wholeThread { if !inReplies { - // Haven't reached end - // of "main" thread yet. - // - // First post in wholeThread can't - // be a self reply, so ignore it. - // - // That aside, first non-self-reply - // in wholeThread means the "main" - // thread is now over. - if idx != 0 && !isSelfReply(status, contextAcctID) { - // Jot some stuff down. - firstReplyIdx = idx + // Check if we've reached replies + // by looking for the first status + // that's not a self-reply, ie., + // not a post in the "main" thread. + switch { + case idx == 0: + // First post in wholeThread can't + // be a self reply anyway because + // it (very likely) doesn't reply + // to anything, so ignore it. + + case !isSelfReply(status, contextAcctID): + // This is not a self-reply, which + // means it's a reply from another + // account. So, replies start here. inReplies = true - markNextVisibleAsReply = true + firstReplyIdx = idx + markNextVisibleAsFirstReply = true } } // Ensure status is actually - // visible to just anyone. + // visible to just anyone, and + // hide / don't include it if not. v, err := p.filter.StatusVisible(ctx, nil, status) if err != nil || !v { - // Skip this one. if !inReplies { + // Main thread entry hidden. wCtx.ThreadHidden++ } else { + // Reply hidden. wCtx.ThreadRepliesHidden++ } continue } - // Prepare status to add to thread context. - apiStatus, err := p.converter.StatusToWebStatus(ctx, status) + // Prepare visible status to add to thread context. + webStatus, err := p.converter.StatusToWebStatus(ctx, status) if err != nil { continue } - if markNextVisibleAsReply { + if markNextVisibleAsFirstReply { // This is the first visible // "reply / comment", so the // little "x amount of replies" // header should go above this. - apiStatus.ThreadFirstReply = true - markNextVisibleAsReply = false + webStatus.ThreadFirstReply = true + markNextVisibleAsFirstReply = false } // If this is a reply, work out the indent of @@ -491,59 +488,47 @@ func (p *Processor) WebContextGet( case !ok: // No parent with // indent, start at 0. - apiStatus.Indent = 0 + webStatus.Indent = 0 case isSelfReply(status, status.AccountID): // Self reply, so indent at same // level as own replied-to status. - apiStatus.Indent = parentIndent + webStatus.Indent = parentIndent case parentIndent == 5: // Already indented as far as we // can go to keep things readable // on thin screens, so just keep // parent's indent. - apiStatus.Indent = parentIndent + webStatus.Indent = parentIndent default: // Reply to someone else who's // indented, but not to TO THE MAX. // Indent by another one. - apiStatus.Indent = parentIndent + 1 + webStatus.Indent = parentIndent + 1 } // Store the indent for this status. - statusIndents[status.ID] = apiStatus.Indent + statusIndents[status.ID] = webStatus.Indent } - switch { - case idx == targetStatusIdx: - // This is the target status itself. - wCtx.Status = apiStatus - - case idx < descendantsIdx: - // Haven't reached descendants yet, - // so this must be an ancestor. - wCtx.Ancestors = append( - wCtx.Ancestors, - apiStatus, - ) - - default: - // We're in descendants town now. - wCtx.Descendants = append( - wCtx.Descendants, - apiStatus, - ) + if webStatus.ID == targetStatusID { + // This is the og + // thread context status. + webStatus.ThreadContextStatus = true + wCtx.Status = webStatus } + + wCtx.Statuses = append(wCtx.Statuses, webStatus) } // Now we've gone through the whole // thread, we can add some additional info. // Length of the "main" thread. If there are - // replies then it's up to where the replies - // start, otherwise it's the whole thing. + // visible replies then it's up to where the + // replies start, else it's the whole thing. if inReplies { wCtx.ThreadLength = firstReplyIdx } else { @@ -553,6 +538,9 @@ func (p *Processor) WebContextGet( // Jot down number of hidden posts so template doesn't have to do it. wCtx.ThreadShown = wCtx.ThreadLength - wCtx.ThreadHidden + // Mark the last "main" visible status. + wCtx.Statuses[wCtx.ThreadShown-1].ThreadLastMain = true + // Number of replies is equal to number // of statuses in the thread that aren't // part of the "main" thread. diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index 9ad5d2c08..7bbca8ae7 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1014,6 +1014,8 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() { "PollOptions": null, "Local": false, "Indent": 0, + "ThreadLastMain": false, + "ThreadContextStatus": false, "ThreadFirstReply": false }`, string(b)) } diff --git a/web/source/css/thread.css b/web/source/css/thread.css index 4f4e3e938..e2e6ae6b7 100644 --- a/web/source/css/thread.css +++ b/web/source/css/thread.css @@ -42,6 +42,15 @@ h2 { margin-right: auto; } + + &.replies.hidden-only { + /* + No visible replies below this column + header, so round off the bottom. + */ + border-bottom-left-radius: $br; + border-bottom-right-radius: $br; + } } .status { diff --git a/web/template/thread.tmpl b/web/template/thread.tmpl index 8b4aa2248..0cf9ecb05 100644 --- a/web/template/thread.tmpl +++ b/web/template/thread.tmpl @@ -20,7 +20,7 @@ {{- define "repliesSummary" -}} {{- if .context.ThreadRepliesShown -}} {{- if .context.ThreadRepliesHidden -}} - {{- if eq .context.ThreadReplies 1 -}} + {{- if eq .context.ThreadRepliesShown 1 -}} {{- /* Some replies are hidden. */ -}} {{ .context.ThreadRepliesShown }} visible reply {{- else if gt .context.ThreadRepliesShown 1 -}} @@ -35,6 +35,8 @@ {{ .context.ThreadReplies }} replies {{- end -}} {{- end -}} + {{- else -}} + {{- .context.ThreadRepliesHidden }} {{ if eq .context.ThreadRepliesHidden 1 }}reply{{ else }}replies{{ end }} hidden or not public {{- end -}} {{- end -}} @@ -60,7 +62,7 @@ {{- with . }}
-
+

{{- template "repliesSummary" . -}}

back to top
@@ -77,41 +79,18 @@ {{- end }}
- {{- range $thisStatus := .context.Ancestors }} - {{- if $thisStatus.ThreadFirstReply }} + {{- range $status := .context.Statuses }} +
+ {{- include "status.tmpl" $status | indent 3 }} +
+ {{- if and $status.ThreadLastMain $.context.ThreadReplies }} {{- include "repliesStart" $ | indent 1 }} {{- end }} -
- {{- include "status.tmpl" $thisStatus | indent 3 }} -
{{- end }} - {{- with $thisStatus := .context.Status }} - {{- if $thisStatus.ThreadFirstReply }} - {{- include "repliesStart" $ | indent 1 }} - {{- end }} -
- {{- include "status.tmpl" $thisStatus | indent 3 }} -
- {{- end }} - - {{- range $thisStatus := .context.Descendants }} - {{- if $thisStatus.ThreadFirstReply }} - {{- include "repliesStart" $ | indent 1 }} - {{- end }} -
- {{- include "status.tmpl" $thisStatus | indent 3 }} -
- {{- end }} {{- if .context.ThreadReplies }}
{{- end }}