Redesign social conversations and shared music
ci / api / api (push) Successful in 19m12s
ci / ts / ts (packages + web) (push) Successful in 2m44s
ci / module boundaries / boundaries (push) Successful in 18m23s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 59s
ci / db / db (push) Failing after 18s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m18s
ci / api / api (push) Successful in 19m12s
ci / ts / ts (packages + web) (push) Successful in 2m44s
ci / module boundaries / boundaries (push) Successful in 18m23s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 59s
ci / db / db (push) Failing after 18s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m18s
This commit is contained in:
1 parent
11a16cd023
commit
1a693848f2
23 files changed
+1145
-579
No files matched your search
@@ -87,7 +87,7 @@ func (h *Handler) GetDirectMessages(ctx context.Context, r gen.GetDirectMessages
|
||||
if utf8.RuneCountInString(q) > 100 {
|
||||
return nil, invalid()
|
||||
}
|
||||
v, e := h.store.messagePage(ctx, id, r.PeerId, r.Params.Before, q)
|
||||
v, e := h.store.filteredMessages(ctx, id, r.PeerId, r.Params.Before, q, r.Params.Music != nil && *r.Params.Music)
|
||||
return gen.GetDirectMessages200JSONResponse(v), e
|
||||
}
|
||||
func validateMessage(m gen.SendDirectMessage) error {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package social
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"pipu/api/internal/api/gen"
|
||||
)
|
||||
|
||||
// Read all previews in one query. Recheck friendship and both block directions
|
||||
// here so a removed relationship cannot expose history through the inbox.
|
||||
func (s *Store) attachLastMessages(ctx context.Context, viewer uuid.UUID, people []gen.SocialPerson) error {
|
||||
positions := map[uuid.UUID]int{}
|
||||
ids := []uuid.UUID{}
|
||||
for i, p := range people {
|
||||
if p.Relationship == "friend" {
|
||||
positions[p.Id] = i
|
||||
ids = append(ids, p.Id)
|
||||
}
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
rows, e := s.pool.Query(ctx, `SELECT p.peer_id,m.id,m.sender_id,
|
||||
CASE WHEN m.deleted_at IS NULL THEN left(m.text,160) ELSE '' END,
|
||||
m.attachment_kind,m.created_at,m.deleted_at IS NOT NULL
|
||||
FROM unnest($2::uuid[]) AS p(peer_id)
|
||||
JOIN friendships f ON f.low_id=LEAST($1::uuid,p.peer_id) AND f.high_id=GREATEST($1::uuid,p.peer_id) AND f.accepted
|
||||
JOIN LATERAL (SELECT * FROM direct_messages
|
||||
WHERE LEAST(sender_id,recipient_id)=LEAST($1::uuid,p.peer_id)
|
||||
AND GREATEST(sender_id,recipient_id)=GREATEST($1::uuid,p.peer_id)
|
||||
ORDER BY seq DESC LIMIT 1) m ON true
|
||||
WHERE NOT EXISTS(SELECT 1 FROM social_blocks b WHERE (b.user_id=$1 AND b.peer_id=p.peer_id) OR (b.peer_id=$1 AND b.user_id=p.peer_id))`, viewer, ids)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var peer uuid.UUID
|
||||
var preview gen.SocialMessagePreview
|
||||
if e = rows.Scan(&peer, &preview.Id, &preview.SenderId, &preview.Text, &preview.AttachmentKind, &preview.CreatedAt, &preview.Deleted); e != nil {
|
||||
return e
|
||||
}
|
||||
people[positions[peer]].LastMessage = &preview
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
@@ -67,6 +67,9 @@ func TestSocialIsolationAndLifecycle(t *testing.T) {
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, 1, inbox.Unread)
|
||||
// Another user cannot acknowledge this user's notification.
|
||||
require.NotNil(t, inbox.People[0].LastMessage)
|
||||
require.Equal(t, m.Id, inbox.People[0].LastMessage.Id)
|
||||
require.Equal(t, "Привет", inbox.People[0].LastMessage.Text)
|
||||
require.NoError(t, s.action(ctx, c, gen.SocialAction{Action: "read_notification", TargetId: inbox.Notifications[0].Id}))
|
||||
inbox, e = s.inbox(ctx, b)
|
||||
require.NoError(t, e)
|
||||
@@ -82,6 +85,11 @@ func TestSocialIsolationAndLifecycle(t *testing.T) {
|
||||
before, e := s.messages(ctx, b, a, &second.Seq)
|
||||
require.NoError(t, e)
|
||||
require.Len(t, before, 1)
|
||||
require.NoError(t, s.updateMessage(ctx, a, b, second.Id, gen.UpdateChatMessageJSONRequestBody{Action: "delete"}))
|
||||
inbox, e = s.inbox(ctx, b)
|
||||
require.NoError(t, e)
|
||||
require.True(t, inbox.People[0].LastMessage.Deleted)
|
||||
require.Empty(t, inbox.People[0].LastMessage.Text)
|
||||
require.NoError(t, action(b, a, "block"))
|
||||
require.NoError(t, action(a, b, "block"))
|
||||
for _, id := range []uuid.UUID{a, b} {
|
||||
@@ -89,6 +97,7 @@ func TestSocialIsolationAndLifecycle(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, blocked.People, 1)
|
||||
require.EqualValues(t, "blocked", blocked.People[0].Relationship)
|
||||
require.Nil(t, blocked.People[0].LastMessage)
|
||||
}
|
||||
require.NoError(t, action(a, b, "unblock"))
|
||||
_, e = s.send(ctx, a, b, gen.SendDirectMessage{Id: uuid.New(), Text: "blocked", AttachmentKind: "none"})
|
||||
@@ -118,6 +127,20 @@ func TestSocialIsolationAndLifecycle(t *testing.T) {
|
||||
attachment, e := s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), AttachmentKind: "playlist", AttachmentId: &share})
|
||||
require.NoError(t, e)
|
||||
require.Equal(t, &share, attachment.AttachmentId)
|
||||
_, e = s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), Text: "After the music", AttachmentKind: "none"})
|
||||
require.NoError(t, e)
|
||||
music, e := s.filteredMessages(ctx, c, a, nil, "", true)
|
||||
require.NoError(t, e)
|
||||
require.Len(t, music, 1)
|
||||
require.Equal(t, attachment.Id, music[0].Id)
|
||||
require.False(t, music[0].Read, "browsing shared music must not acknowledge messages")
|
||||
music, e = s.filteredMessages(ctx, c, a, &attachment.Seq, "", true)
|
||||
require.NoError(t, e)
|
||||
require.Empty(t, music)
|
||||
require.NoError(t, s.updateMessage(ctx, a, c, attachment.Id, gen.UpdateChatMessageJSONRequestBody{Action: "delete"}))
|
||||
music, e = s.filteredMessages(ctx, c, a, nil, "", true)
|
||||
require.NoError(t, e)
|
||||
require.Empty(t, music, "deleted music must not appear in shared attachments")
|
||||
_, e = pool.Exec(ctx, `UPDATE playlists SET share_id=NULL WHERE id=$1`, playlist)
|
||||
require.NoError(t, e)
|
||||
_, e = s.send(ctx, a, c, gen.SendDirectMessage{Id: uuid.New(), AttachmentKind: "playlist", AttachmentId: &share})
|
||||
|
||||
@@ -92,6 +92,9 @@ func (s *Store) inbox(ctx context.Context, id uuid.UUID) (gen.SocialInbox, error
|
||||
if e = s.attachActivity(ctx, id, out.People); e != nil {
|
||||
return out, e
|
||||
}
|
||||
if e = s.attachLastMessages(ctx, id, out.People); e != nil {
|
||||
return out, e
|
||||
}
|
||||
if e = s.pool.QueryRow(ctx, `SELECT activity_visibility,friends_recommendations FROM users WHERE id=$1`, id).Scan(&out.ActivityVisibility, &out.FriendsRecommendations); e != nil {
|
||||
return out, e
|
||||
}
|
||||
@@ -238,6 +241,9 @@ func (s *Store) messages(ctx context.Context, id, peer uuid.UUID, before *int64)
|
||||
return s.messagePage(ctx, id, peer, before, "")
|
||||
}
|
||||
func (s *Store) messagePage(ctx context.Context, id, peer uuid.UUID, before *int64, q string) ([]gen.DirectMessage, error) {
|
||||
return s.filteredMessages(ctx, id, peer, before, q, false)
|
||||
}
|
||||
func (s *Store) filteredMessages(ctx context.Context, id, peer uuid.UUID, before *int64, q string, music bool) ([]gen.DirectMessage, error) {
|
||||
tx, e := pairTx(ctx, s.pool, id, peer)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
@@ -246,7 +252,7 @@ func (s *Store) messagePage(ctx context.Context, id, peer uuid.UUID, before *int
|
||||
if e = allowed(ctx, tx, id, peer); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
rows, e := tx.Query(ctx, `SELECT `+messageColumns+` FROM direct_messages WHERE LEAST(sender_id,recipient_id)=LEAST($1::uuid,$2::uuid) AND GREATEST(sender_id,recipient_id)=GREATEST($1::uuid,$2::uuid) AND ($3::bigint IS NULL OR seq<$3) AND ($4='' OR (deleted_at IS NULL AND strpos(lower(text),lower($4))>0)) ORDER BY seq DESC LIMIT 50`, id, peer, before, q)
|
||||
rows, e := tx.Query(ctx, `SELECT `+messageColumns+` FROM direct_messages WHERE LEAST(sender_id,recipient_id)=LEAST($1::uuid,$2::uuid) AND GREATEST(sender_id,recipient_id)=GREATEST($1::uuid,$2::uuid) AND ($3::bigint IS NULL OR seq<$3) AND ($4='' OR (deleted_at IS NULL AND strpos(lower(text),lower($4))>0)) AND (NOT $5::boolean OR (deleted_at IS NULL AND attachment_kind IN ('track','playlist'))) ORDER BY seq DESC LIMIT 50`, id, peer, before, q, music)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user