2020-01-29 11:22:08 +08:00
// 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 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 ;
2020-02-10 20:43:11 +08:00
using osu.Game.Online.API.Requests ;
using osu.Game.Online.API ;
using System.Collections.Generic ;
2020-02-21 16:02:48 +08:00
using JetBrains.Annotations ;
2020-01-29 11:22:08 +08:00
namespace osu.Game.Overlays.Comments
{
2020-01-29 11:58:53 +08:00
public class CommentsPage : CompositeDrawable
2020-01-29 11:22:08 +08:00
{
public readonly BindableBool ShowDeleted = new BindableBool ( ) ;
2020-02-10 20:43:11 +08:00
public readonly Bindable < CommentsSortCriteria > Sort = new Bindable < CommentsSortCriteria > ( ) ;
public readonly Bindable < CommentableType > Type = new Bindable < CommentableType > ( ) ;
public readonly BindableLong CommentableId = new BindableLong ( ) ;
[Resolved]
private IAPIProvider api { get ; set ; }
2020-01-29 11:22:08 +08:00
private readonly CommentBundle commentBundle ;
2020-02-13 07:21:13 +08:00
private FillFlowContainer flow ;
2020-01-29 11:22:08 +08:00
public CommentsPage ( CommentBundle commentBundle )
{
this . commentBundle = commentBundle ;
}
[BackgroundDependencyLoader]
2020-01-29 11:58:53 +08:00
private void load ( OverlayColourProvider colourProvider )
2020-01-29 11:22:08 +08:00
{
RelativeSizeAxes = Axes . X ;
AutoSizeAxes = Axes . Y ;
2020-01-29 11:58:53 +08:00
AddRangeInternal ( new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . Both ,
Colour = colourProvider . Background5
} ,
flow = new FillFlowContainer
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Direction = FillDirection . Vertical ,
}
} ) ;
2020-01-29 11:22:08 +08:00
if ( ! commentBundle . Comments . Any ( ) )
{
2020-01-29 11:58:53 +08:00
flow . Add ( new NoCommentsPlaceholder ( ) ) ;
2020-01-29 11:22:08 +08:00
return ;
}
2020-02-21 16:02:48 +08:00
appendComments ( null , commentBundle ) ;
2020-02-13 07:21:13 +08:00
}
2020-02-21 16:02:48 +08:00
private readonly Dictionary < long , DrawableComment > commentDictionary = new Dictionary < long , DrawableComment > ( ) ;
2020-02-13 09:50:04 +08:00
2020-02-21 16:02:48 +08:00
/// <summary>
/// Appends retrieved comments to the subtree rooted at a parenting <see cref="DrawableComment"/>.
/// </summary>
/// <param name="parent">The parenting <see cref="DrawableComment"/>.</param>
/// <param name="bundle">The bundle of comments to add.</param>
private void appendComments ( [ CanBeNull ] DrawableComment parent , [ NotNull ] CommentBundle bundle )
2020-02-13 07:21:13 +08:00
{
2020-02-21 16:02:48 +08:00
var orphaned = new List < Comment > ( ) ;
2020-02-13 07:21:13 +08:00
2020-02-21 16:02:48 +08:00
foreach ( var topLevel in bundle . Comments )
add ( topLevel ) ;
foreach ( var child in bundle . IncludedComments )
add ( child ) ;
2020-02-13 07:21:13 +08:00
2020-02-21 16:02:48 +08:00
// Comments whose parents did not previously have corresponding drawables, are now guaranteed that their parents have corresponding drawables.
foreach ( var o in orphaned )
add ( o ) ;
2020-02-13 07:21:13 +08:00
2020-02-21 16:02:48 +08:00
void add ( Comment comment )
{
var drawableComment = getDrawableComment ( comment ) ;
2020-02-13 07:21:13 +08:00
2020-02-21 16:02:48 +08:00
if ( comment . ParentId = = null )
2020-02-13 07:21:13 +08:00
{
2020-02-21 16:02:48 +08:00
// Comment that has no parent is added as a top-level comment to the flow.
flow . Add ( drawableComment ) ;
}
else if ( commentDictionary . TryGetValue ( comment . ParentId . Value , out var parentDrawable ) )
{
2020-02-21 17:46:14 +08:00
// The comment's parent already has a corresponding drawable, so add the parent<->child links.
comment . ParentComment = parentDrawable . Comment ;
2020-02-21 16:02:48 +08:00
parentDrawable . Replies . Add ( drawableComment ) ;
2020-02-13 07:21:13 +08:00
}
else
2020-02-21 16:02:48 +08:00
{
// The comment's parent does not have a corresponding drawable yet, so keep it as orphaned for the time being.
// Note that this comment's corresponding drawable has already been created by this point, so future children will be able to be added without being orphaned themselves.
orphaned . Add ( comment ) ;
}
2020-02-13 07:21:13 +08:00
}
2020-02-11 04:57:48 +08:00
}
2020-02-10 20:43:11 +08:00
2020-02-21 16:02:48 +08:00
private DrawableComment getDrawableComment ( Comment comment )
2020-02-11 04:57:48 +08:00
{
2020-02-21 16:02:48 +08:00
if ( commentDictionary . TryGetValue ( comment . Id , out var existing ) )
return existing ;
2020-02-13 09:50:04 +08:00
2020-02-21 16:02:48 +08:00
return commentDictionary [ comment . Id ] = new DrawableComment ( comment )
{
ShowDeleted = { BindTarget = ShowDeleted } ,
Sort = { BindTarget = Sort } ,
RepliesRequested = onCommentRepliesRequested
} ;
2020-01-29 11:22:08 +08:00
}
2020-02-10 20:43:11 +08:00
private void onCommentRepliesRequested ( DrawableComment drawableComment , int page )
{
2020-02-21 17:42:11 +08:00
var request = new GetCommentsRequest ( CommentableId . Value , Type . Value , Sort . Value , page , drawableComment . Comment . Id ) ;
2020-02-10 20:43:11 +08:00
2020-02-21 16:02:48 +08:00
request . Success + = response = > Schedule ( ( ) = > appendComments ( drawableComment , response ) ) ;
2020-02-10 20:43:11 +08:00
2020-02-21 16:02:48 +08:00
api . PerformAsync ( request ) ;
2020-02-10 20:43:11 +08:00
}
2020-01-29 11:22:08 +08:00
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."
}
} ) ;
}
}
}
}