Merge pull request #14272 from gagahpangeran/pinned-comment

Add pinned comment section in comment container
This commit is contained in:
Dean Herbert 2021-08-24 15:11:49 +09:00 committed by GitHub
commit 16c0f3d5b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 187 additions and 62 deletions

View File

@ -42,19 +42,21 @@ namespace osu.Game.Tests.Visual.Online
() => commentsContainer.ChildrenOfType<CommentsShowMoreButton>().Single().IsLoading); () => commentsContainer.ChildrenOfType<CommentsShowMoreButton>().Single().IsLoading);
} }
[Test] [TestCase(false)]
public void TestSingleCommentsPage() [TestCase(true)]
public void TestSingleCommentsPage(bool withPinned)
{ {
setUpCommentsResponse(exampleComments); setUpCommentsResponse(getExampleComments(withPinned));
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123)); AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
AddUntilStep("show more button hidden", AddUntilStep("show more button hidden",
() => commentsContainer.ChildrenOfType<CommentsShowMoreButton>().Single().Alpha == 0); () => commentsContainer.ChildrenOfType<CommentsShowMoreButton>().Single().Alpha == 0);
} }
[Test] [TestCase(false)]
public void TestMultipleCommentPages() [TestCase(true)]
public void TestMultipleCommentPages(bool withPinned)
{ {
var comments = exampleComments; var comments = getExampleComments(withPinned);
comments.HasMore = true; comments.HasMore = true;
comments.TopLevelCount = 10; comments.TopLevelCount = 10;
@ -64,11 +66,12 @@ namespace osu.Game.Tests.Visual.Online
() => commentsContainer.ChildrenOfType<CommentsShowMoreButton>().Single().Alpha == 1); () => commentsContainer.ChildrenOfType<CommentsShowMoreButton>().Single().Alpha == 1);
} }
[Test] [TestCase(false)]
public void TestMultipleLoads() [TestCase(true)]
public void TestMultipleLoads(bool withPinned)
{ {
var comments = exampleComments; var comments = getExampleComments(withPinned);
int topLevelCommentCount = exampleComments.Comments.Count; int topLevelCommentCount = comments.Comments.Count;
AddStep("hide container", () => commentsContainer.Hide()); AddStep("hide container", () => commentsContainer.Hide());
setUpCommentsResponse(comments); setUpCommentsResponse(comments);
@ -79,6 +82,48 @@ namespace osu.Game.Tests.Visual.Online
() => commentsContainer.ChildrenOfType<DrawableComment>().Count() == topLevelCommentCount); () => commentsContainer.ChildrenOfType<DrawableComment>().Count() == topLevelCommentCount);
} }
[Test]
public void TestNoComment()
{
var comments = getExampleComments();
comments.Comments.Clear();
setUpCommentsResponse(comments);
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
AddAssert("no comment shown", () => !commentsContainer.ChildrenOfType<DrawableComment>().Any());
}
[TestCase(false)]
[TestCase(true)]
public void TestSingleComment(bool withPinned)
{
var comment = new Comment
{
Id = 1,
Message = "This is a single comment",
LegacyName = "SingleUser",
CreatedAt = DateTimeOffset.Now,
VotesCount = 0,
Pinned = withPinned,
};
var bundle = new CommentBundle
{
Comments = new List<Comment> { comment },
IncludedComments = new List<Comment>(),
PinnedComments = new List<Comment>(),
};
if (withPinned)
bundle.PinnedComments.Add(comment);
setUpCommentsResponse(bundle);
AddStep("show comments", () => commentsContainer.ShowComments(CommentableType.Beatmapset, 123));
AddUntilStep("wait comment load", () => commentsContainer.ChildrenOfType<DrawableComment>().Any());
AddAssert("only one comment shown", () =>
commentsContainer.ChildrenOfType<DrawableComment>().Count(d => d.Comment.Pinned == withPinned) == 1);
}
private void setUpCommentsResponse(CommentBundle commentBundle) private void setUpCommentsResponse(CommentBundle commentBundle)
=> AddStep("set up response", () => => AddStep("set up response", () =>
{ {
@ -92,7 +137,9 @@ namespace osu.Game.Tests.Visual.Online
}; };
}); });
private CommentBundle exampleComments => new CommentBundle private CommentBundle getExampleComments(bool withPinned = false)
{
var bundle = new CommentBundle
{ {
Comments = new List<Comment> Comments = new List<Comment>
{ {
@ -124,6 +171,37 @@ namespace osu.Game.Tests.Visual.Online
}, },
}, },
IncludedComments = new List<Comment>(), IncludedComments = new List<Comment>(),
PinnedComments = new List<Comment>(),
}; };
if (withPinned)
{
var pinnedComment = new Comment
{
Id = 15,
Message = "This is pinned comment",
LegacyName = "PinnedUser",
CreatedAt = DateTimeOffset.Now,
VotesCount = 999,
Pinned = true,
RepliesCount = 1,
};
bundle.Comments.Add(pinnedComment);
bundle.PinnedComments.Add(pinnedComment);
bundle.Comments.Add(new Comment
{
Id = 20,
Message = "Reply to pinned comment",
LegacyName = "AbandonedUser",
CreatedAt = DateTimeOffset.Now,
VotesCount = 0,
ParentId = 15,
});
}
return bundle;
}
} }
} }

