Better notes and bans (#14228)
Co-authored-by: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
@@ -8,8 +8,8 @@
|
||||
<ScrollContainer VerticalExpand="True" HorizontalExpand="True" HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical" Name="Notes" Access="Public" VerticalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
<Label Name="NewNoteLabel" Text="{Loc admin-notes-new-note}" />
|
||||
<HistoryLineEdit Name="NewNote"/>
|
||||
<Button Name="ShowMoreButton" Text="{Loc admin-notes-show-more}" Visible="False" HorizontalAlignment="Center" />
|
||||
<Button Name="NewNoteButton" Text="{Loc admin-notes-new-note}" Disabled="True" />
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
|
||||
@@ -1,119 +1,167 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesControl : Control
|
||||
{
|
||||
public event Action<int, string>? OnNoteChanged;
|
||||
public event Action<string>? OnNewNoteEntered;
|
||||
public event Action<int>? OnNoteDeleted;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? NoteChanged;
|
||||
public event Action<NoteType, string, NoteSeverity?, bool, DateTime?>? NewNoteEntered;
|
||||
public event Action<int, NoteType>? NoteDeleted;
|
||||
|
||||
private AdminNotesLinePopup? _popup;
|
||||
private readonly SpriteSystem _sprites;
|
||||
private readonly double _noteFreshDays;
|
||||
private readonly double _noteStaleDays;
|
||||
|
||||
public AdminNotesControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sprites = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
NewNote.OnTextEntered += NewNoteEntered;
|
||||
// There should be a warning somewhere if fresh > stale
|
||||
// I thought about putting it here but then it would spam you every time you open notes
|
||||
_noteFreshDays = _cfg.GetCVar(CCVars.NoteFreshDays);
|
||||
_noteStaleDays = _cfg.GetCVar(CCVars.NoteStaleDays);
|
||||
|
||||
NewNoteButton.OnPressed += OnNewNoteButtonPressed;
|
||||
ShowMoreButton.OnPressed += OnShowMoreButtonPressed;
|
||||
}
|
||||
|
||||
private Dictionary<int, AdminNotesLine> Inputs { get; } = new();
|
||||
private Dictionary<(int noteId, NoteType noteType), AdminNotesLine> Inputs { get; } = new();
|
||||
private bool CanCreate { get; set; }
|
||||
private bool CanDelete { get; set; }
|
||||
private bool CanEdit { get; set; }
|
||||
private string PlayerName { get; set; } = "<Error>";
|
||||
|
||||
private void NewNoteEntered(LineEditEventArgs args)
|
||||
public void SetPlayerName(string playerName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(args.Text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NewNote.Clear();
|
||||
OnNewNoteEntered?.Invoke(args.Text);
|
||||
PlayerName = playerName;
|
||||
}
|
||||
|
||||
private void NoteSubmitted(AdminNotesLine input)
|
||||
private void OnNewNoteButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var text = input.EditText.Trim();
|
||||
if (input.OriginalMessage == text)
|
||||
var noteEdit = new NoteEdit(null, PlayerName, CanCreate, CanEdit);
|
||||
noteEdit.SubmitPressed += OnNoteSubmitted;
|
||||
noteEdit.OpenCentered();
|
||||
}
|
||||
|
||||
private void OnNoteSubmitted(int id, NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
|
||||
{
|
||||
if (id == 0)
|
||||
{
|
||||
NewNoteEntered?.Invoke(type, message, severity, secret, expiryTime);
|
||||
return;
|
||||
}
|
||||
|
||||
OnNoteChanged?.Invoke(input.Id, text);
|
||||
NoteChanged?.Invoke(id, type, message, severity, secret, expiryTime);
|
||||
}
|
||||
|
||||
private bool NoteClicked(AdminNotesLine line)
|
||||
{
|
||||
ClosePopup();
|
||||
|
||||
_popup = new AdminNotesLinePopup(line.Note, CanDelete, CanEdit);
|
||||
_popup.OnEditPressed += noteId =>
|
||||
_popup = new AdminNotesLinePopup(line.Note, PlayerName, CanDelete, CanEdit);
|
||||
_popup.OnEditPressed += (noteId, noteType) =>
|
||||
{
|
||||
if (!Inputs.TryGetValue(noteId, out var input))
|
||||
if (!Inputs.TryGetValue((noteId, noteType), out var input))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
input.SetEditable(true);
|
||||
var noteEdit = new NoteEdit(input.Note, PlayerName, CanCreate, CanEdit);
|
||||
noteEdit.SubmitPressed += OnNoteSubmitted;
|
||||
noteEdit.OpenCentered();
|
||||
};
|
||||
_popup.OnDeletePressed += noteId => OnNoteDeleted?.Invoke(noteId);
|
||||
|
||||
_popup.OnDeletePressed += (noteId, noteType) => NoteDeleted?.Invoke(noteId, noteType);
|
||||
var box = UIBox2.FromDimensions(UserInterfaceManager.MousePositionScaled.Position, Vector2.One);
|
||||
_popup.Open(box);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ClosePopup()
|
||||
public void SetNotes(Dictionary<(int, NoteType), SharedAdminNote> notes)
|
||||
{
|
||||
_popup?.Close();
|
||||
_popup = null;
|
||||
}
|
||||
|
||||
public void SetNotes(Dictionary<int, SharedAdminNote> notes)
|
||||
{
|
||||
foreach (var (id, input) in Inputs)
|
||||
foreach (var (key, input) in Inputs)
|
||||
{
|
||||
if (!notes.ContainsKey(id))
|
||||
if (!notes.ContainsKey(key))
|
||||
{
|
||||
Notes.RemoveChild(input);
|
||||
Inputs.Remove(id);
|
||||
// Yes this is slower than just updating, but new notes get added at the bottom. The user won't notice.
|
||||
Notes.RemoveAllChildren();
|
||||
Inputs.Clear();
|
||||
break;
|
||||
}
|
||||
Notes.RemoveChild(input);
|
||||
Inputs.Remove(key);
|
||||
}
|
||||
|
||||
foreach (var note in notes.Values.OrderBy(note => note.Id))
|
||||
var showMoreButtonVisible = false;
|
||||
foreach (var note in notes.Values.OrderByDescending(note => note.CreatedAt))
|
||||
{
|
||||
if (Inputs.TryGetValue(note.Id, out var input))
|
||||
if (Inputs.TryGetValue((note.Id, note.NoteType), out var input))
|
||||
{
|
||||
input.UpdateNote(note);
|
||||
continue;
|
||||
}
|
||||
|
||||
input = new AdminNotesLine(note);
|
||||
input.OnSubmitted += NoteSubmitted;
|
||||
input = new AdminNotesLine(_sprites, note);
|
||||
input.OnClicked += NoteClicked;
|
||||
|
||||
var timeDiff = DateTime.UtcNow - note.CreatedAt;
|
||||
float alpha;
|
||||
if (_noteFreshDays == 0 || timeDiff.TotalDays <= _noteFreshDays)
|
||||
{
|
||||
alpha = 1f;
|
||||
}
|
||||
else if (_noteStaleDays == 0 || timeDiff.TotalDays > _noteStaleDays)
|
||||
{
|
||||
alpha = 0f;
|
||||
input.Visible = false;
|
||||
showMoreButtonVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = (float) (1 - Math.Clamp((timeDiff.TotalDays - _noteFreshDays) / (_noteStaleDays - _noteFreshDays), 0, 1));
|
||||
}
|
||||
|
||||
input.Modulate = input.Modulate.WithAlpha(alpha);
|
||||
Notes.AddChild(input);
|
||||
Inputs[note.Id] = input;
|
||||
Inputs[(note.Id, note.NoteType)] = input;
|
||||
ShowMoreButton.Visible = showMoreButtonVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShowMoreButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
foreach (var input in Inputs.Values)
|
||||
{
|
||||
input.Modulate = input.Modulate.WithAlpha(1f);
|
||||
input.Visible = true;
|
||||
}
|
||||
|
||||
ShowMoreButton.Visible = false;
|
||||
}
|
||||
|
||||
public void SetPermissions(bool create, bool delete, bool edit)
|
||||
{
|
||||
CanCreate = create;
|
||||
CanDelete = delete;
|
||||
CanEdit = edit;
|
||||
NewNoteLabel.Visible = create;
|
||||
NewNote.Visible = create;
|
||||
NewNoteButton.Visible = create;
|
||||
NewNoteButton.Disabled = !create;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -125,21 +173,14 @@ public sealed partial class AdminNotesControl : Control
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var input in Inputs.Values)
|
||||
{
|
||||
input.OnSubmitted -= NoteSubmitted;
|
||||
}
|
||||
|
||||
Inputs.Clear();
|
||||
NewNote.OnTextEntered -= NewNoteEntered;
|
||||
NewNoteButton.OnPressed -= OnNewNoteButtonPressed;
|
||||
|
||||
if (_popup != null)
|
||||
{
|
||||
UserInterfaceManager.PopupRoot.RemoveChild(_popup);
|
||||
}
|
||||
|
||||
OnNoteChanged = null;
|
||||
OnNewNoteEntered = null;
|
||||
OnNoteDeleted = null;
|
||||
NoteDeleted = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
@@ -14,15 +14,10 @@ public sealed class AdminNotesEui : BaseEui
|
||||
NoteWindow = new AdminNotesWindow();
|
||||
NoteControl = NoteWindow.Notes;
|
||||
|
||||
NoteControl.OnNoteChanged += (id, text) => SendMessage(new EditNoteRequest(id, text));
|
||||
NoteControl.OnNewNoteEntered += text => SendMessage(new CreateNoteRequest(text));
|
||||
NoteControl.OnNoteDeleted += id => SendMessage(new DeleteNoteRequest(id));
|
||||
NoteWindow.OnClose += OnClosed;
|
||||
}
|
||||
|
||||
private void OnClosed()
|
||||
{
|
||||
SendMessage(new CloseEuiMessage());
|
||||
NoteControl.NoteChanged += (id, type, text, severity, secret, expiryTime) => SendMessage(new EditNoteRequest(id, type, text, severity, secret, expiryTime));
|
||||
NoteControl.NewNoteEntered += (type, text, severity, secret, expiryTime) => SendMessage(new CreateNoteRequest(type, text, severity, secret, expiryTime));
|
||||
NoteControl.NoteDeleted += (id, type) => SendMessage(new DeleteNoteRequest(id, type));
|
||||
NoteWindow.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
@@ -43,6 +38,7 @@ public sealed class AdminNotesEui : BaseEui
|
||||
}
|
||||
|
||||
NoteWindow.SetTitlePlayer(s.NotedPlayerName);
|
||||
NoteControl.SetPlayerName(s.NotedPlayerName);
|
||||
NoteControl.SetNotes(s.Notes);
|
||||
NoteControl.SetPermissions(s.CanCreate, s.CanDelete, s.CanEdit);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Orientation="Vertical">
|
||||
<cc:HSeparator Name="Separator"/>
|
||||
<BoxContainer Name ="MetadataContainer" Orientation="Horizontal">
|
||||
<TextureRect Name="SeverityRect" Margin="2"/>
|
||||
<Label Name="TimeLabel" Margin="4 0" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="ServerLabel" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="RoundLabel" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="AdminLabel" />
|
||||
<cc:HSeparator Margin="4 0" />
|
||||
<Label Name="PlaytimeLabel" />
|
||||
<cc:HSeparator Name="SecretSeparator" Visible="False" Margin="4 0" />
|
||||
<Label Name="SecretLabel" Text="{Loc admin-notes-secret} " Visible="False" />
|
||||
</BoxContainer>
|
||||
<RichTextLabel Name="NoteLabel" />
|
||||
<Label Name="ExpiresLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<Label Name="ExtraLabel" Visible="False" Modulate="#1AA7EC" />
|
||||
<Label Name="EditedLabel" Visible="False" />
|
||||
<cc:HSeparator Name="Separator" />
|
||||
</BoxContainer>
|
||||
|
||||
@@ -1,85 +1,172 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using System.Text;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Input;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesLine : BoxContainer
|
||||
{
|
||||
private RichTextLabel? _label;
|
||||
private LineEdit? _edit;
|
||||
private readonly SpriteSystem _sprites;
|
||||
|
||||
public AdminNotesLine(SharedAdminNote note)
|
||||
private const string AdminNotesTextureBase = "/Textures/Interface/AdminNotes/";
|
||||
private static readonly Dictionary<NoteSeverity, string> SeverityIcons = new()
|
||||
{
|
||||
{ NoteSeverity.None, AdminNotesTextureBase + "none_button.png" },
|
||||
{ NoteSeverity.Minor, AdminNotesTextureBase + "minor_button.png" },
|
||||
{ NoteSeverity.Medium, AdminNotesTextureBase + "medium_button.png" },
|
||||
{ NoteSeverity.High, AdminNotesTextureBase + "high_button.png" },
|
||||
};
|
||||
private static readonly Dictionary<NoteType, string> NoteTypeIcons = new()
|
||||
{
|
||||
{ NoteType.Message, AdminNotesTextureBase + "message.png" },
|
||||
{ NoteType.Watchlist, AdminNotesTextureBase + "watchlist.png" },
|
||||
};
|
||||
|
||||
public AdminNotesLine(SpriteSystem sprites, SharedAdminNote note)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_sprites = sprites;
|
||||
|
||||
Note = note;
|
||||
MouseFilter = MouseFilterMode.Pass;
|
||||
|
||||
AddLabel();
|
||||
Separator.Visible = true;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public SharedAdminNote Note { get; private set; }
|
||||
public int Id => Note.Id;
|
||||
public string OriginalMessage => Note.Message;
|
||||
public string EditText => _edit?.Text ?? OriginalMessage;
|
||||
|
||||
public event Action<AdminNotesLine>? OnSubmitted;
|
||||
public event Func<AdminNotesLine, bool>? OnClicked;
|
||||
|
||||
private void AddLabel()
|
||||
/// <summary>
|
||||
/// Attempts to refresh the current note line with new data. The note it draws data on is stored in <see cref="Note"/>
|
||||
/// </summary>
|
||||
private void Refresh()
|
||||
{
|
||||
if (_edit != null)
|
||||
{
|
||||
_edit.OnTextEntered -= Submitted;
|
||||
_edit.OnFocusExit -= Submitted;
|
||||
string? iconPath;
|
||||
if(Note.NoteSeverity is not null)
|
||||
SeverityIcons.TryGetValue(Note.NoteSeverity.Value, out iconPath);
|
||||
else
|
||||
NoteTypeIcons.TryGetValue(Note.NoteType, out iconPath);
|
||||
|
||||
RemoveChild(_edit);
|
||||
_edit = null;
|
||||
if (iconPath is null)
|
||||
{
|
||||
SeverityRect.Visible = false;
|
||||
Logger.WarningS("admin.notes", $"Could not find an icon for note ID {Note.Id}");
|
||||
}
|
||||
else
|
||||
{
|
||||
SeverityRect.Texture = _sprites.Frame0(new SpriteSpecifier.Texture(new ResPath(iconPath)));
|
||||
}
|
||||
|
||||
_label = new RichTextLabel();
|
||||
_label.SetMessage(Note.Message);
|
||||
TimeLabel.Text = Note.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
ServerLabel.Text = Note.ServerName ?? "Unknown";
|
||||
RoundLabel.Text = Note.Round == null ? "Unknown round" : "Round " + Note.Round;
|
||||
AdminLabel.Text = Note.CreatedByName;
|
||||
PlaytimeLabel.Text = $"{Note.PlaytimeAtNote.TotalHours: 0.0}h";
|
||||
|
||||
AddChild(_label);
|
||||
_label.SetPositionFirst();
|
||||
|
||||
Separator.Visible = true;
|
||||
}
|
||||
|
||||
private void AddLineEdit()
|
||||
{
|
||||
if (_label != null)
|
||||
if (Note.Secret)
|
||||
{
|
||||
RemoveChild(_label);
|
||||
_label = null;
|
||||
SecretSeparator.Visible = true;
|
||||
SecretLabel.Visible = true;
|
||||
}
|
||||
|
||||
_edit = new LineEdit {Text = Note.Message};
|
||||
_edit.OnTextEntered += Submitted;
|
||||
_edit.OnFocusExit += Submitted;
|
||||
if (Note.UnbannedTime is not null)
|
||||
{
|
||||
ExtraLabel.Text = Loc.GetString("admin-notes-unbanned", ("admin", Note.UnbannedByName ?? "[error]"), ("date", Note.UnbannedTime));
|
||||
ExtraLabel.Visible = true;
|
||||
}
|
||||
else if (Note.ExpiryTime is not null)
|
||||
{
|
||||
// Notes should never be visible when expired, bans should
|
||||
if (Note.ExpiryTime.Value > DateTime.UtcNow)
|
||||
{
|
||||
ExpiresLabel.Text = Loc.GetString("admin-note-editor-expiry-label-params",
|
||||
("date", Note.ExpiryTime.Value.ToString("yyyy-MM-dd HH:mm:ss")),
|
||||
("expiresIn", (Note.ExpiryTime.Value - DateTime.UtcNow).ToString("d'd 'hh':'mm")));
|
||||
ExpiresLabel.Modulate = Color.FromHex("#86DC3D");
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpiresLabel.Text = Loc.GetString("admin-note-editor-expiry-label-expired");
|
||||
}
|
||||
ExpiresLabel.Visible = true;
|
||||
}
|
||||
|
||||
AddChild(_edit);
|
||||
_edit.SetPositionFirst();
|
||||
_edit.GrabKeyboardFocus();
|
||||
_edit.CursorPosition = _edit.Text.Length;
|
||||
if (Note.LastEditedAt > Note.CreatedAt)
|
||||
{
|
||||
EditedLabel.Text = Loc.GetString("admin-notes-edited", ("author", Note.EditedByName), ("date", Note.LastEditedAt));
|
||||
EditedLabel.Visible = true;
|
||||
}
|
||||
|
||||
Separator.Visible = false;
|
||||
switch (Note.NoteType)
|
||||
{
|
||||
case NoteType.ServerBan:
|
||||
NoteLabel.SetMessage(FormatBanMessage());
|
||||
break;
|
||||
case NoteType.RoleBan:
|
||||
NoteLabel.SetMessage(FormatRoleBanMessage());
|
||||
break;
|
||||
case NoteType.Note:
|
||||
case NoteType.Watchlist:
|
||||
case NoteType.Message:
|
||||
default:
|
||||
NoteLabel.SetMessage(Note.Message);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Note.Seen == true)
|
||||
{
|
||||
ExtraLabel.Text = Loc.GetString("admin-notes-message-seen");
|
||||
ExtraLabel.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Submitted(LineEditEventArgs args)
|
||||
private string FormatBanMessage()
|
||||
{
|
||||
OnSubmitted?.Invoke(this);
|
||||
var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {Loc.GetString("admin-notes-the-server")} ");
|
||||
return FormatBanMessageCommon(banMessage);
|
||||
}
|
||||
|
||||
AddLabel();
|
||||
private string FormatRoleBanMessage()
|
||||
{
|
||||
var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {string.Join(", ", Note.BannedRoles ?? new []{"unknown"})} ");
|
||||
return FormatBanMessageCommon(banMessage);
|
||||
}
|
||||
|
||||
var note = Note with {Message = args.Text};
|
||||
UpdateNote(note);
|
||||
private string FormatBanMessageCommon(StringBuilder sb)
|
||||
{
|
||||
if (Note.ExpiryTime is null)
|
||||
{
|
||||
sb.Append(Loc.GetString("admin-notes-permanently"));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("for ");
|
||||
var banLength = Note.ExpiryTime.Value - Note.CreatedAt;
|
||||
if (banLength.Days > 0)
|
||||
sb.Append(Loc.GetString("admin-notes-days", ("days", banLength.TotalDays.ToString(".00"))));
|
||||
else if (banLength.Hours > 0)
|
||||
sb.Append(Loc.GetString("admin-notes-hours", ("hours", banLength.TotalHours.ToString(".00"))));
|
||||
else
|
||||
sb.Append(Loc.GetString("admin-notes-minutes", ("minutes", banLength.TotalMinutes.ToString(".00"))));
|
||||
}
|
||||
|
||||
sb.Append(" - ");
|
||||
sb.Append(Note.Message);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
@@ -101,24 +188,7 @@ public sealed partial class AdminNotesLine : BoxContainer
|
||||
public void UpdateNote(SharedAdminNote note)
|
||||
{
|
||||
Note = note;
|
||||
_label?.SetMessage(note.Message);
|
||||
|
||||
if (_edit != null && _edit.Text != note.Message)
|
||||
{
|
||||
_edit.Text = note.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEditable(bool editable)
|
||||
{
|
||||
if (editable)
|
||||
{
|
||||
AddLineEdit();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddLabel();
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -130,13 +200,6 @@ public sealed partial class AdminNotesLine : BoxContainer
|
||||
return;
|
||||
}
|
||||
|
||||
if (_edit != null)
|
||||
{
|
||||
_edit.OnTextEntered -= Submitted;
|
||||
_edit.OnFocusExit -= Submitted;
|
||||
}
|
||||
|
||||
OnSubmitted = null;
|
||||
OnClicked = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
<Popup xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252A"/>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252A" BorderThickness="1" BorderColor="#18181B"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="PlayerNameLabel"/>
|
||||
<Label Name="IdLabel"/>
|
||||
<Label Name="TypeLabel"/>
|
||||
<Label Name="SeverityLabel"/>
|
||||
<Label Name="RoundIdLabel"/>
|
||||
<Label Name="CreatedByLabel"/>
|
||||
<Label Name="CreatedAtLabel"/>
|
||||
<Label Name="EditedByLabel"/>
|
||||
<Label Name="EditedAtLabel"/>
|
||||
<Label Name="ExpiryTimeLabel"/>
|
||||
<TextEdit Name="NoteTextEdit" Editable="False" MinHeight="24" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="EditButton" Text="{Loc admin-notes-edit}"/>
|
||||
<Control HorizontalExpand="True"/>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
@@ -9,57 +11,89 @@ namespace Content.Client.Administration.UI.Notes;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesLinePopup : Popup
|
||||
{
|
||||
public event Action<int>? OnEditPressed;
|
||||
public event Action<int>? OnDeletePressed;
|
||||
public event Action<int, NoteType>? OnEditPressed;
|
||||
public event Action<int, NoteType>? OnDeletePressed;
|
||||
|
||||
public AdminNotesLinePopup(SharedAdminNote note, bool showDelete, bool showEdit)
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public AdminNotesLinePopup(SharedAdminNote note, string playerName, bool showDelete, bool showEdit)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
NoteId = note.Id;
|
||||
NoteType = note.NoteType;
|
||||
DeleteButton.Visible = showDelete;
|
||||
EditButton.Visible = showEdit;
|
||||
|
||||
UserInterfaceManager.ModalRoot.AddChild(this);
|
||||
|
||||
PlayerNameLabel.Text = Loc.GetString("admin-notes-for", ("player", playerName));
|
||||
IdLabel.Text = Loc.GetString("admin-notes-id", ("id", note.Id));
|
||||
TypeLabel.Text = Loc.GetString("admin-notes-type", ("type", note.NoteType));
|
||||
SeverityLabel.Text = Loc.GetString("admin-notes-severity", ("severity", note.NoteSeverity ?? NoteSeverity.None));
|
||||
RoundIdLabel.Text = note.Round == null
|
||||
? Loc.GetString("admin-notes-round-id-unknown")
|
||||
: Loc.GetString("admin-notes-round-id", ("id", note.Round));
|
||||
CreatedByLabel.Text = Loc.GetString("admin-notes-created-by", ("author", note.CreatedByName));
|
||||
CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToString("dd MMM yyyy HH:mm:ss")));
|
||||
CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")));
|
||||
EditedByLabel.Text = Loc.GetString("admin-notes-last-edited-by", ("author", note.EditedByName));
|
||||
EditedAtLabel.Text = Loc.GetString("admin-notes-last-edited-at", ("date", note.LastEditedAt.ToString("dd MMM yyyy HH:mm:ss")));
|
||||
EditedAtLabel.Text = Loc.GetString("admin-notes-last-edited-at", ("date", note.LastEditedAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? Loc.GetString("admin-notes-edited-never")));
|
||||
ExpiryTimeLabel.Text = note.ExpiryTime == null
|
||||
? Loc.GetString("admin-notes-expires-never")
|
||||
: Loc.GetString("admin-notes-expires", ("expires", note.ExpiryTime.Value.ToString("yyyy-MM-dd HH:mm:ss")));
|
||||
NoteTextEdit.InsertAtCursor(note.Message);
|
||||
|
||||
if (note.NoteType is NoteType.ServerBan or NoteType.RoleBan)
|
||||
{
|
||||
DeleteButton.Text = Loc.GetString("admin-notes-hide");
|
||||
}
|
||||
|
||||
EditButton.OnPressed += EditPressed;
|
||||
DeleteButton.OnPressed += DeletePressed;
|
||||
}
|
||||
|
||||
private int NoteId { get; }
|
||||
private bool ConfirmingDelete { get; set; }
|
||||
private NoteType NoteType { get; }
|
||||
private TimeSpan? DeleteResetOn { get; set; }
|
||||
|
||||
private void EditPressed(ButtonEventArgs args)
|
||||
{
|
||||
OnEditPressed?.Invoke(NoteId);
|
||||
OnEditPressed?.Invoke(NoteId, NoteType);
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DeletePressed(ButtonEventArgs args)
|
||||
{
|
||||
if (!ConfirmingDelete)
|
||||
if (DeleteResetOn is null)
|
||||
{
|
||||
ConfirmingDelete = true;
|
||||
DeleteResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
|
||||
DeleteButton.Text = Loc.GetString("admin-notes-delete-confirm");
|
||||
DeleteButton.ModulateSelfOverride = Color.Red;
|
||||
return;
|
||||
}
|
||||
|
||||
ConfirmingDelete = false;
|
||||
DeleteButton.ModulateSelfOverride = null;
|
||||
OnDeletePressed?.Invoke(NoteId);
|
||||
ResetDeleteButton();
|
||||
OnDeletePressed?.Invoke(NoteId, NoteType);
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
// This checks for null for free, do not invert it as null always produces a false value
|
||||
if (DeleteResetOn < _gameTiming.CurTime)
|
||||
{
|
||||
ResetDeleteButton();
|
||||
DeleteResetOn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetDeleteButton()
|
||||
{
|
||||
DeleteButton.Text = Loc.GetString("admin-notes-delete");
|
||||
DeleteButton.ModulateSelfOverride = null;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:notes="clr-namespace:Content.Client.Administration.UI.Notes"
|
||||
SetSize="400 400">
|
||||
<notes:AdminNotesControl Name="Notes" Access="Public"/>
|
||||
</DefaultWindow>
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="600 400"
|
||||
Title="Loading...">
|
||||
<notes:AdminNotesControl Name="Notes" Access="Public" Margin="4"/>
|
||||
</ui:FancyWindow>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminNotesWindow : DefaultWindow
|
||||
public sealed partial class AdminNotesWindow : FancyWindow
|
||||
{
|
||||
public AdminNotesWindow()
|
||||
{
|
||||
|
||||
22
Content.Client/Administration/UI/Notes/NoteEdit.xaml
Normal file
22
Content.Client/Administration/UI/Notes/NoteEdit.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="Loading..."
|
||||
MinSize="400 200">
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<TextEdit Name="NoteTextEdit" HorizontalExpand="True" VerticalExpand="True" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
|
||||
Visible="False" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<OptionButton Name="TypeOption" HorizontalAlignment="Center" />
|
||||
<OptionButton Name="SeverityOption" HorizontalAlignment="Center" />
|
||||
<CheckBox Name="SecretCheckBox" Text="{Loc admin-note-editor-secret}"
|
||||
ToolTip="{Loc admin-note-editor-secret-tooltip}" />
|
||||
<CheckBox Name="PermanentCheckBox" Pressed="True" Text="{Loc admin-note-editor-expiry-checkbox}"
|
||||
ToolTip="{Loc admin-note-editor-expiry-checkbox-tooltip}" />
|
||||
<Button Name="SubmitButton" Text="{Loc admin-note-editor-submit}" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
242
Content.Client/Administration/UI/Notes/NoteEdit.xaml.cs
Normal file
242
Content.Client/Administration/UI/Notes/NoteEdit.xaml.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Database;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.Notes;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class NoteEdit : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
|
||||
|
||||
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
PlayerName = playerName;
|
||||
Title = Loc.GetString("admin-note-editor-title-new", ("player", PlayerName));
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
|
||||
TypeOption.OnItemSelected += OnTypeChanged;
|
||||
|
||||
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) Shared.Database.NoteSeverity.None);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) Shared.Database.NoteSeverity.Minor);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) Shared.Database.NoteSeverity.Medium);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-high"), (int) Shared.Database.NoteSeverity.High);
|
||||
SeverityOption.OnItemSelected += OnSeverityChanged;
|
||||
|
||||
PermanentCheckBox.OnPressed += OnPermanentPressed;
|
||||
SecretCheckBox.OnPressed += OnSecretPressed;
|
||||
SubmitButton.OnPressed += OnSubmitButtonPressed;
|
||||
|
||||
if (note is null && !canCreate)
|
||||
{
|
||||
SubmitButton.Disabled = true;
|
||||
TypeOption.Disabled = true;
|
||||
SeverityOption.Disabled = true;
|
||||
}
|
||||
|
||||
if (note is not null)
|
||||
{
|
||||
Title = Loc.GetString("admin-note-editor-title-existing", ("id", note.Id), ("player", PlayerName), ("author", note.CreatedByName));
|
||||
NoteId = note.Id;
|
||||
|
||||
NoteType = note.NoteType;
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-server-ban"), (int) NoteType.ServerBan);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-role-ban"), (int) NoteType.RoleBan);
|
||||
TypeOption.SelectId((int)NoteType);
|
||||
TypeOption.Disabled = true;
|
||||
|
||||
NoteTextEdit.InsertAtCursor(note.Message);
|
||||
|
||||
NoteSeverity = note.NoteSeverity ?? Shared.Database.NoteSeverity.Minor;
|
||||
SeverityOption.SelectId((int)NoteSeverity);
|
||||
SeverityOption.Disabled = note.NoteType is not (NoteType.Note or NoteType.ServerBan or NoteType.RoleBan);
|
||||
|
||||
IsSecret = note.Secret;
|
||||
SecretCheckBox.Pressed = note.Secret;
|
||||
SecretCheckBox.Disabled = note.NoteType is not NoteType.Note;
|
||||
ExpiryTime = note.ExpiryTime;
|
||||
if (ExpiryTime is not null)
|
||||
{
|
||||
PermanentCheckBox.Pressed = false;
|
||||
UpdatePermanentCheckboxFields();
|
||||
ExpiryLineEdit.Text = ExpiryTime.Value.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
if (!canEdit)
|
||||
{
|
||||
SubmitButton.Disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string PlayerName { get; }
|
||||
private int NoteId { get; }
|
||||
private bool IsSecret { get; set; }
|
||||
private NoteType NoteType { get; set; }
|
||||
private NoteSeverity? NoteSeverity { get; set; } = Shared.Database.NoteSeverity.None;
|
||||
private DateTime? ExpiryTime { get; set; }
|
||||
private TimeSpan? DeleteResetOn { get; set; }
|
||||
|
||||
private void OnTypeChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
// We should be resetting the underlying values too but the server handles that anyway
|
||||
switch (args.Id)
|
||||
{
|
||||
case (int) NoteType.Note: // Note: your standard note, does nothing special
|
||||
NoteType = NoteType.Note;
|
||||
SecretCheckBox.Disabled = false;
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = false;
|
||||
PermanentCheckBox.Pressed = true;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Message: // Message: these are shown to the player when they log on
|
||||
NoteType = NoteType.Message;
|
||||
SecretCheckBox.Disabled = true;
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = true;
|
||||
SeverityOption.SelectId((int) Shared.Database.NoteSeverity.None);
|
||||
NoteSeverity = null;
|
||||
PermanentCheckBox.Pressed = false;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Watchlist: // Watchlist: these are always secret and only shown to admins when the player logs on
|
||||
NoteType = NoteType.Watchlist;
|
||||
SecretCheckBox.Disabled = true;
|
||||
SecretCheckBox.Pressed = true;
|
||||
SeverityOption.Disabled = true;
|
||||
SeverityOption.SelectId((int) Shared.Database.NoteSeverity.None);
|
||||
NoteSeverity = null;
|
||||
PermanentCheckBox.Pressed = false;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
default: // Wuh oh
|
||||
throw new ArgumentOutOfRangeException(nameof(args.Id), args.Id, "Unknown note type");
|
||||
}
|
||||
|
||||
TypeOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnPermanentPressed(BaseButton.ButtonEventArgs _)
|
||||
{
|
||||
UpdatePermanentCheckboxFields();
|
||||
}
|
||||
|
||||
private void UpdatePermanentCheckboxFields()
|
||||
{
|
||||
ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
|
||||
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
|
||||
}
|
||||
|
||||
private void OnSecretPressed(BaseButton.ButtonEventArgs _)
|
||||
{
|
||||
IsSecret = SecretCheckBox.Pressed;
|
||||
}
|
||||
|
||||
private void OnSeverityChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
NoteSeverity = (NoteSeverity) args.Id;
|
||||
SeverityOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (!ParseExpiryTime())
|
||||
return;
|
||||
if (DeleteResetOn is null)
|
||||
{
|
||||
DeleteResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
|
||||
SubmitButton.Text = Loc.GetString("admin-note-editor-submit-confirm");
|
||||
SubmitButton.ModulateSelfOverride = Color.Red;
|
||||
// Task.Delay(3000).ContinueWith(_ => ResetSubmitButton()); // TODO: fix
|
||||
return;
|
||||
}
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
SubmitPressed?.Invoke(NoteId, NoteType, Rope.Collapse(NoteTextEdit.TextRope), NoteSeverity, IsSecret, ExpiryTime);
|
||||
|
||||
if (Parent is null)
|
||||
{
|
||||
_console.ExecuteCommand($"adminnotes \"{PlayerName}\"");
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
// This checks for null for free, do not invert it as null always produces a false value
|
||||
if (DeleteResetOn > _gameTiming.CurTime)
|
||||
{
|
||||
ResetSubmitButton();
|
||||
DeleteResetOn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetSubmitButton()
|
||||
{
|
||||
SubmitButton.Text = Loc.GetString("admin-note-editor-submit");
|
||||
SubmitButton.ModulateSelfOverride = null;
|
||||
UpdateDraw();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the currently entered expiry time. As a side effect this function
|
||||
/// will colour its respective line edit to indicate an error
|
||||
/// </summary>
|
||||
/// <returns>True if parsing was successful, false if not</returns>
|
||||
private bool ParseExpiryTime()
|
||||
{
|
||||
// If the checkbox is pressed the note is permanent, so expiry is null
|
||||
if (PermanentCheckBox.Pressed)
|
||||
{
|
||||
ExpiryTime = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !DateTime.TryParse(ExpiryLineEdit.Text, out var result) || DateTime.UtcNow > result)
|
||||
{
|
||||
ExpiryLineEdit.ModulateSelfOverride = Color.Red;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpiryTime = result;
|
||||
ExpiryLineEdit.ModulateSelfOverride = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PermanentCheckBox.OnPressed -= OnPermanentPressed;
|
||||
SecretCheckBox.OnPressed -= OnSecretPressed;
|
||||
SubmitButton.OnPressed -= OnSubmitButtonPressed;
|
||||
|
||||
SubmitPressed = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user