// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; using osu.Game.Users; using osuTK; namespace osu.Game.Overlays.Dashboard { internal class CurrentlyPlayingDisplay : CompositeDrawable { private readonly IBindableList playingUsers = new BindableList(); private FillFlowContainer userFlow; [Resolved] private SpectatorClient spectatorClient { get; set; } [BackgroundDependencyLoader] private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; InternalChild = userFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding(10), Spacing = new Vector2(10), }; } [Resolved] private UserLookupCache users { get; set; } protected override void LoadComplete() { base.LoadComplete(); playingUsers.BindTo(spectatorClient.PlayingUsers); playingUsers.BindCollectionChanged(onPlayingUsersChanged, true); } private void onPlayingUsersChanged(object sender, NotifyCollectionChangedEventArgs e) => Schedule(() => { switch (e.Action) { case NotifyCollectionChangedAction.Add: Debug.Assert(e.NewItems != null); foreach (int userId in e.NewItems) { users.GetUserAsync(userId).ContinueWith(task => { var user = task.GetResultSafely(); if (user != null) { Schedule(() => { // user may no longer be playing. if (!playingUsers.Contains(user.Id)) return; userFlow.Add(createUserPanel(user)); }); } }); } break; case NotifyCollectionChangedAction.Remove: Debug.Assert(e.OldItems != null); foreach (int userId in e.OldItems) userFlow.FirstOrDefault(card => card.User.Id == userId)?.Expire(); break; } }); private PlayingUserPanel createUserPanel(APIUser user) => new PlayingUserPanel(user).With(panel => { panel.Anchor = Anchor.TopCentre; panel.Origin = Anchor.TopCentre; }); private class PlayingUserPanel : CompositeDrawable { public readonly APIUser User; [Resolved(canBeNull: true)] private OsuGame game { get; set; } public PlayingUserPanel(APIUser user) { User = user; AutoSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load(IAPIProvider api) { InternalChildren = new Drawable[] { new FillFlowContainer { AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(2), Width = 290, Children = new Drawable[] { new UserGridPanel(User) { RelativeSizeAxes = Axes.X, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, new PurpleTriangleButton { RelativeSizeAxes = Axes.X, Text = "Watch", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Action = () => game?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), Enabled = { Value = User.Id != api.LocalUser.Value.Id } } } }, }; } } } }