mirror of
https://github.com/ppy/osu.git
synced 2025-02-09 19:42:58 +08:00
Merge pull request #30467 from cdwcgt/friend-add
Add the ability to add/remove friends in `UserProfileHeader`
This commit is contained in:
commit
2bd12e14db
@ -155,7 +155,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
var api = (DummyAPIAccess)API;
|
var api = (DummyAPIAccess)API;
|
||||||
|
|
||||||
api.Friends.Clear();
|
api.Friends.Clear();
|
||||||
api.Friends.Add(friend);
|
api.Friends.Add(new APIRelation
|
||||||
|
{
|
||||||
|
Mutual = true,
|
||||||
|
RelationType = RelationType.Friend,
|
||||||
|
TargetID = friend.OnlineID,
|
||||||
|
TargetUser = friend
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
int playerNumber = 1;
|
int playerNumber = 1;
|
||||||
|
@ -30,14 +30,20 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
if (supportLevel > 3)
|
if (supportLevel > 3)
|
||||||
supportLevel = 0;
|
supportLevel = 0;
|
||||||
|
|
||||||
((DummyAPIAccess)API).Friends.Add(new APIUser
|
((DummyAPIAccess)API).Friends.Add(new APIRelation
|
||||||
{
|
{
|
||||||
Username = @"peppy",
|
TargetID = 2,
|
||||||
Id = 2,
|
RelationType = RelationType.Friend,
|
||||||
Colour = "99EB47",
|
Mutual = true,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
TargetUser = new APIUser
|
||||||
IsSupporter = supportLevel > 0,
|
{
|
||||||
SupportLevel = supportLevel
|
Username = @"peppy",
|
||||||
|
Id = 2,
|
||||||
|
Colour = "99EB47",
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
|
IsSupporter = supportLevel > 0,
|
||||||
|
SupportLevel = supportLevel
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,20 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Profile;
|
using osu.Game.Overlays.Profile;
|
||||||
|
using osu.Game.Overlays.Profile.Header.Components;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -22,6 +27,10 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
|
|
||||||
|
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||||
|
|
||||||
|
private readonly ManualResetEventSlim requestLock = new ManualResetEventSlim();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager configManager { get; set; } = null!;
|
private OsuConfigManager configManager { get; set; } = null!;
|
||||||
|
|
||||||
@ -400,5 +409,97 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
}
|
}
|
||||||
}, new OsuRuleset().RulesetInfo));
|
}, new OsuRuleset().RulesetInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private APIUser nonFriend => new APIUser
|
||||||
|
{
|
||||||
|
Id = 727,
|
||||||
|
Username = "Whatever",
|
||||||
|
};
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddFriend()
|
||||||
|
{
|
||||||
|
AddStep("Setup request", () =>
|
||||||
|
{
|
||||||
|
requestLock.Reset();
|
||||||
|
|
||||||
|
dummyAPI.HandleRequest = request =>
|
||||||
|
{
|
||||||
|
if (request is not AddFriendRequest req)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (req.TargetId != nonFriend.OnlineID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var apiRelation = new APIRelation
|
||||||
|
{
|
||||||
|
TargetID = nonFriend.OnlineID,
|
||||||
|
Mutual = true,
|
||||||
|
RelationType = RelationType.Friend,
|
||||||
|
TargetUser = nonFriend
|
||||||
|
};
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
requestLock.Wait(3000);
|
||||||
|
dummyAPI.Friends.Add(apiRelation);
|
||||||
|
req.TriggerSuccess(new AddFriendResponse
|
||||||
|
{
|
||||||
|
UserRelation = apiRelation
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
AddStep("clear friend list", () => dummyAPI.Friends.Clear());
|
||||||
|
AddStep("Show non-friend user", () => header.User.Value = new UserProfileData(nonFriend, new OsuRuleset().RulesetInfo));
|
||||||
|
AddStep("Click followers button", () => this.ChildrenOfType<FollowersButton>().First().TriggerClick());
|
||||||
|
AddStep("Complete request", () => requestLock.Set());
|
||||||
|
AddUntilStep("Friend added", () => API.Friends.Any(f => f.TargetID == nonFriend.OnlineID));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddFriendNonMutual()
|
||||||
|
{
|
||||||
|
AddStep("Setup request", () =>
|
||||||
|
{
|
||||||
|
requestLock.Reset();
|
||||||
|
|
||||||
|
dummyAPI.HandleRequest = request =>
|
||||||
|
{
|
||||||
|
if (request is not AddFriendRequest req)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (req.TargetId != nonFriend.OnlineID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var apiRelation = new APIRelation
|
||||||
|
{
|
||||||
|
TargetID = nonFriend.OnlineID,
|
||||||
|
Mutual = false,
|
||||||
|
RelationType = RelationType.Friend,
|
||||||
|
TargetUser = nonFriend
|
||||||
|
};
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
requestLock.Wait(3000);
|
||||||
|
dummyAPI.Friends.Add(apiRelation);
|
||||||
|
req.TriggerSuccess(new AddFriendResponse
|
||||||
|
{
|
||||||
|
UserRelation = apiRelation
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
AddStep("clear friend list", () => dummyAPI.Friends.Clear());
|
||||||
|
AddStep("Show non-friend user", () => header.User.Value = new UserProfileData(nonFriend, new OsuRuleset().RulesetInfo));
|
||||||
|
AddStep("Click followers button", () => this.ChildrenOfType<FollowersButton>().First().TriggerClick());
|
||||||
|
AddStep("Complete request", () => requestLock.Set());
|
||||||
|
AddUntilStep("Friend added", () => API.Friends.Any(f => f.TargetID == nonFriend.OnlineID));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Online.API
|
|||||||
private string password;
|
private string password;
|
||||||
|
|
||||||
public IBindable<APIUser> LocalUser => localUser;
|
public IBindable<APIUser> LocalUser => localUser;
|
||||||
public IBindableList<APIUser> Friends => friends;
|
public IBindableList<APIRelation> Friends => friends;
|
||||||
public IBindable<UserActivity> Activity => activity;
|
public IBindable<UserActivity> Activity => activity;
|
||||||
public IBindable<UserStatistics> Statistics => statistics;
|
public IBindable<UserStatistics> Statistics => statistics;
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
private Bindable<APIUser> localUser { get; } = new Bindable<APIUser>(createGuestUser());
|
private Bindable<APIUser> localUser { get; } = new Bindable<APIUser>(createGuestUser());
|
||||||
|
|
||||||
private BindableList<APIUser> friends { get; } = new BindableList<APIUser>();
|
private BindableList<APIRelation> friends { get; } = new BindableList<APIRelation>();
|
||||||
|
|
||||||
private Bindable<UserActivity> activity { get; } = new Bindable<UserActivity>();
|
private Bindable<UserActivity> activity { get; } = new Bindable<UserActivity>();
|
||||||
|
|
||||||
@ -360,19 +360,7 @@ namespace osu.Game.Online.API
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var friendsReq = new GetFriendsRequest();
|
UpdateLocalFriends();
|
||||||
friendsReq.Failure += _ => state.Value = APIState.Failing;
|
|
||||||
friendsReq.Success += res =>
|
|
||||||
{
|
|
||||||
friends.Clear();
|
|
||||||
friends.AddRange(res);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!handleRequest(friendsReq))
|
|
||||||
{
|
|
||||||
state.Value = APIState.Failing;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Success callback event is fired on the main thread, so we should wait for that to run before proceeding.
|
// The Success callback event is fired on the main thread, so we should wait for that to run before proceeding.
|
||||||
// Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests
|
// Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests
|
||||||
@ -624,6 +612,22 @@ namespace osu.Game.Online.API
|
|||||||
localUser.Value.Statistics = newStatistics;
|
localUser.Value.Statistics = newStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateLocalFriends()
|
||||||
|
{
|
||||||
|
if (!IsLoggedIn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var friendsReq = new GetFriendsRequest();
|
||||||
|
friendsReq.Failure += _ => state.Value = APIState.Failing;
|
||||||
|
friendsReq.Success += res =>
|
||||||
|
{
|
||||||
|
friends.Clear();
|
||||||
|
friends.AddRange(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
Queue(friendsReq);
|
||||||
|
}
|
||||||
|
|
||||||
private static APIUser createGuestUser() => new GuestUser();
|
private static APIUser createGuestUser() => new GuestUser();
|
||||||
|
|
||||||
private void setLocalUser(APIUser user) => Scheduler.Add(() =>
|
private void setLocalUser(APIUser user) => Scheduler.Add(() =>
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Online.API
|
|||||||
Id = DUMMY_USER_ID,
|
Id = DUMMY_USER_ID,
|
||||||
});
|
});
|
||||||
|
|
||||||
public BindableList<APIUser> Friends { get; } = new BindableList<APIUser>();
|
public BindableList<APIRelation> Friends { get; } = new BindableList<APIRelation>();
|
||||||
|
|
||||||
public Bindable<UserActivity> Activity { get; } = new Bindable<UserActivity>();
|
public Bindable<UserActivity> Activity { get; } = new Bindable<UserActivity>();
|
||||||
|
|
||||||
@ -201,6 +201,10 @@ namespace osu.Game.Online.API
|
|||||||
LocalUser.Value.Statistics = newStatistics;
|
LocalUser.Value.Statistics = newStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateLocalFriends()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null;
|
public IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null;
|
||||||
|
|
||||||
public IChatClient GetChatClient() => new TestChatClientConnector(this);
|
public IChatClient GetChatClient() => new TestChatClientConnector(this);
|
||||||
@ -214,7 +218,7 @@ namespace osu.Game.Online.API
|
|||||||
public void SetState(APIState newState) => state.Value = newState;
|
public void SetState(APIState newState) => state.Value = newState;
|
||||||
|
|
||||||
IBindable<APIUser> IAPIProvider.LocalUser => LocalUser;
|
IBindable<APIUser> IAPIProvider.LocalUser => LocalUser;
|
||||||
IBindableList<APIUser> IAPIProvider.Friends => Friends;
|
IBindableList<APIRelation> IAPIProvider.Friends => Friends;
|
||||||
IBindable<UserActivity> IAPIProvider.Activity => Activity;
|
IBindable<UserActivity> IAPIProvider.Activity => Activity;
|
||||||
IBindable<UserStatistics?> IAPIProvider.Statistics => Statistics;
|
IBindable<UserStatistics?> IAPIProvider.Statistics => Statistics;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Online.API
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user's friends.
|
/// The user's friends.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBindableList<APIUser> Friends { get; }
|
IBindableList<APIRelation> Friends { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current user's activity.
|
/// The current user's activity.
|
||||||
@ -134,6 +134,11 @@ namespace osu.Game.Online.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void UpdateStatistics(UserStatistics newStatistics);
|
void UpdateStatistics(UserStatistics newStatistics);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the friends status of the current user.
|
||||||
|
/// </summary>
|
||||||
|
void UpdateLocalFriends();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Schedule a callback to run on the update thread.
|
/// Schedule a callback to run on the update thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
30
osu.Game/Online/API/Requests/AddFriendRequest.cs
Normal file
30
osu.Game/Online/API/Requests/AddFriendRequest.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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.Net.Http;
|
||||||
|
using osu.Framework.IO.Network;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class AddFriendRequest : APIRequest<AddFriendResponse>
|
||||||
|
{
|
||||||
|
public readonly int TargetId;
|
||||||
|
|
||||||
|
public AddFriendRequest(int targetId)
|
||||||
|
{
|
||||||
|
TargetId = targetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var req = base.CreateWebRequest();
|
||||||
|
|
||||||
|
req.Method = HttpMethod.Post;
|
||||||
|
req.AddParameter("target", TargetId.ToString(), RequestParameterType.Query);
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Target => @"friends";
|
||||||
|
}
|
||||||
|
}
|
14
osu.Game/Online/API/Requests/AddFriendResponse.cs
Normal file
14
osu.Game/Online/API/Requests/AddFriendResponse.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class AddFriendResponse
|
||||||
|
{
|
||||||
|
[JsonProperty("user_relation")]
|
||||||
|
public APIRelation UserRelation = null!;
|
||||||
|
}
|
||||||
|
}
|
27
osu.Game/Online/API/Requests/DeleteFriendRequest.cs
Normal file
27
osu.Game/Online/API/Requests/DeleteFriendRequest.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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.Net.Http;
|
||||||
|
using osu.Framework.IO.Network;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class DeleteFriendRequest : APIRequest
|
||||||
|
{
|
||||||
|
public readonly int TargetId;
|
||||||
|
|
||||||
|
public DeleteFriendRequest(int targetId)
|
||||||
|
{
|
||||||
|
TargetId = targetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var req = base.CreateWebRequest();
|
||||||
|
req.Method = HttpMethod.Delete;
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Target => $@"friends/{TargetId}";
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class GetFriendsRequest : APIRequest<List<APIUser>>
|
public class GetFriendsRequest : APIRequest<List<APIRelation>>
|
||||||
{
|
{
|
||||||
protected override string Target => @"friends";
|
protected override string Target => @"friends";
|
||||||
}
|
}
|
||||||
|
30
osu.Game/Online/API/Requests/Responses/APIRelation.cs
Normal file
30
osu.Game/Online/API/Requests/Responses/APIRelation.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
|
{
|
||||||
|
public class APIRelation
|
||||||
|
{
|
||||||
|
[JsonProperty("target_id")]
|
||||||
|
public int TargetID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("relation_type")]
|
||||||
|
public RelationType RelationType { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("mutual")]
|
||||||
|
public bool Mutual { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("target")]
|
||||||
|
public APIUser? TargetUser { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
public enum RelationType
|
||||||
|
{
|
||||||
|
Friend,
|
||||||
|
Block,
|
||||||
|
}
|
||||||
|
}
|
@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
|||||||
private LoadingLayer loading;
|
private LoadingLayer loading;
|
||||||
private BasicSearchTextBox searchTextBox;
|
private BasicSearchTextBox searchTextBox;
|
||||||
|
|
||||||
private readonly IBindableList<APIUser> apiFriends = new BindableList<APIUser>();
|
private readonly IBindableList<APIRelation> apiFriends = new BindableList<APIRelation>();
|
||||||
|
|
||||||
public FriendDisplay()
|
public FriendDisplay()
|
||||||
{
|
{
|
||||||
@ -177,7 +177,7 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
|||||||
controlBackground.Colour = colourProvider.Background5;
|
controlBackground.Colour = colourProvider.Background5;
|
||||||
|
|
||||||
apiFriends.BindTo(api.Friends);
|
apiFriends.BindTo(api.Friends);
|
||||||
apiFriends.BindCollectionChanged((_, _) => Schedule(() => Users = apiFriends.ToList()), true);
|
apiFriends.BindCollectionChanged((_, _) => Schedule(() => Users = apiFriends.Select(f => f.TargetUser).ToList()), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using SharpCompress;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
{
|
{
|
||||||
@ -13,15 +23,201 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
|
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
|
||||||
|
|
||||||
public override LocalisableString TooltipText => FriendsStrings.ButtonsDisabled;
|
// Because it is impossible to update the number of friends after the operation,
|
||||||
|
// the number of friends obtained is stored and modified locally.
|
||||||
|
private int followerCount;
|
||||||
|
|
||||||
|
public override LocalisableString TooltipText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (status.Value)
|
||||||
|
{
|
||||||
|
case FriendStatus.Self:
|
||||||
|
return FriendsStrings.ButtonsDisabled;
|
||||||
|
|
||||||
|
case FriendStatus.None:
|
||||||
|
return FriendsStrings.ButtonsAdd;
|
||||||
|
|
||||||
|
case FriendStatus.NotMutual:
|
||||||
|
case FriendStatus.Mutual:
|
||||||
|
return FriendsStrings.ButtonsRemove;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FriendsStrings.TitleCompact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override IconUsage Icon => FontAwesome.Solid.User;
|
protected override IconUsage Icon => FontAwesome.Solid.User;
|
||||||
|
|
||||||
|
private readonly IBindableList<APIRelation> apiFriends = new BindableList<APIRelation>();
|
||||||
|
private readonly IBindable<APIUser> localUser = new Bindable<APIUser>();
|
||||||
|
|
||||||
|
private readonly Bindable<FriendStatus> status = new Bindable<FriendStatus>();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colour { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(IAPIProvider api, INotificationOverlay? notifications)
|
||||||
{
|
{
|
||||||
// todo: when friending/unfriending is implemented, the APIAccess.Friends list should be updated accordingly.
|
localUser.BindTo(api.LocalUser);
|
||||||
User.BindValueChanged(user => SetValue(user.NewValue?.User.FollowerCount ?? 0), true);
|
|
||||||
|
status.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
updateIcon();
|
||||||
|
updateColor();
|
||||||
|
});
|
||||||
|
|
||||||
|
User.BindValueChanged(u =>
|
||||||
|
{
|
||||||
|
followerCount = u.NewValue?.User.FollowerCount ?? 0;
|
||||||
|
updateStatus();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
apiFriends.BindTo(api.Friends);
|
||||||
|
apiFriends.BindCollectionChanged((_, _) => Schedule(updateStatus));
|
||||||
|
|
||||||
|
Action += () =>
|
||||||
|
{
|
||||||
|
if (User.Value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (status.Value == FriendStatus.Self)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ShowLoadingLayer();
|
||||||
|
|
||||||
|
APIRequest req = status.Value == FriendStatus.None ? new AddFriendRequest(User.Value.User.OnlineID) : new DeleteFriendRequest(User.Value.User.OnlineID);
|
||||||
|
|
||||||
|
req.Success += () =>
|
||||||
|
{
|
||||||
|
if (req is AddFriendRequest addedRequest)
|
||||||
|
{
|
||||||
|
SetValue(++followerCount);
|
||||||
|
status.Value = addedRequest.Response?.UserRelation.Mutual == true ? FriendStatus.Mutual : FriendStatus.NotMutual;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetValue(--followerCount);
|
||||||
|
status.Value = FriendStatus.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.UpdateLocalFriends();
|
||||||
|
HideLoadingLayer();
|
||||||
|
};
|
||||||
|
|
||||||
|
req.Failure += e =>
|
||||||
|
{
|
||||||
|
notifications?.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = e.Message,
|
||||||
|
Icon = FontAwesome.Solid.Times,
|
||||||
|
});
|
||||||
|
|
||||||
|
HideLoadingLayer();
|
||||||
|
};
|
||||||
|
|
||||||
|
api.Queue(req);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
if (status.Value > FriendStatus.None)
|
||||||
|
{
|
||||||
|
SetIcon(FontAwesome.Solid.UserTimes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
|
||||||
|
updateIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatus()
|
||||||
|
{
|
||||||
|
SetValue(followerCount);
|
||||||
|
|
||||||
|
if (localUser.Value.OnlineID == User.Value?.User.OnlineID)
|
||||||
|
{
|
||||||
|
status.Value = FriendStatus.Self;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var friend = apiFriends.FirstOrDefault(u => User.Value?.User.OnlineID == u.TargetID);
|
||||||
|
|
||||||
|
if (friend != null)
|
||||||
|
{
|
||||||
|
status.Value = friend.Mutual ? FriendStatus.Mutual : FriendStatus.NotMutual;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status.Value = FriendStatus.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIcon()
|
||||||
|
{
|
||||||
|
switch (status.Value)
|
||||||
|
{
|
||||||
|
case FriendStatus.Self:
|
||||||
|
SetIcon(FontAwesome.Solid.User);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FriendStatus.None:
|
||||||
|
SetIcon(FontAwesome.Solid.UserPlus);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FriendStatus.NotMutual:
|
||||||
|
SetIcon(FontAwesome.Solid.User);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FriendStatus.Mutual:
|
||||||
|
SetIcon(FontAwesome.Solid.UserFriends);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColor()
|
||||||
|
{
|
||||||
|
// https://github.com/ppy/osu-web/blob/0a5367a4a68a6cdf450eb483251b3cf03b3ac7d2/resources/css/bem/user-action-button.less
|
||||||
|
|
||||||
|
switch (status.Value)
|
||||||
|
{
|
||||||
|
case FriendStatus.Self:
|
||||||
|
case FriendStatus.None:
|
||||||
|
IdleColour = colourProvider.Background6;
|
||||||
|
HoverColour = colourProvider.Background5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FriendStatus.NotMutual:
|
||||||
|
IdleColour = colour.Green.Opacity(0.7f);
|
||||||
|
HoverColour = IdleColour.Lighten(0.1f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FriendStatus.Mutual:
|
||||||
|
IdleColour = colour.Pink.Opacity(0.7f);
|
||||||
|
HoverColour = IdleColour.Lighten(0.1f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectTargets.ForEach(d => d.FadeColour(IsHovered ? HoverColour : IdleColour, FADE_DURATION, Easing.OutQuint));
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum FriendStatus
|
||||||
|
{
|
||||||
|
Self,
|
||||||
|
None,
|
||||||
|
NotMutual,
|
||||||
|
Mutual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
{
|
{
|
||||||
@ -14,6 +15,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
|
private readonly LoadingLayer loading;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
@ -40,11 +42,22 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = 10 },
|
Padding = new MarginPadding { Horizontal = 10 },
|
||||||
}
|
},
|
||||||
|
loading = new LoadingLayer(true, false)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void ShowLoadingLayer()
|
||||||
|
{
|
||||||
|
loading.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void HideLoadingLayer()
|
||||||
|
{
|
||||||
|
loading.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider)
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
public abstract partial class ProfileHeaderStatisticsButton : ProfileHeaderButton
|
public abstract partial class ProfileHeaderStatisticsButton : ProfileHeaderButton
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText drawableText;
|
private readonly OsuSpriteText drawableText;
|
||||||
|
private readonly Container iconContainer;
|
||||||
|
|
||||||
protected ProfileHeaderStatisticsButton()
|
protected ProfileHeaderStatisticsButton()
|
||||||
{
|
{
|
||||||
@ -26,13 +27,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SpriteIcon
|
iconContainer = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Icon = Icon,
|
AutoSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Size = new Vector2(50, 14)
|
|
||||||
},
|
},
|
||||||
drawableText = new OsuSpriteText
|
drawableText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
@ -43,10 +42,24 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SetIcon(Icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract IconUsage Icon { get; }
|
protected abstract IconUsage Icon { get; }
|
||||||
|
|
||||||
|
protected void SetIcon(IconUsage icon)
|
||||||
|
{
|
||||||
|
iconContainer.Child = new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Icon = icon,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
Size = new Vector2(50, 14)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected void SetValue(int value) => drawableText.Text = value.ToLocalisableString("#,##0");
|
protected void SetValue(int value) => drawableText.Text = value.ToLocalisableString("#,##0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
HasQuit.BindValueChanged(_ => updateState());
|
HasQuit.BindValueChanged(_ => updateState());
|
||||||
|
|
||||||
isFriend = User != null && api.Friends.Any(u => User.OnlineID == u.Id);
|
isFriend = User != null && api.Friends.Any(u => User.OnlineID == u.TargetID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
Loading…
Reference in New Issue
Block a user