fix: sync refresh complete self profile on session ready
This commit is contained in:
parent
2512eab51d
commit
f67e87689c
8 changed files with 405 additions and 28 deletions
84
internal/rpc/self_profile_refresh.go
Normal file
84
internal/rpc/self_profile_refresh.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// tgSelfUserWithReadModels is the single-object response-boundary projection
|
||||
// used by authorization results and self-profile updates. tgSelfUser itself is
|
||||
// intentionally a pure domain -> TL conversion because many list paths call it
|
||||
// in loops; the read-model pass belongs here, where it stays one query per
|
||||
// response.
|
||||
func (r *Router) tgSelfUserWithReadModels(ctx context.Context, u domain.User) *tg.User {
|
||||
self := r.tgSelfUser(u)
|
||||
users := []tg.UserClass{self}
|
||||
r.applyPeerReadModels(ctx, u.ID, users, nil)
|
||||
return self
|
||||
}
|
||||
|
||||
// pushUpdatesReadySelfProfile repairs the current session's cached self user
|
||||
// when it first becomes eligible for proactive updates. Telegram's updateUser
|
||||
// contract requires the complete User to be carried by the outer updates.users
|
||||
// vector; TDLib applies that vector before invalidating userFull for updateUser.
|
||||
//
|
||||
// This is an ephemeral cache-convergence update: it allocates no PTS, writes no
|
||||
// durable update event and targets only the physical session that just became
|
||||
// ready. A username-registry read failure suppresses the refresh instead of
|
||||
// replacing a possibly richer client cache with the legacy scalar-only shape.
|
||||
func (r *Router) pushUpdatesReadySelfProfile(ctx context.Context, userID int64) {
|
||||
updates, err := r.updatesReadySelfProfile(ctx, userID)
|
||||
if err != nil {
|
||||
r.log.Warn("build updates-ready self profile",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err))
|
||||
return
|
||||
}
|
||||
if updates == nil {
|
||||
return
|
||||
}
|
||||
r.pushCurrentSessionMessage(ctx, "push updates-ready self profile", updates)
|
||||
}
|
||||
|
||||
func (r *Router) updatesReadySelfProfile(ctx context.Context, userID int64) (*tg.Updates, error) {
|
||||
if userID == 0 || r.deps.Users == nil {
|
||||
return nil, nil
|
||||
}
|
||||
u, err := r.deps.Users.Self(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load self user: %w", err)
|
||||
}
|
||||
if u.ID != userID || u.Deleted {
|
||||
return nil, fmt.Errorf("invalid self user: requested %d, got %d deleted=%v", userID, u.ID, u.Deleted)
|
||||
}
|
||||
|
||||
self := r.tgSelfUser(u)
|
||||
users := []tg.UserClass{self}
|
||||
if r.deps.Usernames != nil {
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: userID}
|
||||
list, err := r.deps.Usernames.PeerUsernames(ctx, peer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load self usernames: %w", err)
|
||||
}
|
||||
if len(list) != 0 {
|
||||
applyUsernamesFromRegistry(users, nil, map[domain.Peer][]domain.Username{peer: list})
|
||||
}
|
||||
}
|
||||
// These read models are optional projections. Their existing response-boundary
|
||||
// contract degrades independently; the username registry above is handled
|
||||
// strictly because losing that vector is the cache corruption fixed here.
|
||||
r.applyStoryMaxIDsToPeerObjects(ctx, userID, users, nil)
|
||||
r.applyBotVerificationIconsToPeerObjects(ctx, users, nil)
|
||||
|
||||
return &tg.Updates{
|
||||
Updates: []tg.UpdateClass{&tg.UpdateUser{UserID: userID}},
|
||||
Users: users,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
Seq: 0,
|
||||
}, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue