mirror of https://gitlab.com/ngerakines/tavern.git
Implemented delete note. Closes #38.
This commit is contained in:
parent
5eb7483da5
commit
a641fe2cf8
|
@ -2,11 +2,13 @@ package common
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
func ActivityURL(domain string, activityID uuid.UUID) string {
|
||||
func ActorURL(domain string, name interface{}) string {
|
||||
return fmt.Sprintf("https://%s/users/%s", domain, name)
|
||||
}
|
||||
|
||||
func ActivityURL(domain string, activityID interface{}) string {
|
||||
return fmt.Sprintf("https://%s/activity/%s", domain, activityID)
|
||||
}
|
||||
|
||||
|
@ -14,10 +16,10 @@ func ObjectURL(domain string, objectID interface{}) string {
|
|||
return fmt.Sprintf("https://%s/object/%s", domain, objectID)
|
||||
}
|
||||
|
||||
func ObjectRepliesURL(domain string, objectID uuid.UUID) string {
|
||||
func ObjectRepliesURL(domain string, objectID interface{}) string {
|
||||
return fmt.Sprintf("https://%s/object/%s/replies", domain, objectID)
|
||||
}
|
||||
|
||||
func ObjectRepliesPageURL(domain string, objectID uuid.UUID, page int) string {
|
||||
func ObjectRepliesPageURL(domain string, objectID interface{}, page int) string {
|
||||
return fmt.Sprintf("https://%s/object/%s/replies?page=%d", domain, objectID, page)
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ type ObjectStorage interface {
|
|||
ListObjectPayloadsInObjectReplies(ctx context.Context, objectID uuid.UUID, limit int, offset int) ([]Payload, error)
|
||||
RecordObjectReply(ctx context.Context, objectID, parentObjectID uuid.UUID) (uuid.UUID, error)
|
||||
RecordObjectReplyAll(ctx context.Context, rowID uuid.UUID, createdAt, updatedAt time.Time, objectID, parentObjectID uuid.UUID) (uuid.UUID, error)
|
||||
|
||||
UpdateObjectPayload(ctx context.Context, objectRowID uuid.UUID, payload Payload) error
|
||||
}
|
||||
|
||||
func (s pgStorage) ListObjectPayloadsByObjectIDs(ctx context.Context, objectIDs []string) ([]Payload, error) {
|
||||
|
@ -127,7 +129,7 @@ func (s pgStorage) CountObjectPayloadsInUserConversation(ctx context.Context, us
|
|||
}
|
||||
|
||||
func (s pgStorage) ListObjectPayloadsInUserConversation(ctx context.Context, userID uuid.UUID, conversation string) ([]Payload, error) {
|
||||
query := `SELECT o.payload FROM objects o INNER JOIN user_conversations uc ON uc.object_id = o.id WHERE uc.user_id = $1 AND uc.conversation = $2`
|
||||
query := `SELECT o.payload FROM objects o INNER JOIN user_conversations uc ON uc.object_id = o.id WHERE uc.user_id = $1 AND uc.conversation = $2 ORDER BY uc.created_at ASC`
|
||||
return s.objectPayloads(ctx, query, userID, conversation)
|
||||
}
|
||||
|
||||
|
@ -319,3 +321,9 @@ func (s pgStorage) RecordObjectReplyAll(ctx context.Context, rowID uuid.UUID, cr
|
|||
err := s.db.QueryRowContext(ctx, query, rowID, createdAt, updatedAt, objectID, parentObjectID).Scan(&id)
|
||||
return id, errors.WrapInsertQueryFailedError(err)
|
||||
}
|
||||
|
||||
func (s pgStorage) UpdateObjectPayload(ctx context.Context, objectRowID uuid.UUID, payload Payload) error {
|
||||
now := s.now()
|
||||
_, err := s.db.ExecContext(ctx, "UPDATE objects SET payload = $3, updated_at = $2 WHERE id = $1", objectRowID, now, payload)
|
||||
return errors.WrapUpdateQueryFailedError(err)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,21 @@
|
|||
newForm.hide().appendTo("body").submit();
|
||||
});
|
||||
|
||||
{{ if and .feed_view (eq .feed_view "mine") }}
|
||||
$('body').on('click', 'a.owner_delete', function () {
|
||||
let newForm = jQuery('<form>', {
|
||||
'action': '/notes/delete',
|
||||
'method': 'post',
|
||||
'target': '_top'
|
||||
}).append(jQuery('<input>', {
|
||||
'name': 'object_id',
|
||||
'value': $(this).data('object'),
|
||||
'type': 'hidden'
|
||||
}));
|
||||
newForm.hide().appendTo("body").submit();
|
||||
});
|
||||
{{ end }}
|
||||
|
||||
{{ if .latest }}
|
||||
var latest = "{{ .latest }}";
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
View Conversation
|
||||
</a>
|
||||
{{ end }}
|
||||
{{- if $note.can_delete -}}
|
||||
<a href="#" class="dropdown-item owner_delete"
|
||||
data-object="{{ $note.object_id }}">Delete</a>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
|
|
|
@ -5,13 +5,16 @@
|
|||
{{ if .feed }}
|
||||
<ul class="list-unstyled activity-feed">
|
||||
{{ range $item := .feed }}
|
||||
{{ if or $item.create_note $item.announce_note }}
|
||||
{{ if or $item.create_note $item.announce_note $item.tombstone }}
|
||||
{{ if $item.create_note }}
|
||||
{{template "activity_create_note" (dict "item" $item "actors" $actors "announcements" $announcements "media" $media) }}
|
||||
{{ end }}
|
||||
{{ if $item.announce_note }}
|
||||
{{template "activity_announce_note" (dict "item" $item "actors" $actors "announcements" $announcements "media" $media) }}
|
||||
{{ end }}
|
||||
{{ if $item.tombstone }}
|
||||
{{template "activity_tombstone" (dict "item" $item) }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<p class="text-danger">Unexpected item</p>
|
||||
{{ end }}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{{ define "activity_tombstone" }}
|
||||
{{ with $item := .item }}
|
||||
{{ with $tombstone := $item.tombstone }}
|
||||
<li class="media mb-3 border-top border-secondary pt-2">
|
||||
<div class="media-body">
|
||||
<h3 class="text-danger">Object Removed</h3>
|
||||
<ul class="text-muted">
|
||||
{{ with $tombstone.object_id }}
|
||||
<li>{{ . }}</li>
|
||||
{{ end }}
|
||||
{{ with $tombstone.deleted_at}}
|
||||
<li>Deleted {{ date . }}</li>
|
||||
{{ end }}
|
||||
{{ with $tombstone.former_type }}
|
||||
<li>Former Type "{{ . }}"</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
|
@ -157,6 +157,7 @@ func serverCommandAction(cliCtx *cli.Context) error {
|
|||
"partials/activity_feed",
|
||||
"partials/activity_create_note",
|
||||
"partials/activity_announce_note",
|
||||
"partials/activity_tombstone",
|
||||
},
|
||||
Funcs: template.FuncMap{
|
||||
"date": tmplDate,
|
||||
|
@ -320,6 +321,7 @@ func serverCommandAction(cliCtx *cli.Context) error {
|
|||
authenticated.GET("/compose", h.compose)
|
||||
authenticated.POST("/compose/create/note", h.createNote)
|
||||
authenticated.POST("/dashboard/notes/announce/note", h.announceNote)
|
||||
authenticated.POST("/notes/delete", h.deleteNote)
|
||||
|
||||
authenticated.GET("/configure", h.configure)
|
||||
authenticated.POST("/configure/user", h.saveUserSettings)
|
||||
|
|
|
@ -86,6 +86,8 @@ func (h handler) actorInbox(c *gin.Context) {
|
|||
h.actorInboxCreate(c, user, payload)
|
||||
case "Announce":
|
||||
h.actorInboxAnnounce(c, user, payload)
|
||||
case "Delete":
|
||||
h.actorInboxDelete(c, user, payload)
|
||||
default:
|
||||
h.logger.Warn("User received unexpected payload type", zap.String("type", payloadType), zap.String("user", name))
|
||||
c.Status(http.StatusOK)
|
||||
|
@ -534,6 +536,73 @@ func (h handler) actorInboxAnnounce(c *gin.Context, user *storage.User, payload
|
|||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h handler) actorInboxDelete(c *gin.Context, user *storage.User, payload storage.Payload) {
|
||||
err := h.verifySignature(c)
|
||||
if err != nil {
|
||||
h.unauthorizedJSON(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var objectID string
|
||||
var objectRowID uuid.UUID
|
||||
foundObjectID := false
|
||||
objectIDs := storage.CollectJSONDeepStrings(payload, []string{"object"}, []string{"object", "id"})
|
||||
for _, objectID = range objectIDs {
|
||||
objectRowID, err = h.storage.ObjectRowIDForObjectID(ctx, objectID)
|
||||
if err == nil {
|
||||
foundObjectID = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundObjectID {
|
||||
h.logger.Warn("unable to process delete request",
|
||||
zap.String("user", user.Name),
|
||||
)
|
||||
c.Status(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
activityURL, _ := storage.JSONString(payload, "id")
|
||||
var tombstone storage.Payload
|
||||
if object, hasObject := storage.JSONMap(payload, "object"); hasObject {
|
||||
tombstone = object
|
||||
}
|
||||
if tombstone == nil {
|
||||
// TODO: Replace this with a fetch of the object to get a proper tombstone from the remote instance.
|
||||
tombstone = storage.EmptyPayload()
|
||||
tombstone["type"] = "Tombstone"
|
||||
// TODO: Ensure that the object is, in fact, a note.
|
||||
tombstone["formerType"] = "Note"
|
||||
tombstone["id"] = objectID
|
||||
now := time.Now()
|
||||
deletedAt := now.Format("2006-01-02T15:04:05Z")
|
||||
tombstone["deleted"] = deletedAt
|
||||
}
|
||||
|
||||
txErr := storage.TransactionalStorage(ctx, h.storage, func(storage storage.Storage) error {
|
||||
err = storage.UpdateObjectPayload(ctx, objectRowID, tombstone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = storage.RecordObjectEvent(ctx, activityURL, objectRowID, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
h.internalServerErrorJSON(c, txErr)
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
func skipActorInbox(j storage.Payload) bool {
|
||||
t, _ := storage.JSONString(j, "type")
|
||||
a, _ := storage.JSONString(j, "actor")
|
||||
|
|
|
@ -251,7 +251,7 @@ func (h handler) createNote(c *gin.Context) {
|
|||
}
|
||||
note["cc"] = cc
|
||||
note["type"] = "Note"
|
||||
note["url"] = activityURL
|
||||
note["url"] = noteURL
|
||||
|
||||
replies := storage.EmptyPayload()
|
||||
replies["id"] = common.ObjectRepliesURL(h.domain, createNoteID)
|
||||
|
@ -452,7 +452,7 @@ func (h handler) createNote(c *gin.Context) {
|
|||
}
|
||||
err = nc.SendToInbox(ctx, localActor, foundActor, payload)
|
||||
if err != nil {
|
||||
h.logger.Error("failed sending to mentioned actor", zap.String("target", foundActor.GetID()), zap.String("activity", activityURL))
|
||||
h.logger.Error("failed sending to actor", zap.String("target", foundActor.GetID()), zap.String("activity", activityURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -466,7 +466,7 @@ func (h handler) createNote(c *gin.Context) {
|
|||
}
|
||||
err = nc.SendToInbox(ctx, localActor, foundActor, payload)
|
||||
if err != nil {
|
||||
h.logger.Error("failed sending to mentioned actor", zap.String("target", foundActor.GetID()), zap.String("activity", activityURL))
|
||||
h.logger.Error("failed sending to actor", zap.String("target", foundActor.GetID()), zap.String("activity", activityURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -691,6 +691,116 @@ func (h handler) uploadHash(ctx context.Context, file *multipart.FileHeader) (st
|
|||
return h.storage.CreateImage(ctx, fullLocation, checksum, blurHash, int(written), contentType, bounds.Max.Y, bounds.Max.X, []string{})
|
||||
}
|
||||
|
||||
func (h handler) deleteNote(c *gin.Context) {
|
||||
_, user, _, cont := h.loggedIn(c)
|
||||
if !cont {
|
||||
return
|
||||
}
|
||||
ctx := c.Request.Context()
|
||||
|
||||
userActor, err := h.storage.GetActor(ctx, user.ActorID)
|
||||
if err != nil {
|
||||
h.hardFail(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
objectID := c.PostForm("object_id")
|
||||
|
||||
now := time.Now()
|
||||
deletedAt := now.Format("2006-01-02T15:04:05Z")
|
||||
|
||||
tombstone := storage.EmptyPayload()
|
||||
tombstone["type"] = "Tombstone"
|
||||
// TODO: Ensure that the object is, in fact, a note.
|
||||
tombstone["formerType"] = "Note"
|
||||
tombstone["id"] = objectID
|
||||
tombstone["deleted"] = deletedAt
|
||||
|
||||
activityID := storage.NewV4()
|
||||
activityURL := common.ActivityURL(h.domain, activityID)
|
||||
|
||||
deleteActivity := storage.EmptyPayload()
|
||||
deleteActivity["@context"] = "https://www.w3.org/ns/activitystreams"
|
||||
deleteActivity["id"] = activityURL
|
||||
deleteActivity["type"] = "Delete"
|
||||
deleteActivity["actor"] = common.ActorURL(h.domain, user.Name)
|
||||
deleteActivity["to"] = []string{
|
||||
"https://www.w3.org/ns/activitystreams#Public",
|
||||
}
|
||||
deleteActivity["object"] = tombstone
|
||||
|
||||
payload := deleteActivity.Bytes()
|
||||
|
||||
txErr := storage.TransactionalStorage(ctx, h.storage, func(storage storage.Storage) error {
|
||||
objectRowID, err := storage.ObjectRowIDForObjectID(ctx, objectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Pass in the `Create` activity ID and verify the user, activity, and object all match.
|
||||
count, err := storage.RowCount(ctx, `SELECT COUNT(*) FROM user_object_events WHERE user_id = $1 AND object_id = $2`, user.ID, objectRowID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return errors.NewNotFoundError(nil)
|
||||
}
|
||||
if count > 1 {
|
||||
return fmt.Errorf("more than one activity found for object")
|
||||
}
|
||||
|
||||
err = storage.UpdateObjectPayload(ctx, objectRowID, tombstone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = storage.RecordObjectEvent(ctx, activityURL, objectRowID, deleteActivity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
h.internalServerErrorJSON(c, txErr)
|
||||
return
|
||||
}
|
||||
|
||||
followerTotal, err := h.storage.RowCount(ctx, `SELECT COUNT(*) FROM network_graph WHERE user_id = $1`, user.ID)
|
||||
if err != nil {
|
||||
h.internalServerErrorJSON(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
destinations, err := h.storage.ListAcceptedFollowers(ctx, user.ID, followerTotal, 0)
|
||||
if err != nil {
|
||||
h.internalServerErrorJSON(c, err)
|
||||
return
|
||||
}
|
||||
destinations = uniqueTo(destinations, common.ActivityURL(h.domain, user.Name))
|
||||
|
||||
nc := fed.ActorClient{
|
||||
HTTPClient: h.httpClient,
|
||||
Logger: h.logger,
|
||||
}
|
||||
localActor := h.userActor(user, userActor)
|
||||
|
||||
for _, dest := range destinations {
|
||||
foundActor, err := fed.GetOrFetchActor(ctx, h.storage, h.logger, h.httpClient, dest)
|
||||
if err != nil {
|
||||
h.logger.Error("unable to get or fetch actor", zap.Error(err), zap.String("actor", dest))
|
||||
continue
|
||||
}
|
||||
err = nc.SendToInbox(ctx, localActor, foundActor, payload)
|
||||
if err != nil {
|
||||
h.logger.Error("failed sending to actor", zap.String("target", foundActor.GetID()), zap.String("activity", activityURL))
|
||||
}
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusFound, h.url("feed_mine"))
|
||||
}
|
||||
|
||||
func imageBounds(tempFileLocation string) (image.Rectangle, string, string, error) {
|
||||
file, err := os.Open(tempFileLocation)
|
||||
if err != nil {
|
||||
|
|
|
@ -265,7 +265,7 @@ func (h handler) viewMyFeed(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
vf := &viewFeed{storage: h.storage, logger: h.logger}
|
||||
vf := &viewFeed{storage: h.storage, logger: h.logger, loggedInActor: common.ActorURL(h.domain, user.Name)}
|
||||
err = vf.populateObjectEventPayloads(objects, objectsByObjectID)
|
||||
if err != nil {
|
||||
h.hardFail(c, err)
|
||||
|
@ -317,100 +317,6 @@ func (h handler) viewMyFeed(c *gin.Context) {
|
|||
|
||||
data["feed_view"] = "mine"
|
||||
data["feed_link"] = "feed_mine"
|
||||
//
|
||||
// userActorID := storage.NewActorID(user.Name, h.domain)
|
||||
//
|
||||
// page := intParam(c, "page", 1)
|
||||
// limit := 20
|
||||
//
|
||||
// total, err := h.storage.RowCount(ctx, `SELECT COUNT(*) FROM user_activities WHERE user_id = $1`, user.ID)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// uf, err := h.storage.PaginateUserActivity(ctx, user.ID, limit, (page-1)*limit)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// pageCount := math.Ceil(float64(total) / float64(limit))
|
||||
// data["page_count"] = int(pageCount)
|
||||
// data["page"] = page
|
||||
// data["limit"] = limit
|
||||
//
|
||||
// var objectEventIDs []uuid.UUID
|
||||
// for _, ufi := range uf {
|
||||
// objectEventIDs = append(objectEventIDs, ufi.ObjectEventID)
|
||||
// }
|
||||
//
|
||||
// pairs, err := h.storage.ObjectPairsByObjectEventRowIDs(ctx, objectEventIDs)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// pairsMap := make(map[uuid.UUID]storage.ObjectEventPair)
|
||||
// actorIDs := make([]string, 0)
|
||||
// objectIDs := make([]string, 0)
|
||||
// for _, pair := range pairs {
|
||||
// pairsMap[pair.ObjectEventRowID] = pair
|
||||
// actorIDs = append(actorIDs, pair.ActorID)
|
||||
// objectIDs = append(objectIDs, pair.ObjectID)
|
||||
// }
|
||||
//
|
||||
// announcements, err := h.storage.UserAnnouncementsByObject(ctx, string(userActorID), objectIDs)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
// data["announcements"] = announcements
|
||||
//
|
||||
// vf := &viewFeed{storage: h.storage}
|
||||
// err = vf.populate(uf, pairsMap)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// data["feed"] = vf.feed
|
||||
// if len(uf) > 0 {
|
||||
// data["latest"] = uf[0].CreatedAt.UTC().Unix()
|
||||
// }
|
||||
//
|
||||
// actors, err := h.storage.ActorsByActorID(ctx, append(actorIDs, vf.actorIDs...))
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
// var actorRowIDs []uuid.UUID
|
||||
// for _, actor := range actors {
|
||||
// actorRowIDs = append(actorRowIDs, actor.ID)
|
||||
// }
|
||||
// actorSubjects, err := h.storage.ActorSubjects(ctx, actorRowIDs)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// return
|
||||
// }
|
||||
// allActors := h.gatherActors(actors, actorSubjects)
|
||||
//
|
||||
// var pages []int
|
||||
// for i := page - 3; i <= page+3; i++ {
|
||||
// if i > 0 && i <= int(pageCount) {
|
||||
// pages = append(pages, i)
|
||||
// }
|
||||
// }
|
||||
// data["pages"] = pages
|
||||
// data["actors"] = actorLookup{h.domain, allActors}
|
||||
//
|
||||
// ml := &mediaLookup{h.domain, make(map[string]string)}
|
||||
// err = ml.load(ctx, h.storage, vf.mediaURLs)
|
||||
// if err != nil {
|
||||
// h.hardFail(c, err)
|
||||
// }
|
||||
//
|
||||
// data["media"] = ml
|
||||
|
||||
if cont = h.saveSession(c, session); !cont {
|
||||
return
|
||||
|
@ -438,7 +344,7 @@ func (h handler) viewConversation(c *gin.Context) {
|
|||
conversations := make([]string, 0)
|
||||
var found []string
|
||||
for _, object := range objects {
|
||||
found = storage.CollectJSONDeepStrings(object, []string{"object"}, []string{"object", "id"}, []string{"object", "inReplyTo"})
|
||||
found = storage.CollectJSONDeepStrings(object, []string{"id"}, []string{"object"}, []string{"object", "id"}, []string{"object", "inReplyTo"})
|
||||
objectIDs = append(objectIDs, found...)
|
||||
|
||||
found = storage.CollectJSONDeepStrings(object, []string{"actor"}, []string{"object", "attributedTo"})
|
||||
|
|
|
@ -33,6 +33,12 @@ func (h handler) getObject(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
objectType, hasObjectType := storage.JSONString(objectPayload, "type")
|
||||
if hasObjectType && objectType == "Tombstone" {
|
||||
h.writeJSONLD(c, http.StatusOK, objectPayload)
|
||||
return
|
||||
}
|
||||
|
||||
var destinations []string
|
||||
to, hasTo := storage.JSONStrings(objectPayload, "to")
|
||||
if hasTo {
|
||||
|
|
|
@ -12,12 +12,13 @@ import (
|
|||
)
|
||||
|
||||
type viewFeed struct {
|
||||
logger *zap.Logger
|
||||
feed []map[string]interface{}
|
||||
actorIDs []string
|
||||
objectIDs []string
|
||||
mediaURLs []viewFeedMedia
|
||||
storage storage.Storage
|
||||
loggedInActor string
|
||||
logger *zap.Logger
|
||||
feed []map[string]interface{}
|
||||
actorIDs []string
|
||||
objectIDs []string
|
||||
mediaURLs []viewFeedMedia
|
||||
storage storage.Storage
|
||||
}
|
||||
|
||||
type viewFeedMedia struct {
|
||||
|
@ -35,31 +36,41 @@ func (v *viewFeed) populateObjectEventPayloads(objectEvents []storage.Payload, r
|
|||
switch objectEventType {
|
||||
case "Create":
|
||||
object, _ := storage.JSONMap(objectEvent, "object")
|
||||
view := v.createNoteViewFromPayload(object, refs)
|
||||
name, view := v.createNoteViewFromPayload(object, refs)
|
||||
if view == nil {
|
||||
v.logger.Warn("Could not create view from object event", zap.Reflect("object_event", objectEvent))
|
||||
continue
|
||||
}
|
||||
v.feed = append(v.feed, map[string]interface{}{
|
||||
"create_note": view,
|
||||
name: view,
|
||||
})
|
||||
case "Note":
|
||||
view := v.createNoteViewFromPayload(objectEvent, refs)
|
||||
name, view := v.createNoteViewFromPayload(objectEvent, refs)
|
||||
if view == nil {
|
||||
v.logger.Warn("Could not create view from object event", zap.Reflect("object_event", objectEvent))
|
||||
continue
|
||||
}
|
||||
v.feed = append(v.feed, map[string]interface{}{
|
||||
"create_note": view,
|
||||
name: view,
|
||||
})
|
||||
case "Announce":
|
||||
view := v.announceNoteView(objectEvent, refs)
|
||||
name, view := v.announceNoteView(objectEvent, refs)
|
||||
if view == nil {
|
||||
v.logger.Warn("Could not create view from object event", zap.Reflect("object_event", objectEvent))
|
||||
continue
|
||||
}
|
||||
v.feed = append(v.feed, map[string]interface{}{
|
||||
"announce_note": view,
|
||||
name: view,
|
||||
})
|
||||
|
||||
case "Tombstone":
|
||||
name, view := v.tombstoneView(objectEvent, refs)
|
||||
if view == nil {
|
||||
v.logger.Warn("Could not create view from object event", zap.Reflect("object_event", objectEvent))
|
||||
continue
|
||||
}
|
||||
v.feed = append(v.feed, map[string]interface{}{
|
||||
name: view,
|
||||
})
|
||||
|
||||
default:
|
||||
|
@ -70,7 +81,36 @@ func (v *viewFeed) populateObjectEventPayloads(objectEvents []storage.Payload, r
|
|||
return nil
|
||||
}
|
||||
|
||||
func (v *viewFeed) announceNoteView(p storage.Payload, refs map[string]storage.Payload) map[string]interface{} {
|
||||
func (v *viewFeed) tombstoneView(orig storage.Payload, refs map[string]storage.Payload) (string, map[string]interface{}) {
|
||||
view := make(map[string]interface{})
|
||||
|
||||
objectID, _ := storage.JSONString(orig, "id")
|
||||
|
||||
p, hasP := refs[objectID]
|
||||
if !hasP {
|
||||
v.logger.Warn("refs does not contain object id", zap.String("object_id", objectID))
|
||||
return "", nil
|
||||
}
|
||||
view["object_id"] = objectID
|
||||
|
||||
view["tombstone"] = true
|
||||
|
||||
view["former_type"], _ = storage.JSONString(p, "formerType")
|
||||
|
||||
if deleted, hasDeleted := storage.JSONString(p, "deleted"); hasDeleted {
|
||||
var deletedAt time.Time
|
||||
deletedAt, err := time.Parse("2006-01-02T15:04:05Z", deleted)
|
||||
if err != nil {
|
||||
deletedAt = time.Now()
|
||||
}
|
||||
view["deleted_at"] = deletedAt
|
||||
}
|
||||
|
||||
return "tombstone", view
|
||||
|
||||
}
|
||||
|
||||
func (v *viewFeed) announceNoteView(p storage.Payload, refs map[string]storage.Payload) (string, map[string]interface{}) {
|
||||
announceNote := make(map[string]interface{})
|
||||
|
||||
objectID, _ := storage.JSONString(p, "object")
|
||||
|
@ -85,7 +125,7 @@ func (v *viewFeed) announceNoteView(p storage.Payload, refs map[string]storage.P
|
|||
|
||||
object, hasObject := refs[objectID]
|
||||
if hasObject {
|
||||
noteView := v.createNoteViewFromPayload(object, refs)
|
||||
_, noteView := v.createNoteViewFromPayload(object, refs)
|
||||
if noteView != nil {
|
||||
announceNote["note"] = noteView
|
||||
} else {
|
||||
|
@ -95,13 +135,26 @@ func (v *viewFeed) announceNoteView(p storage.Payload, refs map[string]storage.P
|
|||
v.logger.Warn("announced object not found in feed", zap.String("object_id", objectID), zap.Reflect("object_event", p))
|
||||
}
|
||||
|
||||
return announceNote
|
||||
return "announce_note", announceNote
|
||||
}
|
||||
|
||||
func (v *viewFeed) createNoteViewFromPayload(p storage.Payload, refs map[string]storage.Payload) map[string]interface{} {
|
||||
func (v *viewFeed) createNoteViewFromPayload(orig storage.Payload, refs map[string]storage.Payload) (string, map[string]interface{}) {
|
||||
createNote := make(map[string]interface{})
|
||||
|
||||
objectID, _ := storage.JSONString(p, "id")
|
||||
objectID, _ := storage.JSONString(orig, "id")
|
||||
|
||||
p, hasP := refs[objectID]
|
||||
if !hasP {
|
||||
v.logger.Warn("refs does not contain object id", zap.String("object_id", objectID))
|
||||
return "", nil
|
||||
}
|
||||
createNote["object_id"] = objectID
|
||||
|
||||
objectType, _ := storage.JSONDeepString(p, "type")
|
||||
if objectType == "Tombstone" {
|
||||
return v.tombstoneView(orig, refs)
|
||||
}
|
||||
|
||||
createNote["object_id"] = objectID
|
||||
v.objectIDs = append(v.objectIDs, objectID)
|
||||
|
||||
|
@ -109,7 +162,12 @@ func (v *viewFeed) createNoteViewFromPayload(p storage.Payload, refs map[string]
|
|||
content, hasContent := storage.JSONString(p, "content")
|
||||
|
||||
if !hasContent || !hasAuthor {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if author == v.loggedInActor {
|
||||
fmt.Println(author, v.loggedInActor, author == v.loggedInActor)
|
||||
createNote["can_delete"] = true
|
||||
}
|
||||
|
||||
v.actorIDs = append(v.actorIDs, author)
|
||||
|
@ -212,5 +270,5 @@ func (v *viewFeed) createNoteViewFromPayload(p storage.Payload, refs map[string]
|
|||
createNote["media"] = media
|
||||
}
|
||||
|
||||
return createNote
|
||||
return "create_note", createNote
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue