1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Add handling of expiration

This commit is contained in:
Dean Herbert 2024-03-25 14:28:23 +08:00
parent f0614928b1
commit 057f86dd14
No known key found for this signature in database
3 changed files with 102 additions and 22 deletions

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
@ -141,5 +142,64 @@ namespace osu.Game.Tests.Visual.Menus
return !images.First().IsPresent && images.Last().IsPresent;
});
}
[Test]
public void TestExpiry()
{
AddStep("set multiple images, second expiring soon", () => onlineMenuBanner.Current.Value = new APIMenuContent
{
Images = new[]
{
new APIMenuImage
{
Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png",
Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023",
},
new APIMenuImage
{
Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png",
Url = @"https://osu.ppy.sh/community/contests/189",
Expires = DateTimeOffset.Now.AddSeconds(2),
}
},
});
AddUntilStep("wait for first image shown", () =>
{
var images = onlineMenuBanner.ChildrenOfType<OnlineMenuBanner.MenuImage>();
if (images.Count() != 2)
return false;
return images.First().IsPresent && !images.Last().IsPresent;
});
AddUntilStep("wait for second image shown", () =>
{
var images = onlineMenuBanner.ChildrenOfType<OnlineMenuBanner.MenuImage>();
if (images.Count() != 2)
return false;
return !images.First().IsPresent && images.Last().IsPresent;
});
AddUntilStep("wait for expiry", () =>
{
return onlineMenuBanner
.ChildrenOfType<OnlineMenuBanner.MenuImage>()
.Any(i => !i.Image.IsCurrent);
});
AddUntilStep("wait for first image shown", () =>
{
var images = onlineMenuBanner.ChildrenOfType<OnlineMenuBanner.MenuImage>();
if (images.Count() != 2)
return false;
return images.First().IsPresent && !images.Last().IsPresent;
});
}
}
}

View File

@ -20,6 +20,10 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"url")]
public string Url { get; init; } = string.Empty;
public bool IsCurrent =>
(Begins == null || Begins < DateTimeOffset.UtcNow) &&
(Expires == null || Expires > DateTimeOffset.UtcNow);
/// <summary>
/// The time at which this item should begin displaying. If <c>null</c>, will display immediately.
/// </summary>

View File

@ -29,7 +29,7 @@ namespace osu.Game.Screens.Menu
private const float transition_duration = 500;
private Container content = null!;
private Container<MenuImage> content = null!;
private CancellationTokenSource? cancellationTokenSource;
private int displayIndex = -1;
@ -43,7 +43,7 @@ namespace osu.Game.Screens.Menu
AutoSizeDuration = transition_duration;
AutoSizeEasing = Easing.OutQuint;
InternalChild = content = new Container
InternalChild = content = new Container<MenuImage>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -59,8 +59,7 @@ namespace osu.Game.Screens.Menu
{
base.LoadComplete();
Current.BindValueChanged(_ => loadNewImages(), true);
Current.BindValueChanged(loadNewImages, true);
checkForUpdates();
}
@ -86,25 +85,24 @@ namespace osu.Game.Screens.Menu
});
}
private void loadNewImages()
/// <summary>
/// Takes <see cref="Current"/> and materialises and displays drawables for all valid images to be displayed.
/// </summary>
/// <param name="images"></param>
private void loadNewImages(ValueChangedEvent<APIMenuContent> images)
{
nextDisplay?.Cancel();
cancellationTokenSource?.Cancel();
cancellationTokenSource = null;
var newContent = Current.Value;
// A better fade out would be nice, but the menu content changes *very* rarely
// so let's keep things simple for now.
content.Clear(true);
if (newContent.Images.Length == 0)
return;
LoadComponentsAsync(newContent.Images.Select(i => new MenuImage(i)), loaded =>
LoadComponentsAsync(images.NewValue.Images.Select(i => new MenuImage(i)), loaded =>
{
if (!newContent.Equals(Current.Value))
if (!images.NewValue.Equals(Current.Value))
return;
// start hidden
@ -127,20 +125,38 @@ namespace osu.Game.Screens.Menu
if (!anyHovered)
{
bool previousShowing = displayIndex >= 0;
if (previousShowing)
content[displayIndex % content.Count].FadeOut(400, Easing.OutQuint);
int previousIndex = displayIndex;
displayIndex++;
if (displayIndex == -1)
displayIndex = 0;
using (BeginDelayedSequence(previousShowing ? 300 : 0))
content[displayIndex % content.Count].Show();
// To handle expiration simply, arrange all images in best-next order.
// Fade in the first valid one, then handle fading out the last if required.
var currentRotation = content
.Skip(displayIndex + 1)
.Concat(content.Take(displayIndex + 1));
foreach (var image in currentRotation)
{
if (!image.Image.IsCurrent) continue;
using (BeginDelayedSequence(previousIndex >= 0 ? 300 : 0))
{
displayIndex = content.IndexOf(image);
if (displayIndex != previousIndex)
image.Show();
break;
}
}
if (previousIndex >= 0 && previousIndex != displayIndex)
content[previousIndex].FadeOut(400, Easing.OutQuint);
}
if (content.Count > 1)
{
nextDisplay = Scheduler.AddDelayed(showNext, DelayBetweenRotation);
}
// Re-scheduling this method will both handle rotation and re-checking for expiration dates.
nextDisplay = Scheduler.AddDelayed(showNext, DelayBetweenRotation);
}
[LongRunningLoad]