View File

@ -43,6 +43,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
AddStep(description, () => AddStep(description, () =>
{ {
comment.Pinned = description == "Pinned";
comment.Message = text; comment.Message = text;
container.Add(new DrawableComment(comment)); container.Add(new DrawableComment(comment));
}); });
@ -59,6 +60,7 @@ namespace osu.Game.Tests.Visual.Online
private static object[] comments = private static object[] comments =
{ {
new[] { "Plain", "This is plain comment" }, new[] { "Plain", "This is plain comment" },
new[] { "Pinned", "This is pinned comment" },
new[] { "Link", "Please visit https://osu.ppy.sh" }, new[] { "Link", "Please visit https://osu.ppy.sh" },
new[] new[]

View File

@ -149,6 +149,7 @@ namespace osu.Game.Tests.Visual.Online
} }
}, },
IncludedComments = new List<Comment>(), IncludedComments = new List<Comment>(),
PinnedComments = new List<Comment>(),
UserVotes = new List<long> UserVotes = new List<long>
{ {
5 5
@ -178,6 +179,7 @@ namespace osu.Game.Tests.Visual.Online
}, },
}, },
IncludedComments = new List<Comment>(), IncludedComments = new List<Comment>(),
PinnedComments = new List<Comment>(),
}; };
private class TestCommentsContainer : CommentsContainer private class TestCommentsContainer : CommentsContainer

View File

@ -58,6 +58,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"edited_by_id")] [JsonProperty(@"edited_by_id")]
public long? EditedById { get; set; } public long? EditedById { get; set; }
[JsonProperty(@"pinned")]
public bool Pinned { get; set; }
public User EditedUser { get; set; } public User EditedUser { get; set; }
public bool IsTopLevel => !ParentId.HasValue; public bool IsTopLevel => !ParentId.HasValue;

View File

@ -4,6 +4,7 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Users; using osu.Game.Users;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Online.API.Requests.Responses namespace osu.Game.Online.API.Requests.Responses
{ {
@ -24,6 +25,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"included_comments")] [JsonProperty(@"included_comments")]
public List<Comment> IncludedComments { get; set; } public List<Comment> IncludedComments { get; set; }
[JsonProperty(@"pinned_comments")]
public List<Comment> PinnedComments { get; set; }
private List<long> userVotes; private List<long> userVotes;
[JsonProperty(@"user_votes")] [JsonProperty(@"user_votes")]
@ -49,26 +53,17 @@ namespace osu.Game.Online.API.Requests.Responses
{ {
users = value; users = value;
value.ForEach(u => foreach (var user in value)
{ {
Comments.ForEach(c => foreach (var comment in Comments.Concat(IncludedComments).Concat(PinnedComments))
{ {
if (c.UserId == u.Id) if (comment.UserId == user.Id)
c.User = u; comment.User = user;
if (c.EditedById == u.Id) if (comment.EditedById == user.Id)
c.EditedUser = u; comment.EditedUser = user;
}); }
}
IncludedComments.ForEach(c =>
{
if (c.UserId == u.Id)
c.User = u;
if (c.EditedById == u.Id)
c.EditedUser = u;
});
});
} }
} }

View File

