Add reply links on thread page

This commit is contained in:
r 2019-12-18 22:14:02 +00:00
parent e356520290
commit d7fc7cf2f5
7 changed files with 103 additions and 24 deletions

View file

@ -10,6 +10,15 @@ import (
"time" "time"
) )
type StatusPleroma struct {
InReplyToAccountAcct string `json:"in_reply_to_account_acct"`
}
type ReplyInfo struct {
ID string `json:"id"`
Number int `json:"number"`
}
// Status is struct to hold status. // Status is struct to hold status.
type Status struct { type Status struct {
ID string `json:"id"` ID string `json:"id"`
@ -38,6 +47,13 @@ type Status struct {
Application Application `json:"application"` Application Application `json:"application"`
Language string `json:"language"` Language string `json:"language"`
Pinned interface{} `json:"pinned"` Pinned interface{} `json:"pinned"`
// Custom fields
Pleroma StatusPleroma `json:"pleroma"`
HideAccountInfo bool `json:"hide_account_info"`
ShowReplies bool `json:"show_replies"`
ReplyMap map[string][]ReplyInfo `json:"reply_map"`
ReplyNumber int `json:"reply_number"`
} }
// Context hold information for mastodon context. // Context hold information for mastodon context.

1
model/status.go Normal file
View file

@ -0,0 +1 @@
package model

View file

@ -36,21 +36,21 @@ func NewTimelinePageTemplateData(statuses []*mastodon.Status, hasNext bool, next
} }
type ThreadPageTemplateData struct { type ThreadPageTemplateData struct {
Status *mastodon.Status Statuses []*mastodon.Status
Context *mastodon.Context
PostReply bool PostReply bool
ReplyToID string ReplyToID string
ReplyContent string ReplyContent string
ReplyMap map[string][]mastodon.ReplyInfo
NavbarData *NavbarTemplateData NavbarData *NavbarTemplateData
} }
func NewThreadPageTemplateData(status *mastodon.Status, context *mastodon.Context, postReply bool, replyToID string, replyContent string, navbarData *NavbarTemplateData) *ThreadPageTemplateData { func NewThreadPageTemplateData(statuses []*mastodon.Status, postReply bool, replyToID string, replyContent string, replyMap map[string][]mastodon.ReplyInfo, navbarData *NavbarTemplateData) *ThreadPageTemplateData {
return &ThreadPageTemplateData{ return &ThreadPageTemplateData{
Status: status, Statuses: statuses,
Context: context,
PostReply: postReply, PostReply: postReply,
ReplyToID: replyToID, ReplyToID: replyToID,
ReplyContent: replyContent, ReplyContent: replyContent,
ReplyMap: replyMap,
NavbarData: navbarData, NavbarData: navbarData,
} }
} }

View file

