diff --git a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs index 28f0e6ff9c..dd37dced93 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneHistoricalSection.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Sections; namespace osu.Game.Tests.Visual.Online @@ -37,8 +38,8 @@ namespace osu.Game.Tests.Visual.Online Child = section = new HistoricalSection(), }); - AddStep("Show peppy", () => section.User.Value = new APIUser { Id = 2 }); - AddStep("Show WubWoofWolf", () => section.User.Value = new APIUser { Id = 39828 }); + AddStep("Show peppy", () => section.User.Value = new UserProfileData(new APIUser { Id = 2 })); + AddStep("Show WubWoofWolf", () => section.User.Value = new UserProfileData(new APIUser { Id = 39828 })); } } } diff --git a/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs b/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs index 15e411b9d8..8bd4e398e4 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Game.Overlays.Profile.Sections.Historical; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,6 +12,7 @@ using System.Linq; using osu.Framework.Testing; using osu.Framework.Graphics.Shapes; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays.Profile; namespace osu.Game.Tests.Visual.Online { @@ -22,7 +21,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red); - private readonly Bindable user = new Bindable(); + private readonly Bindable user = new Bindable(); private readonly PlayHistorySubsection section; public TestScenePlayHistorySubsection() @@ -45,49 +44,49 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestNullValues() { - AddStep("Load user", () => user.Value = user_with_null_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_null_values)); AddAssert("Section is hidden", () => section.Alpha == 0); } [Test] public void TestEmptyValues() { - AddStep("Load user", () => user.Value = user_with_empty_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_empty_values)); AddAssert("Section is hidden", () => section.Alpha == 0); } [Test] public void TestOneValue() { - AddStep("Load user", () => user.Value = user_with_one_value); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_one_value)); AddAssert("Section is hidden", () => section.Alpha == 0); } [Test] public void TestTwoValues() { - AddStep("Load user", () => user.Value = user_with_two_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_two_values)); AddAssert("Section is visible", () => section.Alpha == 1); } [Test] public void TestConstantValues() { - AddStep("Load user", () => user.Value = user_with_constant_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_constant_values)); AddAssert("Section is visible", () => section.Alpha == 1); } [Test] public void TestConstantZeroValues() { - AddStep("Load user", () => user.Value = user_with_zero_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_zero_values)); AddAssert("Section is visible", () => section.Alpha == 1); } [Test] public void TestFilledValues() { - AddStep("Load user", () => user.Value = user_with_filled_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_filled_values)); AddAssert("Section is visible", () => section.Alpha == 1); AddAssert("Array length is the same", () => user_with_filled_values.MonthlyPlayCounts.Length == getChartValuesLength()); } @@ -95,7 +94,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestMissingValues() { - AddStep("Load user", () => user.Value = user_with_missing_values); + AddStep("Load user", () => user.Value = new UserProfileData(user_with_missing_values)); AddAssert("Section is visible", () => section.Alpha == 1); AddAssert("Array length is 7", () => getChartValuesLength() == 7); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index bfd6372e6f..6c04716c95 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -29,36 +29,37 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestBasic() { - AddStep("Show example user", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER); + AddStep("Show example user", () => header.User.Value = new UserProfileData(TestSceneUserProfileOverlay.TEST_USER)); } [Test] public void TestOnlineState() { - AddStep("Show online user", () => header.User.Value = new APIUser + AddStep("Show online user", () => header.User.Value = new UserProfileData(new APIUser { Id = 1001, Username = "IAmOnline", LastVisit = DateTimeOffset.Now, IsOnline = true, - }); + })); - AddStep("Show offline user", () => header.User.Value = new APIUser + AddStep("Show offline user", () => header.User.Value = new UserProfileData(new APIUser { Id = 1002, Username = "IAmOffline", LastVisit = DateTimeOffset.Now.AddDays(-10), IsOnline = false, - }); + })); } [Test] public void TestRankedState() { - AddStep("Show ranked user", () => header.User.Value = new APIUser + AddStep("Show ranked user", () => header.User.Value = new UserProfileData(new APIUser { Id = 2001, Username = "RankedUser", + Groups = new[] { new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" } }, Statistics = new UserStatistics { IsRanked = true, @@ -70,9 +71,9 @@ namespace osu.Game.Tests.Visual.Online Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() }, } - }); + })); - AddStep("Show unranked user", () => header.User.Value = new APIUser + AddStep("Show unranked user", () => header.User.Value = new UserProfileData(new APIUser { Id = 2002, Username = "UnrankedUser", @@ -86,7 +87,7 @@ namespace osu.Game.Tests.Visual.Online Data = Enumerable.Range(2345, 85).ToArray() }, } - }); + })); } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index 35c7e7bf28..0066dbb963 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -4,9 +4,12 @@ using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; -using osu.Game.Overlays.Profile; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -14,9 +17,41 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public partial class TestSceneUserProfileOverlay : OsuTestScene { - protected override bool UseOnlineAPI => true; + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; - private readonly TestUserProfileOverlay profile; + private UserProfileOverlay profile = null!; + + [SetUpSteps] + public void SetUp() + { + AddStep("create profile overlay", () => Child = profile = new UserProfileOverlay()); + } + + [Test] + public void TestBlank() + { + AddStep("show overlay", () => profile.Show()); + } + + [Test] + public void TestActualUser() + { + AddStep("set up request handling", () => + { + dummyAPI.HandleRequest = req => + { + if (req is GetUserRequest getUserRequest) + { + getUserRequest.TriggerSuccess(TEST_USER); + return true; + } + + return false; + }; + }); + AddStep("show user", () => profile.ShowUser(new APIUser { Id = 1 })); + AddToggleStep("toggle visibility", visible => profile.State.Value = visible ? Visibility.Visible : Visibility.Hidden); + } public static readonly APIUser TEST_USER = new APIUser { @@ -27,6 +62,11 @@ namespace osu.Game.Tests.Visual.Online JoinDate = DateTimeOffset.Now.AddDays(-1), LastVisit = DateTimeOffset.Now, ProfileOrder = new[] { "me" }, + Groups = new[] + { + new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" }, + new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } } + }, Statistics = new UserStatistics { IsRanked = true, @@ -64,60 +104,5 @@ namespace osu.Game.Tests.Visual.Online Colour = "ff0000", Achievements = Array.Empty(), }; - - public TestSceneUserProfileOverlay() - { - Add(profile = new TestUserProfileOverlay()); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - AddStep("Show offline dummy", () => profile.ShowUser(TEST_USER)); - - AddStep("Show null dummy", () => profile.ShowUser(new APIUser - { - Username = @"Null", - Id = 1, - })); - - AddStep("Show ppy", () => profile.ShowUser(new APIUser - { - Username = @"peppy", - Id = 2, - IsSupporter = true, - CountryCode = CountryCode.AU, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" - })); - - AddStep("Show flyte", () => profile.ShowUser(new APIUser - { - Username = @"flyte", - Id = 3103765, - CountryCode = CountryCode.JP, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - })); - - AddStep("Show bancho", () => profile.ShowUser(new APIUser - { - Username = @"BanchoBot", - Id = 3, - IsBot = true, - CountryCode = CountryCode.SH, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c4.jpg" - })); - - AddStep("Show ppy from username", () => profile.ShowUser(new APIUser { Username = @"peppy" })); - AddStep("Show flyte from username", () => profile.ShowUser(new APIUser { Username = @"flyte" })); - - AddStep("Hide", profile.Hide); - AddStep("Show without reload", profile.Show); - } - - private partial class TestUserProfileOverlay : UserProfileOverlay - { - public new ProfileHeader Header => base.Header; - } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs index fdbd8a4325..bab48806d4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserRanks.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Sections; namespace osu.Game.Tests.Visual.Online @@ -44,7 +45,7 @@ namespace osu.Game.Tests.Visual.Online } }); - AddStep("Show cookiezi", () => ranks.User.Value = new APIUser { Id = 124493 }); + AddStep("Show cookiezi", () => ranks.User.Value = new UserProfileData(new APIUser { Id = 124493 })); } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs new file mode 100644 index 0000000000..d9c2774611 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayContainer.cs @@ -0,0 +1,106 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Volume; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; +using Box = osu.Framework.Graphics.Shapes.Box; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public partial class TestSceneOverlayContainer : OsuManualInputManagerTestScene + { + [SetUp] + public void SetUp() => Schedule(() => Child = new TestOverlay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f) + }); + + [Test] + public void TestScrollBlocked() + { + OsuScrollContainer scroll = null!; + + AddStep("add scroll container", () => + { + Add(scroll = new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, + Child = new Box + { + RelativeSizeAxes = Axes.X, + Height = DrawHeight * 10, + Colour = ColourInfo.GradientVertical(Colour4.Black, Colour4.White), + } + }); + }); + + AddStep("perform scroll", () => + { + InputManager.MoveMouseTo(Content); + InputManager.ScrollVerticalBy(-10); + }); + + AddAssert("scroll didn't receive input", () => scroll.Current == 0); + } + + [Test] + public void TestAltScrollNotBlocked() + { + bool scrollReceived = false; + + AddStep("add volume control receptor", () => Add(new VolumeControlReceptor + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, + ScrollActionRequested = (_, _, _) => scrollReceived = true, + })); + + AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft)); + AddStep("perform scroll", () => + { + InputManager.MoveMouseTo(Content); + InputManager.ScrollVerticalBy(10); + }); + + AddAssert("receptor received scroll input", () => scrollReceived); + AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft)); + } + + private partial class TestOverlay : OsuFocusedOverlayContainer + { + [BackgroundDependencyLoader] + private void load() + { + State.Value = Visibility.Visible; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Overlay content", + Colour = Color4.Black, + }, + }; + } + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 740c170f8f..07b5b53e0e 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -25,8 +25,6 @@ namespace osu.Game.Graphics.Containers protected virtual string PopInSampleName => "UI/overlay-pop-in"; protected virtual string PopOutSampleName => "UI/overlay-pop-out"; - protected override bool BlockScrollInput => false; - protected override bool BlockNonPositionalInput => true; /// @@ -90,6 +88,15 @@ namespace osu.Game.Graphics.Containers base.OnMouseUp(e); } + protected override bool OnScroll(ScrollEvent e) + { + // allow for controlling volume when alt is held. + // mostly for compatibility with osu-stable. + if (e.AltPressed) return false; + + return true; + } + public virtual bool OnPressed(KeyBindingPressEvent e) { if (e.Repeat) diff --git a/osu.Game/Online/API/Requests/Responses/APIUser.cs b/osu.Game/Online/API/Requests/Responses/APIUser.cs index 9cb0c0704d..0e62b03f1a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUser.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUser.cs @@ -255,6 +255,9 @@ namespace osu.Game.Online.API.Requests.Responses [CanBeNull] public Dictionary RulesetsStatistics { get; set; } + [JsonProperty("groups")] + public APIUserGroup[] Groups; + public override string ToString() => Username; /// diff --git a/osu.Game/Online/API/Requests/Responses/APIUserGroup.cs b/osu.Game/Online/API/Requests/Responses/APIUserGroup.cs new file mode 100644 index 0000000000..5f943e583e --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIUserGroup.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIUserGroup + { + [JsonProperty(@"colour")] + public string Colour { get; set; } = null!; + + [JsonProperty(@"has_listing")] + public bool HasListings { get; set; } + + [JsonProperty(@"has_playmodes")] + public bool HasPlaymodes { get; set; } + + [JsonProperty(@"id")] + public int Id { get; set; } + + [JsonProperty(@"identifier")] + public string Identifier { get; set; } = null!; + + [JsonProperty(@"is_probationary")] + public bool IsProbationary { get; set; } + + [JsonProperty(@"name")] + public string Name { get; set; } = null!; + + [JsonProperty(@"short_name")] + public string ShortName { get; set; } = null!; + + [JsonProperty(@"playmodes")] + public string[]? Playmodes { get; set; } + } +} diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index fd831ad4ae..7b9b43f368 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet; @@ -30,6 +31,13 @@ namespace osu.Game.Overlays private readonly Bindable beatmapSet = new Bindable(); + [Resolved] + private IAPIProvider api { get; set; } + + private IBindable apiUser; + + private (BeatmapSetLookupType type, int id)? lastLookup; + /// /// Isolates the beatmap set overlay from the game-wide selected mods bindable /// to avoid affecting the beatmap details section (i.e. ). @@ -72,6 +80,17 @@ namespace osu.Game.Overlays }; } + [BackgroundDependencyLoader] + private void load() + { + apiUser = api.LocalUser.GetBoundCopy(); + apiUser.BindValueChanged(_ => Schedule(() => + { + if (api.IsLoggedIn) + performFetch(); + })); + } + protected override BeatmapSetHeader CreateHeader() => new BeatmapSetHeader(); protected override Color4 BackgroundColour => ColourProvider.Background6; @@ -84,27 +103,20 @@ namespace osu.Game.Overlays public void FetchAndShowBeatmap(int beatmapId) { + lastLookup = (BeatmapSetLookupType.BeatmapId, beatmapId); beatmapSet.Value = null; - var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); - req.Success += res => - { - beatmapSet.Value = res; - Header.HeaderContent.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineID == beatmapId); - }; - API.Queue(req); - + performFetch(); Show(); } public void FetchAndShowBeatmapSet(int beatmapSetId) { + lastLookup = (BeatmapSetLookupType.SetId, beatmapSetId); + beatmapSet.Value = null; - var req = new GetBeatmapSetRequest(beatmapSetId); - req.Success += res => beatmapSet.Value = res; - API.Queue(req); - + performFetch(); Show(); } @@ -118,6 +130,24 @@ namespace osu.Game.Overlays Show(); } + private void performFetch() + { + if (!api.IsLoggedIn) + return; + + if (lastLookup == null) + return; + + var req = new GetBeatmapSetRequest(lastLookup.Value.id, BeatmapSetLookupType.BeatmapId); + req.Success += res => + { + beatmapSet.Value = res; + if (lastLookup.Value.type == BeatmapSetLookupType.BeatmapId) + Header.HeaderContent.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineID == lastLookup.Value.id); + }; + API.Queue(req); + } + private partial class CommentsSection : BeatmapSetLayoutSection { public readonly Bindable BeatmapSet = new Bindable(); diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index f5c7a3f87a..1e80257a57 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Profile.Header { public partial class BottomHeaderContainer : CompositeDrawable { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); private LinkFlowContainer topLinkContainer = null!; private LinkFlowContainer bottomLinkContainer = null!; @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Profile.Header } }; - User.BindValueChanged(user => updateDisplay(user.NewValue)); + User.BindValueChanged(user => updateDisplay(user.NewValue?.User)); } private void updateDisplay(APIUser? user) diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs index 5e4e1184a4..ecf78e7b92 100644 --- a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Header public partial class CentreHeaderContainer : CompositeDrawable { public readonly BindableBool DetailsVisible = new BindableBool(true); - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); private OverlinedInfoContainer hiddenDetailGlobal = null!; private OverlinedInfoContainer hiddenDetailCountry = null!; @@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Profile.Header expandedDetailContainer.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); }); - User.BindValueChanged(user => updateDisplay(user.NewValue)); + User.BindValueChanged(user => updateDisplay(user.NewValue?.User)); } private void updateDisplay(APIUser? user) diff --git a/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs index c0bcec622a..844efa5cf0 100644 --- a/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/FollowersButton.cs @@ -5,14 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Header.Components { public partial class FollowersButton : ProfileHeaderStatisticsButton { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); public override LocalisableString TooltipText => FriendsStrings.ButtonsDisabled; @@ -22,7 +21,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private void load() { // todo: when friending/unfriending is implemented, the APIAccess.Friends list should be updated accordingly. - User.BindValueChanged(user => SetValue(user.NewValue?.FollowerCount ?? 0), true); + User.BindValueChanged(user => SetValue(user.NewValue?.User.FollowerCount ?? 0), true); } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadge.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadge.cs new file mode 100644 index 0000000000..ce56768a72 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadge.cs @@ -0,0 +1,84 @@ +// 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public partial class GroupBadge : Container, IHasTooltip + { + public LocalisableString TooltipText { get; } + + public int TextSize { get; set; } = 12; + + private readonly APIUserGroup group; + + public GroupBadge(APIUserGroup group) + { + this.group = group; + + AutoSizeAxes = Axes.Both; + Masking = true; + CornerRadius = 8; + + TooltipText = group.Name; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider? colourProvider, RulesetStore rulesets) + { + FillFlowContainer innerContainer; + + AddRangeInternal(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider?.Background6 ?? Colour4.Black + }, + innerContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Padding = new MarginPadding { Vertical = 2, Horizontal = 10 }, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Children = new[] + { + new OsuSpriteText + { + Text = group.ShortName, + Colour = Color4Extensions.FromHex(group.Colour), + Shadow = false, + Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true) + } + } + } + }); + + if (group.Playmodes?.Length > 0) + { + innerContainer.AddRange(group.Playmodes.Select(p => + (rulesets.GetRuleset(p)?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }).With(icon => + { + icon.Size = new Vector2(TextSize - 1); + })).ToList() + ); + } + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs new file mode 100644 index 0000000000..33b3de94db --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/GroupBadgeFlow.cs @@ -0,0 +1,32 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osuTK; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public partial class GroupBadgeFlow : FillFlowContainer + { + public readonly Bindable User = new Bindable(); + + public GroupBadgeFlow() + { + AutoSizeAxes = Axes.Both; + Direction = FillDirection.Horizontal; + Spacing = new Vector2(2); + + User.BindValueChanged(user => + { + Clear(true); + + if (user.NewValue?.Groups != null) + AddRange(user.NewValue.Groups.Select(g => new GroupBadge(g))); + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs index 8f84e69bac..78c5231736 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public partial class LevelBadge : CompositeDrawable, IHasTooltip { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); public LocalisableString TooltipText { get; private set; } @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Profile.Header.Components } }; - User.BindValueChanged(user => updateLevel(user.NewValue)); + User.BindValueChanged(user => updateLevel(user.NewValue?.User)); } private void updateLevel(APIUser? user) diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs index ea5ca0c8bd..919ccb0dd4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs +++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public partial class LevelProgressBar : CompositeDrawable, IHasTooltip { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); public LocalisableString TooltipText { get; } @@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Profile.Header.Components } }; - User.BindValueChanged(user => updateProgress(user.NewValue)); + User.BindValueChanged(user => updateProgress(user.NewValue?.User)); } private void updateProgress(APIUser? user) diff --git a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs index c600f7622a..d509ec0f81 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MappingSubscribersButton.cs @@ -5,14 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Header.Components { public partial class MappingSubscribersButton : ProfileHeaderStatisticsButton { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); public override LocalisableString TooltipText => FollowsStrings.MappingFollowers; @@ -21,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load() { - User.BindValueChanged(user => SetValue(user.NewValue?.MappingFollowerCount ?? 0), true); + User.BindValueChanged(user => SetValue(user.NewValue?.User.MappingFollowerCount ?? 0), true); } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs index 3266e3c308..5f934e3916 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; using osu.Game.Resources.Localisation.Web; using osuTK; @@ -16,7 +15,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { public partial class MessageUserButton : ProfileHeaderButton { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); public override LocalisableString TooltipText => UsersStrings.CardSendMessage; @@ -49,12 +48,16 @@ namespace osu.Game.Overlays.Profile.Header.Components { if (!Content.IsPresent) return; - channelManager?.OpenPrivateChannel(User.Value); + channelManager?.OpenPrivateChannel(User.Value?.User); userOverlay?.Hide(); chatOverlay?.Show(); }; - User.ValueChanged += e => Content.Alpha = e.NewValue != null && !e.NewValue.PMFriendsOnly && apiProvider.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0; + User.ValueChanged += e => + { + var user = e.NewValue?.User; + Content.Alpha = user != null && !user.PMFriendsOnly && apiProvider.LocalUser.Value.Id != user.Id ? 1 : 0; + }; } } } diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs index e9e277acd3..0396f42336 100644 --- a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs +++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs @@ -7,14 +7,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Header.Components { public partial class OverlinedTotalPlayTime : CompositeDrawable, IHasTooltip { - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); public LocalisableString TooltipText { get; set; } @@ -39,10 +38,11 @@ namespace osu.Game.Overlays.Profile.Header.Components User.BindValueChanged(updateTime, true); } - private void updateTime(ValueChangedEvent user) + private void updateTime(ValueChangedEvent user) { - TooltipText = (user.NewValue?.Statistics?.PlayTime ?? 0) / 3600 + " hours"; - info.Content = formatTime(user.NewValue?.Statistics?.PlayTime); + int? playTime = user.NewValue?.User.Statistics?.PlayTime; + TooltipText = (playTime ?? 0) / 3600 + " hours"; + info.Content = formatTime(playTime); } private string formatTime(int? secondsNull) diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs index 95fead1246..3bab798caf 100644 --- a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Resources.Localisation.Web; @@ -30,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Header private FillFlowContainer? fillFlow; private RankGraph rankGraph = null!; - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); private bool expanded = true; @@ -171,8 +170,10 @@ namespace osu.Game.Overlays.Profile.Header }; } - private void updateDisplay(APIUser? user) + private void updateDisplay(UserProfileData? data) { + var user = data?.User; + medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0"; ppInfo.Content = user?.Statistics?.PP?.ToLocalisableString("#,##0") ?? (LocalisableString)"0"; diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index 79f9eba57d..edb695a00e 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -20,14 +20,14 @@ namespace osu.Game.Overlays.Profile.Header { private FillFlowContainer badgeFlowContainer = null!; - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { Alpha = 0; AutoSizeAxes = Axes.Y; - User.ValueChanged += e => updateDisplay(e.NewValue); + User.ValueChanged += e => updateDisplay(e.NewValue?.User); InternalChildren = new Drawable[] { diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index a6501f567f..33e24593e0 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -15,7 +15,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Resources.Localisation.Web; using osu.Game.Users.Drawables; @@ -27,7 +26,7 @@ namespace osu.Game.Overlays.Profile.Header { private const float avatar_size = 110; - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); [Resolved] private IAPIProvider api { get; set; } = null!; @@ -40,6 +39,7 @@ namespace osu.Game.Overlays.Profile.Header private UpdateableFlag userFlag = null!; private OsuSpriteText userCountryText = null!; private FillFlowContainer userStats = null!; + private GroupBadgeFlow groupBadgeFlow = null!; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) @@ -90,6 +90,7 @@ namespace osu.Game.Overlays.Profile.Header { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), Children = new Drawable[] { usernameText = new OsuSpriteText @@ -98,10 +99,14 @@ namespace osu.Game.Overlays.Profile.Header }, openUserExternally = new ExternalLinkButton { - Margin = new MarginPadding { Left = 5 }, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, + groupBadgeFlow = new GroupBadgeFlow + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + } } }, titleText = new OsuSpriteText @@ -174,8 +179,10 @@ namespace osu.Game.Overlays.Profile.Header User.BindValueChanged(user => updateUser(user.NewValue)); } - private void updateUser(APIUser? user) + private void updateUser(UserProfileData? data) { + var user = data?.User; + avatar.User = user; usernameText.Text = user?.Username ?? string.Empty; openUserExternally.Link = $@"{api.WebsiteRootUrl}/users/{user?.Id ?? 0}"; @@ -184,6 +191,7 @@ namespace osu.Game.Overlays.Profile.Header supporterTag.SupportLevel = user?.SupportLevel ?? 0; titleText.Text = user?.Title ?? string.Empty; titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff"); + groupBadgeFlow.User.Value = user; userStats.Clear(); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 2b8d2503e0..5f5740a0aa 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Profile.Header; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -20,7 +19,7 @@ namespace osu.Game.Overlays.Profile { private UserCoverBackground coverContainer = null!; - public Bindable User = new Bindable(); + public Bindable User = new Bindable(); private CentreHeaderContainer centreHeaderContainer; private DetailHeaderContainer detailHeaderContainer; @@ -100,7 +99,7 @@ namespace osu.Game.Overlays.Profile protected override OverlayTitle CreateTitle() => new ProfileHeaderTitle(); - private void updateDisplay(APIUser? user) => coverContainer.User = user; + private void updateDisplay(UserProfileData? user) => coverContainer.User = user?.User; private partial class ProfileHeaderTitle : OverlayTitle { diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 62b40545df..edb3ec1733 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Profile { @@ -29,7 +28,7 @@ namespace osu.Game.Overlays.Profile protected override Container Content => content; - public readonly Bindable User = new Bindable(); + public readonly Bindable User = new Bindable(); protected ProfileSection() { diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index e327d71d25..0e74aba369 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps protected override int InitialItemsCount => type == BeatmapSetType.Graveyard ? 2 : 6; - public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) + public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, LocalisableString headerText) : base(user, headerText) { this.type = type; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index c532c693ec..583006031c 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical /// protected abstract LocalisableString GraphCounterName { get; } - protected ChartProfileSubsection(Bindable user, LocalisableString headerText) + protected ChartProfileSubsection(Bindable user, LocalisableString headerText) : base(user, headerText) { } @@ -44,9 +44,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical User.BindValueChanged(onUserChanged, true); } - private void onUserChanged(ValueChangedEvent e) + private void onUserChanged(ValueChangedEvent e) { - var values = GetValues(e.NewValue); + var values = GetValues(e.NewValue?.User); if (values == null || values.Length <= 1) { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 515c1fd146..5d9cfb7a1d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public partial class PaginatedMostPlayedBeatmapContainer : PaginatedProfileSubsection { - public PaginatedMostPlayedBeatmapContainer(Bindable user) + public PaginatedMostPlayedBeatmapContainer(Bindable user) : base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs index c8a6b1da31..f472ded182 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalMonthlyPlaycountsCountLabel; - public PlayHistorySubsection(Bindable user) + public PlayHistorySubsection(Bindable user) : base(user, UsersStrings.ShowExtraHistoricalMonthlyPlaycountsTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs index ac7c616a64..225eaa8c7e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { protected override LocalisableString GraphCounterName => UsersStrings.ShowExtraHistoricalReplaysWatchedCountsCountLabel; - public ReplaysSubsection(Bindable user) + public ReplaysSubsection(Bindable user) : base(user, UsersStrings.ShowExtraHistoricalReplaysWatchedCountsTitle) { } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index acf4827dfd..5e0227de5b 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -14,15 +14,14 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Game.Resources.Localisation.Web; using osu.Framework.Localisation; -using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Profile.Sections.Kudosu { public partial class KudosuInfo : Container { - private readonly Bindable user = new Bindable(); + private readonly Bindable user = new Bindable(); - public KudosuInfo(Bindable user) + public KudosuInfo(Bindable user) { this.user.BindTo(user); CountSection total; @@ -32,7 +31,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu CornerRadius = 3; Child = total = new CountTotal(); - this.user.ValueChanged += u => total.Count = u.NewValue?.Kudosu.Total ?? 0; + this.user.ValueChanged += u => total.Count = u.NewValue?.User.Kudosu.Total ?? 0; } protected override bool OnClick(ClickEvent e) => true; diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs index c5de810f22..fad0095248 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/PaginatedKudosuHistoryContainer.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu { public partial class PaginatedKudosuHistoryContainer : PaginatedProfileSubsection { - public PaginatedKudosuHistoryContainer(Bindable user) + public PaginatedKudosuHistoryContainer(Bindable user) : base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty) { } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index f32221747c..ddaa710d68 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Sections private OsuSpriteText missing = null!; private readonly LocalisableString? missingText; - protected PaginatedProfileSubsection(Bindable user, LocalisableString? headerText = null, LocalisableString? missingText = null) + protected PaginatedProfileSubsection(Bindable user, LocalisableString? headerText = null, LocalisableString? missingText = null) : base(user, headerText, CounterVisibilityState.AlwaysVisible) { this.missingText = missingText; @@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections User.BindValueChanged(onUserChanged, true); } - private void onUserChanged(ValueChangedEvent e) + private void onUserChanged(ValueChangedEvent e) { loadCancellation?.Cancel(); retrievalRequest?.Cancel(); @@ -100,23 +100,23 @@ namespace osu.Game.Overlays.Profile.Sections CurrentPage = null; ItemsContainer.Clear(); - if (e.NewValue != null) + if (e.NewValue?.User != null) { showMore(); - SetCount(GetCount(e.NewValue)); + SetCount(GetCount(e.NewValue.User)); } } private void showMore() { - if (User.Value == null) + if (User.Value?.User == null) return; loadCancellation = new CancellationTokenSource(); CurrentPage = CurrentPage?.TakeNext(ItemsPerPage) ?? new PaginationParameters(InitialItemsCount); - retrievalRequest = CreateRequest(User.Value, CurrentPage.Value); + retrievalRequest = CreateRequest(User.Value.User, CurrentPage.Value); retrievalRequest.Success += items => UpdateItems(items, loadCancellation); api.Queue(retrievalRequest); diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs index 0a0e1a9298..35d3ac1579 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -6,20 +6,19 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Profile.Sections { public abstract partial class ProfileSubsection : FillFlowContainer { - protected readonly Bindable User = new Bindable(); + protected readonly Bindable User = new Bindable(); private readonly LocalisableString headerText; private readonly CounterVisibilityState counterVisibilityState; private ProfileSubsectionHeader header = null!; - protected ProfileSubsection(Bindable user, LocalisableString? headerText = null, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + protected ProfileSubsection(Bindable user, LocalisableString? headerText = null, CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) { this.headerText = headerText ?? string.Empty; this.counterVisibilityState = counterVisibilityState; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 3ef0275f50..735f5021fe 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { private readonly ScoreType type; - public PaginatedScoreContainer(ScoreType type, Bindable user, LocalisableString headerText) + public PaginatedScoreContainer(ScoreType type, Bindable user, LocalisableString headerText) : base(user, headerText) { this.type = type; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index 4e5b11d79f..9e8279cfc0 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { public partial class PaginatedRecentActivityContainer : PaginatedProfileSubsection { - public PaginatedRecentActivityContainer(Bindable user) + public PaginatedRecentActivityContainer(Bindable user) : base(user, missingText: EventsStrings.Empty) { } diff --git a/osu.Game/Overlays/Profile/UserProfileData.cs b/osu.Game/Overlays/Profile/UserProfileData.cs new file mode 100644 index 0000000000..1a1cb8f7d4 --- /dev/null +++ b/osu.Game/Overlays/Profile/UserProfileData.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Overlays.Profile +{ + /// + /// Contains data about a profile presented on the . + /// + public class UserProfileData + { + /// + /// The user whose profile is being presented. + /// + public APIUser User { get; } + + // TODO: add ruleset + + public UserProfileData(APIUser user) + { + User = user; + } + } +} diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 1a7c4173ab..a48d183047 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays Show(); - if (user.OnlineID == Header?.User.Value?.Id) + if (user.OnlineID == Header?.User.Value?.User.Id) return; if (sectionsContainer != null) @@ -116,15 +116,6 @@ namespace osu.Game.Overlays sectionsContainer.ScrollToTop(); - // Check arbitrarily whether this user has already been populated. - // This is only generally used by tests, but should be quite safe unless we want to force a refresh on loading a previous user in the future. - if (user is APIUser apiUser && apiUser.JoinDate != default) - { - userReq = null; - userLoadComplete(apiUser); - return; - } - userReq = user.OnlineID > 1 ? new GetUserRequest(user.OnlineID) : new GetUserRequest(user.Username); userReq.Success += userLoadComplete; API.Queue(userReq); @@ -134,7 +125,8 @@ namespace osu.Game.Overlays { Debug.Assert(sections != null && sectionsContainer != null && tabs != null); - Header.User.Value = user; + var userProfile = new UserProfileData(user); + Header.User.Value = userProfile; if (user.ProfileOrder != null) { @@ -144,7 +136,7 @@ namespace osu.Game.Overlays if (sec != null) { - sec.User.Value = user; + sec.User.Value = userProfile; sectionsContainer.Add(sec); tabs.AddItem(sec);