mirror of
https://github.com/ppy/osu.git
synced 2024-11-12 01:07:25 +08:00
Merge pull request #8010 from EVAST9919/remove-comments-page
Remove CommentsPage component and move it's logic to CommentsContainer
This commit is contained in:
commit
80d06a7e37
@ -3,84 +3,52 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Overlays.Comments;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osuTK;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneCommentsPage : OsuTestScene
|
||||
public class TestSceneOfflineCommentsContainer : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private readonly BindableBool showDeleted = new BindableBool();
|
||||
private readonly Container content;
|
||||
private TestCommentsContainer comments;
|
||||
|
||||
private TestCommentsPage commentsPage;
|
||||
|
||||
public TestSceneCommentsPage()
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Add(new FillFlowContainer
|
||||
Clear();
|
||||
Add(new BasicScrollContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 200,
|
||||
Child = new OsuCheckbox
|
||||
{
|
||||
Current = showDeleted,
|
||||
LabelText = @"Show Deleted"
|
||||
}
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = comments = new TestCommentsContainer()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestAppendDuplicatedComment()
|
||||
{
|
||||
AddStep("Create page", () => createPage(getCommentBundle()));
|
||||
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
|
||||
AddStep("Append existing comment", () => commentsPage?.AppendComments(getCommentSubBundle()));
|
||||
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
|
||||
AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle()));
|
||||
AddUntilStep("Dictionary length is 10", () => comments.DictionaryLength == 10);
|
||||
AddStep("Append existing comment", () => comments.AppendComments(getCommentSubBundle()));
|
||||
AddAssert("Dictionary length is 10", () => comments.DictionaryLength == 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyBundle()
|
||||
public void TestLocalCommentBundle()
|
||||
{
|
||||
AddStep("Create page", () => createPage(getEmptyCommentBundle()));
|
||||
AddAssert("Dictionary length is 0", () => commentsPage?.DictionaryLength == 0);
|
||||
}
|
||||
|
||||
private void createPage(CommentBundle commentBundle)
|
||||
{
|
||||
commentsPage = null;
|
||||
content.Clear();
|
||||
content.Add(commentsPage = new TestCommentsPage(commentBundle)
|
||||
{
|
||||
ShowDeleted = { BindTarget = showDeleted }
|
||||
});
|
||||
AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle()));
|
||||
AddStep("Add empty comment bundle", () => comments.ShowComments(getEmptyCommentBundle()));
|
||||
}
|
||||
|
||||
private CommentBundle getEmptyCommentBundle() => new CommentBundle
|
||||
@ -193,6 +161,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Username = "Good_Admin"
|
||||
}
|
||||
},
|
||||
Total = 10
|
||||
};
|
||||
|
||||
private CommentBundle getCommentSubBundle() => new CommentBundle
|
||||
@ -211,16 +180,18 @@ namespace osu.Game.Tests.Visual.Online
|
||||
IncludedComments = new List<Comment>(),
|
||||
};
|
||||
|
||||
private class TestCommentsPage : CommentsPage
|
||||
private class TestCommentsContainer : CommentsContainer
|
||||
{
|
||||
public TestCommentsPage(CommentBundle commentBundle)
|
||||
: base(commentBundle)
|
||||
{
|
||||
}
|
||||
|
||||
public new void AppendComments([NotNull] CommentBundle bundle) => base.AppendComments(bundle);
|
||||
|
||||
public int DictionaryLength => CommentDictionary.Count;
|
||||
|
||||
public void ShowComments(CommentBundle bundle)
|
||||
{
|
||||
this.ChildrenOfType<TotalCommentsCounter>().Single().Current.Value = 0;
|
||||
ClearComments();
|
||||
OnSuccess(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,9 @@ using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Users;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
@ -147,7 +150,7 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
private void refetchComments()
|
||||
{
|
||||
clearComments();
|
||||
ClearComments();
|
||||
getComments();
|
||||
}
|
||||
|
||||
@ -160,50 +163,125 @@ namespace osu.Game.Overlays.Comments
|
||||
loadCancellation?.Cancel();
|
||||
scheduledCommentsLoad?.Cancel();
|
||||
request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0);
|
||||
request.Success += res => scheduledCommentsLoad = Schedule(() => onSuccess(res));
|
||||
request.Success += res => scheduledCommentsLoad = Schedule(() => OnSuccess(res));
|
||||
api.PerformAsync(request);
|
||||
}
|
||||
|
||||
private void clearComments()
|
||||
protected void ClearComments()
|
||||
{
|
||||
currentPage = 1;
|
||||
deletedCommentsCounter.Count.Value = 0;
|
||||
moreButton.Show();
|
||||
moreButton.IsLoading = true;
|
||||
content.Clear();
|
||||
CommentDictionary.Clear();
|
||||
}
|
||||
|
||||
private void onSuccess(CommentBundle response)
|
||||
protected readonly Dictionary<long, DrawableComment> CommentDictionary = new Dictionary<long, DrawableComment>();
|
||||
|
||||
protected void OnSuccess(CommentBundle response)
|
||||
{
|
||||
loadCancellation = new CancellationTokenSource();
|
||||
commentCounter.Current.Value = response.Total;
|
||||
|
||||
LoadComponentAsync(new CommentsPage(response)
|
||||
if (!response.Comments.Any())
|
||||
{
|
||||
ShowDeleted = { BindTarget = ShowDeleted },
|
||||
Sort = { BindTarget = Sort },
|
||||
Type = { BindTarget = type },
|
||||
CommentableId = { BindTarget = id }
|
||||
}, loaded =>
|
||||
content.Add(new NoCommentsPlaceholder());
|
||||
moreButton.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
AppendComments(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends retrieved comments to the subtree rooted of comments in this page.
|
||||
/// </summary>
|
||||
/// <param name="bundle">The bundle of comments to add.</param>
|
||||
protected void AppendComments([NotNull] CommentBundle bundle)
|
||||
{
|
||||
var topLevelComments = new List<DrawableComment>();
|
||||
var orphaned = new List<Comment>();
|
||||
|
||||
foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments))
|
||||
{
|
||||
content.Add(loaded);
|
||||
// Exclude possible duplicated comments.
|
||||
if (CommentDictionary.ContainsKey(comment.Id))
|
||||
continue;
|
||||
|
||||
deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
|
||||
addNewComment(comment);
|
||||
}
|
||||
|
||||
if (response.HasMore)
|
||||
// Comments whose parents were seen later than themselves can now be added.
|
||||
foreach (var o in orphaned)
|
||||
addNewComment(o);
|
||||
|
||||
if (topLevelComments.Any())
|
||||
{
|
||||
LoadComponentsAsync(topLevelComments, loaded =>
|
||||
{
|
||||
int loadedTopLevelComments = 0;
|
||||
content.Children.OfType<FillFlowContainer>().ForEach(p => loadedTopLevelComments += p.Children.OfType<DrawableComment>().Count());
|
||||
content.AddRange(loaded);
|
||||
|
||||
moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments;
|
||||
moreButton.IsLoading = false;
|
||||
deletedCommentsCounter.Count.Value += topLevelComments.Select(d => d.Comment).Count(c => c.IsDeleted && c.IsTopLevel);
|
||||
|
||||
if (bundle.HasMore)
|
||||
{
|
||||
int loadedTopLevelComments = 0;
|
||||
content.Children.OfType<DrawableComment>().ForEach(p => loadedTopLevelComments++);
|
||||
|
||||
moreButton.Current.Value = bundle.TopLevelCount - loadedTopLevelComments;
|
||||
moreButton.IsLoading = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
moreButton.Hide();
|
||||
}
|
||||
}, (loadCancellation = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
void addNewComment(Comment comment)
|
||||
{
|
||||
var drawableComment = getDrawableComment(comment);
|
||||
|
||||
if (comment.ParentId == null)
|
||||
{
|
||||
// Comments that have no parent are added as top-level comments to the flow.
|
||||
topLevelComments.Add(drawableComment);
|
||||
}
|
||||
else if (CommentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable))
|
||||
{
|
||||
// The comment's parent has already been seen, so the parent<-> child links can be added.
|
||||
comment.ParentComment = parentDrawable.Comment;
|
||||
parentDrawable.Replies.Add(drawableComment);
|
||||
}
|
||||
else
|
||||
{
|
||||
moreButton.Hide();
|
||||
// The comment's parent has not been seen yet, so keep it orphaned for the time being. This can occur if the comments arrive out of order.
|
||||
// Since this comment has now been seen, any further children can be added to it without being orphaned themselves.
|
||||
orphaned.Add(comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commentCounter.Current.Value = response.Total;
|
||||
}, loadCancellation.Token);
|
||||
private DrawableComment getDrawableComment(Comment comment)
|
||||
{
|
||||
if (CommentDictionary.TryGetValue(comment.Id, out var existing))
|
||||
return existing;
|
||||
|
||||
return CommentDictionary[comment.Id] = new DrawableComment(comment)
|
||||
{
|
||||
ShowDeleted = { BindTarget = ShowDeleted },
|
||||
Sort = { BindTarget = Sort },
|
||||
RepliesRequested = onCommentRepliesRequested
|
||||
};
|
||||
}
|
||||
|
||||
private void onCommentRepliesRequested(DrawableComment drawableComment, int page)
|
||||
{
|
||||
var req = new GetCommentsRequest(id.Value, type.Value, Sort.Value, page, drawableComment.Comment.Id);
|
||||
|
||||
req.Success += response => Schedule(() => AppendComments(response));
|
||||
|
||||
api.PerformAsync(req);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@ -212,5 +290,30 @@ namespace osu.Game.Overlays.Comments
|
||||
loadCancellation?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
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."
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
// 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;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
public class CommentsPage : CompositeDrawable
|
||||
{
|
||||
public readonly BindableBool ShowDeleted = new BindableBool();
|
||||
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; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
AppendComments(commentBundle);
|
||||
}
|
||||
|
||||
private DrawableComment getDrawableComment(Comment comment)
|
||||
{
|
||||
if (CommentDictionary.TryGetValue(comment.Id, out var existing))
|
||||
return existing;
|
||||
|
||||
return CommentDictionary[comment.Id] = new DrawableComment(comment)
|
||||
{
|
||||
ShowDeleted = { BindTarget = ShowDeleted },
|
||||
Sort = { BindTarget = Sort },
|
||||
RepliesRequested = onCommentRepliesRequested
|
||||
};
|
||||
}
|
||||
|
||||
private void onCommentRepliesRequested(DrawableComment drawableComment, int page)
|
||||
{
|
||||
var request = new GetCommentsRequest(CommentableId.Value, Type.Value, Sort.Value, page, drawableComment.Comment.Id);
|
||||
|
||||
request.Success += response => Schedule(() => AppendComments(response));
|
||||
|
||||
api.PerformAsync(request);
|
||||
}
|
||||
|
||||
protected readonly Dictionary<long, DrawableComment> CommentDictionary = new Dictionary<long, DrawableComment>();
|
||||
|
||||
/// <summary>
|
||||
/// Appends retrieved comments to the subtree rooted of comments in this page.
|
||||
/// </summary>
|
||||
/// <param name="bundle">The bundle of comments to add.</param>
|
||||
protected void AppendComments([NotNull] CommentBundle bundle)
|
||||
{
|
||||
var orphaned = new List<Comment>();
|
||||
|
||||
foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments))
|
||||
{
|
||||
// Exclude possible duplicated comments.
|
||||
if (CommentDictionary.ContainsKey(comment.Id))
|
||||
continue;
|
||||
|
||||
addNewComment(comment);
|
||||
}
|
||||
|
||||
// Comments whose parents were seen later than themselves can now be added.
|
||||
foreach (var o in orphaned)
|
||||
addNewComment(o);
|
||||
|
||||
void addNewComment(Comment comment)
|
||||
{
|
||||
var drawableComment = getDrawableComment(comment);
|
||||
|
||||
if (comment.ParentId == null)
|
||||
{
|
||||
// Comments that have no parent are added as top-level comments to the flow.
|
||||
flow.Add(drawableComment);
|
||||
}
|
||||
else if (CommentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable))
|
||||
{
|
||||
// The comment's parent has already been seen, so the parent<-> child links can be added.
|
||||
comment.ParentComment = parentDrawable.Comment;
|
||||
parentDrawable.Replies.Add(drawableComment);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The comment's parent has not been seen yet, so keep it orphaned for the time being. This can occur if the comments arrive out of order.
|
||||
// Since this comment has now been seen, any further children can be added to it without being orphaned themselves.
|
||||
orphaned.Add(comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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."
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user