// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Sprites; using System.Linq; using osu.Game.Online.API.Requests; using osu.Game.Online.API; using System.Collections.Generic; namespace osu.Game.Overlays.Comments { public class CommentsPage : CompositeDrawable { public readonly BindableBool ShowDeleted = new BindableBool(); public readonly Bindable Sort = new Bindable(); public readonly Bindable Type = new Bindable(); public readonly BindableLong CommentableId = new BindableLong(); [Resolved] private IAPIProvider api { get; set; } private readonly CommentBundle commentBundle; private FillFlowContainer flow; public CommentsPage(CommentBundle commentBundle) { this.commentBundle = commentBundle; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; AddRangeInternal(new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5 }, flow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, } }); if (!commentBundle.Comments.Any()) { flow.Add(new NoCommentsPlaceholder()); return; } createBaseTree(commentBundle.Comments); } private readonly Dictionary nodeDictionary = new Dictionary(); private void createBaseTree(List comments) { var topLevelNodes = new List(); var orphanedNodes = new List(); foreach (var comment in comments) { nodeDictionary.Add(comment.Id, comment); if (comment.IsTopLevel) topLevelNodes.Add(comment); var orphanedNodesCopy = new List(orphanedNodes); foreach (var orphan in orphanedNodesCopy) { if (orphan.ParentId == comment.Id) { comment.ChildComments.Add(orphan); orphanedNodes.Remove(orphan); } } // No need to find parent for top-level comment if (!comment.ParentId.HasValue) continue; if (nodeDictionary.ContainsKey(comment.ParentId.Value)) nodeDictionary[comment.ParentId.Value].ChildComments.Add(comment); else orphanedNodes.Add(comment); } foreach (var comment in topLevelNodes) flow.Add(createCommentWithReplies(comment)); } private DrawableComment createCommentWithReplies(Comment comment) { if (comment.ParentId.HasValue) comment.ParentComment = nodeDictionary[comment.ParentId.Value]; var drawableComment = createDrawableComment(comment); var replies = comment.ChildComments; if (replies.Any()) drawableComment.Replies.AddRange(replies.Select(createCommentWithReplies)); return drawableComment; } private void onCommentRepliesRequested(DrawableComment drawableComment, int page) { var request = new GetCommentRepliesRequest(drawableComment.Comment.Id, Type.Value, CommentableId.Value, Sort.Value, page); request.Success += response => onCommentRepliesReceived(response, drawableComment); api.PerformAsync(request); } private void onCommentRepliesReceived(CommentBundle response, DrawableComment drawableComment) { // We may receive already loaded comments var uniqueComments = response.Comments.Where(c => !drawableComment.ContainsReply(c.Id)).ToList(); uniqueComments.ForEach(c => c.ParentComment = drawableComment.Comment); drawableComment.Replies.AddRange(uniqueComments.Select(createDrawableComment)); } private DrawableComment createDrawableComment(Comment comment) => new DrawableComment(comment) { ShowDeleted = { BindTarget = ShowDeleted }, Sort = { BindTarget = Sort }, RepliesRequested = onCommentRepliesRequested }; private class NoCommentsPlaceholder : CompositeDrawable { [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { Height = 80; RelativeSizeAxes = Axes.X; AddRangeInternal(new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background4 }, new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Margin = new MarginPadding { Left = 50 }, Text = @"No comments yet." } }); } } } }