mirror of
https://github.com/ppy/osu
synced 2024-12-14 10:57:41 +00:00
Merge pull request #22283 from Feodor0090/comment-reply
Add ability to leave replies to comments
This commit is contained in:
commit
c6dfbab702
@ -11,6 +11,8 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -21,6 +23,7 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Comments;
|
||||
using osu.Game.Overlays.Comments.Buttons;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
@ -278,6 +281,93 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddAssert("Request is correct", () => request != null && request.CommentID == 2 && request.Comment == report_text && request.Reason == CommentReportReason.Other);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestReply()
|
||||
{
|
||||
addTestComments();
|
||||
DrawableComment? targetComment = null;
|
||||
AddUntilStep("Comment exists", () =>
|
||||
{
|
||||
var comments = this.ChildrenOfType<DrawableComment>();
|
||||
targetComment = comments.SingleOrDefault(x => x.Comment.Id == 2);
|
||||
return targetComment != null;
|
||||
});
|
||||
AddStep("Setup request handling", () =>
|
||||
{
|
||||
requestLock.Reset();
|
||||
|
||||
dummyAPI.HandleRequest = r =>
|
||||
{
|
||||
if (!(r is CommentPostRequest req))
|
||||
return false;
|
||||
|
||||
if (req.ParentCommentId != 2)
|
||||
throw new ArgumentException("Wrong parent ID in request!");
|
||||
|
||||
if (req.CommentableId != 123 || req.Commentable != CommentableType.Beatmapset)
|
||||
throw new ArgumentException("Wrong commentable data in request!");
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
requestLock.Wait(10000);
|
||||
req.TriggerSuccess(new CommentBundle
|
||||
{
|
||||
Comments = new List<Comment>
|
||||
{
|
||||
new Comment
|
||||
{
|
||||
Id = 98,
|
||||
Message = req.Message,
|
||||
LegacyName = "FirstUser",
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
VotesCount = 98,
|
||||
ParentId = req.ParentCommentId,
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
});
|
||||
AddStep("Click reply button", () =>
|
||||
{
|
||||
var btn = targetComment.ChildrenOfType<LinkFlowContainer>().Skip(1).First();
|
||||
var texts = btn.ChildrenOfType<SpriteText>();
|
||||
InputManager.MoveMouseTo(texts.Skip(1).First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("There is 0 replies", () =>
|
||||
{
|
||||
var replLabel = targetComment.ChildrenOfType<ShowRepliesButton>().First().ChildrenOfType<SpriteText>().First();
|
||||
return replLabel.Text.ToString().Contains('0') && targetComment!.Comment.RepliesCount == 0;
|
||||
});
|
||||
AddStep("Focus field", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(targetComment.ChildrenOfType<TextBox>().First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddStep("Enter text", () =>
|
||||
{
|
||||
targetComment.ChildrenOfType<TextBox>().First().Current.Value = "random reply";
|
||||
});
|
||||
AddStep("Submit", () =>
|
||||
{
|
||||
InputManager.Key(Key.Enter);
|
||||
});
|
||||
AddStep("Complete request", () => requestLock.Set());
|
||||
AddUntilStep("There is 1 reply", () =>
|
||||
{
|
||||
var replLabel = targetComment.ChildrenOfType<ShowRepliesButton>().First().ChildrenOfType<SpriteText>().First();
|
||||
return replLabel.Text.ToString().Contains('1') && targetComment!.Comment.RepliesCount == 1;
|
||||
});
|
||||
AddUntilStep("Submitted comment shown", () =>
|
||||
{
|
||||
var r = targetComment.ChildrenOfType<DrawableComment>().Skip(1).FirstOrDefault();
|
||||
return r != null && r.Comment.Message == "random reply";
|
||||
});
|
||||
}
|
||||
|
||||
private void addTestComments()
|
||||
{
|
||||
AddStep("set up response", () =>
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Humanizer;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Events;
|
||||
@ -15,7 +13,12 @@ namespace osu.Game.Overlays.Comments.Buttons
|
||||
|
||||
public ShowRepliesButton(int count)
|
||||
{
|
||||
Text = "reply".ToQuantity(count);
|
||||
Count = count;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
set => Text = "reply".ToQuantity(value);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -35,6 +35,8 @@ namespace osu.Game.Overlays.Comments
|
||||
private RoundedButton commitButton = null!;
|
||||
private LoadingSpinner loadingSpinner = null!;
|
||||
|
||||
protected TextBox TextBox { get; private set; } = null!;
|
||||
|
||||
protected bool ShowLoadingSpinner
|
||||
{
|
||||
set
|
||||
@ -51,8 +53,6 @@ namespace osu.Game.Overlays.Comments
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
EditorTextBox textBox;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Masking = true;
|
||||
@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Comments
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
textBox = new EditorTextBox
|
||||
TextBox = new EditorTextBox
|
||||
{
|
||||
Height = 40,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -133,7 +133,7 @@ namespace osu.Game.Overlays.Comments
|
||||
}
|
||||
});
|
||||
|
||||
textBox.OnCommit += (_, _) => commitButton.TriggerClick();
|
||||
TextBox.OnCommit += (_, _) => commitButton.TriggerClick();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -301,7 +301,7 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
void addNewComment(Comment comment)
|
||||
{
|
||||
var drawableComment = getDrawableComment(comment);
|
||||
var drawableComment = GetDrawableComment(comment);
|
||||
|
||||
if (comment.ParentId == null)
|
||||
{
|
||||
@ -333,7 +333,7 @@ namespace osu.Game.Overlays.Comments
|
||||
if (CommentDictionary.ContainsKey(comment.Id))
|
||||
continue;
|
||||
|
||||
topLevelComments.Add(getDrawableComment(comment));
|
||||
topLevelComments.Add(GetDrawableComment(comment));
|
||||
}
|
||||
|
||||
if (topLevelComments.Any())
|
||||
@ -351,7 +351,7 @@ namespace osu.Game.Overlays.Comments
|
||||
}
|
||||
}
|
||||
|
||||
private DrawableComment getDrawableComment(Comment comment)
|
||||
public DrawableComment GetDrawableComment(Comment comment)
|
||||
{
|
||||
if (CommentDictionary.TryGetValue(comment.Id, out var existing))
|
||||
return existing;
|
||||
|
@ -22,6 +22,7 @@ using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
@ -74,6 +75,7 @@ namespace osu.Game.Overlays.Comments
|
||||
private OsuSpriteText deletedLabel = null!;
|
||||
private GridContainer content = null!;
|
||||
private VotePill votePill = null!;
|
||||
private Container<CommentEditor> replyEditorContainer = null!;
|
||||
|
||||
[Resolved]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
@ -232,6 +234,12 @@ namespace osu.Game.Overlays.Comments
|
||||
}
|
||||
}
|
||||
},
|
||||
replyEditorContainer = new Container<CommentEditor>
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding { Top = 10 },
|
||||
},
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
@ -254,6 +262,7 @@ namespace osu.Game.Overlays.Comments
|
||||
},
|
||||
childCommentsVisibilityContainer = new FillFlowContainer
|
||||
{
|
||||
Name = @"Children comments",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
@ -344,6 +353,8 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
actionsContainer.AddLink(CommonStrings.ButtonsPermalink, copyUrl);
|
||||
actionsContainer.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
actionsContainer.AddLink(CommonStrings.ButtonsReply.ToLower(), toggleReply);
|
||||
actionsContainer.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
|
||||
if (Comment.UserId.HasValue && Comment.UserId.Value == api.LocalUser.Value.Id)
|
||||
actionsContainer.AddLink(CommonStrings.ButtonsDelete.ToLower(), deleteComment);
|
||||
@ -419,8 +430,9 @@ namespace osu.Game.Overlays.Comments
|
||||
if (!ShowDeleted.Value)
|
||||
Hide();
|
||||
});
|
||||
request.Failure += _ => Schedule(() =>
|
||||
request.Failure += e => Schedule(() =>
|
||||
{
|
||||
Logger.Error(e, "Failed to delete comment");
|
||||
actionsLoading.Hide();
|
||||
actionsContainer.Show();
|
||||
});
|
||||
@ -433,6 +445,26 @@ namespace osu.Game.Overlays.Comments
|
||||
onScreenDisplay?.Display(new CopyUrlToast());
|
||||
}
|
||||
|
||||
private void toggleReply()
|
||||
{
|
||||
if (replyEditorContainer.Count == 0)
|
||||
{
|
||||
replyEditorContainer.Add(new ReplyCommentEditor(Comment)
|
||||
{
|
||||
OnPost = comments =>
|
||||
{
|
||||
Comment.RepliesCount += comments.Length;
|
||||
showRepliesButton.Count = Comment.RepliesCount;
|
||||
Replies.AddRange(comments);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
replyEditorContainer.Clear(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
ShowDeleted.BindValueChanged(show =>
|
||||
@ -445,8 +477,6 @@ namespace osu.Game.Overlays.Comments
|
||||
base.LoadComplete();
|
||||
}
|
||||
|
||||
public bool ContainsReply(long replyId) => loadedReplies.ContainsKey(replyId);
|
||||
|
||||
private void onRepliesAdded(IEnumerable<DrawableComment> replies)
|
||||
{
|
||||
var page = createRepliesPage(replies);
|
||||
|
70
osu.Game/Overlays/Comments/ReplyCommentEditor.cs
Normal file
70
osu.Game/Overlays/Comments/ReplyCommentEditor.cs
Normal file
@ -0,0 +1,70 @@
|
||||
// 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 System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
public partial class ReplyCommentEditor : CancellableCommentEditor
|
||||
{
|
||||
[Resolved]
|
||||
private CommentsContainer commentsContainer { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private readonly Comment parentComment;
|
||||
|
||||
public Action<DrawableComment[]>? OnPost;
|
||||
|
||||
protected override LocalisableString FooterText => default;
|
||||
protected override LocalisableString CommitButtonText => CommonStrings.ButtonsReply;
|
||||
protected override LocalisableString TextBoxPlaceholder => CommentsStrings.PlaceholderReply;
|
||||
|
||||
public ReplyCommentEditor(Comment parent)
|
||||
{
|
||||
parentComment = parent;
|
||||
OnCancel = () => this.FadeOut(200).Expire();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
GetContainingInputManager().ChangeFocus(TextBox);
|
||||
}
|
||||
|
||||
protected override void OnCommit(string text)
|
||||
{
|
||||
ShowLoadingSpinner = true;
|
||||
CommentPostRequest req = new CommentPostRequest(commentsContainer.Type.Value, commentsContainer.Id.Value, text, parentComment.Id);
|
||||
req.Failure += e => Schedule(() =>
|
||||
{
|
||||
ShowLoadingSpinner = false;
|
||||
Logger.Error(e, "Posting reply comment failed.");
|
||||
});
|
||||
req.Success += cb => Schedule(processPostedComments, cb);
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
private void processPostedComments(CommentBundle cb)
|
||||
{
|
||||
foreach (var comment in cb.Comments)
|
||||
comment.ParentComment = parentComment;
|
||||
|
||||
var drawables = cb.Comments.Select(commentsContainer.GetDrawableComment).ToArray();
|
||||
OnPost?.Invoke(drawables);
|
||||
|
||||
OnCancel!.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user