// 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.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; 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; namespace osu.Game.Overlays.Profile.Header.Components { public partial class FollowersButton : ProfileHeaderStatisticsButton { public readonly Bindable User = new Bindable(); // 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; private readonly IBindableList apiFriends = new BindableList(); private readonly IBindable localUser = new Bindable(); private readonly Bindable status = new Bindable(); [Resolved] private OsuColour colour { get; set; } = null!; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; [BackgroundDependencyLoader] private void load(IAPIProvider api, INotificationOverlay? notifications) { localUser.BindTo(api.LocalUser); 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 FriendAddRequest(User.Value.User.OnlineID) : new FriendDeleteRequest(User.Value.User.OnlineID); req.Success += () => { followerCount += status.Value == FriendStatus.None ? 1 : -1; api.UpdateLocalFriends(); updateStatus(); 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() { switch (status.Value) { case FriendStatus.Self: case FriendStatus.None: IdleColour = colourProvider.Background6; HoverColour = colourProvider.Background5; SetBackgroundColour(colourProvider.Background6, 200); break; case FriendStatus.NotMutual: IdleColour = colour.Green; HoverColour = colour.Green.Lighten(0.1f); SetBackgroundColour(colour.Green, 200); break; case FriendStatus.Mutual: IdleColour = colour.Pink; HoverColour = colour.Pink1.Lighten(0.1f); SetBackgroundColour(colour.Pink, 200); break; } } private enum FriendStatus { Self, None, NotMutual, Mutual, } } }