11import Component from '@glimmer/component' ;
2- import { inject as service } from '@ember/service' ;
3- import type Store from '@ember-data/store' ;
4- import type LeaderboardEntryModel from 'codecrafters-frontend/models/leaderboard-entry' ;
52import type AuthenticatorService from 'codecrafters-frontend/services/authenticator' ;
63import type LanguageModel from 'codecrafters-frontend/models/language' ;
4+ import type LeaderboardEntryModel from 'codecrafters-frontend/models/leaderboard-entry' ;
5+ import type LeaderboardRankCalculationModel from 'codecrafters-frontend/models/leaderboard-rank-calculation' ;
6+ import type Store from '@ember-data/store' ;
7+ import { inject as service } from '@ember/service' ;
8+ import { task } from 'ember-concurrency' ;
9+ import { tracked } from '@glimmer/tracking' ;
710
811interface Signature {
912 Element : HTMLDivElement ;
1013
1114 Args : {
1215 language : LanguageModel ;
13- surroundingEntries : LeaderboardEntryModel [ ] ;
1416 topEntries : LeaderboardEntryModel [ ] ;
1517 } ;
1618}
@@ -19,6 +21,17 @@ export default class LeaderboardPageEntriesTable extends Component<Signature> {
1921 @service declare authenticator : AuthenticatorService ;
2022 @service declare store : Store ;
2123
24+ @tracked surroundingEntries : LeaderboardEntryModel [ ] = [ ] ;
25+ @tracked userRankCalculation : LeaderboardRankCalculationModel | null = null ;
26+
27+ constructor ( owner : unknown , args : Signature [ 'Args' ] ) {
28+ super ( owner , args ) ;
29+
30+ if ( this . authenticator . isAuthenticated ) {
31+ this . loadUserSpecificResourcesTask . perform ( ) ;
32+ }
33+ }
34+
2235 get explanationMarkdownForScore ( ) {
2336 return `
2437The highest possible score for this track is ${ this . args . language . leaderboard ! . highestPossibleScore } .
@@ -36,24 +49,64 @@ Harder stages have higher scores assigned to them.
3649 }
3750
3851 get shouldShowSurroundingEntries ( ) : boolean {
39- return ! ! ( this . authenticator . isAuthenticated && ! this . userIsInTopLeaderboardEntries && this . sortedSurroundingEntries . length > 0 ) ;
52+ return ! this . userIsInTopLeaderboardEntries && this . surroundingEntries . length > 0 ;
4053 }
4154
4255 get sortedSurroundingEntries ( ) {
43- return this . args . surroundingEntries . filter ( ( entry ) => ! entry . isBanned ) . sort ( ( a , b ) => b . score - a . score ) ;
56+ return this . surroundingEntries . filter ( ( entry ) => ! entry . isBanned ) . sort ( ( a , b ) => b . score - a . score ) ;
57+ }
58+
59+ get sortedSurroundingEntriesWithRanks ( ) {
60+ return this . sortedSurroundingEntries . map ( ( entry , index ) => ( {
61+ entry : entry ,
62+ rank : this . userRankCalculation ! . rank + ( index - this . userEntryIndexInSurroundingEntries ) ,
63+ } ) ) ;
4464 }
4565
4666 get sortedTopEntries ( ) {
4767 return this . args . topEntries . filter ( ( entry ) => ! entry . isBanned ) . sort ( ( a , b ) => b . score - a . score ) ;
4868 }
4969
70+ get userEntryIndexInSurroundingEntries ( ) {
71+ return this . sortedSurroundingEntries . findIndex ( ( entry ) => entry . user . id === this . authenticator . currentUserId ) ;
72+ }
73+
5074 get userIsInTopLeaderboardEntries ( ) : boolean {
5175 if ( ! this . authenticator . isAuthenticated ) {
5276 return false ;
5377 }
5478
5579 return this . args . topEntries . some ( ( entry ) => entry . user . id === this . authenticator . currentUserId ) ;
5680 }
81+
82+ loadUserSpecificResourcesTask = task ( { keepLatest : true } , async ( ) : Promise < void > => {
83+ if ( ! this . userIsInTopLeaderboardEntries ) {
84+ this . surroundingEntries = ( await this . store . query ( 'leaderboard-entry' , {
85+ include : 'leaderboard,user' ,
86+ leaderboard_id : this . args . language . leaderboard ! . id ,
87+ user_id : this . authenticator . currentUserId , // Only used in tests since mirage doesn't have auth context
88+ filter_type : 'around_me' ,
89+ } ) ) as unknown as LeaderboardEntryModel [ ] ;
90+
91+ const userRankCalculations = ( await this . store . query ( 'leaderboard-rank-calculation' , {
92+ include : 'user' ,
93+ leaderboard_id : this . args . language . leaderboard ! . id ,
94+ user_id : this . authenticator . currentUserId , // Only used in tests since mirage doesn't have auth context
95+ } ) ) as unknown as LeaderboardRankCalculationModel [ ] ;
96+
97+ this . userRankCalculation = userRankCalculations [ 0 ] || null ;
98+
99+ // TODO: Also look at "outdated" user rank calculations?
100+ if ( ! this . userRankCalculation ) {
101+ this . userRankCalculation = await this . store
102+ . createRecord ( 'leaderboard-rank-calculation' , {
103+ leaderboard : this . args . language . leaderboard ! ,
104+ user : this . authenticator . currentUser ! ,
105+ } )
106+ . save ( ) ;
107+ }
108+ }
109+ } ) ;
57110}
58111
59112declare module '@glint/environment-ember-loose/registry' {
0 commit comments