Skip to content

Commit bd5b520

Browse files
authored
Merge pull request #1255 from getfider/tiptap
Featured comment editor
2 parents 651469a + 9eb8db1 commit bd5b520

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1983
-811
lines changed

app/handlers/apiv1/post.go

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package apiv1
22

33
import (
4-
"fmt"
5-
64
"github.com/getfider/fider/app/actions"
75
"github.com/getfider/fider/app/metrics"
86
"github.com/getfider/fider/app/models/cmd"
@@ -11,7 +9,6 @@ import (
119
"github.com/getfider/fider/app/models/query"
1210
"github.com/getfider/fider/app/pkg/bus"
1311
"github.com/getfider/fider/app/pkg/env"
14-
"github.com/getfider/fider/app/pkg/markdown"
1512
"github.com/getfider/fider/app/pkg/web"
1613
"github.com/getfider/fider/app/tasks"
1714
)
@@ -215,9 +212,9 @@ func ListComments() web.HandlerFunc {
215212
return c.Failure(err)
216213
}
217214

218-
// the content of the comment needs to be sanitized before it is returned
219215
for _, comment := range getComments.Result {
220-
comment.Content = markdown.StripMentionMetaData(comment.Content)
216+
commentString := entity.CommentString(comment.Content)
217+
comment.Content = commentString.SanitizeMentions()
221218
}
222219

223220
return c.Ok(getComments.Result)
@@ -237,7 +234,8 @@ func GetComment() web.HandlerFunc {
237234
return c.Failure(err)
238235
}
239236

240-
commentByID.Result.Content = markdown.StripMentionMetaData(commentByID.Result.Content)
237+
commentString := entity.CommentString(commentByID.Result.Content)
238+
commentByID.Result.Content = commentString.SanitizeMentions()
241239

242240
return c.Ok(commentByID.Result)
243241
}
@@ -289,10 +287,8 @@ func PostComment() web.HandlerFunc {
289287
}
290288

