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.
|
// display this status in the web view.
|
||||||
Indent int
|
Indent int
|
||||||
|
|
||||||
// This status is the first status after
|
// This status is the last visible status
|
||||||
// the "main" thread, so it and everything
|
// 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".
|
// below it can be considered "replies".
|
||||||
ThreadFirstReply bool
|
ThreadFirstReply bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,16 @@ type ThreadContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebThreadContext struct {
|
type WebThreadContext struct {
|
||||||
// Parents in the thread.
|
// Status around which this
|
||||||
Ancestors []*WebStatus `json:"ancestors"`
|
// thread ctx was constructed.
|
||||||
|
Status *WebStatus
|
||||||
|
|
||||||
// Children in the thread.
|
// Ordered slice of statuses
|
||||||
Descendants []*WebStatus `json:"descendants"`
|
// for rendering in template.
|
||||||
|
//
|
||||||
// The status around which the ancestors
|
// Includes ancestors, target
|
||||||
// + descendants context was constructed.
|
// status, and descendants.
|
||||||
Status *WebStatus `json:"-"`
|
Statuses []*WebStatus
|
||||||
|
|
||||||
// Total length of
|
// Total length of
|
||||||
// the main thread.
|
// the main thread.
|
||||||
|
|
|
@ -400,8 +400,7 @@ func (p *Processor) WebContextGet(
|
||||||
|
|
||||||
// Start preparing web context.
|
// Start preparing web context.
|
||||||
wCtx := &apimodel.WebThreadContext{
|
wCtx := &apimodel.WebThreadContext{
|
||||||
Ancestors: make([]*apimodel.WebStatus, 0, len(iCtx.ancestors)),
|
Statuses: make([]*apimodel.WebStatus, 0, len(wholeThread)),
|
||||||
Descendants: make([]*apimodel.WebStatus, 0, len(iCtx.descendants)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -415,72 +414,70 @@ func (p *Processor) WebContextGet(
|
||||||
// ie., who created first post in the thread.
|
// ie., who created first post in the thread.
|
||||||
contextAcctID = wholeThread[0].AccountID
|
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"
|
// Whether we've reached end of "main"
|
||||||
// thread and are now looking at replies.
|
// thread and are now looking at replies.
|
||||||
inReplies bool
|
inReplies bool
|
||||||
|
|
||||||
// Index in wholeThread where
|
// Index in wholeThread
|
||||||
// the "main" thread ends.
|
// where replies begin.
|
||||||
firstReplyIdx int
|
firstReplyIdx int
|
||||||
|
|
||||||
// We should mark the next **VISIBLE**
|
// We should mark the next **VISIBLE**
|
||||||
// reply as the first reply.
|
// reply as the first reply.
|
||||||
markNextVisibleAsReply bool
|
markNextVisibleAsFirstReply bool
|
||||||
)
|
)
|
||||||
|
|
||||||
for idx, status := range wholeThread {
|
for idx, status := range wholeThread {
|
||||||
if !inReplies {
|
if !inReplies {
|
||||||
// Haven't reached end
|
// Check if we've reached replies
|
||||||
// of "main" thread yet.
|
// by looking for the first status
|
||||||
//
|
// that's not a self-reply, ie.,
|
||||||
// First post in wholeThread can't
|
// not a post in the "main" thread.
|
||||||
// be a self reply, so ignore it.
|
switch {
|
||||||
//
|
case idx == 0:
|
||||||
// That aside, first non-self-reply
|
// First post in wholeThread can't
|
||||||
// in wholeThread means the "main"
|
// be a self reply anyway because
|
||||||
// thread is now over.
|
// it (very likely) doesn't reply
|
||||||
if idx != 0 && !isSelfReply(status, contextAcctID) {
|
// to anything, so ignore it.
|
||||||
// Jot some stuff down.
|
|
||||||
firstReplyIdx = idx
|
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
|
inReplies = true
|
||||||
markNextVisibleAsReply = true
|
firstReplyIdx = idx
|
||||||
|
markNextVisibleAsFirstReply = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure status is actually
|
// 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)
|
v, err := p.filter.StatusVisible(ctx, nil, status)
|
||||||
if err != nil || !v {
|
if err != nil || !v {
|
||||||
// Skip this one.
|
|
||||||
if !inReplies {
|
if !inReplies {
|
||||||
|
// Main thread entry hidden.
|
||||||
wCtx.ThreadHidden++
|
wCtx.ThreadHidden++
|
||||||
} else {
|
} else {
|
||||||
|
// Reply hidden.
|
||||||
wCtx.ThreadRepliesHidden++
|
wCtx.ThreadRepliesHidden++
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare status to add to thread context.
|
// Prepare visible status to add to thread context.
|
||||||
apiStatus, err := p.converter.StatusToWebStatus(ctx, status)
|
webStatus, err := p.converter.StatusToWebStatus(ctx, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if markNextVisibleAsReply {
|
if markNextVisibleAsFirstReply {
|
||||||
// This is the first visible
|
// This is the first visible
|
||||||
// "reply / comment", so the
|
// "reply / comment", so the
|
||||||
// little "x amount of replies"
|
// little "x amount of replies"
|
||||||
// header should go above this.
|
// header should go above this.
|
||||||
apiStatus.ThreadFirstReply = true
|
webStatus.ThreadFirstReply = true
|
||||||
markNextVisibleAsReply = false
|
markNextVisibleAsFirstReply = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a reply, work out the indent of
|
// If this is a reply, work out the indent of
|
||||||
|
@ -491,59 +488,47 @@ func (p *Processor) WebContextGet(
|
||||||
case !ok:
|
case !ok:
|
||||||
// No parent with
|
// No parent with
|
||||||
// indent, start at 0.
|
// indent, start at 0.
|
||||||
apiStatus.Indent = 0
|
webStatus.Indent = 0
|
||||||
|
|
||||||
case isSelfReply(status, status.AccountID):
|
case isSelfReply(status, status.AccountID):
|
||||||
// Self reply, so indent at same
|
// Self reply, so indent at same
|
||||||
// level as own replied-to status.
|
// level as own replied-to status.
|
||||||
apiStatus.Indent = parentIndent
|
webStatus.Indent = parentIndent
|
||||||
|
|
||||||
case parentIndent == 5:
|
case parentIndent == 5:
|
||||||
// Already indented as far as we
|
// Already indented as far as we
|
||||||
// can go to keep things readable
|
// can go to keep things readable
|
||||||
// on thin screens, so just keep
|
// on thin screens, so just keep
|
||||||
// parent's indent.
|
// parent's indent.
|
||||||
apiStatus.Indent = parentIndent
|
webStatus.Indent = parentIndent
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Reply to someone else who's
|
// Reply to someone else who's
|
||||||
// indented, but not to TO THE MAX.
|
// indented, but not to TO THE MAX.
|
||||||
// Indent by another one.
|
// Indent by another one.
|
||||||
apiStatus.Indent = parentIndent + 1
|
webStatus.Indent = parentIndent + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the indent for this status.
|
// Store the indent for this status.
|
||||||
statusIndents[status.ID] = apiStatus.Indent
|
statusIndents[status.ID] = webStatus.Indent
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
if webStatus.ID == targetStatusID {
|
||||||
case idx == targetStatusIdx:
|
// This is the og
|
||||||
// This is the target status itself.
|
// thread context status.
|
||||||
wCtx.Status = apiStatus
|
webStatus.ThreadContextStatus = true
|
||||||
|
wCtx.Status = webStatus
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wCtx.Statuses = append(wCtx.Statuses, webStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we've gone through the whole
|
// Now we've gone through the whole
|
||||||
// thread, we can add some additional info.
|
// thread, we can add some additional info.
|
||||||
|
|
||||||
// Length of the "main" thread. If there are
|
// Length of the "main" thread. If there are
|
||||||
// replies then it's up to where the replies
|
// visible replies then it's up to where the
|
||||||
// start, otherwise it's the whole thing.
|
// replies start, else it's the whole thing.
|
||||||
if inReplies {
|
if inReplies {
|
||||||
wCtx.ThreadLength = firstReplyIdx
|
wCtx.ThreadLength = firstReplyIdx
|
||||||
} else {
|
} else {
|
||||||
|
@ -553,6 +538,9 @@ func (p *Processor) WebContextGet(
|
||||||
// Jot down number of hidden posts so template doesn't have to do it.
|
// Jot down number of hidden posts so template doesn't have to do it.
|
||||||
wCtx.ThreadShown = wCtx.ThreadLength - wCtx.ThreadHidden
|
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
|
// Number of replies is equal to number
|
||||||
// of statuses in the thread that aren't
|
// of statuses in the thread that aren't
|
||||||
// part of the "main" thread.
|
// part of the "main" thread.
|
||||||
|
|
|
@ -1014,6 +1014,8 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
|
||||||
"PollOptions": null,
|
"PollOptions": null,
|
||||||
"Local": false,
|
"Local": false,
|
||||||
"Indent": 0,
|
"Indent": 0,
|
||||||
|
"ThreadLastMain": false,
|
||||||
|
"ThreadContextStatus": false,
|
||||||
"ThreadFirstReply": false
|
"ThreadFirstReply": false
|
||||||
}`, string(b))
|
}`, string(b))
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,15 @@
|
||||||
h2 {
|
h2 {
|
||||||
margin-right: auto;
|
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 {
|
.status {
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
{{- define "repliesSummary" -}}
|
{{- define "repliesSummary" -}}
|
||||||
{{- if .context.ThreadRepliesShown -}}
|
{{- if .context.ThreadRepliesShown -}}
|
||||||
{{- if .context.ThreadRepliesHidden -}}
|
{{- if .context.ThreadRepliesHidden -}}
|
||||||
{{- if eq .context.ThreadReplies 1 -}}
|
{{- if eq .context.ThreadRepliesShown 1 -}}
|
||||||
{{- /* Some replies are hidden. */ -}}
|
{{- /* Some replies are hidden. */ -}}
|
||||||
{{ .context.ThreadRepliesShown }} visible reply
|
{{ .context.ThreadRepliesShown }} visible reply
|
||||||
{{- else if gt .context.ThreadRepliesShown 1 -}}
|
{{- else if gt .context.ThreadRepliesShown 1 -}}
|
||||||
|
@ -35,6 +35,8 @@
|
||||||
{{ .context.ThreadReplies }} replies
|
{{ .context.ThreadReplies }} replies
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- .context.ThreadRepliesHidden }} {{ if eq .context.ThreadRepliesHidden 1 }}reply{{ else }}replies{{ end }} hidden or not public
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@
|
||||||
{{- with . }}
|
{{- with . }}
|
||||||
</section>
|
</section>
|
||||||
<section class="thread thread-replies" aria-labelledby="replies" open>
|
<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>
|
<h2 id="replies">{{- template "repliesSummary" . -}}</h2>
|
||||||
<a href="#thread-summary">back to top</a>
|
<a href="#thread-summary">back to top</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,41 +79,18 @@
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{- range $thisStatus := .context.Ancestors }}
|
{{- range $status := .context.Statuses }}
|
||||||
{{- if $thisStatus.ThreadFirstReply }}
|
<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 }}
|
{{- include "repliesStart" $ | indent 1 }}
|
||||||
{{- end }}
|
{{- 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 }}
|
{{- 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 }}
|
{{- if .context.ThreadReplies }}
|
||||||
</section>
|
</section>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
Loading…
Reference in a new issue