@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Comments
private CancellationTokenSource loadCancellation; private CancellationTokenSource loadCancellation;
private int currentPage; private int currentPage;
private FillFlowContainer pinnedContent;
private FillFlowContainer content; private FillFlowContainer content;
private DeletedCommentsCounter deletedCommentsCounter; private DeletedCommentsCounter deletedCommentsCounter;
private CommentsShowMoreButton moreButton; private CommentsShowMoreButton moreButton;
@ -63,6 +64,25 @@ namespace osu.Game.Overlays.Comments
Children = new Drawable[] Children = new Drawable[]
{ {
commentCounter = new TotalCommentsCounter(), commentCounter = new TotalCommentsCounter(),
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background4,
},
pinnedContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
},
},
new CommentsHeader new CommentsHeader
{ {
Sort = { BindTarget = Sort }, Sort = { BindTarget = Sort },
@ -173,6 +193,7 @@ namespace osu.Game.Overlays.Comments
deletedCommentsCounter.Count.Value = 0; deletedCommentsCounter.Count.Value = 0;
moreButton.Show(); moreButton.Show();
moreButton.IsLoading = true; moreButton.IsLoading = true;
pinnedContent.Clear();
content.Clear(); content.Clear();
CommentDictionary.Clear(); CommentDictionary.Clear();
} }
@ -202,7 +223,7 @@ namespace osu.Game.Overlays.Comments
var topLevelComments = new List<DrawableComment>(); var topLevelComments = new List<DrawableComment>();
var orphaned = new List<Comment>(); var orphaned = new List<Comment>();
foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments)) foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments).Concat(bundle.PinnedComments))
{ {
// Exclude possible duplicated comments. // Exclude possible duplicated comments.
if (CommentDictionary.ContainsKey(comment.Id)) if (CommentDictionary.ContainsKey(comment.Id))
@ -219,13 +240,15 @@ namespace osu.Game.Overlays.Comments
{ {
LoadComponentsAsync(topLevelComments, loaded => LoadComponentsAsync(topLevelComments, loaded =>
{ {
content.AddRange(loaded); pinnedContent.AddRange(loaded.Where(d => d.Comment.Pinned));
content.AddRange(loaded.Where(d => !d.Comment.Pinned));
deletedCommentsCounter.Count.Value += topLevelComments.Select(d => d.Comment).Count(c => c.IsDeleted && c.IsTopLevel); deletedCommentsCounter.Count.Value += topLevelComments.Select(d => d.Comment).Count(c => c.IsDeleted && c.IsTopLevel);
if (bundle.HasMore) if (bundle.HasMore)
{ {
int loadedTopLevelComments = 0; int loadedTopLevelComments = 0;
pinnedContent.Children.OfType<DrawableComment>().ForEach(p => loadedTopLevelComments++);
content.Children.OfType<DrawableComment>().ForEach(p => loadedTopLevelComments++); content.Children.OfType<DrawableComment>().ForEach(p => loadedTopLevelComments++);
moreButton.Current.Value = bundle.TopLevelCount - loadedTopLevelComments; moreButton.Current.Value = bundle.TopLevelCount - loadedTopLevelComments;
@ -300,11 +323,6 @@ namespace osu.Game.Overlays.Comments
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{ {
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background4
},
new OsuSpriteText new OsuSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,

View File

@ -21,6 +21,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using System.Collections.Specialized; using System.Collections.Specialized;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Overlays.Comments.Buttons; using osu.Game.Overlays.Comments.Buttons;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.Comments namespace osu.Game.Overlays.Comments
{ {
@ -137,12 +138,13 @@ namespace osu.Game.Overlays.Comments
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0), Spacing = new Vector2(10, 0),
Children = new Drawable[] Children = new[]
{ {
username = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)) username = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold))
{ {
AutoSizeAxes = Axes.Both AutoSizeAxes = Axes.Both
}, },
Comment.Pinned ? new PinnedCommentNotice() : Empty(),
new ParentUsername(Comment), new ParentUsername(Comment),
new OsuSpriteText new OsuSpriteText
{ {
@ -321,9 +323,7 @@ namespace osu.Game.Overlays.Comments
this.FadeTo(show.NewValue ? 1 : 0); this.FadeTo(show.NewValue ? 1 : 0);
}, true); }, true);
childrenExpanded.BindValueChanged(expanded => childCommentsVisibilityContainer.FadeTo(expanded.NewValue ? 1 : 0), true); childrenExpanded.BindValueChanged(expanded => childCommentsVisibilityContainer.FadeTo(expanded.NewValue ? 1 : 0), true);
updateButtonsState(); updateButtonsState();
base.LoadComplete(); base.LoadComplete();
} }
@ -392,6 +392,33 @@ namespace osu.Game.Overlays.Comments
}; };
} }
private class PinnedCommentNotice : FillFlowContainer
{
public PinnedCommentNotice()
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(2, 0);
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.Solid.Thumbtack,
Size = new Vector2(14),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
Text = CommentsStrings.Pinned,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
}
};
}
}
private class ParentUsername : FillFlowContainer, IHasTooltip private class ParentUsername : FillFlowContainer, IHasTooltip
{ {
public LocalisableString TooltipText => getParentMessage(); public LocalisableString TooltipText => getParentMessage();