diff --git a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs index cd09a1d20f..4a5f452ed1 100644 --- a/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs +++ b/osu.Game.Tests/Visual/DailyChallenge/TestSceneDailyChallenge.cs @@ -4,16 +4,32 @@ using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Game.Online.API; +using osu.Game.Online.Metadata; using osu.Game.Online.Rooms; +using osu.Game.Overlays; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual.Metadata; using osu.Game.Tests.Visual.OnlinePlay; namespace osu.Game.Tests.Visual.DailyChallenge { public partial class TestSceneDailyChallenge : OnlinePlayTestScene { + [Cached(typeof(MetadataClient))] + private TestMetadataClient metadataClient = new TestMetadataClient(); + + [Cached(typeof(INotificationOverlay))] + private NotificationOverlay notificationOverlay = new NotificationOverlay(); + + [BackgroundDependencyLoader] + private void load() + { + base.Content.Add(notificationOverlay); + } + [Test] public void TestDailyChallenge() { @@ -36,5 +52,62 @@ namespace osu.Game.Tests.Visual.DailyChallenge AddStep("add room", () => API.Perform(new CreateRoomRequest(room))); AddStep("push screen", () => LoadScreen(new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room))); } + + [Test] + public void TestNotifications() + { + var room = new Room + { + RoomID = { Value = 1234 }, + Name = { Value = "Daily Challenge: June 4, 2024" }, + Playlist = + { + new PlaylistItem(CreateAPIBeatmapSet().Beatmaps.First()) + { + RequiredMods = [new APIMod(new OsuModTraceable())], + AllowedMods = [new APIMod(new OsuModDoubleTime())] + } + }, + EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, + Category = { Value = RoomCategory.DailyChallenge } + }; + + AddStep("add room", () => API.Perform(new CreateRoomRequest(room))); + AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1234 }); + AddStep("push screen", () => LoadScreen(new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room))); + AddStep("daily challenge ended", () => metadataClient.DailyChallengeInfo.Value = null); + AddStep("install custom handler", () => + { + ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case GetRoomRequest r: + { + r.TriggerSuccess(new Room + { + RoomID = { Value = 1235, }, + Name = { Value = "Daily Challenge: June 5, 2024" }, + Playlist = + { + new PlaylistItem(CreateAPIBeatmapSet().Beatmaps.First()) + { + RequiredMods = [new APIMod(new OsuModTraceable())], + AllowedMods = [new APIMod(new OsuModDoubleTime())] + } + }, + EndDate = { Value = DateTimeOffset.Now.AddHours(12) }, + Category = { Value = RoomCategory.DailyChallenge } + }); + return true; + } + + default: + return false; + } + }; + }); + AddStep("next daily challenge started", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1235 }); + } } } diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs index 4b4e4a7a62..d176f36162 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallenge.cs @@ -30,6 +30,7 @@ using osu.Game.Online.Metadata; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay.Components; @@ -54,6 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge private readonly Bindable> userMods = new Bindable>(Array.Empty()); private readonly IBindable apiState = new Bindable(); + private readonly IBindable dailyChallengeInfo = new Bindable(); private OnlinePlayScreenWaveContainer waves = null!; private DailyChallengeLeaderboard leaderboard = null!; @@ -65,6 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge private DailyChallengeTotalsDisplay totals = null!; private DailyChallengeEventFeed feed = null!; + private SimpleNotification? waitForNextChallengeNotification; + [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum); @@ -98,6 +102,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge [Resolved] private PreviewTrackManager previewTrackManager { get; set; } = null!; + [Resolved] + private INotificationOverlay? notificationOverlay { get; set; } + public override bool DisallowExternalBeatmapRulesetChanges => true; public override bool? ApplyModTrackAdjustments => true; @@ -336,6 +343,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge } metadataClient.MultiplayerRoomScoreSet += onRoomScoreSet; + dailyChallengeInfo.BindTo(metadataClient.DailyChallengeInfo); ((IBindable)breakdown.UserBestScore).BindTo(leaderboard.UserBestScore); } @@ -388,6 +396,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge apiState.BindTo(API.State); apiState.BindValueChanged(onlineStateChanged, true); + + dailyChallengeInfo.BindValueChanged(dailyChallengeChanged); } private void trySetDailyChallengeBeatmap() @@ -405,6 +415,29 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge Schedule(forcefullyExit); }); + private void dailyChallengeChanged(ValueChangedEvent change) + { + if (change.OldValue?.RoomID == room.RoomID.Value && change.NewValue == null) + { + notificationOverlay?.Post(waitForNextChallengeNotification = new SimpleNotification + { + Text = "Today's daily challenge has concluded. Thanks for playing! The next one should appear in a few minutes." + }); + } + + if (change.NewValue != null && change.NewValue.Value.RoomID != room.RoomID.Value) + { + var roomRequest = new GetRoomRequest(change.NewValue.Value.RoomID); + + roomRequest.Success += room => + { + waitForNextChallengeNotification?.Close(false); + notificationOverlay?.Post(new NewDailyChallengeNotification(room)); + }; + API.Queue(roomRequest); + } + } + private void forcefullyExit() { Logger.Log($"{this} forcefully exiting due to loss of API connection"); diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs new file mode 100644 index 0000000000..36ec8b37a7 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/NewDailyChallengeNotification.cs @@ -0,0 +1,44 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Rooms; +using osu.Game.Overlays.Notifications; +using osu.Game.Screens.Menu; + +namespace osu.Game.Screens.OnlinePlay.DailyChallenge +{ + public partial class NewDailyChallengeNotification : SimpleNotification + { + private readonly Room room; + + private BeatmapCardNano card = null!; + + public NewDailyChallengeNotification(Room room) + { + this.room = room; + } + + [BackgroundDependencyLoader] + private void load(OsuGame? game) + { + Text = "Today's daily challenge is here! Click here to play."; + Content.Add(card = new BeatmapCardNano((APIBeatmapSet)room.Playlist.Single().Beatmap.BeatmapSet!)); + Activated = () => + { + game?.PerformFromScreen(s => s.Push(new DailyChallenge(room)), [typeof(MainMenu)]); + return true; + }; + } + + protected override void Update() + { + base.Update(); + card.Width = Content.DrawWidth; + } + } +} diff --git a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs index fa64a83352..2a0af0b10e 100644 --- a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs +++ b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Metadata public override IBindableDictionary UserStates => userStates; private readonly BindableDictionary userStates = new BindableDictionary(); - public override IBindable DailyChallengeInfo => dailyChallengeInfo; + public override Bindable DailyChallengeInfo => dailyChallengeInfo; private readonly Bindable dailyChallengeInfo = new Bindable(); [Resolved]