feat: post reshare enhancements
This commit is contained in:
@@ -75,39 +75,33 @@ func (s *PostService) Get(ctx context.Context, id, viewerID string) (*model.Post
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Reshare records that the caller has reshared the given post. Idempotent
|
||||
// — calling it twice with the same (post, user) is a no-op. Resharing
|
||||
// your own post is rejected: there's nothing to bring onto an account
|
||||
// that already authored the post.
|
||||
func (s *PostService) Reshare(ctx context.Context, postID, callerID string) error {
|
||||
p, err := s.posts.FindByID(ctx, postID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.UserID == callerID {
|
||||
return errors.New("cannot reshare your own post")
|
||||
}
|
||||
return s.posts.AddReshare(ctx, postID, callerID)
|
||||
// Reshare materialises a reshare as a brand-new posts row owned by the
|
||||
// caller, with reshared_from_post_id pointing to the canonical original.
|
||||
// Idempotent — calling it twice returns the same reshare row. Resharing
|
||||
// one's own post (canonical or chained) is rejected at the repo layer.
|
||||
//
|
||||
// Returns the new post so the frontend can drop it into local state
|
||||
// without a round-trip.
|
||||
func (s *PostService) Reshare(ctx context.Context, postID, callerID string) (*model.Post, error) {
|
||||
return s.posts.Reshare(ctx, postID, callerID)
|
||||
}
|
||||
|
||||
// Unreshare removes a previously recorded reshare. Idempotent — no-ops
|
||||
// when no row exists.
|
||||
// Unreshare removes the caller's reshare of a canonical post by deleting
|
||||
// their own reshare posts row. Idempotent — no-ops when nothing to delete.
|
||||
func (s *PostService) Unreshare(ctx context.Context, postID, callerID string) error {
|
||||
return s.posts.RemoveReshare(ctx, postID, callerID)
|
||||
return s.posts.Unreshare(ctx, postID, callerID)
|
||||
}
|
||||
|
||||
// hydrateReshareStates fills in ViewerHasReshared on each post via one
|
||||
// batched lookup against post_reshares. Skips the query when the viewer
|
||||
// is anonymous (`viewerID == ""`) since the answer is uniformly false.
|
||||
// Also fast-paths posts authored by the viewer — they can't be reshared
|
||||
// by themselves, so we leave the flag at its zero value.
|
||||
// batched lookup. Skips the query when the viewer is anonymous (the
|
||||
// answer is uniformly false) or the slice is empty.
|
||||
func (s *PostService) hydrateReshareStates(ctx context.Context, viewerID string, posts []*model.Post) error {
|
||||
if viewerID == "" || len(posts) == 0 {
|
||||
return nil
|
||||
}
|
||||
ids := make([]string, 0, len(posts))
|
||||
for _, p := range posts {
|
||||
if p == nil || p.UserID == viewerID {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, p.ID)
|
||||
@@ -150,9 +144,9 @@ func (s *PostService) Delete(ctx context.Context, postID, callerID string) error
|
||||
return s.posts.Delete(ctx, postID, callerID)
|
||||
}
|
||||
|
||||
// ReplaceImages swaps the image set on a post. The caller must own the
|
||||
// post. Returns the fresh post payload so the client can reconcile its
|
||||
// local state without a separate fetch.
|
||||
// ReplaceImages swaps the image set on a post. Owner-only — every
|
||||
// reshare is its own posts row, owned by the resharer, so this works
|
||||
// uniformly for canonical posts and reshares.
|
||||
func (s *PostService) ReplaceImages(ctx context.Context, postID, callerID string, imageURLs []string) (*model.Post, error) {
|
||||
if err := s.posts.ReplaceImages(ctx, postID, callerID, imageURLs); err != nil {
|
||||
return nil, err
|
||||
@@ -180,12 +174,11 @@ func (s *PostService) ListByHandle(ctx context.Context, handle, viewerID string,
|
||||
return posts, total, nil
|
||||
}
|
||||
|
||||
// ListMine is the owner's view of their own profile feed — authored
|
||||
// posts plus reshares (the repo's ListByUser unions the two). The
|
||||
// viewer here is by construction the feed owner, so every reshared
|
||||
// row is one the viewer themselves reshared — we flip
|
||||
// ViewerHasReshared inline rather than spending a hydration round-trip
|
||||
// to learn what we already know.
|
||||
// ListMine is the owner's view of their own profile feed. Every post
|
||||
// they own — authored or reshared — comes from posts.user_id, so the
|
||||
// viewer is by construction the owner. ViewerHasReshared is true for
|
||||
// reshare rows (the user IS the resharer) and false for canonical
|
||||
// rows (you don't "reshare your own post").
|
||||
func (s *PostService) ListMine(ctx context.Context, userID string, limit, offset int) ([]model.Post, int, error) {
|
||||
if limit <= 0 || limit > 50 {
|
||||
limit = 20
|
||||
@@ -198,7 +191,7 @@ func (s *PostService) ListMine(ctx context.Context, userID string, limit, offset
|
||||
return nil, 0, err
|
||||
}
|
||||
for i := range posts {
|
||||
if posts[i].ResharedAt != nil {
|
||||
if posts[i].ResharedFromPostID != nil {
|
||||
posts[i].ViewerHasReshared = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user