fix(admin): resolve moderation, gift, and admin-panel bugs from QA
- scam/fake: enforce mutual exclusivity (scam precedence) at TL conversion, admin command, and UI toggles; both flags could render as neither - scam/fake flicker: persist scam/fake in the Redis user base cache so cache hits no longer drop the flags - getSavedStarGifts: hidden (unsaved) gifts are now force-excluded for non-owner viewers; same guard for get-by-ref - convertStarGift: map already-upgraded/owner-invalid/unavailable to a clean client error instead of 500 - channel force-settings: send only changed fields (partial patch) and re-sync toggles after reload, so one setting no longer resets another - give gifts: removed custom collectible numbers (auto sequential only); locked sender to the system account 777000 - give gifts UI: fixed picker card spacing/right gap; static (hover-play) Lottie previews on emoji + picker to stop render lag; fixed emoji ID field overflow
This commit is contained in:
parent
313624eab2
commit
90792cdfab
22 changed files with 214 additions and 135 deletions
|
|
@ -451,7 +451,9 @@ func tgChannel(viewerUserID int64, ch domain.Channel, self *domain.ChannelMember
|
|||
Creator: ch.CreatorUserID == viewerUserID && viewerUserID != 0,
|
||||
Verified: ch.Verified,
|
||||
Scam: ch.Scam,
|
||||
Fake: ch.Fake,
|
||||
// scam and fake are mutually exclusive in Telegram; a peer flagged as
|
||||
// both would render neither badge on clients. scam takes precedence.
|
||||
Fake: ch.Fake && !ch.Scam,
|
||||
Gigagroup: ch.Gigagroup,
|
||||
Broadcast: ch.Broadcast,
|
||||
Megagroup: ch.Megagroup,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ func tgUser(u domain.User) *tg.User {
|
|||
Phone: u.Phone,
|
||||
Verified: u.Verified,
|
||||
Scam: u.Scam,
|
||||
Fake: u.Fake,
|
||||
// scam and fake are mutually exclusive in Telegram; a peer flagged as
|
||||
// both would render neither badge on clients. scam takes precedence.
|
||||
Fake: u.Fake && !u.Scam,
|
||||
Support: u.Support,
|
||||
Contact: u.Contact,
|
||||
MutualContact: u.Mutual,
|
||||
|
|
|
|||
|
|
@ -523,10 +523,17 @@ func (r *Router) onPaymentsGetSavedStarGifts(ctx context.Context, req *tg.Paymen
|
|||
if r.deps.Gifts == nil {
|
||||
return emptySavedStarGifts(), nil
|
||||
}
|
||||
// Gifts hidden from the profile (unsaved) are visible only to the owner (or a
|
||||
// channel admin). Never trust the client's exclude_unsaved flag for other
|
||||
// viewers: force-exclude hidden gifts unless the requester manages the owner.
|
||||
excludeUnsaved := req.ExcludeUnsaved
|
||||
if r.ensureCanManageStarGiftOwner(ctx, userID, owner) != nil {
|
||||
excludeUnsaved = true
|
||||
}
|
||||
collectionID, _ := req.GetCollectionID()
|
||||
page, err := r.deps.Gifts.ListSavedFiltered(ctx, domain.SavedStarGiftFilter{
|
||||
Owner: owner,
|
||||
ExcludeUnsaved: req.ExcludeUnsaved,
|
||||
ExcludeUnsaved: excludeUnsaved,
|
||||
ExcludeSaved: req.ExcludeSaved,
|
||||
ExcludeUnlimited: req.ExcludeUnlimited,
|
||||
ExcludeUnique: req.ExcludeUnique,
|
||||
|
|
@ -556,6 +563,17 @@ func (r *Router) onPaymentsGetSavedStarGift(ctx context.Context, refs []tg.Input
|
|||
return emptySavedStarGifts(), nil
|
||||
}
|
||||
gifts := make([]domain.SavedStarGift, 0, len(refs))
|
||||
// A gift hidden from the profile (unsaved) is visible only to the owner or a
|
||||
// channel admin. Memoize the manage check per owner to avoid repeat lookups.
|
||||
manageCache := make(map[domain.Peer]bool)
|
||||
canManageOwner := func(owner domain.Peer) bool {
|
||||
if v, ok := manageCache[owner]; ok {
|
||||
return v
|
||||
}
|
||||
v := r.ensureCanManageStarGiftOwner(ctx, userID, owner) == nil
|
||||
manageCache[owner] = v
|
||||
return v
|
||||
}
|
||||
for _, ref := range refs {
|
||||
dref, ok, err := r.starGiftRefFromInput(ctx, userID, ref)
|
||||
if err != nil {
|
||||
|
|
@ -569,6 +587,9 @@ func (r *Router) onPaymentsGetSavedStarGift(ctx context.Context, refs []tg.Input
|
|||
return nil, internalErr()
|
||||
}
|
||||
if found && !g.Converted {
|
||||
if g.Unsaved && !canManageOwner(g.Owner) {
|
||||
continue
|
||||
}
|
||||
gifts = append(gifts, g)
|
||||
}
|
||||
}
|
||||
|
|
@ -669,9 +690,14 @@ func (r *Router) onPaymentsConvertStarGift(ctx context.Context, ref tg.InputSave
|
|||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrStarGiftNotFound):
|
||||
return false, starGiftInvalidErr()
|
||||
case errors.Is(err, domain.ErrStarGiftAlreadyConverted):
|
||||
case errors.Is(err, domain.ErrStarGiftNotFound),
|
||||
errors.Is(err, domain.ErrStarGiftAlreadyConverted),
|
||||
errors.Is(err, domain.ErrStarGiftAlreadyUpgraded),
|
||||
errors.Is(err, domain.ErrStarGiftOwnerInvalid),
|
||||
errors.Is(err, domain.ErrStarGiftUnavailable):
|
||||
// These are known business conditions (e.g. converting an already
|
||||
// upgraded/unique gift). Surface a clean client error instead of a
|
||||
// 500 INTERNAL_SERVER_ERROR.
|
||||
return false, starGiftInvalidErr()
|
||||
default:
|
||||
return false, internalErr()
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ func (r *Router) adminGrantUpgradedStarGift(ctx context.Context, senderID int64,
|
|||
ModelAttributeID: grant.ModelAttributeID,
|
||||
PatternAttributeID: grant.PatternAttributeID,
|
||||
BackdropAttributeID: grant.BackdropAttributeID,
|
||||
Num: grant.Num,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue