diff --git a/internal/api/client/statuses/statuscreate_test.go b/internal/api/client/statuses/statuscreate_test.go index d32feb6c7..aab66f849 100644 --- a/internal/api/client/statuses/statuscreate_test.go +++ b/internal/api/client/statuses/statuscreate_test.go @@ -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) diff --git a/internal/api/model/status.go b/internal/api/model/status.go index c29ab3e82..a2364e6a9 100644 --- a/internal/api/model/status.go +++ b/internal/api/model/status.go @@ -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"` diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go index 16c82c08f..258c84807 100644 --- a/internal/db/bundb/account.go +++ b/internal/db/bundb/account.go @@ -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. diff --git a/internal/gtsmodel/accountsettings.go b/internal/gtsmodel/accountsettings.go index bedcb7f63..2e37b3470 100644 --- a/internal/gtsmodel/accountsettings.go +++ b/internal/gtsmodel/accountsettings.go @@ -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. diff --git a/internal/processing/account/rss.go b/internal/processing/account/rss.go index 22ba0fe42..fb5f56108 100644 --- a/internal/processing/account/rss.go +++ b/internal/processing/account/rss.go @@ -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) diff --git a/internal/processing/account/rss_test.go b/internal/processing/account/rss_test.go index e4706d3b7..4cf7575dd 100644 --- a/internal/processing/account/rss_test.go +++ b/internal/processing/account/rss_test.go @@ -42,6 +42,16 @@ func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() { Posts from @admin@localhost:8080 Wed, 20 Oct 2021 10:41:37 +0000 Wed, 20 Oct 2021 10:41:37 +0000 + + introduction post + http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY + @the_mighty_zork@localhost:8080 made a new post: "hello everyone!" + + @the_mighty_zork@localhost:8080 + http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY + Wed, 20 Oct 2021 10:40:37 +0000 + http://localhost:8080/@the_mighty_zork/feed.rss + open to see some puppies http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37 diff --git a/internal/processing/account/statuses.go b/internal/processing/account/statuses.go index 8029a460b..3a92d73ee 100644 --- a/internal/processing/account/statuses.go +++ b/internal/processing/account/statuses.go @@ -184,6 +184,7 @@ func (p *Processor) WebStatusesGet( log.Errorf(ctx, "error convering to web status: %v", err) continue } + items = append(items, item) } diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index cb7c38620..4b4a443c7 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -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, "
", "\n
")
 	webStatus.Content = strings.ReplaceAll(webStatus.Content, "
", "
\n") diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go index a44afe67e..67961e1f6 100644 --- a/internal/typeutils/internaltofrontend_test.go +++ b/internal/typeutils/internaltofrontend_test.go @@ -1401,6 +1401,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() { "emojis": [], "fields": [] }, + "reblog_account": null, "media_attachments": [ { "id": "01HE7Y3C432WRSNS10EZM86SA5", diff --git a/internal/typeutils/internaltorss.go b/internal/typeutils/internaltorss.go index 4f4b2b93a..8e7370632 100644 --- a/internal/typeutils/internaltorss.go +++ b/internal/typeutils/internaltorss.go @@ -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 diff --git a/testrig/testmodels.go b/testrig/testmodels.go index 43143c902..049085fb2 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -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, }, diff --git a/web/source/css/status.css b/web/source/css/status.css index 97713dae2..1486d4dec 100644 --- a/web/source/css/status.css +++ b/web/source/css/status.css @@ -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%; } -} \ No newline at end of file +} diff --git a/web/template/profile.tmpl b/web/template/profile.tmpl index aff487c3d..4c9567668 100644 --- a/web/template/profile.tmpl +++ b/web/template/profile.tmpl @@ -247,6 +247,16 @@ class="status expanded" {{- includeAttr "status_attributes.tmpl" . | indentAttr 6 }} > + {{- if .ReblogAccount }} +
+   + {{- if $.account.DisplayName }} + {{- emojify $.account.Emojis (escape $.account.DisplayName) }} boosted + {{- else }} + {{- $.account.Username }} boosted + {{- end }} +
+ {{- end }} {{- include "status.tmpl" . | indent 6 }} {{- end }} diff --git a/web/template/status_header.tmpl b/web/template/status_header.tmpl index 01b73aea0..57a40152a 100644 --- a/web/template/status_header.tmpl +++ b/web/template/status_header.tmpl @@ -48,6 +48,16 @@ alt="Avatar for {{ .Username -}}" title="Avatar for {{ .Username -}}" > + {{ if $.ReblogAccount }} + Avatar for {{ $.ReblogAccount.Username -}} + {{ end }} + +
@@ -63,4 +73,4 @@ (open profile) -{{- end }} \ No newline at end of file +{{- end }}