1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-14 03:25:11 +08:00

Also refactor placeholder logic to make more sense

This commit is contained in:
Dean Herbert 2022-01-30 03:37:57 +09:00
parent b434e29a7c
commit c401629dd8
4 changed files with 86 additions and 116 deletions

View File

@ -114,12 +114,12 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestPlaceholderStates()
{
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
AddStep(@"None selected", () => leaderboard.SetRetrievalState(PlaceholderState.NoneSelected));
AddStep(@"Empty Scores", () => leaderboard.SetErrorState(LeaderboardErrorState.NoScores));
AddStep(@"Network failure", () => leaderboard.SetErrorState(LeaderboardErrorState.NetworkFailure));
AddStep(@"No supporter", () => leaderboard.SetErrorState(LeaderboardErrorState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetErrorState(LeaderboardErrorState.NotLoggedIn));
AddStep(@"Unavailable", () => leaderboard.SetErrorState(LeaderboardErrorState.Unavailable));
AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardErrorState.NoneSelected));
}
private void showPersonalBestWithNullPosition()
@ -401,22 +401,9 @@ namespace osu.Game.Tests.Visual.SongSelect
};
}
private void showBeatmapWithStatus(BeatmapOnlineStatus status)
{
leaderboard.BeatmapInfo = new BeatmapInfo
{
OnlineID = 1113057,
Status = status,
};
}
private class FailableLeaderboard : BeatmapLeaderboard
{
public void SetRetrievalState(PlaceholderState state)
{
Scores = null;
PlaceholderState = state;
}
public new void SetErrorState(LeaderboardErrorState errorState) => base.SetErrorState(errorState);
public new ICollection<ScoreInfo> Scores
{

View File

@ -53,6 +53,8 @@ namespace osu.Game.Online.Leaderboards
private APIRequest fetchScoresRequest;
private LeaderboardErrorState errorState;
[Resolved(CanBeNull = true)]
private IAPIProvider api { get; set; }
@ -169,14 +171,37 @@ namespace osu.Game.Online.Leaderboards
RefetchScores();
}
/// <summary>
/// Perform a full refetch of scores using current criteria.
/// </summary>
public void RefetchScores() => Scheduler.AddOnce(refetchScores);
/// <summary>
/// Reset the leaderboard into an empty state.
/// </summary>
protected virtual void Reset()
{
cancelPendingWork();
Scores = null;
}
/// <summary>
/// Call when a retrieval or display failure happened to show a relevant message to the user.
/// </summary>
/// <param name="errorState">The state to display.</param>
protected void SetErrorState(LeaderboardErrorState errorState)
{
switch (errorState)
{
case LeaderboardErrorState.NoError:
throw new InvalidOperationException($"State {errorState} cannot be set by a leaderboard implementation.");
}
Debug.Assert(scores?.Any() != true);
setErrorState(errorState);
}
/// <summary>
/// Performs a fetch/refresh of scores to be displayed.
/// </summary>
@ -195,7 +220,7 @@ namespace osu.Game.Online.Leaderboards
Reset();
PlaceholderState = PlaceholderState.Retrieving;
setErrorState(LeaderboardErrorState.NoError);
loading.Show();
currentFetchCancellationSource = new CancellationTokenSource();
@ -210,7 +235,7 @@ namespace osu.Game.Online.Leaderboards
if (e is OperationCanceledException || currentFetchCancellationSource.IsCancellationRequested)
return;
PlaceholderState = PlaceholderState.NetworkFailure;
SetErrorState(LeaderboardErrorState.NetworkFailure);
});
api?.Queue(fetchScoresRequest);
@ -223,77 +248,6 @@ namespace osu.Game.Online.Leaderboards
fetchScoresRequest?.Cancel();
}
#region Placeholder handling
private Placeholder currentPlaceholder;
private PlaceholderState placeholderState;
/// <summary>
/// Update the placeholder visibility.
/// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores.
/// </summary>
protected PlaceholderState PlaceholderState
{
get => placeholderState;
set
{
if (value == placeholderState)
return;
placeholderState = value;
switch (placeholderState)
{
case PlaceholderState.NetworkFailure:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(new ClickablePlaceholder(@"Couldn't fetch scores!", FontAwesome.Solid.Sync)
{
Action = RefetchScores
});
break;
case PlaceholderState.NoneSelected:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(new MessagePlaceholder(@"Please select a beatmap!"));
break;
case PlaceholderState.Unavailable:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!"));
break;
case PlaceholderState.NoScores:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(new MessagePlaceholder(@"No records yet!"));
break;
case PlaceholderState.NotLoggedIn:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(new LoginPlaceholder(@"Please sign in to view online leaderboards!"));
break;
case PlaceholderState.NotSupporter:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!"));
break;
case PlaceholderState.Retrieving:
Debug.Assert(scores?.Any() != true);
replacePlaceholder(null);
break;
case PlaceholderState.Successful:
Debug.Assert(scores?.Any() == true);
replacePlaceholder(null);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private void updateScoresDrawables()
{
currentScoresAsyncLoadCancellationSource?.Cancel();
@ -305,13 +259,14 @@ namespace osu.Game.Online.Leaderboards
if (scores?.Any() != true)
{
SetErrorState(LeaderboardErrorState.NoScores);
loading.Hide();
PlaceholderState = PlaceholderState.NoScores;
return;
}
// ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful;
setErrorState(LeaderboardErrorState.NoError);
loading.Show();
LoadComponentAsync(new FillFlowContainer<LeaderboardScore>
{
@ -339,25 +294,61 @@ namespace osu.Game.Online.Leaderboards
}, (currentScoresAsyncLoadCancellationSource = new CancellationTokenSource()).Token);
}
private void replacePlaceholder(Placeholder placeholder)
#region Placeholder handling
private Placeholder placeholder;
private void setErrorState(LeaderboardErrorState errorState)
{
if (placeholder != null && placeholder.Equals(currentPlaceholder))
if (errorState == this.errorState)
return;
currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
this.errorState = errorState;
placeholder?.FadeOut(150, Easing.OutQuint).Expire();
placeholder = getPlaceholderFor(errorState);
if (placeholder == null)
{
currentPlaceholder = null;
return;
}
placeholderContainer.Child = placeholder;
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
}
currentPlaceholder = placeholder;
private Placeholder getPlaceholderFor(LeaderboardErrorState errorState)
{
switch (errorState)
{
case LeaderboardErrorState.NetworkFailure:
return new ClickablePlaceholder(@"Couldn't fetch scores!", FontAwesome.Solid.Sync)
{
Action = RefetchScores
};
case LeaderboardErrorState.NoneSelected:
return new MessagePlaceholder(@"Please select a beatmap!");
case LeaderboardErrorState.Unavailable:
return new MessagePlaceholder(@"Leaderboards are not available for this beatmap!");
case LeaderboardErrorState.NoScores:
return new MessagePlaceholder(@"No records yet!");
case LeaderboardErrorState.NotLoggedIn:
return new LoginPlaceholder(@"Please sign in to view online leaderboards!");
case LeaderboardErrorState.NotSupporter:
return new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!");
case LeaderboardErrorState.NoError:
return null;
default:
throw new ArgumentOutOfRangeException();
}
}
#endregion

View File

@ -3,10 +3,9 @@
namespace osu.Game.Online.Leaderboards
{
public enum PlaceholderState
public enum LeaderboardErrorState
{
Successful,
Retrieving,
NoError,
NetworkFailure,
Unavailable,
NoneSelected,

View File

@ -33,19 +33,12 @@ namespace osu.Game.Screens.Select.Leaderboards
set
{
if (beatmapInfo == null && value == null)
{
// always null scores to ensure a correct initial display.
// see weird `scoresLoadedOnce` logic in base implementation.
Scores = null;
return;
}
if (beatmapInfo?.Equals(value) == true)
return;
beatmapInfo = value;
Scores = null;
RefetchScores();
}
}
@ -114,7 +107,7 @@ namespace osu.Game.Screens.Select.Leaderboards
if (fetchBeatmapInfo == null)
{
PlaceholderState = PlaceholderState.NoneSelected;
SetErrorState(LeaderboardErrorState.NoneSelected);
return null;
}
@ -126,19 +119,19 @@ namespace osu.Game.Screens.Select.Leaderboards
if (api?.IsLoggedIn != true)
{
PlaceholderState = PlaceholderState.NotLoggedIn;
SetErrorState(LeaderboardErrorState.NotLoggedIn);
return null;
}
if (fetchBeatmapInfo.OnlineID <= 0 || fetchBeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
{
PlaceholderState = PlaceholderState.Unavailable;
SetErrorState(LeaderboardErrorState.Unavailable);
return null;
}
if (!api.LocalUser.Value.IsSupporter && (Scope != BeatmapLeaderboardScope.Global || filterMods))
{
PlaceholderState = PlaceholderState.NotSupporter;
SetErrorState(LeaderboardErrorState.NotSupporter);
return null;
}