mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 19:54:15 +08:00
Allow reporting users from their profile page (#36335)
This PR refactors the report popover to have an optional confirmation message after completing the request. This was initially meant for #32584, but then I realized I can first test it out on the user profiles, so I've implemented it here as well. Can be reviewed commit by commit. https://github.com/user-attachments/assets/cd59c560-c824-4a5e-bab6-5ecbf5125af1 --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
This commit is contained in:
committed by
GitHub
Unverified
parent
a72f4495cd
commit
2006485d60
@@ -253,6 +253,11 @@ namespace osu.Game.Tests.Visual.Online
|
||||
InputManager.MoveMouseTo(btn);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddStep("Set reason to other", () =>
|
||||
{
|
||||
var reason = this.ChildrenOfType<OsuEnumDropdown<CommentReportReason>>().Single();
|
||||
reason.Current.Value = CommentReportReason.Other;
|
||||
});
|
||||
AddStep("Try to report", () =>
|
||||
{
|
||||
var btn = this.ChildrenOfType<ReportCommentPopover>().Single().ChildrenOfType<RoundedButton>().Single();
|
||||
@@ -261,12 +266,10 @@ namespace osu.Game.Tests.Visual.Online
|
||||
});
|
||||
AddWaitStep("Wait", 3);
|
||||
AddAssert("Nothing happened", () => this.ChildrenOfType<ReportCommentPopover>().Any());
|
||||
AddStep("Set report data", () =>
|
||||
AddStep("Add comment", () =>
|
||||
{
|
||||
var field = this.ChildrenOfType<ReportCommentPopover>().Single().ChildrenOfType<OsuTextBox>().First();
|
||||
field.Current.Value = report_text;
|
||||
var reason = this.ChildrenOfType<OsuEnumDropdown<CommentReportReason>>().Single();
|
||||
reason.Current.Value = CommentReportReason.Other;
|
||||
});
|
||||
AddStep("Try to report", () =>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// 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.Linq;
|
||||
using System.Net.Http;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Chat;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public partial class TestSceneReportPopover : OsuTestScene
|
||||
{
|
||||
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||
|
||||
private ReportPopoverContainer popover = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUp()
|
||||
{
|
||||
AddStep("create popover", () =>
|
||||
{
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = popover = new ReportPopoverContainer(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSuccess()
|
||||
{
|
||||
ChatReportRequest pendingRequest = null!;
|
||||
|
||||
AddStep("setup request handling", () =>
|
||||
{
|
||||
dummyAPI.HandleRequest += request =>
|
||||
{
|
||||
if (request is ChatReportRequest chatReportRequest)
|
||||
{
|
||||
pendingRequest = chatReportRequest;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("show popover", () => popover.ShowPopover());
|
||||
AddStep("input reason", () => this.ChildrenOfType<OsuTextBox>().First().Text = "reason");
|
||||
AddStep("send report", () => this.ChildrenOfType<Button>().First().TriggerClick());
|
||||
AddUntilStep("wait for loading layer to hide", () => this.ChildrenOfType<LoadingLayer>().First().IsPresent, () => Is.True);
|
||||
AddWaitStep("wait some", 3);
|
||||
AddStep("complete request", () => pendingRequest.TriggerSuccess());
|
||||
AddUntilStep("wait for loading layer to hide", () => this.ChildrenOfType<LoadingLayer>().First().IsPresent, () => Is.False);
|
||||
AddAssert("ensure form is not present", () => this.ChildrenOfType<ReverseChildIDFillFlowContainer<Drawable>>().First().IsPresent, () => Is.False);
|
||||
AddAssert("ensure confirmation is present", () => this.ChildrenOfType<ReportPopover<ChatReportReason>.ReportConfirmation>().First().IsPresent, () => Is.True);
|
||||
AddUntilStep("wait for popover to hide", () => this.ChildrenOfType<ReportPopoverContainer.TestReportPopover>().First().IsPresent, () => Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFailure()
|
||||
{
|
||||
ChatReportRequest pendingRequest = null!;
|
||||
|
||||
AddStep("setup request handling", () =>
|
||||
{
|
||||
dummyAPI.HandleRequest += request =>
|
||||
{
|
||||
if (request is ChatReportRequest chatReportRequest)
|
||||
{
|
||||
pendingRequest = chatReportRequest;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("show popover", () => popover.ShowPopover());
|
||||
AddStep("input reason", () => this.ChildrenOfType<OsuTextBox>().First().Text = "reason");
|
||||
AddStep("send report", () => this.ChildrenOfType<Button>().First().TriggerClick());
|
||||
AddUntilStep("wait for loading layer to hide", () => this.ChildrenOfType<LoadingLayer>().First().IsPresent, () => Is.True);
|
||||
AddWaitStep("wait some", 3);
|
||||
AddStep("fail request", () => pendingRequest.TriggerFailure(new APIException("test error", new HttpRequestException("test error"))));
|
||||
AddUntilStep("wait for loading layer to hide", () => this.ChildrenOfType<LoadingLayer>().First().IsPresent, () => Is.False);
|
||||
AddAssert("ensure form is present", () => this.ChildrenOfType<ReverseChildIDFillFlowContainer<Drawable>>().First().IsPresent, () => Is.True);
|
||||
AddAssert("ensure error is present", () => this.ChildrenOfType<ErrorTextFlowContainer>().First().IsPresent, () => Is.True);
|
||||
AddAssert("ensure confirmation is not present", () => this.ChildrenOfType<ReportPopover<ChatReportReason>.ReportConfirmation>().First().IsPresent, () => Is.False);
|
||||
}
|
||||
|
||||
protected partial class ReportPopoverContainer : Drawable, IHasPopover
|
||||
{
|
||||
public Popover GetPopover() => new TestReportPopover("test");
|
||||
|
||||
public partial class TestReportPopover : ReportPopover<ChatReportReason>
|
||||
{
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
public TestReportPopover(string name)
|
||||
: base($"Report {name}?")
|
||||
{
|
||||
}
|
||||
|
||||
protected override APIRequest GetRequest(ChatReportReason reason, string comment) => new ChatReportRequest(1, reason, comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
|
||||
@@ -23,36 +24,223 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
public abstract partial class ReportPopover<TReportReason> : OsuPopover
|
||||
where TReportReason : struct, Enum
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to run when the report is finalised.
|
||||
/// The arguments to this action are: the reason for the report, and an optional additional comment.
|
||||
/// </summary>
|
||||
public Action<TReportReason, string>? Action;
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The action to run when the report is submitted.
|
||||
/// </summary>
|
||||
public event Action? Submitted;
|
||||
|
||||
/// <summary>
|
||||
/// The action to run when the report is submitted successfully.
|
||||
/// </summary>
|
||||
public event Action? Success;
|
||||
|
||||
/// <summary>
|
||||
/// The action to run when the report failed to submit.
|
||||
/// </summary>
|
||||
public event Action? Failure;
|
||||
|
||||
private ReverseChildIDFillFlowContainer<Drawable> form = null!;
|
||||
private ReportConfirmation confirmation = null!;
|
||||
private OsuEnumDropdown<TReportReason> reasonDropdown = null!;
|
||||
private OsuTextBox commentsTextBox = null!;
|
||||
private ErrorTextFlowContainer errorMessage = null!;
|
||||
private RoundedButton submitButton = null!;
|
||||
private LoadingLayer loadingLayer = null!;
|
||||
|
||||
private readonly LocalisableString header;
|
||||
|
||||
private readonly bool showConfirmation;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ReportPopover{TReportReason}"/>.
|
||||
/// </summary>
|
||||
/// <param name="headerString">The text to display in the header of the popover.</param>
|
||||
protected ReportPopover(LocalisableString headerString)
|
||||
/// <param name="showConfirmation">
|
||||
/// Whether the popover should show a generic "Thank you for your report" confirmation message.
|
||||
/// Set this to `true` if you're displaying a custom message outside of this popover.
|
||||
/// </param>
|
||||
protected ReportPopover(LocalisableString headerString, bool showConfirmation = true)
|
||||
: base(false)
|
||||
{
|
||||
header = headerString;
|
||||
this.showConfirmation = showConfirmation;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
Content.AutoSizeAxes = Axes.Y;
|
||||
Content.Width = 500;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
Width = 500,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(7),
|
||||
form = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(7),
|
||||
Padding = new MarginPadding(20),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Icon = FontAwesome.Solid.ExclamationTriangle,
|
||||
Size = new Vector2(36),
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = header,
|
||||
Font = OsuFont.Torus.With(size: 25),
|
||||
Margin = new MarginPadding { Bottom = 10 }
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = UsersStrings.ReportReason,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 40,
|
||||
Child = reasonDropdown = new OsuEnumDropdown<TReportReason>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = UsersStrings.ReportComments,
|
||||
},
|
||||
commentsTextBox = new OsuTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
PlaceholderText = UsersStrings.ReportPlaceholder,
|
||||
},
|
||||
errorMessage = new ErrorTextFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
submitButton = new RoundedButton
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Width = 200,
|
||||
BackgroundColour = colours.Red3,
|
||||
Text = UsersStrings.ReportActionsSend,
|
||||
Action = () =>
|
||||
{
|
||||
if (showConfirmation)
|
||||
loadingLayer.Show();
|
||||
|
||||
// we don't want size easing to mess up any transforms that are happening
|
||||
// when the popover is appearing, hence easing is only enabled after
|
||||
// the report is submitted
|
||||
Content.AutoSizeEasing = Easing.OutQuint;
|
||||
Content.AutoSizeDuration = 500F;
|
||||
|
||||
Submitted?.Invoke();
|
||||
performRequest();
|
||||
|
||||
if (!showConfirmation)
|
||||
this.HidePopover();
|
||||
},
|
||||
Margin = new MarginPadding { Bottom = 5, Top = 10 },
|
||||
},
|
||||
},
|
||||
},
|
||||
confirmation = new ReportConfirmation(),
|
||||
loadingLayer = new LoadingLayer(true)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
|
||||
commentsTextBox.Current.BindValueChanged(_ => updateStatus());
|
||||
|
||||
reasonDropdown.Current.BindValueChanged(_ => updateStatus());
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
private void performRequest()
|
||||
{
|
||||
var request = GetRequest(reasonDropdown.Current.Value, commentsTextBox.Text);
|
||||
|
||||
request.Success += handleSuccess;
|
||||
request.Failure += handleFailure;
|
||||
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
private void handleSuccess()
|
||||
{
|
||||
if (showConfirmation)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
form.Hide();
|
||||
confirmation.Show();
|
||||
|
||||
loadingLayer.Hide();
|
||||
Scheduler.AddDelayed(this.HidePopover, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
Success?.Invoke();
|
||||
}
|
||||
|
||||
private void handleFailure(Exception e)
|
||||
{
|
||||
if (showConfirmation)
|
||||
{
|
||||
Schedule(() => errorMessage.AddErrors([e.Message]));
|
||||
loadingLayer.Hide();
|
||||
}
|
||||
|
||||
Failure?.Invoke();
|
||||
}
|
||||
|
||||
private void updateStatus()
|
||||
{
|
||||
submitButton.Enabled.Value = !string.IsNullOrWhiteSpace(commentsTextBox.Current.Value) || !IsCommentRequired(reasonDropdown.Current.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the API request responsible for submitting this report.
|
||||
/// </summary>
|
||||
/// <param name="reason">The reason for this report.</param>
|
||||
/// <param name="comments">An optional comment explaining the report.</param>
|
||||
/// <returns></returns>
|
||||
protected abstract APIRequest GetRequest(TReportReason reason, string comments);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an additional comment is required for submitting the report with the supplied <paramref name="reason"/>.
|
||||
/// </summary>
|
||||
protected virtual bool IsCommentRequired(TReportReason reason) => true;
|
||||
|
||||
public partial class ReportConfirmation : FillFlowContainer
|
||||
{
|
||||
public ReportConfirmation()
|
||||
{
|
||||
Direction = FillDirection.Vertical;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Spacing = new Vector2(7);
|
||||
Padding = new MarginPadding(20);
|
||||
Alpha = 0;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
@@ -66,68 +254,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = header,
|
||||
Text = UsersStrings.ReportThanks,
|
||||
Font = OsuFont.Torus.With(size: 25),
|
||||
Margin = new MarginPadding { Bottom = 10 }
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = UsersStrings.ReportReason,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 40,
|
||||
Child = reasonDropdown = new OsuEnumDropdown<TReportReason>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = UsersStrings.ReportComments,
|
||||
},
|
||||
commentsTextBox = new OsuTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
PlaceholderText = UsersStrings.ReportPlaceholder,
|
||||
},
|
||||
submitButton = new RoundedButton
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Width = 200,
|
||||
BackgroundColour = colours.Red3,
|
||||
Text = UsersStrings.ReportActionsSend,
|
||||
Action = () =>
|
||||
{
|
||||
Action?.Invoke(reasonDropdown.Current.Value, commentsTextBox.Text);
|
||||
this.HidePopover();
|
||||
},
|
||||
Margin = new MarginPadding { Bottom = 5, Top = 10 },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
commentsTextBox.Current.BindValueChanged(_ => updateStatus());
|
||||
|
||||
reasonDropdown.Current.BindValueChanged(_ => updateStatus());
|
||||
|
||||
updateStatus();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatus()
|
||||
{
|
||||
submitButton.Enabled.Value = !string.IsNullOrWhiteSpace(commentsTextBox.Current.Value) || !IsCommentRequired(reasonDropdown.Current.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an additional comment is required for submitting the report with the supplied <paramref name="reason"/>.
|
||||
/// </summary>
|
||||
protected virtual bool IsCommentRequired(TReportReason reason) => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Overlays.Profile;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class UserReportRequest : APIRequest
|
||||
{
|
||||
public readonly int UserID;
|
||||
public readonly UserReportReason Reason;
|
||||
public readonly string Comment;
|
||||
|
||||
public UserReportRequest(int userID, UserReportReason reason, string comment)
|
||||
{
|
||||
UserID = userID;
|
||||
Reason = reason;
|
||||
Comment = comment;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var request = base.CreateWebRequest();
|
||||
request.Method = HttpMethod.Post;
|
||||
|
||||
request.AddParameter(@"reportable_type", @"user");
|
||||
request.AddParameter(@"reportable_id", $"{UserID}");
|
||||
request.AddParameter(@"reason", Reason.ToString());
|
||||
request.AddParameter(@"comments", Comment);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
protected override string Target => @"reports";
|
||||
}
|
||||
}
|
||||
@@ -12,28 +12,21 @@ namespace osu.Game.Overlays.Chat
|
||||
{
|
||||
public partial class ReportChatPopover : ReportPopover<ChatReportReason>
|
||||
{
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private ChannelManager channelManager { get; set; } = null!;
|
||||
|
||||
private readonly Message message;
|
||||
|
||||
public ReportChatPopover(Message message)
|
||||
: base(ReportStrings.UserTitle(message.Sender?.Username ?? @"Someone"))
|
||||
: base(ReportStrings.UserTitle(message.Sender?.Username ?? @"Someone"), false)
|
||||
{
|
||||
this.message = message;
|
||||
Action = report;
|
||||
}
|
||||
|
||||
protected override bool IsCommentRequired(ChatReportReason reason) => reason == ChatReportReason.Other;
|
||||
|
||||
private void report(ChatReportReason reason, string comments)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var request = new ChatReportRequest(message.Id, reason, comments);
|
||||
|
||||
request.Success += () =>
|
||||
Success += () =>
|
||||
{
|
||||
string thanksMessage;
|
||||
|
||||
@@ -41,10 +34,10 @@ namespace osu.Game.Overlays.Chat
|
||||
{
|
||||
case ChannelType.PM:
|
||||
thanksMessage = """
|
||||
Chat moderators have been alerted. You have reported a private message so they will not be able to read history to maintain your privacy. Please make sure to include as much details as you can.
|
||||
You can submit a second report with more details if required, or contact abuse@ppy.sh if a user is being extremely offensive.
|
||||
You can also block a user via the block button on their user profile, or by right-clicking on their name in the chat and selecting "Block".
|
||||
""";
|
||||
Chat moderators have been alerted. You have reported a private message so they will not be able to read history to maintain your privacy. Please make sure to include as much details as you can.
|
||||
You can submit a second report with more details if required, or contact abuse@ppy.sh if a user is being extremely offensive.
|
||||
You can also block a user via the block button on their user profile, or by right-clicking on their name in the chat and selecting "Block".
|
||||
""";
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -54,8 +47,10 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
channelManager.CurrentChannel.Value.AddNewMessages(new InfoMessage(thanksMessage));
|
||||
};
|
||||
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
protected override APIRequest GetRequest(ChatReportReason reason, string comments) => new ChatReportRequest(message.Id, reason, comments);
|
||||
|
||||
protected override bool IsCommentRequired(ChatReportReason reason) => reason == ChatReportReason.Other;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
@@ -29,9 +27,6 @@ namespace osu.Game.Overlays.Comments
|
||||
private LinkFlowContainer link = null!;
|
||||
private LoadingSpinner loading = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider? colourProvider { get; set; }
|
||||
|
||||
@@ -60,19 +55,17 @@ namespace osu.Game.Overlays.Comments
|
||||
link.AddLink(ReportStrings.CommentButton.ToLower(), this.ShowPopover);
|
||||
}
|
||||
|
||||
public Popover GetPopover() => new ReportCommentPopover(comment)
|
||||
public Popover GetPopover()
|
||||
{
|
||||
Action = report
|
||||
};
|
||||
var popover = new ReportCommentPopover(comment);
|
||||
|
||||
private void report(CommentReportReason reason, string comments)
|
||||
{
|
||||
var request = new CommentReportRequest(comment.Id, reason, comments);
|
||||
popover.Submitted += () =>
|
||||
{
|
||||
link.Hide();
|
||||
loading.Show();
|
||||
};
|
||||
|
||||
link.Hide();
|
||||
loading.Show();
|
||||
|
||||
request.Success += () => Schedule(() =>
|
||||
popover.Success += () => Schedule(() =>
|
||||
{
|
||||
loading.Hide();
|
||||
|
||||
@@ -83,13 +76,13 @@ namespace osu.Game.Overlays.Comments
|
||||
this.FadeOut(2000, Easing.InQuint).Expire();
|
||||
});
|
||||
|
||||
request.Failure += _ => Schedule(() =>
|
||||
popover.Failure += () => Schedule(() =>
|
||||
{
|
||||
loading.Hide();
|
||||
link.Show();
|
||||
});
|
||||
|
||||
api.Queue(request);
|
||||
return popover;
|
||||
}
|
||||
|
||||
public float LineBaseHeight => link.ChildrenOfType<IHasLineBaseHeight>().FirstOrDefault()?.LineBaseHeight ?? DrawHeight;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
@@ -9,9 +11,16 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
public partial class ReportCommentPopover : ReportPopover<CommentReportReason>
|
||||
{
|
||||
public ReportCommentPopover(Comment? comment)
|
||||
: base(ReportStrings.CommentTitle(comment?.User?.Username ?? comment?.LegacyName ?? @"Someone"))
|
||||
private readonly Comment comment;
|
||||
|
||||
protected override bool IsCommentRequired(CommentReportReason reason) => reason == CommentReportReason.Other;
|
||||
|
||||
public ReportCommentPopover(Comment comment)
|
||||
: base(ReportStrings.CommentTitle(comment.User?.Username ?? comment.LegacyName ?? @"Someone"), false)
|
||||
{
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
protected override APIRequest GetRequest(CommentReportReason reason, string comments) => new CommentReportRequest(comment.Id, reason, comments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
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.Game.Online.API;
|
||||
@@ -18,25 +21,50 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
|
||||
|
||||
private UserReportPopoverTarget reportPopoverTarget = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// This is a bit of a dirty hack. Because `ReportUserPopover` is spawned from `UserActionsPopover`,
|
||||
// and that they both share the same `PopoverContainer`, the former will get destroyed when the latter
|
||||
// is opened, causing it to get destroyed as well.
|
||||
//
|
||||
// This is worked around by having an additional dummy popover target on the actions button,
|
||||
// which is then passed to `UserActionsPopover` and the user report action. This way the popover
|
||||
// can remain attached to it once the actions popover is destroyed.
|
||||
Add(reportPopoverTarget = new UserReportPopoverTarget
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
User.BindValueChanged(_ => Alpha = User.Value?.User.OnlineID == api.LocalUser.Value.OnlineID ? 0 : 1, true);
|
||||
User.BindValueChanged(_ =>
|
||||
{
|
||||
Alpha = User.Value?.User.OnlineID == api.LocalUser.Value.OnlineID ? 0 : 1;
|
||||
reportPopoverTarget.User = User.Value?.User;
|
||||
}, true);
|
||||
}
|
||||
|
||||
public override Popover GetPopover() => new UserActionPopover(User.Value!.User);
|
||||
public override Popover GetPopover() => new UserActionPopover(User.Value!.User, reportPopoverTarget);
|
||||
|
||||
private partial class UserActionPopover : ProfileActionPopover
|
||||
{
|
||||
private readonly APIUser user;
|
||||
|
||||
public UserActionPopover(APIUser user)
|
||||
private readonly IHasPopover reportPopoverTarget;
|
||||
|
||||
public UserActionPopover(APIUser user, IHasPopover reportPopoverTarget)
|
||||
{
|
||||
this.user = user;
|
||||
this.reportPopoverTarget = reportPopoverTarget;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -53,9 +81,24 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
dialogOverlay?.Push(userBlocked ? ConfirmBlockActionDialog.Unblock(user) : ConfirmBlockActionDialog.Block(user));
|
||||
this.HidePopover();
|
||||
}
|
||||
}
|
||||
},
|
||||
new ProfilePopoverAction(FontAwesome.Solid.ExclamationTriangle, ReportStrings.UserButton)
|
||||
{
|
||||
Action = () =>
|
||||
{
|
||||
this.HidePopover();
|
||||
reportPopoverTarget.ShowPopover();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private partial class UserReportPopoverTarget : Container, IHasPopover
|
||||
{
|
||||
public APIUser? User;
|
||||
|
||||
public Popover? GetPopover() => User != null ? new ReportUserPopover(User) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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.Game.Graphics.UserInterfaceV2;
|
||||
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.Profile
|
||||
{
|
||||
public partial class ReportUserPopover : ReportPopover<UserReportReason>
|
||||
{
|
||||
private readonly APIUser user;
|
||||
|
||||
public ReportUserPopover(APIUser user)
|
||||
: base(ReportStrings.UserTitle(user.Username))
|
||||
{
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
protected override APIRequest GetRequest(UserReportReason reason, string comments) => new UserReportRequest(user.Id, reason, comments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Profile
|
||||
{
|
||||
public enum UserReportReason
|
||||
{
|
||||
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsCheating))]
|
||||
Cheating,
|
||||
|
||||
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsMultipleAccounts))]
|
||||
MultipleAccounts,
|
||||
|
||||
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsInappropriateChat))]
|
||||
InappropriateChat,
|
||||
|
||||
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsUnwantedContent))]
|
||||
UnwantedContent,
|
||||
|
||||
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.ReportOptionsOther))]
|
||||
Other,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user