291289
addNewComment := &cmd.AddNewComment{
292-
Post: getPost.Result,
293-
Content: entity.CommentString(action.Content).FormatMentionJson(func(mention entity.Mention) string {
294-
return fmt.Sprintf(`{"id":%d,"name":"%s"}`, mention.ID, mention.Name)
295-
}),
290+
Post: getPost.Result,
291+
Content: action.Content,
296292
}
297293
if err := bus.Dispatch(c, addNewComment); err != nil {
298294
return c.Failure(err)
@@ -331,9 +327,10 @@ func UpdateComment() web.HandlerFunc {
331327
return c.Failure(err)
332328
}
333329

334-
contentToSave := entity.CommentString(action.Content).FormatMentionJson(func(mention entity.Mention) string {
335-
return fmt.Sprintf(`{"id":%d,"name":"%s"}`, mention.ID, mention.Name)
336-
})
330+
comment := &entity.Comment{
331+
ID: action.ID,
332+
Content: action.Content,
333+
}
337334

338335
err := bus.Dispatch(c,
339336
&cmd.UploadImages{
@@ -342,7 +339,7 @@ func UpdateComment() web.HandlerFunc {
342339
},
343340
&cmd.UpdateComment{
344341
CommentID: action.ID,
345-
Content: contentToSave,
342+
Content: action.Content,
346343
},
347344
&cmd.SetAttachments{
348345
Post: action.Post,
@@ -356,7 +353,7 @@ func UpdateComment() web.HandlerFunc {
356353

357354
// Update the content
358355

359-
c.Enqueue(tasks.NotifyAboutUpdatedComment(action.Content, getPost.Result))
356+
c.Enqueue(tasks.NotifyAboutUpdatedComment(getPost.Result, comment))
360357

361358
return c.Ok(web.Map{})
362359
}

app/handlers/apiv1/post_test.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ func TestCreatePostHandler_WithoutTitle(t *testing.T) {
6767
func TestCreatePostHandler_WithNonExistentTag(t *testing.T) {
6868
if env.Config.PostCreationWithTagsEnabled {
6969
RegisterT(t)
70-
70+
7171
bus.AddHandler(func(ctx context.Context, q *query.GetTagBySlug) error {
7272
return app.ErrNotFound
7373
})
7474
bus.AddHandler(func(ctx context.Context, q *query.GetPostBySlug) error {
7575
return app.ErrNotFound
7676
})
77-
77+
7878
code, _ := mock.NewServer().
7979
OnTenant(mock.DemoTenant).
8080
AsUser(mock.JonSnow).
@@ -87,7 +87,7 @@ func TestCreatePostHandler_WithNonExistentTag(t *testing.T) {
8787
func TestCreatePostHandler_WithPrivateTagAsVisitor(t *testing.T) {
8888
if env.Config.PostCreationWithTagsEnabled {
8989
RegisterT(t)
90-
90+
9191
privateTag := &entity.Tag{
9292
ID: 1,
9393
Name: "private_tag",
@@ -102,11 +102,11 @@ func TestCreatePostHandler_WithPrivateTagAsVisitor(t *testing.T) {
102102
}
103103
return app.ErrNotFound
104104
})
105-
105+
106106
bus.AddHandler(func(ctx context.Context, q *query.GetPostBySlug) error {
107107
return app.ErrNotFound
108108
})
109-
109+
110110
code, _ := mock.NewServer().
111111
OnTenant(mock.DemoTenant).
112112
AsUser(mock.AryaStark).
@@ -119,7 +119,7 @@ func TestCreatePostHandler_WithPrivateTagAsVisitor(t *testing.T) {
119119
func TestCreatePostHandler_WithPublicTagAsVisitor(t *testing.T) {
120120
if env.Config.PostCreationWithTagsEnabled {
121121
RegisterT(t)
122-
122+
123123
var newPost *cmd.AddNewPost
124124
bus.AddHandler(func(ctx context.Context, c *cmd.AddNewPost) error {
125125
newPost = c
@@ -130,7 +130,7 @@ func TestCreatePostHandler_WithPublicTagAsVisitor(t *testing.T) {
130130
}
131131
return nil
132132
})
133-
133+
134134
publicTag := &entity.Tag{
135135
ID: 1,
136136
Name: "public_tag",
@@ -145,21 +145,21 @@ func TestCreatePostHandler_WithPublicTagAsVisitor(t *testing.T) {
145145
}
146146
return app.ErrNotFound
147147
})
148-
148+
149149
var tagAssignment *cmd.AssignTag
150150
bus.AddHandler(func(ctx context.Context, c *cmd.AssignTag) error {
151151
tagAssignment = c
152152
return nil
153153
})
154-
154+
155155
bus.AddHandler(func(ctx context.Context, q *query.GetPostBySlug) error {
156156
return app.ErrNotFound
157157
})
158-
158+
159159
bus.AddHandler(func(ctx context.Context, c *cmd.SetAttachments) error { return nil })
160160
bus.AddHandler(func(ctx context.Context, c *cmd.AddVote) error { return nil })
161161
bus.AddHandler(func(ctx context.Context, c *cmd.UploadImages) error { return nil })
162-
162+
163163
code, _ := mock.NewServer().
164164
OnTenant(mock.DemoTenant).
165165
AsUser(mock.AryaStark).
@@ -174,7 +174,7 @@ func TestCreatePostHandler_WithPublicTagAsVisitor(t *testing.T) {
174174
func TestCreatePostHandler_WithPublicTagAndPrivateTagAsCollaborator(t *testing.T) {
175175
if env.Config.PostCreationWithTagsEnabled {
176176
RegisterT(t)
177-
177+
178178
var newPost *cmd.AddNewPost
179179
bus.AddHandler(func(ctx context.Context, c *cmd.AddNewPost) error {
180180
newPost = c
@@ -185,7 +185,7 @@ func TestCreatePostHandler_WithPublicTagAndPrivateTagAsCollaborator(t *testing.T
185185
}
186186
return nil
187187
})
188-
188+
189189
publicTag := &entity.Tag{
190190
ID: 1,
191191
Name: "public_tag",
@@ -211,7 +211,7 @@ func TestCreatePostHandler_WithPublicTagAndPrivateTagAsCollaborator(t *testing.T
211211
}
212212
return app.ErrNotFound
213213
})
214-
214+
215215
tagAssignments := make([]*cmd.AssignTag, 2)
216216
bus.AddHandler(func(ctx context.Context, c *cmd.AssignTag) error {
217217
if c.Tag.Slug == "public_tag" {
@@ -221,15 +221,15 @@ func TestCreatePostHandler_WithPublicTagAndPrivateTagAsCollaborator(t *testing.T
221221
}
222222
return nil
223223
})
224-
224+
225225
bus.AddHandler(func(ctx context.Context, q *query.GetPostBySlug) error {
226226
return app.ErrNotFound
227227
})
228-
228+
229229
bus.AddHandler(func(ctx context.Context, c *cmd.SetAttachments) error { return nil })
230230
bus.AddHandler(func(ctx context.Context, c *cmd.AddVote) error { return nil })
231231
bus.AddHandler(func(ctx context.Context, c *cmd.UploadImages) error { return nil })
232-
232+
233233
code, _ := mock.NewServer().
234234
OnTenant(mock.DemoTenant).
235235
AsUser(mock.JonSnow).
@@ -746,11 +746,11 @@ func TestPostCommentHandlerMentions(t *testing.T) {
746746
OnTenant(mock.DemoTenant).
747747
AsUser(mock.JonSnow).
748748
AddParam("number", post.Number).
749-
ExecutePost(apiv1.PostComment(), `{ "content": "Hello @{\"id\":1,\"name\":\"Jon Snow\",\"isNew\":true}!" }`)
749+
ExecutePost(apiv1.PostComment(), `{ "content": "Hello @[Jon Snow]!" }`)
750750

751751
Expect(code).Equals(http.StatusOK)
752752
Expect(newComment.Post).Equals(post)
753-
Expect(newComment.Content).Equals("Hello @{\"id\":1,\"name\":\"Jon Snow\"}!")
753+
Expect(newComment.Content).Equals("Hello @[Jon Snow]!")
754754
}
755755

756756
func TestPostCommentHandler_WithoutContent(t *testing.T) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package cmd
2+
3+
type AddMentionNotification struct {
4+
UserID int `db:"user_id"`
5+
CommentID int `db:"comment_id"`
6+
}

app/models/entity/comment.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,4 @@ type Comment struct {
2020
EditedAt *time.Time `json:"editedAt,omitempty"`
2121
EditedBy *User `json:"editedBy,omitempty"`
2222
ReactionCounts []ReactionCounts `json:"reactionCounts,omitempty"`
23-
Mentions []Mention `json:"_"`
24-
}
25-
26-
func (c *Comment) ParseMentions() {
27-
mentionString := CommentString(c.Content)
28-
c.Mentions = mentionString.ParseMentions()
2923
}

app/models/entity/mention.go

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,29 @@
11
package entity
22

33
import (
4-
"encoding/json"
54
"regexp"
6-
"strings"
75
)
86

9-
type Mention struct {
10-
ID int `json:"id"`
11-
Name string `json:"name"`
12-
IsNew bool `json:"isNew"`
13-
}
14-
157
type CommentString string
168

17-
const mentionRegex = `@{([^{}]+)}`
9+
const mentionRegex = `@\[(.*?)\]`
1810

19-
func (commentString CommentString) ParseMentions() []Mention {
11+
func (commentString CommentString) ParseMentions() []string {
2012
r, _ := regexp.Compile(mentionRegex)
13+
matches := r.FindAllStringSubmatch(string(commentString), -1)
2114

22-
// Remove escaped quotes from the input string
23-
input := strings.ReplaceAll(string(commentString), `\"`, `"`)
24-
25-
matches := r.FindAllString(input, -1)
26-
27-
mentions := []Mention{}
15+
mentions := []string{}
2816

2917
for _, match := range matches {
30-
31-
jsonMention := match[1:]
32-
33-
var mention Mention
34-
err := json.Unmarshal([]byte(jsonMention), &mention)
35-
if err == nil {
36-
if mention.ID > 0 && mention.Name != "" {
37-
mentions = append(mentions, mention)
38-
}
18+
if len(match) >= 2 && match[1] != "" {
19+
mentions = append(mentions, match[1])
3920
}
4021
}
4122

4223
return mentions
4324
}
4425

45-
func (mentionString CommentString) FormatMentionJson(jsonOperator func(Mention) string) string {
46-
26+
func (commentString CommentString) SanitizeMentions() string {
4727
r, _ := regexp.Compile(mentionRegex)
48-
49-
// Remove escaped quotes from the input string
50-
input := strings.ReplaceAll(string(mentionString), `\"`, `"`)
51-
52-
return r.ReplaceAllStringFunc(input, func(match string) string {
53-
jsonMention := match[1:]
54-
55-
var mention Mention
56-
57-
err := json.Unmarshal([]byte(jsonMention), &mention)
58-
if err != nil {
59-
return match
60-
}
61-
62-
if mention.ID == 0 || mention.Name == "" {
63-
return match
64-
}
65-
66-
return "@" + jsonOperator(mention)
67-
})
68-
28+
return r.ReplaceAllString(string(commentString), "@$1")
6929
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package entity
2+
3+
import (
4+
"time"
5+
)
6+
7+
// NotificationLog represents a record of a notification that was sent to a user
8+
type MentionNotification struct {
9+
ID int `json:"id" db:"id"`
10+
TenantID int `json:"-" db:"tenant_id"`
11+
UserID int `json:"userId" db:"user_id"`
12+
CommentID int `json:"commentId,omitempty" db:"comment_id"`
13+
CreatedAt time.Time `json:"createdAt" db:"created_on"`
14+
}

0 commit comments

Comments
 (0)