feat: sparser robot nudges, typed unread badge, lobby unread bump
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m26s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 53s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m26s
Three owner-requested polish changes: - robot: replace the lengthening 60-90 min -> 6 h proactive-nudge ramp with a flat uniform 9-12 h wait before every nudge; the existing sleep-window gate still skips and defers a nudge that would land in the robot's night. - ui: colour the lobby/in-game unread dot by type -- the regular danger colour when a chat message is unread, a softer amber (--warn) when only nudges are. Adds a per-viewer unread_messages flag (chat_messages.kind='message') across the backend DTO, FlatBuffers wire, gateway transcode and the UI store. - ui: float games with any unread notification to the top of the lobby's your-turn and opponent-turn sections (finished keeps its order), reusing the existing unread_chat flag. Docs (ARCHITECTURE 7, FUNCTIONAL + _ru) updated. No DB migration; the new wire field is backward-compatible.
This commit is contained in:
@@ -112,6 +112,10 @@ type gameDTO struct {
|
||||
// entry (message or nudge) in this game. It seeds the lobby and in-game unread badge.
|
||||
// gameDTOFromGame leaves it false (it has no viewer); the handlers fill it.
|
||||
UnreadChat bool `json:"unread_chat"`
|
||||
// UnreadMessages is a per-viewer flag: at least one of the unread entries is a real chat
|
||||
// message (not just a nudge). It colours the badge — set with UnreadChat it is the regular
|
||||
// badge, UnreadChat alone is the softer nudge-only badge. gameDTOFromGame leaves it false.
|
||||
UnreadMessages bool `json:"unread_messages"`
|
||||
}
|
||||
|
||||
// moveResultDTO is the outcome of a committed move. Rack carries the actor's refilled rack as
|
||||
|
||||
@@ -443,16 +443,20 @@ func (s *Server) handleListGames(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
memo := map[string]string{}
|
||||
// One query seeds the lobby's per-card unread badge for every listed game.
|
||||
var unread map[uuid.UUID]bool
|
||||
// Two queries seed the lobby's per-card unread badge for every listed game: which games have
|
||||
// any unread entry, and which of those have a real message (so the badge can colour
|
||||
// message-bearing games apart from nudge-only ones).
|
||||
var unread, unreadMsg map[uuid.UUID]bool
|
||||
if s.social != nil {
|
||||
unread, _ = s.social.UnreadGames(c.Request.Context(), uid)
|
||||
unreadMsg, _ = s.social.UnreadMessageGames(c.Request.Context(), uid)
|
||||
}
|
||||
out := make([]gameDTO, 0, len(games))
|
||||
for _, g := range games {
|
||||
dto := gameDTOFromGame(g)
|
||||
s.fillSeatNames(c.Request.Context(), &dto, memo)
|
||||
dto.UnreadChat = unread[g.ID]
|
||||
dto.UnreadMessages = unreadMsg[g.ID]
|
||||
out = append(out, dto)
|
||||
}
|
||||
c.JSON(http.StatusOK, gameListDTO{Games: out, AtGameLimit: atLimit})
|
||||
@@ -536,8 +540,9 @@ func (s *Server) writeMoveResult(c *gin.Context, res game.MoveResult) {
|
||||
c.JSON(http.StatusOK, dto)
|
||||
}
|
||||
|
||||
// setUnreadChat fills the per-game unread-chat flag for viewer uid on one game's DTO,
|
||||
// when the social domain is present. It is best-effort: a lookup error leaves the flag
|
||||
// setUnreadChat fills the per-game unread-chat flags for viewer uid on one game's DTO, when the
|
||||
// social domain is present: UnreadChat for any unread entry and UnreadMessages when one of them is
|
||||
// a real message (so the badge can be coloured). It is best-effort: a lookup error leaves the flag
|
||||
// false (a missing badge) rather than failing the surrounding game response.
|
||||
func (s *Server) setUnreadChat(ctx context.Context, g *gameDTO, uid uuid.UUID) {
|
||||
if s.social == nil {
|
||||
@@ -550,4 +555,7 @@ func (s *Server) setUnreadChat(ctx context.Context, g *gameDTO, uid uuid.UUID) {
|
||||
if unread, err := s.social.HasUnread(ctx, gid, uid); err == nil {
|
||||
g.UnreadChat = unread
|
||||
}
|
||||
if msg, err := s.social.HasUnreadMessage(ctx, gid, uid); err == nil {
|
||||
g.UnreadMessages = msg
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user