1
0
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:
Krzysztof Gutkowski
2026-05-05 12:49:24 +02:00
committed by GitHub
Unverified
parent a72f4495cd
commit 2006485d60
10 changed files with 494 additions and 113 deletions
@@ -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 -17
View File
@@ -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,
}
}