mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-24 20:56:39 +00:00
[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
This commit is contained in:
parent
bbbdf01213
commit
c83e96b8a7
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
// 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, 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
|
||||
// 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.
|
||||
|
|
|
@ -1014,6 +1014,8 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
|
|||
"PollOptions": null,
|
||||
"Local": false,
|
||||
"Indent": 0,
|
||||
"ThreadLastMain": false,
|
||||
"ThreadContextStatus": false,
|
||||
"ThreadFirstReply": false
|
||||
}`, string(b))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 . }}
|
||||
</section>
|
||||
<section class="thread thread-replies" aria-labelledby="replies" open>
|
||||
<div class="col-header replies">
|
||||
<div class="col-header replies{{- if not .context.ThreadRepliesShown }} hidden-only{{- end -}}">
|
||||
<h2 id="replies">{{- template "repliesSummary" . -}}</h2>
|
||||
<a href="#thread-summary">back to top</a>
|
||||
</div>
|
||||
|
@ -77,41 +79,18 @@
|
|||
{{- end }}
|
||||
</div>
|
||||
|
||||
{{- range $thisStatus := .context.Ancestors }}
|
||||
{{- if $thisStatus.ThreadFirstReply }}
|
||||
{{- range $status := .context.Statuses }}
|
||||
<article
|
||||
class="status{{- if $status.ThreadContextStatus }} expanded{{- end -}}{{- if $status.Indent }} indent-{{ $status.Indent }}{{- end -}}"
|
||||
{{- includeAttr "status_attributes.tmpl" $status | indentAttr 3 }}
|
||||
>
|
||||
{{- include "status.tmpl" $status | indent 3 }}
|
||||
</article>
|
||||
{{- if and $status.ThreadLastMain $.context.ThreadReplies }}
|
||||
{{- include "repliesStart" $ | indent 1 }}
|
||||
{{- end }}
|
||||
<article
|
||||
class="status{{- if $thisStatus.Indent }} indent-{{ $thisStatus.Indent }}{{- end -}}"
|
||||
{{- includeAttr "status_attributes.tmpl" $thisStatus | indentAttr 3 }}
|
||||
>
|
||||
{{- include "status.tmpl" $thisStatus | indent 3 }}
|
||||
</article>
|
||||
{{- end }}
|
||||
|
||||
{{- with $thisStatus := .context.Status }}
|
||||
{{- if $thisStatus.ThreadFirstReply }}
|
||||
{{- include "repliesStart" $ | indent 1 }}
|
||||
{{- end }}
|
||||
<article
|
||||
class="status expanded{{- if $thisStatus.Indent }} indent-{{ $thisStatus.Indent }}{{- end -}}"
|
||||
{{- includeAttr "status_attributes.tmpl" $thisStatus | indentAttr 3 }}
|
||||
>
|
||||
{{- include "status.tmpl" $thisStatus | indent 3 }}
|
||||
</article>
|
||||
{{- end }}
|
||||
|
||||
{{- range $thisStatus := .context.Descendants }}
|
||||
{{- if $thisStatus.ThreadFirstReply }}
|
||||
{{- include "repliesStart" $ | indent 1 }}
|
||||
{{- end }}
|
||||
<article
|
||||
class="status{{- if $thisStatus.Indent }} indent-{{ $thisStatus.Indent }}{{- end -}}"
|
||||
{{- includeAttr "status_attributes.tmpl" $thisStatus | indentAttr 3 }}
|
||||
>
|
||||
{{- include "status.tmpl" $thisStatus | indent 3 }}
|
||||
</article>
|
||||
{{- end }}
|
||||
{{- if .context.ThreadReplies }}
|
||||
</section>
|
||||
{{- end }}
|
||||
|
|
Loading…
Reference in a new issue