@@ -105,12 +105,13 @@ func teamMemberLeaderboardBatchFunc(db *database.DB, c *cache.CacheWithRegistry)
105105 entryTags = []model.LeaderboardEntryTag {}
106106 }
107107
108+ rank := entry .Rank
108109 modelEntries [j ] = model.LeaderboardEntry {
109110 ID : entry .ID ,
110111 Name : entry .Name ,
111112 Description : entry .Description ,
112113 Score : entry .Score ,
113- Rank : entry . Rank ,
114+ Rank : & rank ,
114115 Tags : entryTags ,
115116 Image : entry .Image ,
116117 }
@@ -123,65 +124,71 @@ func teamMemberLeaderboardBatchFunc(db *database.DB, c *cache.CacheWithRegistry)
123124 }
124125}
125126
126- // getOrComputeTags retrieves or computes tags for a specific user viewing the leaderboard.
127- // This includes ME tag and TEAM_LEAD tag (with batched role loading).
127+ // getOrComputeTags computes tags for a specific user viewing the leaderboard.
128+ // TEAM_LEAD tags are cached per team (viewer-independent).
129+ // ME tags are computed on-the-fly (cheap ID comparison, no caching needed).
128130func getOrComputeTags (ctx context.Context , db * database.DB , c * cache.CacheWithRegistry , teamID , currentUserID string , entries []cachedLeaderboardEntry ) cachedLeaderboardTags {
129- // Check tag cache for this user
130- tagCacheKey := cache .TeamMemberLeaderboardTagsKey (teamID , currentUserID )
131- if cached , ok := c .Get (tagCacheKey ); ok {
132- if tags , ok := cached .(cachedLeaderboardTags ); ok {
131+ // Get or compute TEAM_LEAD tags (cached per team, not per user)
132+ teamLeadTags := getOrComputeTeamLeadTags (ctx , db , c , teamID , entries )
133+
134+ // Build final tags map with ME tag computed on-the-fly
135+ tags := make (cachedLeaderboardTags )
136+ for _ , entry := range entries {
137+ entryTags := []model.LeaderboardEntryTag {}
138+
139+ // ME tag - compute on-the-fly (cheap ID comparison)
140+ if entry .ID == currentUserID {
141+ entryTags = append (entryTags , model .LeaderboardEntryTagMe )
142+ }
143+
144+ // TEAM_LEAD tag - from cached per-team data
145+ if teamLeadTags [entry .ID ] {
146+ entryTags = append (entryTags , model .LeaderboardEntryTagTeamLead )
147+ }
148+
149+ if len (entryTags ) > 0 {
150+ tags [entry .ID ] = entryTags
151+ }
152+ }
153+
154+ return tags
155+ }
156+
157+ // getOrComputeTeamLeadTags returns a map of entry IDs that are team leads for this team.
158+ // This is cached per team (not per viewer) since TEAM_LEAD is viewer-independent.
159+ func getOrComputeTeamLeadTags (ctx context.Context , db * database.DB , c * cache.CacheWithRegistry , teamID string , entries []cachedLeaderboardEntry ) map [string ]bool {
160+ cacheKey := cache .TeamMemberLeaderboardTeamLeadTagsKey (teamID )
161+ if cached , ok := c .Get (cacheKey ); ok {
162+ if tags , ok := cached .(map [string ]bool ); ok {
133163 return tags
134164 }
135165 }
136166
137- // Collect all entry IDs for batched role loading
167+ // Cache miss - collect all entry IDs for batched role loading
138168 entryIDs := make ([]string , len (entries ))
139169 for i , entry := range entries {
140170 entryIDs [i ] = entry .ID
141171 }
142172
143173 // Batch load roles for all entries in ONE query
144- entryRoles := make (map [string ][] * model. UserRole )
174+ teamLeadTags := make (map [string ]bool )
145175 if len (entryIDs ) > 0 && db != nil {
146176 rows , err := db .Queries .GetAllRolesForUsers (ctx , entryIDs )
147177 if err == nil {
148- // Group roles by user ID
149178 for _ , row := range rows {
150179 role := convertToUserRole (row )
151- entryRoles [row .UserID ] = append (entryRoles [row .UserID ], role )
152- }
153- }
154- }
155-
156- // Compute tags for this user
157- tags := make (cachedLeaderboardTags )
158- for _ , entry := range entries {
159- entryTags := []model.LeaderboardEntryTag {}
160-
161- // ME tag - simple ID comparison
162- if entry .ID == currentUserID {
163- entryTags = append (entryTags , model .LeaderboardEntryTagMe )
164- }
165-
166- // TEAM_LEAD tag - check if entry is team lead of THIS team
167- for _ , role := range entryRoles [entry .ID ] {
168- if role .Role == model .RoleTypeTeamLead && role .Scope != nil {
169- if role .Scope .Type == model .ScopeTypeTeam && role .Scope .ID == teamID {
170- entryTags = append (entryTags , model .LeaderboardEntryTagTeamLead )
171- break
180+ if role .Role == model .RoleTypeTeamLead && role .Scope != nil {
181+ if role .Scope .Type == model .ScopeTypeTeam && role .Scope .ID == teamID {
182+ teamLeadTags [row .UserID ] = true
183+ }
172184 }
173185 }
174186 }
175-
176- if len (entryTags ) > 0 {
177- tags [entry .ID ] = entryTags
178- }
179187 }
180188
181- // Cache tags for this user
182- c .Set (tagCacheKey , tags )
183-
184- return tags
189+ // Cache TEAM_LEAD tags per team
190+ c .Set (cacheKey , teamLeadTags )
191+ return teamLeadTags
185192}
186193
187194// convertToUserRole converts a database role row to a model.UserRole
0 commit comments