Improve admin message seen/dismiss state. (#26223)
Fixes #26211 Admin messages now have separate "seen" and "dismissed" fields. The idea is that an admin should be able to tell whether a user pressed the "dismiss for now" button. Instead of using "seen" as "show this message to players when they join", "dismissed" is now used for this. Existing notes in the database will automatically be marked as dismissed on migration. A note cannot be dismissed without being seen (enforced via constraint in the database too, aren't I fancy). As part of this, it has become impossible for a player to play without dismissing the message in some form. Instead of a shitty popup window, the popup is now a fullscreen overlay that blocks clicks behind it, making the game unplayable. Also, if a user somehow has multiple messages they will be combined into one popup. Also I had enough respect for the codebase to make it look better and clean up the code somewhat. Yippee.
This commit is contained in:
committed by
GitHub
parent
f87480dd36
commit
d776c4b392
@@ -2,6 +2,7 @@ using Content.Client.Eui;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.Eui;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Content.Shared.Administration.Notes.AdminMessageEuiMsg;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
@@ -14,9 +15,8 @@ public sealed class AdminMessageEui : BaseEui
|
||||
public AdminMessageEui()
|
||||
{
|
||||
_popup = new AdminMessagePopupWindow();
|
||||
_popup.OnAcceptPressed += () => SendMessage(new Accept());
|
||||
_popup.OnDismissPressed += () => SendMessage(new Dismiss());
|
||||
_popup.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||
_popup.OnAcceptPressed += () => SendMessage(new Dismiss(true));
|
||||
_popup.OnDismissPressed += () => SendMessage(new Dismiss(false));
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
@@ -26,13 +26,17 @@ public sealed class AdminMessageEui : BaseEui
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.SetMessage(s.Message);
|
||||
_popup.SetDetails(s.AdminName, s.AddedOn);
|
||||
_popup.Timer = s.Time;
|
||||
_popup.SetState(s);
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
_popup.OpenCentered();
|
||||
_popup.UserInterfaceManager.WindowRoot.AddChild(_popup);
|
||||
LayoutContainer.SetAnchorPreset(_popup, LayoutContainer.LayoutPreset.Wide);
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
_popup.Orphan();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<Control xmlns="https://spacestation14.io" Margin="0 0 0 8">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<RichTextLabel Name="Admin" Margin="0 0 0 4" />
|
||||
<RichTextLabel Name="Message" Margin="2 0 0 0" />
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminMessagePopupMessage : Control
|
||||
{
|
||||
public AdminMessagePopupMessage(AdminMessageEuiState.Message message)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString(
|
||||
"admin-notes-message-admin",
|
||||
("admin", message.AdminName),
|
||||
("date", message.AddedOn.ToLocalTime()))));
|
||||
|
||||
Message.SetMessage(message.Text);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,36 @@
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
VerticalExpand="True" HorizontalExpand="True"
|
||||
Title="{Loc admin-notes-message-window-title}"
|
||||
MinSize="600 170">
|
||||
<PanelContainer VerticalExpand="True" HorizontalExpand="True" StyleClasses="BackgroundDark">
|
||||
<ScrollContainer HScrollEnabled="False" VerticalExpand="True" HorizontalExpand="True" Margin="4">
|
||||
<BoxContainer Orientation="Vertical" SeparationOverride="10" VerticalAlignment="Bottom">
|
||||
<Label Name="AdminLabel" Text="Loading..." />
|
||||
<RichTextLabel Name="MessageLabel" />
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer MouseFilter="Stop">
|
||||
<PanelContainer.PanelOverride>
|
||||
<!-- semi-transparent background -->
|
||||
<gfx:StyleBoxFlat BackgroundColor="#000000AA" />
|
||||
</PanelContainer.PanelOverride>
|
||||
|
||||
<Control HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="600">
|
||||
<PanelContainer StyleClasses="AngleRect" />
|
||||
|
||||
<BoxContainer Orientation="Vertical" Margin="4">
|
||||
<RichTextLabel Name="Description" />
|
||||
|
||||
<!-- Contains actual messages -->
|
||||
<ScrollContainer HScrollEnabled="False" Margin="4" VerticalExpand="True" ReturnMeasure="True" MaxHeight="400">
|
||||
<BoxContainer Orientation="Vertical" Name="MessageContainer" Margin="0 2 0 0" />
|
||||
</ScrollContainer>
|
||||
|
||||
<Label Name="WaitLabel" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="DismissButton"
|
||||
Text="{Loc 'admin-notes-message-dismiss'}" />
|
||||
Text="{Loc 'admin-notes-message-dismiss'}"
|
||||
Disabled="True"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenRight" />
|
||||
<Button Name="AcceptButton"
|
||||
Text="{Loc 'admin-notes-message-accept'}"
|
||||
Disabled="True" />
|
||||
Disabled="True"
|
||||
HorizontalExpand="True"
|
||||
StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</Control>
|
||||
</PanelContainer>
|
||||
</ui:FancyWindow>
|
||||
</Control>
|
||||
|
||||
@@ -1,56 +1,65 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Administration.UI.AdminRemarks;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AdminMessagePopupWindow : FancyWindow
|
||||
public sealed partial class AdminMessagePopupWindow : Control
|
||||
{
|
||||
private float _timer = float.MaxValue;
|
||||
public float Timer
|
||||
{
|
||||
get => _timer;
|
||||
set
|
||||
{
|
||||
WaitLabel.Text = Loc.GetString("admin-notes-message-wait", ("time", MathF.Floor(value)));
|
||||
_timer = value;
|
||||
}
|
||||
}
|
||||
|
||||
public event Action? OnDismissPressed;
|
||||
|
||||
public event Action? OnAcceptPressed;
|
||||
|
||||
public AdminMessagePopupWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
|
||||
|
||||
AcceptButton.OnPressed += OnAcceptButtonPressed;
|
||||
DismissButton.OnPressed += OnDismissButtonPressed;
|
||||
}
|
||||
|
||||
public void SetMessage(string message)
|
||||
public float Timer
|
||||
{
|
||||
MessageLabel.SetMessage(message);
|
||||
get => _timer;
|
||||
private set
|
||||
{
|
||||
WaitLabel.Text = Loc.GetString("admin-notes-message-wait", ("time", MathF.Floor(value)));
|
||||
_timer = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDetails(string adminName, DateTime addedOn)
|
||||
public void SetState(AdminMessageEuiState state)
|
||||
{
|
||||
AdminLabel.Text = Loc.GetString("admin-notes-message-admin", ("admin", adminName), ("date", addedOn));
|
||||
Timer = (float) state.Time.TotalSeconds;
|
||||
|
||||
MessageContainer.RemoveAllChildren();
|
||||
|
||||
foreach (var message in state.Messages)
|
||||
{
|
||||
MessageContainer.AddChild(new AdminMessagePopupMessage(message));
|
||||
}
|
||||
|
||||
Description.SetMessage(FormattedMessage.FromMarkup(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
|
||||
}
|
||||
|
||||
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
OnDismissPressed?.Invoke();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnAcceptButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
OnAcceptPressed?.Invoke();
|
||||
Close();
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
@@ -70,6 +79,7 @@ public sealed partial class AdminMessagePopupWindow : FancyWindow
|
||||
else
|
||||
{
|
||||
AcceptButton.Disabled = false;
|
||||
DismissButton.Disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1768
Content.Server.Database/Migrations/Postgres/20240318022005_AdminMessageDismiss.Designer.cs
generated
Normal file
1768
Content.Server.Database/Migrations/Postgres/20240318022005_AdminMessageDismiss.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AdminMessageDismiss : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "dismissed",
|
||||
table: "admin_messages",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.Sql("UPDATE admin_messages SET dismissed = seen");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "NotDismissedAndSeen",
|
||||
table: "admin_messages",
|
||||
sql: "NOT dismissed OR seen");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "NotDismissedAndSeen",
|
||||
table: "admin_messages");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "dismissed",
|
||||
table: "admin_messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,6 +183,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<bool>("Dismissed")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("dismissed");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_time");
|
||||
@@ -232,7 +236,10 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_messages_round_id");
|
||||
|
||||
b.ToTable("admin_messages", (string)null);
|
||||
b.ToTable("admin_messages", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
|
||||
1699
Content.Server.Database/Migrations/Sqlite/20240318021959_AdminMessageDismiss.Designer.cs
generated
Normal file
1699
Content.Server.Database/Migrations/Sqlite/20240318021959_AdminMessageDismiss.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AdminMessageDismiss : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "dismissed",
|
||||
table: "admin_messages",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.Sql("UPDATE admin_messages SET dismissed = seen");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "NotDismissedAndSeen",
|
||||
table: "admin_messages",
|
||||
sql: "NOT dismissed OR seen");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "NotDismissedAndSeen",
|
||||
table: "admin_messages");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "dismissed",
|
||||
table: "admin_messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,6 +166,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("deleted_by_id");
|
||||
|
||||
b.Property<bool>("Dismissed")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("dismissed");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("expiration_time");
|
||||
@@ -215,7 +219,10 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_messages_round_id");
|
||||
|
||||
b.ToTable("admin_messages", (string)null);
|
||||
b.ToTable("admin_messages", null, t =>
|
||||
{
|
||||
t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
|
||||
|
||||
@@ -268,6 +268,11 @@ namespace Content.Server.Database
|
||||
.HasPrincipalKey(author => author.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
// A message cannot be "dismissed" without also being "seen".
|
||||
modelBuilder.Entity<AdminMessage>().ToTable(t =>
|
||||
t.HasCheckConstraint("NotDismissedAndSeen",
|
||||
"NOT dismissed OR seen"));
|
||||
|
||||
modelBuilder.Entity<ServerBan>()
|
||||
.HasOne(ban => ban.CreatedBy)
|
||||
.WithMany(author => author.AdminServerBansCreated)
|
||||
@@ -969,6 +974,15 @@ namespace Content.Server.Database
|
||||
[ForeignKey("DeletedBy")] public Guid? DeletedById { get; set; }
|
||||
public Player? DeletedBy { get; set; }
|
||||
public DateTime? DeletedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the message has been seen at least once by the player.
|
||||
/// </summary>
|
||||
public bool Seen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the message has been dismissed permanently by the player.
|
||||
/// </summary>
|
||||
public bool Dismissed { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Database;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration.Notes;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Timing;
|
||||
using static Content.Shared.Administration.Notes.AdminMessageEuiMsg;
|
||||
|
||||
namespace Content.Server.Administration.Notes;
|
||||
@@ -12,32 +14,33 @@ public sealed class AdminMessageEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IAdminNotesManager _notesMan = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
private readonly float _closeWait;
|
||||
private AdminMessageRecord? _message;
|
||||
private DateTime _startTime;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public AdminMessageEui()
|
||||
private readonly TimeSpan _closeWait;
|
||||
private readonly TimeSpan _endTime;
|
||||
private readonly AdminMessageRecord[] _messages;
|
||||
|
||||
public AdminMessageEui(AdminMessageRecord[] messages)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_closeWait = _cfg.GetCVar(CCVars.MessageWaitTime);
|
||||
_closeWait = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.MessageWaitTime));
|
||||
_endTime = _gameTiming.RealTime + _closeWait;
|
||||
_messages = messages;
|
||||
}
|
||||
|
||||
public void SetMessage(AdminMessageRecord message)
|
||||
public override void Opened()
|
||||
{
|
||||
_message = message;
|
||||
_startTime = DateTime.UtcNow;
|
||||
StateDirty();
|
||||
}
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
if (_message == null)
|
||||
return new AdminMessageEuiState(float.MaxValue, "An error has occurred.", string.Empty, DateTime.MinValue);
|
||||
return new AdminMessageEuiState(
|
||||
_closeWait,
|
||||
_message.Message,
|
||||
_message.CreatedBy?.LastSeenUserName ?? "[System]",
|
||||
_message.CreatedAt.UtcDateTime
|
||||
_messages.Select(x => new AdminMessageEuiState.Message(
|
||||
x.Message,
|
||||
x.CreatedBy?.LastSeenUserName ?? Loc.GetString("admin-notes-fallback-admin-name"),
|
||||
x.CreatedAt.UtcDateTime)).ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,15 +50,14 @@ public sealed class AdminMessageEui : BaseEui
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case Accept:
|
||||
if (_message == null)
|
||||
break;
|
||||
// No escape
|
||||
if (DateTime.UtcNow - _startTime >= TimeSpan.FromSeconds(_closeWait))
|
||||
await _notesMan.MarkMessageAsSeen(_message.Id);
|
||||
Close();
|
||||
break;
|
||||
case Dismiss:
|
||||
case Dismiss dismiss:
|
||||
if (_gameTiming.RealTime < _endTime)
|
||||
return;
|
||||
|
||||
foreach (var message in _messages)
|
||||
{
|
||||
await _notesMan.MarkMessageAsSeen(message.Id, dismiss.Permanent);
|
||||
}
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -331,9 +331,9 @@ public sealed class AdminNotesManager : IAdminNotesManager, IPostInjectInit
|
||||
return await _db.GetMessages(player);
|
||||
}
|
||||
|
||||
public async Task MarkMessageAsSeen(int id)
|
||||
public async Task MarkMessageAsSeen(int id, bool dismissedToo)
|
||||
{
|
||||
await _db.MarkMessageAsSeen(id);
|
||||
await _db.MarkMessageAsSeen(id, dismissedToo);
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.EUI;
|
||||
@@ -52,7 +53,7 @@ public sealed class AdminNotesSystem : EntitySystem
|
||||
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus != SessionStatus.Connected)
|
||||
if (e.NewStatus != SessionStatus.InGame)
|
||||
return;
|
||||
|
||||
var messages = await _notes.GetNewMessages(e.Session.UserId);
|
||||
@@ -69,19 +70,11 @@ public sealed class AdminNotesSystem : EntitySystem
|
||||
_chat.SendAdminAlert(Loc.GetString("admin-notes-watchlist", ("player", username), ("message", watchlist.Message)));
|
||||
}
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
var messageString = Loc.GetString("admin-notes-new-message", ("admin", message.CreatedBy?.LastSeenUserName ?? "[System]"), ("message", message.Message));
|
||||
// Only open the popup if the user hasn't seen it yet
|
||||
if (!message.Seen)
|
||||
{
|
||||
var ui = new AdminMessageEui();
|
||||
_euis.OpenEui(ui, e.Session);
|
||||
ui.SetMessage(message);
|
||||
var messagesToShow = messages.OrderBy(x => x.CreatedAt).Where(x => !x.Dismissed).ToArray();
|
||||
if (messagesToShow.Length == 0)
|
||||
return;
|
||||
|
||||
// Only send the message if they haven't seen it yet
|
||||
_chat.DispatchServerMessage(e.Session, messageString);
|
||||
}
|
||||
}
|
||||
var ui = new AdminMessageEui(messagesToShow);
|
||||
_euis.OpenEui(ui, e.Session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,5 +45,13 @@ public interface IAdminNotesManager
|
||||
/// <param name="player">Desired player's <see cref="Guid"/></param>
|
||||
/// <returns>All unread messages</returns>
|
||||
Task<List<AdminMessageRecord>> GetNewMessages(Guid player);
|
||||
Task MarkMessageAsSeen(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Mark an admin message as being seen by the target player.
|
||||
/// </summary>
|
||||
/// <param name="id">The database ID of the admin message.</param>
|
||||
/// <param name="dismissedToo">
|
||||
/// If true, the message is "permanently dismissed" and will not be shown to the player again when they join.
|
||||
/// </param>
|
||||
Task MarkMessageAsSeen(int id, bool dismissedToo);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,8 @@ public sealed record AdminMessageRecord(
|
||||
bool Deleted,
|
||||
PlayerRecord? DeletedBy,
|
||||
DateTimeOffset? DeletedAt,
|
||||
bool Seen) : IAdminRemarksRecord;
|
||||
bool Seen,
|
||||
bool Dismissed) : IAdminRemarksRecord;
|
||||
|
||||
|
||||
public sealed record PlayerRecord(
|
||||
|
||||
@@ -1143,7 +1143,8 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
||||
entity.Deleted,
|
||||
MakePlayerRecord(entity.DeletedBy),
|
||||
NormalizeDatabaseTime(entity.DeletedAt),
|
||||
entity.Seen);
|
||||
entity.Seen,
|
||||
entity.Dismissed);
|
||||
}
|
||||
|
||||
public async Task<ServerBanNoteRecord?> GetServerBanAsNoteAsync(int id)
|
||||
@@ -1422,11 +1423,13 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
||||
return entities.Select(MakeAdminMessageRecord).ToList();
|
||||
}
|
||||
|
||||
public async Task MarkMessageAsSeen(int id)
|
||||
public async Task MarkMessageAsSeen(int id, bool dismissedToo)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
var message = await db.DbContext.AdminMessages.SingleAsync(m => m.Id == id);
|
||||
message.Seen = true;
|
||||
if (dismissedToo)
|
||||
message.Dismissed = true;
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -274,7 +274,15 @@ namespace Content.Server.Database
|
||||
Task DeleteAdminMessage(int id, Guid deletedBy, DateTimeOffset deletedAt);
|
||||
Task HideServerBanFromNotes(int id, Guid deletedBy, DateTimeOffset deletedAt);
|
||||
Task HideServerRoleBanFromNotes(int id, Guid deletedBy, DateTimeOffset deletedAt);
|
||||
Task MarkMessageAsSeen(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Mark an admin message as being seen by the target player.
|
||||
/// </summary>
|
||||
/// <param name="id">The database ID of the admin message.</param>
|
||||
/// <param name="dismissedToo">
|
||||
/// If true, the message is "permanently dismissed" and will not be shown to the player again when they join.
|
||||
/// </param>
|
||||
Task MarkMessageAsSeen(int id, bool dismissedToo);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -847,10 +855,10 @@ namespace Content.Server.Database
|
||||
return RunDbCommand(() => _db.HideServerRoleBanFromNotes(id, deletedBy, deletedAt));
|
||||
}
|
||||
|
||||
public Task MarkMessageAsSeen(int id)
|
||||
public Task MarkMessageAsSeen(int id, bool dismissedToo)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.MarkMessageAsSeen(id));
|
||||
return RunDbCommand(() => _db.MarkMessageAsSeen(id, dismissedToo));
|
||||
}
|
||||
|
||||
// Wrapper functions to run DB commands from the thread pool.
|
||||
|
||||
@@ -1,39 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Eui;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Administration.Notes;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class AdminMessageEuiState : EuiStateBase
|
||||
public sealed class AdminMessageEuiState(TimeSpan time, AdminMessageEuiState.Message[] messages) : EuiStateBase
|
||||
{
|
||||
public float Time { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string AdminName { get; set; }
|
||||
public DateTime AddedOn { get; set; }
|
||||
public TimeSpan Time { get; } = time;
|
||||
public Message[] Messages { get; } = messages;
|
||||
|
||||
public AdminMessageEuiState(float time, string message, string adminName, DateTime addedOn)
|
||||
[Serializable]
|
||||
public sealed class Message(string text, string adminName, DateTime addedOn)
|
||||
{
|
||||
Message = message;
|
||||
Time = time;
|
||||
AdminName = adminName;
|
||||
AddedOn = addedOn;
|
||||
public string Text = text;
|
||||
public string AdminName = adminName;
|
||||
public DateTime AddedOn = addedOn;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdminMessageEuiMsg
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class Accept : EuiMessageBase
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class Dismiss : EuiMessageBase
|
||||
public sealed class Dismiss(bool permanent) : EuiMessageBase
|
||||
{
|
||||
public bool Permanent { get; } = permanent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,11 @@ admin-notes-hide = Hide
|
||||
admin-notes-delete-confirm = Confirm delete
|
||||
admin-notes-edited = Last edit by {$author} on {$date}
|
||||
admin-notes-unbanned = Unbanned by {$admin} on {$date}
|
||||
admin-notes-message-window-title = Alert!
|
||||
admin-notes-message-admin = New message from {$admin}, added on {$date}
|
||||
admin-notes-message-desc = [color=white]You have received { $count ->
|
||||
[1] an administrative message
|
||||
*[other] administrative messages
|
||||
} since the last time you played on this server.[/color]
|
||||
admin-notes-message-admin = From [bold]{ $admin }[/bold], written on { TOSTRING($date, "f") }:
|
||||
admin-notes-message-wait = The accept button will be enabled after {$time} seconds.
|
||||
admin-notes-message-accept = Dismiss permanently
|
||||
admin-notes-message-dismiss = Dismiss for now
|
||||
@@ -68,6 +71,7 @@ admin-notes-verb-text = Open Admin Notes
|
||||
# Watchlist and message login
|
||||
admin-notes-watchlist = Watchlist for {$player}: {$message}
|
||||
admin-notes-new-message = You've received an admin message from {$admin}: {$message}
|
||||
admin-notes-fallback-admin-name = [System]
|
||||
|
||||
# Admin remarks
|
||||
admin-remarks-command-description = Opens the admin remarks page
|
||||
|
||||
Reference in New Issue
Block a user