package syncapi import ( "context" "slices" "github.com/dariuszjaniak/warp/server/internal/storage" ) // pushRulesType is the account data push rules travel as. The push server // renders it rather than the account data store, because a user who never // changed a rule has none stored and still has the server defaults. const pushRulesType = "m.push_rules" // UnreadNotifications is a joined room's unread counts for the user // syncing. type UnreadNotifications struct { NotificationCount int `json:"notification_count"` HighlightCount int `json:"highlight_count"` } // unreadNotifications fills in the counts of every joined room in the // response, in one read. // // Every joined room reported gets its counts, not only the ones whose // counts moved: a room is in a response because something in it changed, // and a client showing a new message wants the count beside it whether or // not this particular sync is the one that moved it. // // With byThread — the filter's unread_thread_notifications — the counts // are split: unread_notifications is the main timeline's, and each thread // with something unread is in unread_thread_notifications. A thread read // to the end drops out of the map rather than being reported as zero, // which the spec allows and a client takes the same way: the room is in // the response because its counts moved, and the map it is sent stands // for all of the room's threads, so a thread missing from it is read. func (s *API) unreadNotifications(ctx context.Context, userID string, byThread bool, resp *Response) error { if s.push == nil || len(resp.Rooms.Join) == 0 { return nil } roomIDs := make([]string, 0, len(resp.Rooms.Join)) for roomID := range resp.Rooms.Join { roomIDs = append(roomIDs, roomID) } slices.Sort(roomIDs) if byThread { counts, err := s.push.ThreadNotificationCounts(ctx, userID, roomIDs) if err != nil { return err } for _, roomID := range roomIDs { room := resp.Rooms.Join[roomID] main := counts[roomID][storage.ThreadMain] room.UnreadNotifications = &UnreadNotifications{ NotificationCount: main.Notifications, HighlightCount: main.Highlights, } for thread, c := range counts[roomID] { if thread == storage.ThreadMain { continue } if room.UnreadThreadNotifications == nil { room.UnreadThreadNotifications = map[string]UnreadNotifications{} } room.UnreadThreadNotifications[thread] = UnreadNotifications{ NotificationCount: c.Notifications, HighlightCount: c.Highlights, } } resp.Rooms.Join[roomID] = room } return nil } counts, err := s.push.NotificationCounts(ctx, userID, roomIDs) if err != nil { return err } for _, roomID := range roomIDs { room := resp.Rooms.Join[roomID] c := counts[roomID] room.UnreadNotifications = &UnreadNotifications{ NotificationCount: c.Notifications, HighlightCount: c.Highlights, } resp.Rooms.Join[roomID] = room } return nil }