Added support for recording tags with user activity.

This commit is contained in:
Nick Gerakines 2020-03-01 01:27:06 -05:00
parent 6c7185c508
commit a4ad0c4213
5 changed files with 56 additions and 23 deletions

View File

@ -11,4 +11,9 @@ var tagRegex = `(?:^|\B)([#]{1}(\w+))`
func FindMentionedActors(input string) []string {
mr := regexp.MustCompile(mentionRegex)
return mr.FindAllString(input, -1)
}
}
func FindMentionedTags(input string) []string {
tr := regexp.MustCompile(tagRegex)
return tr.FindAllString(input, -1)
}

View File

@ -24,8 +24,9 @@ type UserStorage interface {
GetUserByName(ctx context.Context, name string) (*User, error)
GetUserBySession(ctx context.Context, session sessions.Session) (*User, error)
UpdateUserLastAuth(ctx context.Context, userID uuid.UUID) error
RecordUserActivity(ctx context.Context, userID, activityID uuid.UUID) error
PaginateUseActivity(ctx context.Context, userID uuid.UUID, limit, offset int) ([]UserFeed, error)
RecordUserActivity(ctx context.Context, userID, activityID uuid.UUID, isPublic bool, tags []string) error
PaginateUserActivity(ctx context.Context, userID uuid.UUID, limit, offset int) ([]UserFeed, error)
PublicUserActivityByTag(ctx context.Context, tag string, limit, offset int) ([]UserFeed, error)
}
type User struct {

View File

@ -4,20 +4,30 @@ import (
"context"
"github.com/gofrs/uuid"
"github.com/lib/pq"
"github.com/ngerakines/tavern/errors"
)
func (s pgStorage) RecordUserActivity(ctx context.Context, userID, activityID uuid.UUID) error {
_, err := s.db.ExecContext(ctx, "INSERT INTO user_activities (id, user_id, activity_id, created_at) VALUES ($1, $2, $3, $4)", NewV4(), userID, activityID, s.now())
func (s pgStorage) RecordUserActivity(ctx context.Context, userID, activityID uuid.UUID, isPublic bool, tags []string) error {
_, err := s.db.ExecContext(ctx, `INSERT INTO user_activities (id, user_id, activity_id, created_at, "public", tags) VALUES ($1, $2, $3, $4, $5, $6)`, NewV4(), userID, activityID, s.now(), isPublic, pq.Array(tags))
return errors.WrapInsertQueryFailedError(err)
}
func (s pgStorage) PaginateUseActivity(ctx context.Context, userID uuid.UUID, limit, offset int) ([]UserFeed, error) {
func (s pgStorage) PaginateUserActivity(ctx context.Context, userID uuid.UUID, limit, offset int) ([]UserFeed, error) {
query := `SELECT id, user_id, activity_id, created_at FROM user_activities WHERE user_id = $3 ORDER BY created_at ASC LIMIT $1 OFFSET $2`
return s.queryUserActivity(ctx, query, limit, offset, userID)
}
func (s pgStorage) PublicUserActivityByTag(ctx context.Context, tag string, limit, offset int) ([]UserFeed, error) {
query := `SELECT id, user_id, activity_id, created_at FROM user_activities WHERE public = TRUE AND tags @> ARRAY[$3]::varchar[] ORDER BY created_at ASC LIMIT $1 OFFSET $2`
return s.queryUserActivity(ctx, query, limit, offset, tag)
}
func (s pgStorage) queryUserActivity(ctx context.Context, query string, args ...interface{}) ([]UserFeed, error) {
var results []UserFeed
rows, err := s.db.QueryContext(ctx, query, limit, offset, userID)
rows, err := s.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, errors.NewSelectQueryFailedError(err)
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"math"
"net/http"
"strings"
"time"
"github.com/gin-contrib/sessions"
@ -58,7 +59,7 @@ func (h handler) dashboardNotes(c *gin.Context) {
return
}
uf, err := h.storage.PaginateUseActivity(ctx, user.ID, limit, (page-1)*limit)
uf, err := h.storage.PaginateUserActivity(ctx, user.ID, limit, (page-1)*limit)
if err != nil {
h.hardFail(c, err)
return
@ -166,6 +167,11 @@ func (h handler) createNote(c *gin.Context) {
}
}
mentionedTags := storage.FindMentionedTags(content)
isPublic := true
firstTags := make([]string, 0)
createNoteID := storage.NewV4()
createNote := storage.EmptyPayload()
createNote["@context"] = "https://www.w3.org/ns/activitystreams"
@ -189,7 +195,7 @@ func (h handler) createNote(c *gin.Context) {
note["type"] = "Note"
note["url"] = activityURL
if len(mentionedActors) > 0 {
if len(mentionedActors)+len(mentionedTags) > 0 {
tag := make([]map[string]interface{}, 0)
for name, actor := range mentionedActors {
tag = append(tag, map[string]interface{}{
@ -198,6 +204,17 @@ func (h handler) createNote(c *gin.Context) {
"name": name,
})
}
for _, hashtag := range mentionedTags {
clean := strings.TrimPrefix(hashtag, "#")
tag = append(tag, map[string]interface{}{
"type": "Hashtag",
"href": fmt.Sprintf("https://%s/tags/%s", h.domain, clean),
"name": hashtag,
})
if len(firstTags) < 5 {
firstTags = append(firstTags, clean)
}
}
note["tag"] = tag
}
@ -217,7 +234,7 @@ func (h handler) createNote(c *gin.Context) {
return
}
err = h.storage.RecordUserActivity(ctx, user.ID, objectEventID)
err = h.storage.RecordUserActivity(ctx, user.ID, objectEventID, isPublic, firstTags)
if err != nil {
h.flashErrorOrFail(c, "/dashboard/notes", err)
return
@ -319,7 +336,7 @@ func (h handler) announceNote(c *gin.Context) {
return
}
err = h.storage.RecordUserActivity(ctx, user.ID, objectEventID)
err = h.storage.RecordUserActivity(ctx, user.ID, objectEventID, true, []string{})
if err != nil {
h.flashErrorOrFail(c, "/dashboard/notes", err)
return

View File

@ -14,10 +14,10 @@ import (
)
func (h handler) nodeInfo(c *gin.Context) {
if !requireAccept(c, "application/json") {
h.writeJSONError(c, http.StatusNotAcceptable, fmt.Errorf("client does not indicate that they accept application/json responses"))
return
}
// if !requireAccept(c, "application/json") {
// h.writeJSONError(c, http.StatusNotAcceptable, fmt.Errorf("client does not indicate that they accept application/json responses"))
// return
// }
c.JSON(http.StatusOK, map[string]interface{}{
"links": []interface{}{
@ -30,10 +30,10 @@ func (h handler) nodeInfo(c *gin.Context) {
}
func (h handler) nodeInfoDetails(c *gin.Context) {
if !requireAccept(c, "application/json") {
h.writeJSONError(c, http.StatusNotAcceptable, fmt.Errorf("client does not indicate that they accept application/json responses"))
return
}
// if !requireAccept(c, "application/json") {
// h.writeJSONError(c, http.StatusNotAcceptable, fmt.Errorf("client does not indicate that they accept application/json responses"))
// return
// }
c.JSON(http.StatusOK, map[string]interface{}{
"version": "2.0",
@ -55,10 +55,10 @@ func (h handler) nodeInfoDetails(c *gin.Context) {
}
func (h handler) webFinger(c *gin.Context) {
if !requireAccept(c, "application/jrd+json") {
h.writeJSONError(c, http.StatusNotAcceptable, fmt.Errorf("client does not indicate that they accept application/jrd+json responses"))
return
}
// if !requireAccept(c, "application/jrd+json") {
// h.writeJSONError(c, http.StatusNotAcceptable, fmt.Errorf("client does not indicate that they accept application/jrd+json responses"))
// return
// }
username, domain, err := fingerUserDomain(c.Query("resource"), h.domain)
if err != nil {