@ -267,34 +267,46 @@ func (svc *service) ServeThreadPage(ctx context.Context, client io.Writer, c *ma
return return
} }
context, err := c.GetStatusContext(ctx, id)
if err != nil {
return
}
u, err := c.GetAccountCurrentUser(ctx) u, err := c.GetAccountCurrentUser(ctx)
if err != nil { if err != nil {
return return
} }
var content string var content string
var replyToID string
if reply { if reply {
replyToID = id
if u.ID != status.Account.ID { if u.ID != status.Account.ID {
content += "@" + status.Account.Acct + " " content += "@" + status.Account.Acct + " "
} }
for _, m := range status.Mentions { for i := range status.Mentions {
if u.ID != m.ID { if status.Mentions[i].ID != u.ID && status.Mentions[i].ID != status.Account.ID {
content += "@" + m.Acct + " " content += "@" + status.Mentions[i].Acct + " "
} }
} }
} }
context, err := c.GetStatusContext(ctx, id)
if err != nil {
return
}
statuses := append(append(context.Ancestors, status), context.Descendants...)
replyMap := make(map[string][]mastodon.ReplyInfo)
for i := range statuses {
statuses[i].ShowReplies = true
statuses[i].ReplyMap = replyMap
addToReplyMap(replyMap, statuses[i].InReplyToID, statuses[i].ID, i+1)
}
navbarData, err := svc.getNavbarTemplateData(ctx, client, c) navbarData, err := svc.getNavbarTemplateData(ctx, client, c)
if err != nil { if err != nil {
return return
} }
data := renderer.NewThreadPageTemplateData(status, context, reply, id, content, navbarData) data := renderer.NewThreadPageTemplateData(statuses, reply, replyToID, content, replyMap, navbarData)
err = svc.renderer.RenderThreadPage(ctx, client, data) err = svc.renderer.RenderThreadPage(ctx, client, data)
if err != nil { if err != nil {
return return
@ -323,7 +335,7 @@ func (svc *service) ServeNotificationPage(ctx context.Context, client io.Writer,
switch notifications[i].Type { switch notifications[i].Type {
case "reblog", "favourite": case "reblog", "favourite":
if notifications[i].Status != nil { if notifications[i].Status != nil {
notifications[i].Status.Account.ID = "" notifications[i].Status.HideAccountInfo = true
} }
} }
if notifications[i].Pleroma != nil && notifications[i].Pleroma.IsSeen { if notifications[i].Pleroma != nil && notifications[i].Pleroma.IsSeen {
@ -418,3 +430,19 @@ func (svc *service) PostTweet(ctx context.Context, client io.Writer, c *mastodon
return s.ID, nil return s.ID, nil
} }
func addToReplyMap(m map[string][]mastodon.ReplyInfo, key interface{}, val string, number int) {
if key == nil {
return
}
keyStr, ok := key.(string)
if !ok {
return
}
_, ok = m[keyStr]
if !ok {
m[keyStr] = []mastodon.ReplyInfo{}
}
m[keyStr] = append(m[keyStr], mastodon.ReplyInfo{val, number})
}

View file

@ -7,7 +7,7 @@
} }
.status-content { .status-content {
margin: 8px 0; margin: 4px 0 8px 0;
} }
.status-content p { .status-content p {
@ -18,6 +18,7 @@
height: 48px; height: 48px;
width: 48px; width: 48px;
margin-right: 8px; margin-right: 8px;
object-fit: contain;
} }
.status { .status {
@ -118,6 +119,7 @@
height: 24px; height: 24px;
width: 24px; width: 24px;
margin-bottom: -8px; margin-bottom: -8px;
margin-right: 0px;
} }
.retweet-info .status-dname{ .retweet-info .status-dname{
@ -161,3 +163,26 @@
.notification-follow-uname { .notification-follow-uname {
margin-top: 8px; margin-top: 8px;
} }
.status-reply-to {
vertical-align: center;
font-size: 10pt
}
.status-reply-container .icon {
font-size: 10pt;
vertical-align: sub;
margin-right: -2px;
}
.status-reply-text {
font-size: 10pt;
}
.status-reply {
font-size: 10pt;
}
.status-reply-info-divider {
margin: 0 4px;
}

View file

@ -11,17 +11,30 @@
{{block "status" .}} {{block "status" .}}
<div class="status-container"> <div class="status-container">
<div> <div>
{{if ne .Account.ID ""}} {{if not .HideAccountInfo}}
<img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="profile-avatar" /> <img class="status-profile-img" src="{{.Account.AvatarStatic}}" alt="profile-avatar" />
{{end}} {{end}}
</div> </div>
<div class="status"> <div class="status">
{{if ne .Account.ID ""}} {{if not .HideAccountInfo}}
<div class="status-name"> <div class="status-name">
<span class="status-dname"> {{WithEmojis .Account.DisplayName .Account.Emojis}} </span> <span class="status-dname"> {{WithEmojis .Account.DisplayName .Account.Emojis}} </span>
<span class="status-uname"> {{.Account.Acct}} </span> <span class="status-uname"> {{.Account.Acct}} </span>
</div> </div>
{{end}} {{end}}
<div class="status-reply-container">
{{if .InReplyToID}}
<span class="icon dripicons-forward"></span>
<a class="status-reply-to" href="{{if not .ShowReplies}}/thread/{{.InReplyToID}}{{end}}#status-{{.InReplyToID}}"> reply to {{.Pleroma.InReplyToAccountAcct}} </a>
{{if index .ReplyMap .ID}} <span class="status-reply-info-divider"> - </span> {{end}}
{{end}}
{{if .ShowReplies}}
{{if index .ReplyMap .ID}} <span class="status-reply-text"> replies: </span> {{end}}
{{range index .ReplyMap .ID}}
<a class="status-reply" href="#status-{{.ID}}">#{{.Number}}</a>
{{end}}
{{end}}
</div>
<div class="status-content"> {{WithEmojis .Content .Emojis}} </div> <div class="status-content"> {{WithEmojis .Content .Emojis}} </div>
<div class="status-media-container"> <div class="status-media-container">
{{range .MediaAttachments}} {{range .MediaAttachments}}

View file

@ -2,12 +2,10 @@
{{template "navigation.tmpl" .NavbarData}} {{template "navigation.tmpl" .NavbarData}}
<div class="page-title"> Thread </div> <div class="page-title"> Thread </div>
{{range .Context.Ancestors}} {{range .Statuses}}
{{template "status.tmpl" .}} {{template "status.tmpl" .}}
{{end}}
{{template "status.tmpl" .Status}} {{if eq .ID $.ReplyToID}}
{{if .PostReply}}
<form class="timeline-post-form" action="/post" method="POST" enctype="multipart/form-data"> <form class="timeline-post-form" action="/post" method="POST" enctype="multipart/form-data">
<input type="hidden" name="reply_to_id" value="{{.ReplyToID}}" /> <input type="hidden" name="reply_to_id" value="{{.ReplyToID}}" />
<label for="post-content"> Reply to {{.Status.Account.DisplayName}} </label> <label for="post-content"> Reply to {{.Status.Account.DisplayName}} </label>
@ -20,8 +18,6 @@
</form> </form>
{{end}} {{end}}
{{range .Context.Descendants}}
{{template "status.tmpl" .}}
{{end}} {{end}}
{{template "footer.tmpl"}} {{template "footer.tmpl"}}