diff --git a/Content.Client/CartridgeLoader/Cartridges/NewsReadUi.cs b/Content.Client/CartridgeLoader/Cartridges/NewsReadUi.cs
deleted file mode 100644
index 6874e9644d..0000000000
--- a/Content.Client/CartridgeLoader/Cartridges/NewsReadUi.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Content.Client.UserInterface.Fragments;
-using Content.Shared.CartridgeLoader.Cartridges;
-using Content.Shared.CartridgeLoader;
-using Robust.Client.GameObjects;
-using Robust.Client.UserInterface;
-
-namespace Content.Client.CartridgeLoader.Cartridges;
-
-public sealed partial class NewsReadUi : UIFragment
-{
- private NewsReadUiFragment? _fragment;
-
- public override Control GetUIFragmentRoot()
- {
- return _fragment!;
- }
-
- public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
- {
- _fragment = new NewsReadUiFragment();
-
- _fragment.OnNextButtonPressed += () =>
- {
- SendNewsReadMessage(NewsReadUiAction.Next, userInterface);
- };
- _fragment.OnPrevButtonPressed += () =>
- {
- SendNewsReadMessage(NewsReadUiAction.Prev, userInterface);
- };
- _fragment.OnNotificationSwithPressed += () =>
- {
- SendNewsReadMessage(NewsReadUiAction.NotificationSwith, userInterface);
- };
- }
-
- public override void UpdateState(BoundUserInterfaceState state)
- {
- if (state is NewsReadBoundUserInterfaceState cast)
- _fragment?.UpdateState(cast.Article, cast.TargetNum, cast.TotalNum, cast.NotificationOn);
- else if (state is NewsReadEmptyBoundUserInterfaceState empty)
- _fragment?.UpdateEmptyState(empty.NotificationOn);
- }
-
- private void SendNewsReadMessage(NewsReadUiAction action, BoundUserInterface userInterface)
- {
- var newsMessage = new NewsReadUiMessageEvent(action);
- var message = new CartridgeUiMessage(newsMessage);
- userInterface.SendMessage(message);
- }
-}
diff --git a/Content.Client/CartridgeLoader/Cartridges/NewsReaderUi.cs b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUi.cs
new file mode 100644
index 0000000000..8ad665d361
--- /dev/null
+++ b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUi.cs
@@ -0,0 +1,54 @@
+using Content.Client.UserInterface.Fragments;
+using Content.Shared.CartridgeLoader.Cartridges;
+using Content.Shared.CartridgeLoader;
+using Robust.Client.UserInterface;
+
+namespace Content.Client.CartridgeLoader.Cartridges;
+
+public sealed partial class NewsReaderUi : UIFragment
+{
+ private NewsReaderUiFragment? _fragment;
+
+ public override Control GetUIFragmentRoot()
+ {
+ return _fragment!;
+ }
+
+ public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
+ {
+ _fragment = new NewsReaderUiFragment();
+
+ _fragment.OnNextButtonPressed += () =>
+ {
+ SendNewsReaderMessage(NewsReaderUiAction.Next, userInterface);
+ };
+ _fragment.OnPrevButtonPressed += () =>
+ {
+ SendNewsReaderMessage(NewsReaderUiAction.Prev, userInterface);
+ };
+ _fragment.OnNotificationSwithPressed += () =>
+ {
+ SendNewsReaderMessage(NewsReaderUiAction.NotificationSwitch, userInterface);
+ };
+ }
+
+ public override void UpdateState(BoundUserInterfaceState state)
+ {
+ switch (state)
+ {
+ case NewsReaderBoundUserInterfaceState cast:
+ _fragment?.UpdateState(cast.Article, cast.TargetNum, cast.TotalNum, cast.NotificationOn);
+ break;
+ case NewsReaderEmptyBoundUserInterfaceState empty:
+ _fragment?.UpdateEmptyState(empty.NotificationOn);
+ break;
+ }
+ }
+
+ private void SendNewsReaderMessage(NewsReaderUiAction action, BoundUserInterface userInterface)
+ {
+ var newsMessage = new NewsReaderUiMessageEvent(action);
+ var message = new CartridgeUiMessage(newsMessage);
+ userInterface.SendMessage(message);
+ }
+}
diff --git a/Content.Client/CartridgeLoader/Cartridges/NewsReadUiFragment.xaml b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml
similarity index 70%
rename from Content.Client/CartridgeLoader/Cartridges/NewsReadUiFragment.xaml
rename to Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml
index 7431713ea8..bd5879408e 100644
--- a/Content.Client/CartridgeLoader/Cartridges/NewsReadUiFragment.xaml
+++ b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml
@@ -1,21 +1,30 @@
-
+ xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
+ xmlns="https://spacestation14.io"
+ Margin="1 0 2 0"
+ Orientation="Vertical"
+ HorizontalExpand="True"
+ VerticalExpand="True">
+ Text="{Loc 'news-read-ui-next-text'}"
+ ToolTip="{Loc 'news-read-ui-next-tooltip'}"/>
@@ -46,9 +55,11 @@
+
-
+
diff --git a/Content.Client/CartridgeLoader/Cartridges/NewsReadUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs
similarity index 69%
rename from Content.Client/CartridgeLoader/Cartridges/NewsReadUiFragment.xaml.cs
rename to Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs
index df558429c9..f3b2d373d7 100644
--- a/Content.Client/CartridgeLoader/Cartridges/NewsReadUiFragment.xaml.cs
+++ b/Content.Client/CartridgeLoader/Cartridges/NewsReaderUiFragment.xaml.cs
@@ -7,23 +7,20 @@ using Robust.Client.UserInterface.XAML;
namespace Content.Client.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
-public sealed partial class NewsReadUiFragment : BoxContainer
+public sealed partial class NewsReaderUiFragment : BoxContainer
{
public event Action? OnNextButtonPressed;
public event Action? OnPrevButtonPressed;
public event Action? OnNotificationSwithPressed;
- public NewsReadUiFragment()
+ public NewsReaderUiFragment()
{
RobustXamlLoader.Load(this);
- Orientation = LayoutOrientation.Vertical;
- HorizontalExpand = true;
- VerticalExpand = true;
Next.OnPressed += _ => OnNextButtonPressed?.Invoke();
Prev.OnPressed += _ => OnPrevButtonPressed?.Invoke();
- NotificationSwith.OnPressed += _ => OnNotificationSwithPressed?.Invoke();
+ NotificationSwitch.OnPressed += _ => OnNotificationSwithPressed?.Invoke();
}
public void UpdateState(NewsArticle article, int targetNum, int totalNum, bool notificationOn)
@@ -33,17 +30,20 @@ public sealed partial class NewsReadUiFragment : BoxContainer
ShareTime.Visible = true;
Author.Visible = true;
- PageName.Text = article.Name;
+ PageName.Text = article.Title;
PageText.SetMarkup(article.Content);
PageNum.Text = $"{targetNum}/{totalNum}";
- NotificationSwith.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
+ NotificationSwitch.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
- string shareTime = article.ShareTime.ToString("hh\\:mm\\:ss");
+ string shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
ShareTime.SetMarkup(Loc.GetString("news-read-ui-time-prefix-text") + " " + shareTime);
Author.SetMarkup(Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author != null ? article.Author : Loc.GetString("news-read-ui-no-author")));
+
+ Prev.Disabled = targetNum <= 1;
+ Next.Disabled = targetNum >= totalNum;
}
public void UpdateEmptyState(bool notificationOn)
@@ -55,6 +55,6 @@ public sealed partial class NewsReadUiFragment : BoxContainer
PageName.Text = Loc.GetString("news-read-ui-not-found-text");
- NotificationSwith.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
+ NotificationSwitch.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
}
}
diff --git a/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml b/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml
new file mode 100644
index 0000000000..02aec7e900
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs b/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs
new file mode 100644
index 0000000000..2918faa83d
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/ArticleEditorPanel.xaml.cs
@@ -0,0 +1,109 @@
+using Content.Client.Message;
+using Content.Client.Stylesheets;
+using Content.Shared.MassMedia.Systems;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+
+namespace Content.Client.MassMedia.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class ArticleEditorPanel : Control
+{
+ public event Action? PublishButtonPressed;
+
+ private bool _preview;
+
+ public ArticleEditorPanel()
+ {
+ RobustXamlLoader.Load(this);
+
+ ButtonPublish.StyleClasses.Add(StyleBase.ButtonOpenLeft);
+ ButtonPublish.StyleClasses.Add(StyleNano.StyleClassButtonColorGreen);
+
+ ContentField.GetChild(0).Margin = new Thickness(9, 3);
+ // Customize scrollbar width and margin. This is not possible in xaml
+ var scrollbar = ContentField.GetChild(1);
+ scrollbar.SetWidth = 6f;
+ scrollbar.Margin = new Thickness(9, 0, 2 , 0);
+
+ RichTextInfoLabel.TooltipSupplier = sender =>
+ {
+ var label = new RichTextLabel();
+ label.SetMarkup(Loc.GetString("news-write-ui-richtext-tooltip"));
+
+ var tooltip = new Tooltip();
+ tooltip.GetChild(0).Children.Clear();
+ tooltip.GetChild(0).Children.Add(label);
+
+ return tooltip;
+ };
+
+ ButtonPreview.OnPressed += OnPreview;
+ ButtonCancel.OnPressed += OnCancel;
+ ButtonPublish.OnPressed += OnPublish;
+
+ TitleField.OnTextChanged += args => OnTextChanged(args.Text.Length, args.Control, SharedNewsSystem.MaxTitleLength);
+ ContentField.OnTextChanged += args => OnTextChanged(Rope.CalcTotalLength(args.TextRope), args.Control, SharedNewsSystem.MaxContentLength);
+ }
+
+ private void OnTextChanged(long length, Control control, long maxLength)
+ {
+ if (length > maxLength)
+ {
+ control.ModulateSelfOverride = Color.Red;
+ control.ToolTip = Loc.GetString("news-writer-text-length-exceeded");
+ }
+ else
+ {
+ control.ModulateSelfOverride = null;
+ control.ToolTip = string.Empty;
+ }
+ }
+
+ private void OnPreview(BaseButton.ButtonEventArgs eventArgs)
+ {
+ _preview = !_preview;
+
+ TextEditPanel.Visible = !_preview;
+ PreviewPanel.Visible = _preview;
+ PreviewLabel.SetMarkup(Rope.Collapse(ContentField.TextRope));
+ }
+
+ private void OnCancel(BaseButton.ButtonEventArgs eventArgs)
+ {
+ Reset();
+ Visible = false;
+ }
+
+ private void OnPublish(BaseButton.ButtonEventArgs eventArgs)
+ {
+ PublishButtonPressed?.Invoke();
+ Reset();
+ Visible = false;
+ }
+
+ private void Reset()
+ {
+ _preview = false;
+ TextEditPanel.Visible = true;
+ PreviewPanel.Visible = false;
+ PreviewLabel.SetMarkup("");
+ TitleField.Text = "";
+ ContentField.TextRope = Rope.Leaf.Empty;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ ButtonPreview.OnPressed -= OnPreview;
+ ButtonCancel.OnPressed -= OnCancel;
+ ButtonPublish.OnPressed -= OnPublish;
+ }
+}
diff --git a/Content.Client/MassMedia/Ui/MiniArticleCardControl.xaml b/Content.Client/MassMedia/Ui/MiniArticleCardControl.xaml
deleted file mode 100644
index ede51fc23b..0000000000
--- a/Content.Client/MassMedia/Ui/MiniArticleCardControl.xaml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/MassMedia/Ui/MiniArticleCardControl.xaml.cs b/Content.Client/MassMedia/Ui/MiniArticleCardControl.xaml.cs
deleted file mode 100644
index 3c5076e68f..0000000000
--- a/Content.Client/MassMedia/Ui/MiniArticleCardControl.xaml.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Content.Client.Message;
-using Content.Shared.Research.Prototypes;
-using Robust.Client.AutoGenerated;
-using Robust.Client.GameObjects;
-using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
-
-namespace Content.Client.MassMedia.Ui;
-
-[GenerateTypedNameReferences]
-public sealed partial class MiniArticleCardControl : Control
-{
- public Action? OnDeletePressed;
- public int ArticleNum;
-
- public MiniArticleCardControl(string name, string author)
- {
- RobustXamlLoader.Load(this);
-
- NameLabel.Text = name;
- Author.SetMarkup(author);
-
- Delete.OnPressed += _ => OnDeletePressed?.Invoke();
- }
-}
diff --git a/Content.Client/MassMedia/Ui/NewsArticleCard.xaml b/Content.Client/MassMedia/Ui/NewsArticleCard.xaml
new file mode 100644
index 0000000000..abfc1cbcf1
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/NewsArticleCard.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ButtonSmall
+ ButtonColorRed
+
+
+
+
+
diff --git a/Content.Client/MassMedia/Ui/NewsArticleCard.xaml.cs b/Content.Client/MassMedia/Ui/NewsArticleCard.xaml.cs
new file mode 100644
index 0000000000..f6fa3e00be
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/NewsArticleCard.xaml.cs
@@ -0,0 +1,47 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client.MassMedia.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class NewsArticleCard : Control
+{
+ private string? _authorMarkup;
+ private TimeSpan? _publicationTime;
+
+ public Action? OnDeletePressed;
+ public int ArtcileNumber;
+
+ public string? Title
+ {
+ get => TitleLabel.Text;
+ set => TitleLabel.Text = value?.Length <= 30 ? value : $"{value?[..30]}...";
+ }
+
+ public string? Author
+ {
+ get => _authorMarkup;
+ set
+ {
+ _authorMarkup = value;
+ AuthorLabel.Text = _authorMarkup ?? "";
+ }
+ }
+
+ public TimeSpan? PublicationTime
+ {
+ get => _publicationTime;
+ set
+ {
+ _publicationTime = value;
+ PublishTimeLabel.Text = value?.ToString(@"hh\:mm\:ss") ?? "";
+ }
+ }
+
+ public NewsArticleCard()
+ {
+ RobustXamlLoader.Load(this);
+ DeleteButton.OnPressed += _ => OnDeletePressed?.Invoke();
+ }
+}
diff --git a/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs b/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs
deleted file mode 100644
index d1d61d5adb..0000000000
--- a/Content.Client/MassMedia/Ui/NewsWriteBoundUserInterface.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using JetBrains.Annotations;
-using Content.Shared.MassMedia.Components;
-using Content.Shared.MassMedia.Systems;
-using Robust.Shared.Utility;
-
-namespace Content.Client.MassMedia.Ui
-{
- [UsedImplicitly]
- public sealed class NewsWriteBoundUserInterface : BoundUserInterface
- {
- [ViewVariables]
- private NewsWriteMenu? _menu;
-
- [ViewVariables]
- private string _windowName = Loc.GetString("news-read-ui-default-title");
-
- public NewsWriteBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
- {
- }
-
- protected override void Open()
- {
- _menu = new NewsWriteMenu(_windowName);
-
- _menu.OpenCentered();
- _menu.OnClose += Close;
-
- _menu.ShareButtonPressed += OnShareButtonPressed;
- _menu.DeleteButtonPressed += OnDeleteButtonPressed;
-
- SendMessage(new NewsWriteArticlesRequestMessage());
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
-
- _menu?.Close();
- _menu?.Dispose();
- }
-
- protected override void UpdateState(BoundUserInterfaceState state)
- {
- base.UpdateState(state);
- if (_menu == null || state is not NewsWriteBoundUserInterfaceState cast)
- return;
-
- _menu.UpdateUI(cast.Articles, cast.ShareAvalible);
- }
-
- private void OnShareButtonPressed()
- {
- if (_menu == null || _menu.NameInput.Text.Length == 0)
- return;
-
- var stringContent = Rope.Collapse(_menu.ContentInput.TextRope);
-
- if (stringContent.Length == 0)
- return;
-
- var stringName = _menu.NameInput.Text.Trim();
- var name = stringName[..Math.Min(stringName.Length, (SharedNewsSystem.MaxNameLength))];
- var content = stringContent[..Math.Min(stringContent.Length, (SharedNewsSystem.MaxArticleLength))];
- _menu.ContentInput.TextRope = new Rope.Leaf(string.Empty);
- _menu.NameInput.Text = string.Empty;
- SendMessage(new NewsWriteShareMessage(name, content));
- }
-
- private void OnDeleteButtonPressed(int articleNum)
- {
- if (_menu == null)
- return;
-
- SendMessage(new NewsWriteDeleteMessage(articleNum));
- }
- }
-}
diff --git a/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml b/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml
deleted file mode 100644
index 08d113f8a9..0000000000
--- a/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml.cs b/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml.cs
deleted file mode 100644
index 89ab1490af..0000000000
--- a/Content.Client/MassMedia/Ui/NewsWriteMenu.xaml.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.CustomControls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Prototypes;
-using Content.Shared.MassMedia.Systems;
-
-namespace Content.Client.MassMedia.Ui;
-
-[GenerateTypedNameReferences]
-public sealed partial class NewsWriteMenu : DefaultWindow
-{
- public event Action? ShareButtonPressed;
- public event Action? DeleteButtonPressed;
-
- public NewsWriteMenu(string name)
- {
- RobustXamlLoader.Load(this);
- IoCManager.InjectDependencies(this);
-
- if (Window != null)
- Window.Title = name;
-
- Share.OnPressed += _ => ShareButtonPressed?.Invoke();
- }
-
- public void UpdateUI(NewsArticle[] articles, bool shareAvalible)
- {
- ArticleCardsContainer.Children.Clear();
-
- for (int i = 0; i < articles.Length; i++)
- {
- var article = articles[i];
- var mini = new MiniArticleCardControl(article.Name, (article.Author != null ? article.Author : Loc.GetString("news-read-ui-no-author")));
- mini.ArticleNum = i;
- mini.OnDeletePressed += () => DeleteButtonPressed?.Invoke(mini.ArticleNum);
-
- ArticleCardsContainer.AddChild(mini);
- }
-
- Share.Disabled = !shareAvalible;
- }
-}
diff --git a/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs b/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs
new file mode 100644
index 0000000000..80eca82e32
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/NewsWriterBoundUserInterface.cs
@@ -0,0 +1,84 @@
+using JetBrains.Annotations;
+using Content.Shared.MassMedia.Systems;
+using Content.Shared.MassMedia.Components;
+using Robust.Shared.Timing;
+using Robust.Shared.Utility;
+
+namespace Content.Client.MassMedia.Ui;
+
+[UsedImplicitly]
+public sealed class NewsWriterBoundUserInterface : BoundUserInterface
+{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+
+ [ViewVariables]
+ private NewsWriterMenu? _menu;
+
+ public NewsWriterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+
+ }
+
+ protected override void Open()
+ {
+ _menu = new NewsWriterMenu(_gameTiming);
+
+ _menu.OpenCentered();
+ _menu.OnClose += Close;
+
+ _menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;
+ _menu.DeleteButtonPressed += OnDeleteButtonPressed;
+
+ SendMessage(new NewsWriterArticlesRequestMessage());
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ _menu?.Close();
+ _menu?.Dispose();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+ if (state is not NewsWriterBoundUserInterfaceState cast)
+ return;
+
+ _menu?.UpdateUI(cast.Articles, cast.PublishEnabled, cast.NextPublish);
+ }
+
+ private void OnPublishButtonPressed()
+ {
+ var title = _menu?.ArticleEditorPanel.TitleField.Text.Trim() ?? "";
+ if (_menu == null || title.Length == 0)
+ return;
+
+ var stringContent = Rope.Collapse(_menu.ArticleEditorPanel.ContentField.TextRope).Trim();
+
+ if (stringContent.Length == 0)
+ return;
+
+ var name = title.Length <= SharedNewsSystem.MaxTitleLength
+ ? title
+ : $"{title[..(SharedNewsSystem.MaxTitleLength - 3)]}...";
+
+ var content = stringContent.Length <= SharedNewsSystem.MaxContentLength
+ ? stringContent
+ : $"{stringContent[..(SharedNewsSystem.MaxContentLength - 3)]}...";
+
+
+ SendMessage(new NewsWriterPublishMessage(name, content));
+ }
+
+ private void OnDeleteButtonPressed(int articleNum)
+ {
+ if (_menu == null)
+ return;
+
+ SendMessage(new NewsWriterDeleteMessage(articleNum));
+ }
+}
diff --git a/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml
new file mode 100644
index 0000000000..64932bc6cf
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs
new file mode 100644
index 0000000000..e2d57935e3
--- /dev/null
+++ b/Content.Client/MassMedia/Ui/NewsWriterMenu.xaml.cs
@@ -0,0 +1,97 @@
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.XAML;
+using Content.Shared.MassMedia.Systems;
+using Robust.Client.UserInterface.Controls;
+using Robust.Shared.Timing;
+
+namespace Content.Client.MassMedia.Ui;
+
+[GenerateTypedNameReferences]
+public sealed partial class NewsWriterMenu : FancyWindow
+{
+ private readonly IGameTiming _gameTiming;
+
+ private TimeSpan? _nextPublish;
+
+ public event Action? DeleteButtonPressed;
+
+ public NewsWriterMenu(IGameTiming gameTiming)
+ {
+ RobustXamlLoader.Load(this);
+
+ _gameTiming = gameTiming;
+ ContentsContainer.RectClipContent = false;
+
+ // Customize scrollbar width and margin. This is not possible in xaml
+ var scrollbar = ArticleListScrollbar.GetChild(1);
+ scrollbar.SetWidth = 6f;
+ scrollbar.Margin = new Thickness(0, 0, 2 , 0);
+
+ ButtonCreate.OnPressed += OnCreate;
+ }
+
+ public void UpdateUI(NewsArticle[] articles, bool publishEnabled, TimeSpan nextPublish)
+ {
+ ArticlesContainer.Children.Clear();
+ ArticleCount.Text = Loc.GetString("news-write-ui-article-count-text", ("count", articles.Length));
+
+ //Iterate backwards to have the newest article at the top
+ for (var i = articles.Length - 1; i >= 0 ; i--)
+ {
+ var article = articles[i];
+ var control = new NewsArticleCard
+ {
+ Title = article.Title,
+ Author = article.Author ?? Loc.GetString("news-read-ui-no-author"),
+ PublicationTime = article.ShareTime,
+ ArtcileNumber = i
+ };
+ control.OnDeletePressed += () => DeleteButtonPressed?.Invoke(control.ArtcileNumber);
+
+ ArticlesContainer.AddChild(control);
+ }
+
+ ButtonCreate.Disabled = !publishEnabled;
+ _nextPublish = nextPublish;
+ }
+
+ protected override void FrameUpdate(FrameEventArgs args)
+ {
+ base.FrameUpdate(args);
+ if (!_nextPublish.HasValue)
+ return;
+
+ var remainingTime = _nextPublish.Value.Subtract(_gameTiming.CurTime);
+ if (remainingTime.TotalSeconds <= 0)
+ {
+ _nextPublish = null;
+ ButtonCreate.Text = Loc.GetString("news-write-ui-create-text");
+ return;
+ }
+
+ ButtonCreate.Text = remainingTime.Seconds.ToString("D2");
+ }
+
+ protected override void Resized()
+ {
+ base.Resized();
+ var margin = ArticleEditorPanel.Margin;
+ // Bandaid for the funny 1 pixel margin differences
+ ArticleEditorPanel.Margin = new Thickness(Width - 1, margin.Top, margin.Right, margin.Bottom);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (!disposing)
+ return;
+
+ ButtonCreate.OnPressed -= OnCreate;
+ }
+
+ private void OnCreate(BaseButton.ButtonEventArgs buttonEventArgs)
+ {
+ ArticleEditorPanel.Visible = true;
+ }
+}
diff --git a/Content.Client/Paper/UI/PaperBoundUserInterface.cs b/Content.Client/Paper/UI/PaperBoundUserInterface.cs
index 30f1502779..6b12cfe3a7 100644
--- a/Content.Client/Paper/UI/PaperBoundUserInterface.cs
+++ b/Content.Client/Paper/UI/PaperBoundUserInterface.cs
@@ -24,7 +24,7 @@ public sealed class PaperBoundUserInterface : BoundUserInterface
_window.OnClose += Close;
_window.Input.OnKeyBindDown += args => // Solution while TextEdit don't have events
{
- if (args.Function == EngineKeyFunctions.TextSubmit)
+ if (args.Function == EngineKeyFunctions.MultilineTextSubmit)
{
var text = Rope.Collapse(_window.Input.TextRope);
Input_OnTextEntered(text);
diff --git a/Content.Client/Stylesheets/StyleNano.cs b/Content.Client/Stylesheets/StyleNano.cs
index f668cdaa40..46c054c00c 100644
--- a/Content.Client/Stylesheets/StyleNano.cs
+++ b/Content.Client/Stylesheets/StyleNano.cs
@@ -74,6 +74,7 @@ namespace Content.Client.Stylesheets
public const string StyleClassLabelKeyText = "LabelKeyText";
public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor";
public const string StyleClassLabelBig = "LabelBig";
+ public const string StyleClassLabelSmall = "LabelSmall";
public const string StyleClassButtonBig = "ButtonBig";
public const string StyleClassPopupMessageSmall = "PopupMessageSmall";
@@ -329,6 +330,12 @@ namespace Content.Client.Stylesheets
chatFilterButton.SetPatchMargin(StyleBox.Margin.All, 5);
chatFilterButton.SetPadding(StyleBox.Margin.All, 2);
+ var smallButtonTex = resCache.GetTexture("/Textures/Interface/Nano/button_small.svg.96dpi.png");
+ var smallButtonBase = new StyleBoxTexture
+ {
+ Texture = smallButtonTex,
+ };
+
var textureInvertedTriangle = resCache.GetTexture("/Textures/Interface/Nano/inverted_triangle.svg.png");
var lineEditTex = resCache.GetTexture("/Textures/Interface/Nano/lineedit.png");
@@ -646,6 +653,23 @@ namespace Content.Client.Stylesheets
.Pseudo(ContainerButton.StylePseudoClassDisabled)
.Prop(Control.StylePropertyModulateSelf, ButtonColorCautionDisabled),
+ // Colors for confirm buttons confirm states.
+ Element()
+ .Pseudo(ConfirmButton.ConfirmPrefix + ContainerButton.StylePseudoClassNormal)
+ .Prop(Control.StylePropertyModulateSelf, ButtonColorCautionDefault),
+
+ Element()
+ .Pseudo(ConfirmButton.ConfirmPrefix + ContainerButton.StylePseudoClassHover)
+ .Prop(Control.StylePropertyModulateSelf, ButtonColorCautionHovered),
+
+ Element()
+ .Pseudo(ConfirmButton.ConfirmPrefix + ContainerButton.StylePseudoClassPressed)
+ .Prop(Control.StylePropertyModulateSelf, ButtonColorCautionPressed),
+
+ Element()
+ .Pseudo(ConfirmButton.ConfirmPrefix + ContainerButton.StylePseudoClassDisabled)
+ .Prop(Control.StylePropertyModulateSelf, ButtonColorCautionDisabled),
+
new StyleRule(new SelectorChild(
new SelectorElement(typeof(Button), null, null, new[] {ContainerButton.StylePseudoClassDisabled}),
new SelectorElement(typeof(Label), null, null, null)),
@@ -1189,14 +1213,6 @@ namespace Content.Client.Stylesheets
new StyleProperty(StripeBack.StylePropertyBackground, stripeBack),
}),
- // StyleClassLabelBig
- new StyleRule(
- SelectorElement.Class(StyleClassLabelBig),
- new[]
- {
- new StyleProperty("font", notoSans16),
- }),
-
// StyleClassItemStatus
new StyleRule(SelectorElement.Class(StyleClassItemStatus), new[]
{
@@ -1303,10 +1319,29 @@ namespace Content.Client.Stylesheets
new StyleProperty(PanelContainer.StylePropertyPanel, new StyleBoxFlat { BackgroundColor = NanoGold, ContentMarginBottomOverride = 2, ContentMarginLeftOverride = 2}),
}),
+ // Labels ---
+ Element