Feat: display boosts on public profile

This commit is contained in:
vdyotte 2024-09-24 00:44:09 -04:00
parent d9e59820ed
commit af5a766f62
No known key found for this signature in database
GPG key ID: B170DEDABC527E66
14 changed files with 112 additions and 34 deletions

View file

@ -447,7 +447,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusMessedUpIntPolicy() {
suite.Equal(http.StatusBadRequest, recorder.Code)
// We should have a helpful error
// message telling us how we screwed up.
// message telling us how we screwed up.
suite.Equal(`{
"error": "Bad Request: error converting followers_only.can_reply.always: policyURI public is not feasible for visibility followers_only"
}`, out)

View file

@ -118,6 +118,10 @@ type WebStatus struct {
// Override API account with web account.
Account *WebAccount `json:"account"`
// Account that reblogged the status.
// needed to properly render reblogged statuses on profile pages.
ReblogAccount *WebAccount `json:"reblog_account"`
// Web version of media
// attached to this status.
MediaAttachments []*WebAttachment `json:"media_attachments"`

View file

@ -1017,6 +1017,7 @@ func (a *accountDB) GetAccountWebStatuses(
) ([]*gtsmodel.Status, error) {
// Check for an easy case: account exposes no statuses via the web.
webVisibility := account.Settings.WebVisibility
hideBoosts := *account.Settings.HideBoosts
if webVisibility == gtsmodel.VisibilityNone {
return nil, db.ErrNoEntries
}
@ -1035,9 +1036,12 @@ func (a *accountDB) GetAccountWebStatuses(
// Select only IDs from table
Column("status.id").
Where("? = ?", bun.Ident("status.account_id"), account.ID).
// Don't show replies or boosts.
Where("? IS NULL", bun.Ident("status.in_reply_to_uri")).
Where("? IS NULL", bun.Ident("status.boost_of_id"))
// Don't show replies.
Where("? IS NULL", bun.Ident("status.in_reply_to_uri"))
if hideBoosts {
q = q.Where("? IS NULL", bun.Ident("status.boost_of_id"))
}
// Select statuses for this account according
// to their web visibility preference.

View file

@ -33,7 +33,7 @@ type AccountSettings struct {
Theme string `bun:",nullzero"` // Preset CSS theme filename selected by this Account (empty string if nothing set).
CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
EnableRSS *bool `bun:",nullzero,notnull,default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
HideBoosts *bool `bun:",nullzero,notnull,default:false"` // Hide boosts from this accounts profile page.
HideBoosts *bool `bun:",nullzero,notnull,default:false"` // Hide boosts from this accounts profile page.
HideCollections *bool `bun:",nullzero,notnull,default:false"` // Hide this account's followers/following collections.
WebVisibility Visibility `bun:",nullzero,notnull,default:public"` // Visibility level of statuses that visitors can view via the web profile.
InteractionPolicyDirect *InteractionPolicy `bun:""` // Interaction policy to use for new direct visibility statuses by this account. If null, assume default policy.

View file

@ -123,8 +123,8 @@ func (p *Processor) GetRSSFeedForUsername(ctx context.Context, username string)
}
// Add each status to the rss feed.
for _, status := range statuses {
item, err := p.converter.StatusToRSSItem(ctx, status)
for _, s := range statuses {
item, err := p.converter.StatusToRSSItem(ctx, s)
if err != nil {
err = gtserror.Newf("error converting status to feed item: %w", err)
return "", gtserror.NewErrorInternalError(err)

View file

@ -42,6 +42,16 @@ func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() {
<description>Posts from @admin@localhost:8080</description>
<pubDate>Wed, 20 Oct 2021 10:41:37 +0000</pubDate>
<lastBuildDate>Wed, 20 Oct 2021 10:41:37 +0000</lastBuildDate>
<item>
<title>introduction post</title>
<link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>
<description>@the_mighty_zork@localhost:8080 made a new post: &#34;hello everyone!&#34;</description>
<content:encoded><![CDATA[hello everyone!]]></content:encoded>
<author>@the_mighty_zork@localhost:8080</author>
<guid isPermaLink="true">http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>
<pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>
<source>http://localhost:8080/@the_mighty_zork/feed.rss</source>
</item>
<item>
<title>open to see some puppies</title>
<link>http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37</link>

View file

@ -184,6 +184,7 @@ func (p *Processor) WebStatusesGet(
log.Errorf(ctx, "error convering to web status: %v", err)
continue
}
items = append(items, item)
}

View file

@ -310,7 +310,7 @@ func (c *Converter) accountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
enableRSS bool
theme string
customCSS string
hideBoosts bool
hideBoosts bool
hideCollections bool
)
@ -1046,7 +1046,15 @@ func (c *Converter) StatusToWebStatus(
ctx context.Context,
s *gtsmodel.Status,
) (*apimodel.WebStatus, error) {
apiStatus, err := c.statusToFrontend(ctx, s,
isBoost := s.BoostOf != nil
status := s
if isBoost {
status = s.BoostOf
}
apiStatus, err := c.statusToFrontend(ctx, status,
nil, // No authed requester.
statusfilter.FilterContextNone, // No filters.
nil, // No filters.
@ -1057,7 +1065,7 @@ func (c *Converter) StatusToWebStatus(
}
// Convert status author to web model.
acct, err := c.AccountToWebAccount(ctx, s.Account)
acct, err := c.AccountToWebAccount(ctx, status.Account)
if err != nil {
return nil, err
}
@ -1067,6 +1075,14 @@ func (c *Converter) StatusToWebStatus(
Account: acct,
}
if isBoost {
reblogAcct, err := c.AccountToWebAccount(ctx, s.Account)
if err != nil {
return nil, err
}
webStatus.ReblogAccount = reblogAcct
}
// Whack a newline before and after each "pre" to make it easier to outdent it.
webStatus.Content = strings.ReplaceAll(webStatus.Content, "<pre>", "\n<pre>")
webStatus.Content = strings.ReplaceAll(webStatus.Content, "</pre>", "</pre>\n")

View file

@ -1401,6 +1401,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
"emojis": [],
"fields": []
},
"reblog_account": null,
"media_attachments": [
{
"id": "01HE7Y3C432WRSNS10EZM86SA5",

View file

@ -39,6 +39,12 @@
func (c *Converter) StatusToRSSItem(ctx context.Context, s *gtsmodel.Status) (*feeds.Item, error) {
// see https://cyber.harvard.edu/rss/rss.html
// If status is a boost,
// display the boost instead.
if s.BoostOf != nil {
s = s.BoostOf
}
// Title -- The title of the item.
// example: Venice Film Festival Tries to Quit Sinking
var title string

View file

@ -657,7 +657,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
Sensitive: util.Ptr(false),
Language: "en",
EnableRSS: util.Ptr(false),
HideBoosts: util.Ptr(false),
HideBoosts: util.Ptr(false),
HideCollections: util.Ptr(false),
WebVisibility: gtsmodel.VisibilityPublic,
},
@ -669,7 +669,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
Sensitive: util.Ptr(false),
Language: "en",
EnableRSS: util.Ptr(true),
HideBoosts: util.Ptr(false),
HideBoosts: util.Ptr(false),
HideCollections: util.Ptr(false),
WebVisibility: gtsmodel.VisibilityPublic,
},
@ -681,7 +681,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
Sensitive: util.Ptr(false),
Language: "en",
EnableRSS: util.Ptr(true),
HideBoosts: util.Ptr(false),
HideBoosts: util.Ptr(false),
HideCollections: util.Ptr(false),
WebVisibility: gtsmodel.VisibilityUnlocked,
},
@ -693,7 +693,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
Sensitive: util.Ptr(true),
Language: "fr",
EnableRSS: util.Ptr(false),
HideBoosts: util.Ptr(false),
HideBoosts: util.Ptr(false),
HideCollections: util.Ptr(true),
WebVisibility: gtsmodel.VisibilityPublic,
},

View file

@ -41,6 +41,12 @@ main {
text-decoration: none;
}
.boosted {
padding: 0 0.75rem 0.75rem;
color: var(--fg-reduced);
font-weight: bold;
}
.status-header > address {
/*
Avoid stretching so wide that user
@ -59,17 +65,27 @@ main {
"avatar author-strap author-strap";
gap: 0 0.5rem;
font-style: normal;
.avatar {
grid-area: avatar;
height: 3.5rem;
width: 3.5rem;
object-fit: cover;
position: relative;
border: 0.15rem solid $avatar-border;
border-radius: $br;
overflow: hidden; /* hides corners from img overflowing */
.boosted-avatar {
height: 50%;
width: 50%;
z-index: 10;
position: absolute;
bottom: 0;
inset-inline-end: 0;
}
img {
height: 100%;
width: 100%;
@ -77,7 +93,7 @@ main {
background: $bg;
}
}
.author-strap {
grid-area: author-strap;
display: grid;
@ -87,7 +103,7 @@ main {
"display display"
"user user";
gap: 0 0.5rem;
.displayname, .username {
justify-self: start;
align-self: start;
@ -95,12 +111,12 @@ main {
font-size: 1rem;
line-height: 1.3rem;
}
.displayname {
grid-area: display;
font-weight: bold;
}
.username {
grid-area: user;
color: $link-fg;
@ -200,34 +216,34 @@ main {
.poll {
background-color: $gray2;
z-index: 2;
display: flex;
flex-direction: column;
border-radius: $br;
padding: 0.5rem;
margin: 0;
gap: 1rem;
.poll-options {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 1rem;
.poll-option {
display: flex;
flex-direction: column;
gap: 0.1rem;
label {
cursor: default;
}
meter {
width: 100%;
}
.poll-vote-summary {
display: flex;
flex-wrap: wrap;
@ -236,7 +252,7 @@ main {
}
}
}
.poll-info {
background-color: $gray4;
display: flex;
@ -245,7 +261,7 @@ main {
border-radius: $br-inner;
padding: 0.25rem;
gap: 0.25rem;
span {
justify-self: center;
white-space: nowrap;
@ -301,12 +317,12 @@ main {
width: 100%;
z-index: 3;
overflow: hidden;
display: grid;
padding: 1rem;
grid-template-columns: 1fr auto 1fr;
grid-template-rows: 1fr 1fr;
grid-template-areas:
grid-template-areas:
"eye sensitive ."
". sensitive .";
@ -369,7 +385,7 @@ main {
height: 100%;
padding: 0.8rem;
border: 0.2rem dashed $white2;
display: flex;
flex-direction: column;
align-items: center;
@ -518,4 +534,4 @@ main {
.plyr {
max-height: 100%;
}
}
}

View file

@ -247,6 +247,16 @@
class="status expanded"
{{- includeAttr "status_attributes.tmpl" . | indentAttr 6 }}
>
{{- if .ReblogAccount }}
<div class="boosted text-cutoff">
<i class="fa fa-retweet" aria-hidden="true"></i>&nbsp
{{- if $.account.DisplayName }}
{{- emojify $.account.Emojis (escape $.account.DisplayName) }} boosted
{{- else }}
{{- $.account.Username }} boosted
{{- end }}
</div>
{{- end }}
{{- include "status.tmpl" . | indent 6 }}
</article>
{{- end }}

View file

@ -48,6 +48,16 @@
alt="Avatar for {{ .Username -}}"
title="Avatar for {{ .Username -}}"
>
{{ if $.ReblogAccount }}
<img
class="boosted-avatar"
src="{{ $.ReblogAccount.Avatar }}"
alt="Avatar for {{ $.ReblogAccount.Username -}}"
title="Avatar for {{ $.ReblogAccount.Username -}}"
>
{{ end }}
</picture>
<div class="author-strap">
<span class="displayname text-cutoff">
@ -63,4 +73,4 @@
<span class="sr-only">(open profile)</span>
</a>
</address>
{{- end }}
{{- end }}