fix: chat accepts posts
This commit is contained in:
@@ -547,6 +547,12 @@ type AddAttachmentParams struct {
|
||||
Description *string
|
||||
SiteName *string
|
||||
ImageURL *string
|
||||
|
||||
// Shared entity: the durable reference plus a snapshot used only as a
|
||||
// fallback. Live data is re-resolved per viewer at read time.
|
||||
EntityType *string
|
||||
EntityID *string
|
||||
Snapshot []byte
|
||||
}
|
||||
|
||||
// AddAttachment persists one attachment. conversation_id is denormalized here
|
||||
@@ -557,17 +563,20 @@ func (r *ChatRepo) AddAttachment(ctx context.Context, p AddAttachmentParams) (*m
|
||||
INSERT INTO message_attachments
|
||||
(message_id, conversation_id, ord, kind, r2_key, mime, size_bytes,
|
||||
width, height, duration_ms, blurhash, file_name,
|
||||
url, title, description, site_name, image_url)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
|
||||
url, title, description, site_name, image_url,
|
||||
entity_type, entity_id, snapshot)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20)
|
||||
RETURNING id, message_id, ord, kind, mime, size_bytes, width, height,
|
||||
duration_ms, blurhash, file_name, url, title, description,
|
||||
site_name, image_url, created_at`,
|
||||
site_name, image_url, entity_type, entity_id, snapshot, created_at`,
|
||||
p.MessageID, p.ConversationID, p.Ord, p.Kind, p.R2Key, p.Mime, p.SizeBytes,
|
||||
p.Width, p.Height, p.DurationMs, p.Blurhash, p.FileName,
|
||||
p.URL, p.Title, p.Description, p.SiteName, p.ImageURL,
|
||||
p.EntityType, p.EntityID, p.Snapshot,
|
||||
).Scan(&a.ID, &a.MessageID, &a.Ord, &a.Kind, &a.Mime, &a.SizeBytes, &a.Width,
|
||||
&a.Height, &a.DurationMs, &a.Blurhash, &a.FileName, &a.URL, &a.Title,
|
||||
&a.Description, &a.SiteName, &a.ImageURL, &a.CreatedAt)
|
||||
&a.Description, &a.SiteName, &a.ImageURL,
|
||||
&a.EntityType, &a.EntityID, &a.Entity, &a.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("add attachment: %w", err)
|
||||
}
|
||||
@@ -583,7 +592,7 @@ func (r *ChatRepo) AttachmentsFor(ctx context.Context, messageIDs []string) (map
|
||||
rows, err := r.pg.Pool.Query(ctx, `
|
||||
SELECT id, message_id, ord, kind, mime, size_bytes, width, height,
|
||||
duration_ms, blurhash, file_name, url, title, description,
|
||||
site_name, image_url, created_at
|
||||
site_name, image_url, entity_type, entity_id, snapshot, created_at
|
||||
FROM message_attachments
|
||||
WHERE message_id = ANY($1)
|
||||
ORDER BY message_id, ord`, messageIDs)
|
||||
@@ -595,7 +604,8 @@ func (r *ChatRepo) AttachmentsFor(ctx context.Context, messageIDs []string) (map
|
||||
var a model.Attachment
|
||||
if err := rows.Scan(&a.ID, &a.MessageID, &a.Ord, &a.Kind, &a.Mime, &a.SizeBytes,
|
||||
&a.Width, &a.Height, &a.DurationMs, &a.Blurhash, &a.FileName, &a.URL,
|
||||
&a.Title, &a.Description, &a.SiteName, &a.ImageURL, &a.CreatedAt); err != nil {
|
||||
&a.Title, &a.Description, &a.SiteName, &a.ImageURL,
|
||||
&a.EntityType, &a.EntityID, &a.Entity, &a.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan attachment: %w", err)
|
||||
}
|
||||
out[a.MessageID] = append(out[a.MessageID], a)
|
||||
@@ -640,7 +650,8 @@ func (r *ChatRepo) Gallery(ctx context.Context, conversationID, userID string, k
|
||||
rows, err := r.pg.Pool.Query(ctx, `
|
||||
SELECT a.id, a.message_id, a.ord, a.kind, a.mime, a.size_bytes, a.width,
|
||||
a.height, a.duration_ms, a.blurhash, a.file_name, a.url, a.title,
|
||||
a.description, a.site_name, a.image_url, a.created_at
|
||||
a.description, a.site_name, a.image_url,
|
||||
a.entity_type, a.entity_id, a.snapshot, a.created_at
|
||||
FROM message_attachments a
|
||||
JOIN messages m ON m.id = a.message_id
|
||||
WHERE a.conversation_id = $1
|
||||
@@ -661,7 +672,8 @@ func (r *ChatRepo) Gallery(ctx context.Context, conversationID, userID string, k
|
||||
var a model.Attachment
|
||||
if err := rows.Scan(&a.ID, &a.MessageID, &a.Ord, &a.Kind, &a.Mime, &a.SizeBytes,
|
||||
&a.Width, &a.Height, &a.DurationMs, &a.Blurhash, &a.FileName, &a.URL,
|
||||
&a.Title, &a.Description, &a.SiteName, &a.ImageURL, &a.CreatedAt); err != nil {
|
||||
&a.Title, &a.Description, &a.SiteName, &a.ImageURL,
|
||||
&a.EntityType, &a.EntityID, &a.Entity, &a.CreatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan gallery row: %w", err)
|
||||
}
|
||||
out = append(out, a)
|
||||
|
||||
@@ -160,6 +160,46 @@ func (r *PostRepo) FindByID(ctx context.Context, id string) (*model.Post, error)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// FindByIDs loads many posts at once, keyed by id. Missing and soft-deleted
|
||||
// ids are simply absent from the map.
|
||||
//
|
||||
// Exists so a page of chat messages sharing N posts costs one query instead of
|
||||
// N — the same reason ImagesFor is batched.
|
||||
func (r *PostRepo) FindByIDs(ctx context.Context, ids []string) (map[string]*model.Post, error) {
|
||||
out := make(map[string]*model.Post, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
q := `SELECT ` + postSelectColumns + postSelectFrom + `WHERE p.id = ANY($1) AND p.deleted_at IS NULL`
|
||||
rows, err := r.pg.Pool.Query(ctx, q, ids)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find posts: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
found := make([]string, 0, len(ids))
|
||||
for rows.Next() {
|
||||
p := &model.Post{}
|
||||
if err := scanPost(rows, p); err != nil {
|
||||
return nil, fmt.Errorf("scan post: %w", err)
|
||||
}
|
||||
out[p.ID] = p
|
||||
found = append(found, p.ID)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("find posts: %w", err)
|
||||
}
|
||||
|
||||
imgs, err := r.ImagesFor(ctx, found)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for id, p := range out {
|
||||
p.Images = imgs[id]
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListByHandle returns the profile feed for a user. Unknown handle →
|
||||
// empty page (matches pre-existing behaviour).
|
||||
func (r *PostRepo) ListByHandle(ctx context.Context, handle string, limit, offset int) ([]model.Post, int, error) {
|
||||
|
||||
@@ -242,6 +242,35 @@ func (r *BlockRepo) Unblock(ctx context.Context, blockerID, blockedID string) er
|
||||
|
||||
// BlockState reports, in one round-trip, whether a blocked b and whether
|
||||
// b blocked a.
|
||||
// BlockedWith returns the subset of others that viewerID is blocked with in
|
||||
// EITHER direction — the set whose content must be hidden from them.
|
||||
//
|
||||
// The batched counterpart to BlockState, for resolving many authors at once.
|
||||
func (r *BlockRepo) BlockedWith(ctx context.Context, viewerID string, others []string) (map[string]bool, error) {
|
||||
out := make(map[string]bool, len(others))
|
||||
if viewerID == "" || len(others) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
rows, err := r.pg.Pool.Query(ctx, `
|
||||
SELECT CASE WHEN blocker_id = $1 THEN blocked_id ELSE blocker_id END
|
||||
FROM user_blocks
|
||||
WHERE (blocker_id = $1 AND blocked_id = ANY($2))
|
||||
OR (blocked_id = $1 AND blocker_id = ANY($2))`,
|
||||
viewerID, others)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("blocked with: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var id string
|
||||
if err := rows.Scan(&id); err != nil {
|
||||
return nil, fmt.Errorf("scan blocked with: %w", err)
|
||||
}
|
||||
out[id] = true
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (r *BlockRepo) BlockState(ctx context.Context, a, b string) (aBlockedB, bBlockedA bool, err error) {
|
||||
err = r.pg.Pool.QueryRow(ctx, `
|
||||
SELECT
|
||||
|
||||
Reference in New Issue
Block a user