gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 67106c9dc4b2139878ba1fc44ef268f0a4efd804
parent 2216f253c18491b01efce09d00c5f1962dd74c96
Author: Michael Manfre <mmanfre@gmail.com>
Date:   Sat, 19 Nov 2022 04:07:51 -0500

[feature] Support markdown format for Account bio/note (#1037)

* [feature] Status format also controls bio format

* test
Diffstat:
Mdocs/user_guide/posts.md | 2+-
Mdocs/user_guide/user_panel.md | 2++
Minternal/processing/account/update.go | 12++++++++----
Minternal/processing/account/update_test.go | 38++++++++++++++++++++++++++++++++++++++
Mweb/source/settings/user/profile.js | 6+++---
Mweb/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 @@ -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 @@ -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 @@ -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 @@ -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 := `<p><em>hello</em> <del>here</del> i am!</p>` + + form := &apimodel.UpdateCredentialsRequest{ + Note: &note, + } + + // 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 @@ -69,14 +69,14 @@ module.exports = function UserProfile() { <div className="files"> <div> <h3>Header</h3> - <File + <File id="header" fileType="image/*" /> </div> <div> <h3>Avatar</h3> - <File + <File id="avatar" fileType="image/*" /> @@ -101,7 +101,7 @@ module.exports = function UserProfile() { id="enable_rss" name="Enable RSS feed of Public posts" /> - { !allowCustomCSS ? null : + { !allowCustomCSS ? null : <TextArea id="custom_css" name="Custom CSS" diff --git a/web/source/settings/user/settings.js b/web/source/settings/user/settings.js @@ -62,7 +62,7 @@ module.exports = function UserSettings() { }> <a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#privacy-settings" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post privacy settings (opens in a new tab)</a> </Select> - <Select id="source.status_format" name="Default post format" options={ + <Select id="source.status_format" name="Default post (and bio) format" options={ <> <option value="plain">Plain (default)</option> <option value="markdown">Markdown</option> @@ -99,7 +99,7 @@ function PasswordChange() { setError("New password and confirm new password did not match!"); return; } - + setStatus("PATCHing"); setError(""); return Promise.try(() => {