2727 Icon =" @Icons.Material.Filled.Favorite"
2828 Class =" @HeartCssClass"
2929 DisableRipple =" true"
30- OnClick =" ToggleHeart "
30+ OnClick =" ToggleMatching "
3131 AriaLabel =" Start matching" />
32-
33-
32+ <MudText class =" spark-text" Typo =" Typo.caption" >
33+ @PendingMessage
34+ </MudText >
3435 @if (pendingAction != QueueAction .None ) {
35- < MudText class = " spark-text" Typo = " Typo.caption" >
36- @PendingMessage
37- < / MudText >
3836 < MudProgressLinear Value = " @((10 - secondsLeft) * 10)" Class = " w-64" / >
3937 }
40-
41- @if (! string .IsNullOrEmpty (errorMessage )) {
42- < MudAlert Severity = " Severity.Error" Dense = " true" Elevation = " 0" > @errorMessage < / MudAlert >
43- }
4438 </MudStack >
4539 <MudSpacer />
4640</MudStack >
6963
7064@code {
7165 // --- state ---
72- private int onlineUserCount ;
73- private bool isQueueing = false ;
66+ private bool blnMatchStopped = false ;
67+ private int onlineUserCount ;
7468 private string ? errorMessage ;
7569
76- private enum QueueAction { None , Start , Stop }
70+ private enum QueueAction { None , Pending , Queueing }
7771 private QueueAction pendingAction = QueueAction .None ;
7872 private int secondsLeft = 0 ;
7973 private CancellationTokenSource ? pendingCts ;
8074
8175 // --- computed UI text/classes ---
8276 private string TooltipText =>
8377 pendingAction switch {
84- QueueAction .Start => $" Starting in {secondsLeft }s… Tap to cancel" ,
85- QueueAction .Stop => $" Stopping…" ,
86- _ => isQueueing ? " Stop matching" : " Start matching"
78+ QueueAction .Pending => $" Starting in {secondsLeft }s… Tap to cancel" ,
79+ QueueAction .Queueing => " Stop Matching" ,
80+ QueueAction .None => " Start Matching" ,
81+ _ => " Start Matching"
8782 };
8883
89- private string PendingMessage =>
90- pendingAction == QueueAction .Start
91- ? $" Match starting in {secondsLeft } second{(secondsLeft == 1 ? " " : " s" )}… Tap again to cancel."
92- : $" Stopping…" ;
84+ private string PendingMessage => pendingAction switch {
85+ QueueAction .Pending => $" Match starting in {secondsLeft } second{(secondsLeft == 1 ? " " : " s" )}… Tap again to cancel." ,
86+ QueueAction .Queueing => blnMatchStopped ? " Stopping..." : " Looking for a match... Tap again to cancel." ,
87+ QueueAction .None => " Tap heart to start matching."
88+ };
9389
9490 private string HeartCssClass =>
95- $" gold-heart {(pendingAction == QueueAction .Start ? " countdown " : isQueueing ? " steady" : " " )}" ;
91+ $" gold-heart {(pendingAction == QueueAction .None ? " steady" : " countdown " )}" ;
9692
97- protected override async Task OnInitializedAsync () {
98- // safe to keep; remove if you don't render it
93+ protected override async Task OnInitializedAsync ()
94+ {
9995 onlineUserCount = await UserService .GetOnlineUserCountAsync ();
100-
101- var existing = await MatchService .GetLatestActiveMatchAsync (UserSession .intUserID );
102- if (existing != null )
96+ if (await MatchService .GetLatestActiveMatchAsync (UserSession .intUserID ) != null )
97+ {
10398 Navigation .NavigateTo (" /match" );
99+ }
104100 }
105-
106- public async ValueTask DisposeAsync () // <- implements IAsyncDisposable
101+ public async ValueTask DisposeAsync ()
107102 {
108- // cancel any pending countdown
109- pendingCts ? .Cancel ();
110- pendingCts ? .Dispose ();
103+ // cancel any pending countdown, stop matching
104+ pendingCts ? .Cancel ();
105+ pendingCts ? .Dispose ();
111106 pendingCts = null ;
112-
113- // optional: auto-exit queue on leave
114- // if (isQueueing)
115- // await StopMatchingCore(CancellationToken.None);
107+ blnMatchStopped = false ;
108+ await StopMatching ();
116109 }
117-
118- private async Task ToggleHeart () {
119- if (pendingAction != QueueAction .None ) {
120- // cancel grace period
121- pendingCts ? .Cancel ();
122- pendingAction = QueueAction .None ;
123- secondsLeft = 0 ;
124- StateHasChanged ();
125- return ;
110+ private async Task ToggleMatching () {
111+ switch (pendingAction ) {
112+ case QueueAction .Pending : {
113+ pendingCts ? .Cancel ();
114+ pendingCts ? .Dispose ();
115+ pendingCts = null ;
116+ pendingAction = QueueAction .None ;
117+ StateHasChanged ( );
118+ } break ;
119+ case QueueAction .Queueing : {
120+ blnMatchStopped = true ;
121+ StateHasChanged ( );
122+ await StopMatching ();
123+ } break ;
124+ case QueueAction .None : {
125+ StartGracePeriod ();
126+ } break ;
126127 }
127-
128- if (! isQueueing )
129- StartGracePeriod (QueueAction .Start );
130- else
131- await StopMatching (); // stop immediately; animation stops
132128 }
133-
134- private void StartGracePeriod ( QueueAction action ) {
129+ private void StartGracePeriod () {
130+ pendingAction = QueueAction . Pending ;
135131 errorMessage = null ;
136- pendingAction = action ;
137132 secondsLeft = 5 ;
138-
139133 pendingCts ? .Cancel ();
140134 pendingCts ? .Dispose ();
141135 pendingCts = new CancellationTokenSource ();
142136 _ = RunGracePeriodAsync (pendingCts .Token );
143-
144137 StateHasChanged ();
145138 }
146-
147139 private async Task RunGracePeriodAsync (CancellationToken token ) {
148140 try {
149141 while (secondsLeft > 0 ) {
150142 await Task .Delay (1000 , token );
151143 secondsLeft -- ;
152144 StateHasChanged ();
153145 }
154-
155- if (pendingAction == QueueAction .Start )
156- await StartMatching ();
146+
147+ pendingCts ? .Dispose ();
148+ pendingCts = null ;
149+ await StartMatching ();
157150 }
158151 catch (OperationCanceledException ) { /* user canceled */ }
159152 finally {
153+ blnMatchStopped = false ;
160154 pendingAction = QueueAction .None ;
161155 secondsLeft = 0 ;
162156 StateHasChanged ();
163157 }
164158 }
159+ private async Task StartMatching ()
160+ {
161+ errorMessage = null ;
162+ pendingAction = QueueAction .Queueing ;
163+ blnMatchStopped = false ;
164+ StateHasChanged ();
165165
166- private async Task StartMatching () {
167- try {
168- var resp = await HttpClient .GetAsync ($" http://sparkcheck-matcher:9988/enterQueue?intUserID={UserSession .intUserID }" );
169- if (! resp .IsSuccessStatusCode ) {
170- errorMessage = $" Matcher error: {(int )resp .StatusCode } {resp .ReasonPhrase }" ;
171- return ;
172- }
173-
174- var content = await resp .Content .ReadAsStringAsync ();
175- using var json = System .Text .Json .JsonDocument .Parse (content );
176- var status = json .RootElement .TryGetProperty (" status" , out var s ) ? s .GetString () : null ;
177-
178- if (status == " success" ) {
179- isQueueing = true ; // keep pulse on
180- Navigation .NavigateTo (" /match" );
181- }
182- else if (status == " error" ) {
183- errorMessage = json .RootElement .TryGetProperty (" message" , out var m ) ? m .GetString () : " Unknown error." ;
184- }
185- else {
186- errorMessage = " Unexpected matcher response." ;
166+ try
167+ {
168+ var response = await
169+ HttpClient .GetAsync ($" http://sparkcheck-matcher:9988/enterQueue?intUserID={UserSession .intUserID }" );
170+ if (response .IsSuccessStatusCode )
171+ {
172+ var content = await response .Content .ReadAsStringAsync ();
173+ var json = System .Text .Json .JsonDocument .Parse (content );
174+ var status = json .RootElement .GetProperty (" status" ).GetString ();
175+ switch (status )
176+ {
177+ case " success" :
178+ Navigation .NavigateTo (" /match" );
179+ break ;
180+ case " error" :
181+ errorMessage = json .RootElement .GetProperty (" message" ).GetString ();
182+ await StopMatching ();
183+ break ;
184+ case " stopped" :
185+ break ;
186+ default :
187+ errorMessage = content ;
188+ break ;
189+ }
187190 }
188191 }
189- catch (Exception ex ) {
192+ catch (Exception ex )
193+ {
190194 errorMessage = $" Error starting match: {ex .Message }" ;
191195 }
192- finally {
196+ finally
197+ {
198+ blnMatchStopped = false ;
199+ pendingAction = QueueAction .None ;
193200 StateHasChanged ();
194201 }
195202 }
196203
197- private async Task StopMatching () {
198- try {
199- var resp = await HttpClient .GetAsync ($" http://sparkcheck-matcher:9988/exitQueue?intUserID={UserSession .intUserID }" );
200- if (! resp .IsSuccessStatusCode ) {
201- errorMessage = $" Matcher error: {(int )resp .StatusCode } {resp .ReasonPhrase }" ;
202- return ;
203- }
204- isQueueing = false ; // pulse off
204+ private async Task StopMatching ()
205+ {
206+ try
207+ {
208+ await HttpClient .GetAsync ($" http://sparkcheck-matcher:9988/exitQueue?intUserID={UserSession .intUserID }" );
205209 }
206- catch (Exception ex ) {
210+ catch (Exception ex )
211+ {
207212 errorMessage = $" Error stopping match: {ex .Message }" ;
208213 }
209- finally {
210- StateHasChanged ();
211- }
212214 }
213215}
0 commit comments