From 67106c9dc4b2139878ba1fc44ef268f0a4efd804 Mon Sep 17 00:00:00 2001 From: Michael Manfre Date: Sat, 19 Nov 2022 04:07:51 -0500 Subject: [PATCH] [feature] Support markdown format for Account bio/note (#1037) * [feature] Status format also controls bio format * test --- docs/user_guide/posts.md | 2 +- docs/user_guide/user_panel.md | 2 ++ internal/processing/account/update.go | 12 ++++--- internal/processing/account/update_test.go | 38 ++++++++++++++++++++++ web/source/settings/user/profile.js | 6 ++-- web/source/settings/user/settings.js | 4 +-- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/docs/user_guide/posts.md b/docs/user_guide/posts.md index 1e3b827b5..a5c33cf25 100644 --- a/docs/user_guide/posts.md +++ b/docs/user_guide/posts.md @@ -106,7 +106,7 @@ When set to `false`, likes/faves of your post will not be accepted by your GoToS ## Input Types -GoToSocial currently accepts two different types of input for posts. These are: +GoToSocial currently accepts two different types of input for posts (and user bio). These are: * `plain` * `markdown` diff --git a/docs/user_guide/user_panel.md b/docs/user_guide/user_panel.md index 41200d275..fc5c54835 100644 --- a/docs/user_guide/user_panel.md +++ b/docs/user_guide/user_panel.md @@ -32,6 +32,8 @@ Your bio is a longer text that introduces your account and your self. Your bio i - Describe your boundaries and preferences when it comes to other people interacting with you - Link hashtags that you often use when you post +The bio accepts either `plain` or `markdown` formatting. This is set by the default post format setting described in [Post Settings](#post-settings). + After updating your display name and bio, click on the `Save profile info` button at the bottom of the Profile Info section to save your changes. ### Manually Approve Followers / Lock Your Account diff --git a/internal/processing/account/update.go b/internal/processing/account/update.go index bc4570c76..ed9fa6d4d 100644 --- a/internal/processing/account/update.go +++ b/internal/processing/account/update.go @@ -66,7 +66,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form account.NoteRaw = *form.Note // Process note to generate a valid HTML representation - note, err := p.processNote(ctx, *form.Note, account.ID) + note, err := p.processNote(ctx, *form.Note, account) if err != nil { return nil, gtserror.NewErrorBadRequest(err) } @@ -241,13 +241,13 @@ func (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHead return processingMedia.LoadAttachment(ctx) } -func (p *processor) processNote(ctx context.Context, note string, accountID string) (string, error) { +func (p *processor) processNote(ctx context.Context, note string, account *gtsmodel.Account) (string, error) { if note == "" { return "", nil } tagStrings := util.DeriveHashtagsFromText(note) - tags, err := p.db.TagStringsToTags(ctx, tagStrings, accountID) + tags, err := p.db.TagStringsToTags(ctx, tagStrings, account.ID) if err != nil { return "", err } @@ -255,7 +255,7 @@ func (p *processor) processNote(ctx context.Context, note string, accountID stri mentionStrings := util.DeriveMentionNamesFromText(note) mentions := []*gtsmodel.Mention{} for _, mentionString := range mentionStrings { - mention, err := p.parseMention(ctx, mentionString, accountID, "") + mention, err := p.parseMention(ctx, mentionString, account.ID, "") if err != nil { continue } @@ -266,5 +266,9 @@ func (p *processor) processNote(ctx context.Context, note string, accountID stri // emojiStrings := util.DeriveEmojisFromText(note) // emojis, err := p.db.EmojiStringsToEmojis(ctx, emojiStrings) + if account.StatusFormat == "markdown" { + return p.formatter.FromMarkdown(ctx, note, mentions, tags, nil), nil + } + return p.formatter.FromPlain(ctx, note, mentions, tags), nil } diff --git a/internal/processing/account/update_test.go b/internal/processing/account/update_test.go index 0483154c6..3f6c338e8 100644 --- a/internal/processing/account/update_test.go +++ b/internal/processing/account/update_test.go @@ -112,6 +112,44 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMention() { suite.Equal(noteExpected, dbAccount.Note) } +func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMarkdownNote() { + testAccount := suite.testAccounts["local_account_1"] + + note := "*hello* ~~here~~ i am!" + expectedNote := `

hello here i am!

` + + form := &apimodel.UpdateCredentialsRequest{ + Note: ¬e, + } + + // set default post language of account 1 to markdown + testAccount.StatusFormat = "markdown" + + // should get no error from the update function, and an api model account returned + apiAccount, errWithCode := suite.accountProcessor.Update(context.Background(), testAccount, form) + // reset test account to avoid breaking other tests + testAccount.StatusFormat = "plain" + suite.NoError(errWithCode) + suite.NotNil(apiAccount) + + // fields on the profile should be updated + suite.Equal(expectedNote, apiAccount.Note) + + // we should have an update in the client api channel + msg := <-suite.fromClientAPIChan + suite.Equal(ap.ActivityUpdate, msg.APActivityType) + suite.Equal(ap.ObjectProfile, msg.APObjectType) + suite.NotNil(msg.OriginAccount) + suite.Equal(testAccount.ID, msg.OriginAccount.ID) + suite.Nil(msg.TargetAccount) + + // fields should be updated in the database as well + dbAccount, err := suite.db.GetAccountByID(context.Background(), testAccount.ID) + suite.NoError(err) + suite.Equal(expectedNote, dbAccount.Note) + +} + func TestAccountUpdateTestSuite(t *testing.T) { suite.Run(t, new(AccountUpdateTestSuite)) } diff --git a/web/source/settings/user/profile.js b/web/source/settings/user/profile.js index 21e86ce66..dfd1eee95 100644 --- a/web/source/settings/user/profile.js +++ b/web/source/settings/user/profile.js @@ -69,14 +69,14 @@ module.exports = function UserProfile() {

Header

-

Avatar

- @@ -101,7 +101,7 @@ module.exports = function UserProfile() { id="enable_rss" name="Enable RSS feed of Public posts" /> - { !allowCustomCSS ? null : + { !allowCustomCSS ? null :