[chore/frontend] Opengraph: for statuses with media, omit account and instance avatars (#4638)
# Description This produces a better experience for consumers that can handle multiple OpenGraph attachments, like Discord (see #4631 for an example). _This is among the first Go code I've written, so please tell me what I've done wrong! The testing is also a bit light, but the existing testing was light as well._ closes #4631 ## Checklist Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]` If this is a documentation change, only the first two checkboxes must be filled (you can delete the others if you want). - [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md). - [x] I/we have not used so-called 'AI' to create the proposed changes. - [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat. - [x] I/we have performed a self-review of added code. - [x] I/we have written code that is legible and maintainable by others. - [x] I/we have commented the added code, particularly in hard-to-understand areas. - [x] I/we have made any necessary changes to documentation. _(none)_ - [x] I/we have added tests that cover new code. - [x] I/we have run tests and they pass locally with the changes.\* _Some bundb tests timed out on my machine, but they shouldn't be affected._ - [x] I/we have run `go fmt ./...` and `golangci-lint run`.\* _There are some existing lint failures but they may be because I didn't install the exact same version of Go._ Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4638 Co-authored-by: Jordan Rose <jrose@belkadan.com> Co-committed-by: Jordan Rose <jrose@belkadan.com>
This commit is contained in:
@@ -180,8 +180,8 @@ func (o *OGMeta) WithStatus(status *apimodel.WebStatus) *OGMeta {
|
||||
o.Description = o.Title
|
||||
}
|
||||
|
||||
// Prepend account image.
|
||||
o.prependMedia(ogImgForAcct(status.Account))
|
||||
// Gather ogMedias for this status.
|
||||
ogMedias := []OGMedia{}
|
||||
|
||||
if l := len(status.MediaAttachments); l != 0 && !status.Sensitive {
|
||||
|
||||
@@ -202,10 +202,6 @@ func (o *OGMeta) WithStatus(status *apimodel.WebStatus) *OGMeta {
|
||||
Alt: desc,
|
||||
}
|
||||
|
||||
// Gather ogMedias for
|
||||
// this attachment.
|
||||
ogMedias := []OGMedia{}
|
||||
|
||||
// Add further tags
|
||||
// depending on type.
|
||||
switch a.Type {
|
||||
@@ -252,18 +248,23 @@ func (o *OGMeta) WithStatus(status *apimodel.WebStatus) *OGMeta {
|
||||
)
|
||||
}
|
||||
|
||||
// Prepend gathered entries.
|
||||
// Replace generic entries with gathered ones.
|
||||
//
|
||||
// This will cause the full-size
|
||||
// entry to appear before its
|
||||
// thumbnail entry (if set).
|
||||
o.prependMedia(ogMedias...)
|
||||
o.Media = ogMedias
|
||||
|
||||
// Done!
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(ogMedias) == 0 {
|
||||
// Prepend account image to default media.
|
||||
o.prependMedia(ogImgForAcct(status.Account))
|
||||
}
|
||||
|
||||
o.ArticlePublisher = status.Account.URL
|
||||
o.ArticleAuthor = status.Account.URL
|
||||
o.ArticlePublishedTime = status.CreatedAt
|
||||
|
||||
@@ -21,6 +21,9 @@ import (
|
||||
"testing"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
@@ -138,6 +141,226 @@ func (suite *OpenGraphTestSuite) TestWithAccountNoNote() {
|
||||
}, *accountMeta)
|
||||
}
|
||||
|
||||
func (suite *OpenGraphTestSuite) TestWithStatus() {
|
||||
baseMeta := OGBase(&apimodel.InstanceV1{
|
||||
AccountDomain: "example.org",
|
||||
Languages: []string{"en"},
|
||||
Thumbnail: "https://example.org/instance-avatar.webp",
|
||||
ThumbnailType: "image/webp",
|
||||
})
|
||||
|
||||
acct := &apimodel.Account{
|
||||
Acct: "example_account",
|
||||
DisplayName: "example person!!",
|
||||
URL: "https://example.org/@example_account",
|
||||
Note: "", // <- empty
|
||||
Username: "example_account",
|
||||
Avatar: "https://example.org/avatar.jpg",
|
||||
}
|
||||
|
||||
apiStatus := &apimodel.Status{
|
||||
ID: "12345",
|
||||
CreatedAt: "2025-01-18T00:00:00+00:00",
|
||||
EditedAt: util.Ptr("2025-01-18T11:00:00+00:00"),
|
||||
Sensitive: false,
|
||||
SpoilerText: "",
|
||||
Visibility: typeutils.VisToAPIVis(gtsmodel.VisibilityPublic),
|
||||
LocalOnly: false,
|
||||
Language: util.Ptr("en"),
|
||||
URI: "https://example.org/statuses/12345",
|
||||
URL: "https://example.org/@example_account/12345",
|
||||
Content: "<b>test status</b>",
|
||||
Account: acct,
|
||||
MediaAttachments: []*apimodel.Attachment{},
|
||||
Text: "**test status**",
|
||||
ContentType: apimodel.StatusContentTypeMarkdown,
|
||||
}
|
||||
|
||||
status := &apimodel.WebStatus{
|
||||
Status: apiStatus,
|
||||
SpoilerContent: "", // <- empty
|
||||
Account: &apimodel.WebAccount{
|
||||
Account: acct,
|
||||
AvatarAttachment: nil,
|
||||
HeaderAttachment: nil,
|
||||
WebLayout: gtsmodel.WebLayoutMicroblog.String(),
|
||||
},
|
||||
}
|
||||
|
||||
statusMeta := baseMeta.WithStatus(status)
|
||||
|
||||
suite.EqualValues(OGMeta{
|
||||
Title: "Post by example person!!, @example_account@example.org",
|
||||
Type: "article",
|
||||
Locale: "en",
|
||||
URL: "https://example.org/@example_account/12345",
|
||||
SiteName: "example.org",
|
||||
Description: "**test status**",
|
||||
Media: []OGMedia{
|
||||
{
|
||||
OGType: "image",
|
||||
Alt: "Avatar for example_account",
|
||||
URL: "https://example.org/avatar.jpg",
|
||||
},
|
||||
{
|
||||
// Instance avatar.
|
||||
OGType: "image",
|
||||
URL: "https://example.org/instance-avatar.webp",
|
||||
MIMEType: "image/webp",
|
||||
},
|
||||
},
|
||||
ArticlePublisher: "https://example.org/@example_account",
|
||||
ArticleAuthor: "https://example.org/@example_account",
|
||||
ArticleModifiedTime: "2025-01-18T11:00:00+00:00",
|
||||
ArticlePublishedTime: "2025-01-18T00:00:00+00:00",
|
||||
ProfileUsername: "",
|
||||
}, *statusMeta)
|
||||
}
|
||||
|
||||
func (suite *OpenGraphTestSuite) TestWithStatusWithImage() {
|
||||
baseMeta := OGBase(&apimodel.InstanceV1{
|
||||
AccountDomain: "example.org",
|
||||
Languages: []string{"en"},
|
||||
Thumbnail: "https://example.org/instance-avatar.webp",
|
||||
ThumbnailType: "image/webp",
|
||||
})
|
||||
|
||||
acct := &apimodel.Account{
|
||||
Acct: "example_account",
|
||||
DisplayName: "example person!!",
|
||||
URL: "https://example.org/@example_account",
|
||||
Note: "", // <- empty
|
||||
Username: "example_account",
|
||||
Avatar: "https://example.org/avatar.jpg",
|
||||
}
|
||||
|
||||
imageAttachment := &apimodel.Attachment{
|
||||
ID: "00IMAGE00",
|
||||
Type: "image",
|
||||
URL: util.Ptr("https://example.org/@example_account/12345/example.png"),
|
||||
TextURL: util.Ptr("https://example.org/@example_account/12345/example.png"),
|
||||
PreviewURL: util.Ptr("https://example.org/@example_account/12345/small/example.png"),
|
||||
RemoteURL: nil,
|
||||
PreviewRemoteURL: nil,
|
||||
Meta: &apimodel.MediaMeta{
|
||||
Original: apimodel.MediaDimensions{
|
||||
Width: 1920,
|
||||
Height: 1080,
|
||||
Size: "1920x1080",
|
||||
Aspect: 1920.0 / 1080,
|
||||
},
|
||||
Small: apimodel.MediaDimensions{
|
||||
Width: 320,
|
||||
Height: 240,
|
||||
Size: "320x240",
|
||||
Aspect: 320.0 / 240,
|
||||
},
|
||||
Focus: nil,
|
||||
},
|
||||
Description: util.Ptr("an example image"),
|
||||
Blurhash: util.Ptr("LKE3VIw}0KD%a2o{M|t7NFWps:t7"), // <- from testmodels
|
||||
}
|
||||
|
||||
anotherImageAttachment := &apimodel.Attachment{
|
||||
ID: "00IMAGE11",
|
||||
Type: "image",
|
||||
URL: util.Ptr("https://example.org/@example_account/12345/example2.png"),
|
||||
TextURL: util.Ptr("https://example.org/@example_account/12345/example2.png"),
|
||||
PreviewURL: util.Ptr("https://example.org/@example_account/12345/small/example2.png"),
|
||||
RemoteURL: nil,
|
||||
PreviewRemoteURL: nil,
|
||||
Meta: &apimodel.MediaMeta{
|
||||
Original: apimodel.MediaDimensions{
|
||||
Width: 1000,
|
||||
Height: 1000,
|
||||
Size: "1000x1000",
|
||||
Aspect: 1,
|
||||
},
|
||||
Small: apimodel.MediaDimensions{
|
||||
Width: 200,
|
||||
Height: 200,
|
||||
Size: "200x200",
|
||||
Aspect: 1,
|
||||
},
|
||||
Focus: nil,
|
||||
},
|
||||
Description: util.Ptr("another example image"),
|
||||
Blurhash: util.Ptr("LNABP8o#Dge,S6M}axxVEQjYxWbH"), // <- from testmodels
|
||||
}
|
||||
|
||||
apiStatus := &apimodel.Status{
|
||||
ID: "12345",
|
||||
CreatedAt: "2025-01-18T00:00:00+00:00",
|
||||
EditedAt: util.Ptr("2025-01-18T11:00:00+00:00"),
|
||||
Sensitive: false,
|
||||
SpoilerText: "",
|
||||
Visibility: typeutils.VisToAPIVis(gtsmodel.VisibilityPublic),
|
||||
LocalOnly: false,
|
||||
Language: util.Ptr("en"),
|
||||
URI: "https://example.org/statuses/12345",
|
||||
URL: "https://example.org/@example_account/12345",
|
||||
Content: "<b>test status</b>",
|
||||
Account: acct,
|
||||
MediaAttachments: []*apimodel.Attachment{imageAttachment, anotherImageAttachment},
|
||||
Text: "**test status**",
|
||||
ContentType: apimodel.StatusContentTypeMarkdown,
|
||||
}
|
||||
|
||||
webAttachment := &apimodel.WebAttachment{
|
||||
Attachment: imageAttachment,
|
||||
Sensitive: false,
|
||||
MIMEType: "image/png",
|
||||
PreviewMIMEType: "image/png",
|
||||
ParentStatusLink: "https://example.org/@example_account/12345",
|
||||
}
|
||||
|
||||
anotherWebAttachment := &apimodel.WebAttachment{
|
||||
Attachment: anotherImageAttachment,
|
||||
Sensitive: false,
|
||||
MIMEType: "image/png",
|
||||
PreviewMIMEType: "image/png",
|
||||
ParentStatusLink: "https://example.org/@example_account/12345",
|
||||
}
|
||||
|
||||
status := &apimodel.WebStatus{
|
||||
Status: apiStatus,
|
||||
SpoilerContent: "", // <- empty
|
||||
MediaAttachments: []*apimodel.WebAttachment{webAttachment, anotherWebAttachment},
|
||||
Account: &apimodel.WebAccount{
|
||||
Account: acct,
|
||||
AvatarAttachment: nil,
|
||||
HeaderAttachment: nil,
|
||||
WebLayout: gtsmodel.WebLayoutMicroblog.String(),
|
||||
},
|
||||
}
|
||||
|
||||
statusMeta := baseMeta.WithStatus(status)
|
||||
|
||||
suite.EqualValues(OGMeta{
|
||||
Title: "Post by example person!!, @example_account@example.org",
|
||||
Type: "article",
|
||||
Locale: "en",
|
||||
URL: "https://example.org/@example_account/12345",
|
||||
SiteName: "example.org",
|
||||
Description: "**test status**",
|
||||
Media: []OGMedia{
|
||||
{
|
||||
OGType: "image",
|
||||
Alt: "an example image",
|
||||
URL: "https://example.org/@example_account/12345/example.png",
|
||||
MIMEType: "image/png",
|
||||
Width: "1920",
|
||||
Height: "1080",
|
||||
},
|
||||
},
|
||||
ArticlePublisher: "https://example.org/@example_account",
|
||||
ArticleAuthor: "https://example.org/@example_account",
|
||||
ArticleModifiedTime: "2025-01-18T11:00:00+00:00",
|
||||
ArticlePublishedTime: "2025-01-18T00:00:00+00:00",
|
||||
ProfileUsername: "",
|
||||
}, *statusMeta)
|
||||
}
|
||||
|
||||
func TestOpenGraphTestSuite(t *testing.T) {
|
||||
suite.Run(t, &OpenGraphTestSuite{})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user