1
0

NoteEdit.xaml.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. using Content.Client.UserInterface.Controls;
  2. using Content.Shared.Administration.Notes;
  3. using Content.Shared.Database;
  4. using Robust.Client.AutoGenerated;
  5. using Robust.Client.Console;
  6. using Robust.Client.UserInterface;
  7. using Robust.Client.UserInterface.Controls;
  8. using Robust.Client.UserInterface.XAML;
  9. using Robust.Shared.Timing;
  10. using Robust.Shared.Utility;
  11. namespace Content.Client.Administration.UI.Notes;
  12. [GenerateTypedNameReferences]
  13. public sealed partial class NoteEdit : FancyWindow
  14. {
  15. [Dependency] private readonly IGameTiming _gameTiming = default!;
  16. [Dependency] private readonly IClientConsoleHost _console = default!;
  17. private enum Multipliers
  18. {
  19. Minutes,
  20. Hours,
  21. Days,
  22. Weeks,
  23. Months,
  24. Years,
  25. Centuries
  26. }
  27. public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
  28. public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
  29. {
  30. IoCManager.InjectDependencies(this);
  31. RobustXamlLoader.Load(this);
  32. PlayerName = playerName;
  33. Title = Loc.GetString("admin-note-editor-title-new", ("player", PlayerName));
  34. IsCreating = note is null;
  35. CanCreate = canCreate;
  36. CanEdit = canEdit;
  37. ResetSubmitButton();
  38. // It's weird to use minutes as the IDs, but it works and makes sense kind of :)
  39. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-minutes"), (int) Multipliers.Minutes);
  40. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-hours"), (int) Multipliers.Hours);
  41. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-days"), (int) Multipliers.Days);
  42. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-weeks"), (int) Multipliers.Weeks);
  43. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-months"), (int) Multipliers.Months);
  44. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-years"), (int) Multipliers.Years);
  45. ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-centuries"), (int) Multipliers.Centuries);
  46. ExpiryLengthDropdown.OnItemSelected += OnLengthChanged;
  47. ExpiryLengthDropdown.SelectId((int) Multipliers.Weeks);
  48. ExpiryLineEdit.OnTextChanged += OnTextChanged;
  49. TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
  50. TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
  51. TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
  52. TypeOption.OnItemSelected += OnTypeChanged;
  53. SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-select"), -1);
  54. SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) Shared.Database.NoteSeverity.None);
  55. SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) Shared.Database.NoteSeverity.Minor);
  56. SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) Shared.Database.NoteSeverity.Medium);
  57. SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-high"), (int) Shared.Database.NoteSeverity.High);
  58. SeverityOption.OnItemSelected += OnSeverityChanged;
  59. PermanentCheckBox.OnPressed += OnPermanentPressed;
  60. SecretCheckBox.OnPressed += OnSecretPressed;
  61. SubmitButton.OnPressed += OnSubmitButtonPressed;
  62. SubmitButton.OnMouseEntered += OnSubmitButtonMouseEntered;
  63. SubmitButton.OnMouseExited += OnSubmitButtonMouseExited;
  64. if (note is null && !canCreate)
  65. {
  66. TypeOption.Disabled = true;
  67. SeverityOption.Disabled = true;
  68. }
  69. if (note is not null)
  70. {
  71. Title = Loc.GetString("admin-note-editor-title-existing", ("id", note.Id), ("player", PlayerName), ("author", note.CreatedByName));
  72. NoteId = note.Id;
  73. NoteType = note.NoteType;
  74. TypeOption.AddItem(Loc.GetString("admin-note-editor-type-server-ban"), (int) NoteType.ServerBan);
  75. TypeOption.AddItem(Loc.GetString("admin-note-editor-type-role-ban"), (int) NoteType.RoleBan);
  76. TypeOption.SelectId((int)NoteType);
  77. TypeOption.Disabled = true;
  78. NoteTextEdit.InsertAtCursor(note.Message);
  79. NoteSeverity = note.NoteSeverity ?? Shared.Database.NoteSeverity.Minor;
  80. SeverityOption.SelectId((int)NoteSeverity);
  81. SeverityOption.Disabled = note.NoteType is not (NoteType.Note or NoteType.ServerBan or NoteType.RoleBan);
  82. IsSecret = note.Secret;
  83. SecretCheckBox.Pressed = note.Secret;
  84. SecretCheckBox.Disabled = note.NoteType is not NoteType.Note;
  85. ExpiryTime = note.ExpiryTime;
  86. if (ExpiryTime is not null)
  87. {
  88. PermanentCheckBox.Pressed = false;
  89. UpdatePermanentCheckboxFields();
  90. ExpiryLineEdit.Text = ExpiryTime.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss");
  91. }
  92. }
  93. UpdateSubmitButton();
  94. }
  95. private void OnSubmitButtonMouseEntered(GUIMouseHoverEventArgs args)
  96. {
  97. if (!SubmitButton.Disabled)
  98. return;
  99. SeverityOption.ModulateSelfOverride = Color.Red;
  100. }
  101. private void OnSubmitButtonMouseExited(GUIMouseHoverEventArgs args)
  102. {
  103. SeverityOption.ModulateSelfOverride = null;
  104. }
  105. private NoteSeverity? _noteSeverity = null;
  106. private string PlayerName { get; }
  107. private int NoteId { get; }
  108. private bool IsSecret { get; set; }
  109. private NoteType NoteType { get; set; }
  110. private NoteSeverity? NoteSeverity
  111. {
  112. get => _noteSeverity;
  113. set
  114. {
  115. _noteSeverity = value;
  116. UpdateSubmitButton();
  117. }
  118. }
  119. private DateTime? ExpiryTime { get; set; }
  120. private TimeSpan? DeleteResetOn { get; set; }
  121. private bool IsCreating { get; set; }
  122. private bool CanCreate { get; set; }
  123. private bool CanEdit { get; set; }
  124. private void OnTypeChanged(OptionButton.ItemSelectedEventArgs args)
  125. {
  126. // We should be resetting the underlying values too but the server handles that anyway
  127. switch (args.Id)
  128. {
  129. case (int) NoteType.Note: // Note: your standard note, does nothing special
  130. NoteType = NoteType.Note;
  131. SecretCheckBox.Disabled = false;
  132. SecretCheckBox.Pressed = false;
  133. SeverityOption.Disabled = false;
  134. PermanentCheckBox.Pressed = true;
  135. SubmitButton.Disabled = true;
  136. UpdatePermanentCheckboxFields();
  137. break;
  138. case (int) NoteType.Message: // Message: these are shown to the player when they log on
  139. NoteType = NoteType.Message;
  140. SecretCheckBox.Disabled = true;
  141. SecretCheckBox.Pressed = false;
  142. SeverityOption.Disabled = true;
  143. SeverityOption.SelectId((int) Shared.Database.NoteSeverity.None);
  144. NoteSeverity = null;
  145. PermanentCheckBox.Pressed = false;
  146. UpdatePermanentCheckboxFields();
  147. break;
  148. case (int) NoteType.Watchlist: // Watchlist: these are always secret and only shown to admins when the player logs on
  149. NoteType = NoteType.Watchlist;
  150. SecretCheckBox.Disabled = true;
  151. SecretCheckBox.Pressed = true;
  152. SeverityOption.Disabled = true;
  153. SeverityOption.SelectId((int) Shared.Database.NoteSeverity.None);
  154. NoteSeverity = null;
  155. PermanentCheckBox.Pressed = false;
  156. UpdatePermanentCheckboxFields();
  157. break;
  158. default: // Wuh oh
  159. throw new ArgumentOutOfRangeException(nameof(args.Id), args.Id, "Unknown note type");
  160. }
  161. TypeOption.SelectId(args.Id);
  162. }
  163. private void OnPermanentPressed(BaseButton.ButtonEventArgs _)
  164. {
  165. UpdatePermanentCheckboxFields();
  166. }
  167. private void UpdatePermanentCheckboxFields()
  168. {
  169. ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
  170. ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
  171. ExpiryLengthDropdown.Visible = !PermanentCheckBox.Pressed;
  172. ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? 1.ToString() : string.Empty;
  173. }
  174. private void OnSecretPressed(BaseButton.ButtonEventArgs _)
  175. {
  176. IsSecret = SecretCheckBox.Pressed;
  177. }
  178. private void OnSeverityChanged(OptionButton.ItemSelectedEventArgs args)
  179. {
  180. NoteSeverity = args.Id == -1 ? NoteSeverity = null : (NoteSeverity) args.Id;
  181. SeverityOption.SelectId(args.Id);
  182. }
  183. private void OnLengthChanged(OptionButton.ItemSelectedEventArgs args)
  184. {
  185. ExpiryLengthDropdown.SelectId(args.Id);
  186. }
  187. private void OnTextChanged(HistoryLineEdit.LineEditEventArgs args)
  188. {
  189. ParseExpiryTime();
  190. }
  191. private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
  192. {
  193. if (!ParseExpiryTime())
  194. return;
  195. if (DeleteResetOn is null)
  196. {
  197. DeleteResetOn = _gameTiming.RealTime + TimeSpan.FromSeconds(3);
  198. SubmitButton.Text = Loc.GetString("admin-note-editor-submit-confirm");
  199. SubmitButton.ModulateSelfOverride = Color.Red;
  200. // Task.Delay(3000).ContinueWith(_ => ResetSubmitButton()); // TODO: fix
  201. return;
  202. }
  203. ResetSubmitButton();
  204. SubmitPressed?.Invoke(NoteId, NoteType, Rope.Collapse(NoteTextEdit.TextRope), NoteSeverity, IsSecret, ExpiryTime);
  205. if (Parent is null)
  206. {
  207. _console.ExecuteCommand($"adminnotes \"{PlayerName}\"");
  208. }
  209. Close();
  210. }
  211. protected override void FrameUpdate(FrameEventArgs args)
  212. {
  213. base.FrameUpdate(args);
  214. // This checks for null for free, do not invert it as null always produces a false value
  215. if (DeleteResetOn < _gameTiming.RealTime)
  216. {
  217. ResetSubmitButton();
  218. DeleteResetOn = null;
  219. }
  220. }
  221. /// <summary>
  222. /// Updates whether or not the submit button is disabled.
  223. /// </summary>
  224. private void UpdateSubmitButton()
  225. {
  226. if (!CanEdit)
  227. {
  228. SubmitButton.Disabled = true;
  229. return;
  230. }
  231. if (IsCreating && !CanCreate)
  232. {
  233. SubmitButton.Disabled = true;
  234. return;
  235. }
  236. SubmitButton.Disabled = (NoteType != NoteType.Watchlist && NoteType != NoteType.Message) && NoteSeverity == null;
  237. }
  238. private void ResetSubmitButton()
  239. {
  240. SubmitButton.Text = Loc.GetString("admin-note-editor-submit");
  241. SubmitButton.ModulateSelfOverride = null;
  242. UpdateDraw();
  243. }
  244. /// <summary>
  245. /// Tries to parse the currently entered expiry time. As a side effect this function
  246. /// will colour its respective line edit to indicate an error
  247. /// </summary>
  248. /// <returns>True if parsing was successful, false if not</returns>
  249. private bool ParseExpiryTime()
  250. {
  251. // If the checkbox is pressed the note is permanent, so expiry is null
  252. if (PermanentCheckBox.Pressed)
  253. {
  254. ExpiryTime = null;
  255. return true;
  256. }
  257. if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !uint.TryParse(ExpiryLineEdit.Text, out var inputInt))
  258. {
  259. ExpiryLineEdit.ModulateSelfOverride = Color.Red;
  260. return false;
  261. }
  262. var mult = ExpiryLengthDropdown.SelectedId switch
  263. {
  264. (int) Multipliers.Minutes => TimeSpan.FromMinutes(1).TotalMinutes,
  265. (int) Multipliers.Hours => TimeSpan.FromHours(1).TotalMinutes,
  266. (int) Multipliers.Days => TimeSpan.FromDays(1).TotalMinutes,
  267. (int) Multipliers.Weeks => TimeSpan.FromDays(7).TotalMinutes,
  268. (int) Multipliers.Months => TimeSpan.FromDays(30).TotalMinutes,
  269. (int) Multipliers.Years => TimeSpan.FromDays(365).TotalMinutes,
  270. (int) Multipliers.Centuries => TimeSpan.FromDays(36525).TotalMinutes,
  271. _ => throw new ArgumentOutOfRangeException(nameof(ExpiryLengthDropdown.SelectedId), "Multiplier out of range :(")
  272. };
  273. ExpiryTime = DateTime.UtcNow.AddMinutes(inputInt * mult);
  274. ExpiryLineEdit.ModulateSelfOverride = null;
  275. return true;
  276. }
  277. protected override void Dispose(bool disposing)
  278. {
  279. base.Dispose(disposing);
  280. if (!disposing)
  281. {
  282. return;
  283. }
  284. PermanentCheckBox.OnPressed -= OnPermanentPressed;
  285. SecretCheckBox.OnPressed -= OnSecretPressed;
  286. SubmitButton.OnPressed -= OnSubmitButtonPressed;
  287. SubmitButton.OnMouseEntered -= OnSubmitButtonMouseEntered;
  288. SubmitButton.OnMouseExited -= OnSubmitButtonMouseExited;
  289. SubmitPressed = null;
  290. }
  291. }