From fa7b45dfb133dca53206c105c83fbaafeb6810bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Nov 2022 13:51:49 +0900 Subject: [PATCH 1/4] Fix chat day separator not being added on pending message resolution Closes #21316. --- osu.Game/Overlays/Chat/DrawableChannel.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 544daf7d2c..7f9be1a782 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -134,8 +134,7 @@ namespace osu.Game.Overlays.Chat foreach (var message in displayMessages) { - if (lastMessage == null || lastMessage.Timestamp.ToLocalTime().Date != message.Timestamp.ToLocalTime().Date) - ChatLineFlow.Add(CreateDaySeparator(message.Timestamp)); + addDaySeparatorIfRequired(lastMessage, message); ChatLineFlow.Add(CreateChatLine(message)); lastMessage = message; @@ -183,10 +182,18 @@ namespace osu.Game.Overlays.Chat ChatLineFlow.Remove(found, false); found.Message = updated; + + addDaySeparatorIfRequired(chatLines.LastOrDefault()?.Message, updated); ChatLineFlow.Add(found); } }); + private void addDaySeparatorIfRequired(Message lastMessage, Message message) + { + if (lastMessage == null || lastMessage.Timestamp.ToLocalTime().Date != message.Timestamp.ToLocalTime().Date) + ChatLineFlow.Add(CreateDaySeparator(message.Timestamp)); + } + private void messageRemoved(Message removed) => Schedule(() => { chatLines.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); From e53b4321ffa1eb91524558e15f786a8476b92905 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 21 Nov 2022 14:01:10 +0900 Subject: [PATCH 2/4] Ensure two day separators are not added in a row --- osu.Game/Overlays/Chat/DrawableChannel.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 7f9be1a782..3d2eafd973 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -191,7 +191,15 @@ namespace osu.Game.Overlays.Chat private void addDaySeparatorIfRequired(Message lastMessage, Message message) { if (lastMessage == null || lastMessage.Timestamp.ToLocalTime().Date != message.Timestamp.ToLocalTime().Date) + { + // A day separator is displayed even if no messages are in the channel. + // If there are no messages after it, the simplest way to ensure it is fresh is to remove it + // and add a new one instead. + if (ChatLineFlow.LastOrDefault() is DaySeparator ds) + ChatLineFlow.Remove(ds, true); + ChatLineFlow.Add(CreateDaySeparator(message.Timestamp)); + } } private void messageRemoved(Message removed) => Schedule(() => From 76bb529cfa750daf08d15f61f32e034b49ecbd83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 21 Nov 2022 20:30:42 +0100 Subject: [PATCH 3/4] Add test for local echo adding day separator --- .../Visual/Online/TestSceneDrawableChannel.cs | 87 +++++++++++++++++++ osu.Game/Online/Chat/StandAloneChatDisplay.cs | 4 +- osu.Game/Overlays/Chat/DaySeparator.cs | 8 +- 3 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneDrawableChannel.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneDrawableChannel.cs b/osu.Game.Tests/Visual/Online/TestSceneDrawableChannel.cs new file mode 100644 index 0000000000..dc1e00ee8f --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneDrawableChannel.cs @@ -0,0 +1,87 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays.Chat; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneDrawableChannel : OsuTestScene + { + private Channel channel = null!; + private DrawableChannel drawableChannel = null!; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create channel", () => channel = new Channel + { + Id = 1, + Name = "Test channel" + }); + AddStep("create drawable channel", () => Child = drawableChannel = new DrawableChannel(channel) + { + RelativeSizeAxes = Axes.Both + }); + } + + [Test] + public void TestDaySeparators() + { + var localUser = new APIUser + { + Id = 3, + Username = "LocalUser" + }; + string uuid = Guid.NewGuid().ToString(); + AddStep("add local echo message", () => channel.AddLocalEcho(new LocalEchoMessage + { + Sender = localUser, + Content = "Hi there all!", + Timestamp = new DateTimeOffset(2022, 11, 21, 20, 11, 13, TimeSpan.Zero), + Uuid = uuid + })); + AddUntilStep("one day separator present", () => drawableChannel.ChildrenOfType().Count() == 1); + + AddStep("add two prior messages to channel", () => channel.AddNewMessages( + new Message(1) + { + Sender = new APIUser + { + Id = 1, + Username = "TestUser" + }, + Content = "This is a message", + Timestamp = new DateTimeOffset(2021, 10, 10, 13, 33, 23, TimeSpan.Zero), + }, + new Message(2) + { + Sender = new APIUser + { + Id = 2, + Username = "TestUser2" + }, + Content = "This is another message", + Timestamp = new DateTimeOffset(2021, 10, 11, 13, 33, 23, TimeSpan.Zero) + })); + AddUntilStep("three day separators present", () => drawableChannel.ChildrenOfType().Count() == 3); + + AddStep("resolve pending message", () => channel.ReplaceMessage(channel.Messages.OfType().Single(), new Message(3) + { + Sender = localUser, + Content = "Hi there all!", + Timestamp = new DateTimeOffset(2022, 11, 22, 20, 11, 16, TimeSpan.Zero), + Uuid = uuid + })); + AddUntilStep("three day separators present", () => drawableChannel.ChildrenOfType().Count() == 3); + AddAssert("last day separator is from correct day", () => drawableChannel.ChildrenOfType().Last().Date.Date == new DateTime(2022, 11, 22)); + } + } +} diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 81db3f0d53..e45a14136d 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -174,8 +174,8 @@ namespace osu.Game.Online.Chat protected override float Spacing => 5; protected override float DateAlign => 125; - public StandAloneDaySeparator(DateTimeOffset time) - : base(time) + public StandAloneDaySeparator(DateTimeOffset date) + : base(date) { } diff --git a/osu.Game/Overlays/Chat/DaySeparator.cs b/osu.Game/Overlays/Chat/DaySeparator.cs index be0b53785c..d68f325738 100644 --- a/osu.Game/Overlays/Chat/DaySeparator.cs +++ b/osu.Game/Overlays/Chat/DaySeparator.cs @@ -22,14 +22,14 @@ namespace osu.Game.Overlays.Chat protected virtual float Spacing => 15; - private readonly DateTimeOffset time; + public readonly DateTimeOffset Date; [Resolved(CanBeNull = true)] private OverlayColourProvider? colourProvider { get; set; } - public DaySeparator(DateTimeOffset time) + public DaySeparator(DateTimeOffset date) { - this.time = time; + Date = date; Height = 40; } @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Chat { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - Text = time.ToLocalTime().ToLocalisableString(@"dd MMMM yyyy").ToUpper(), + Text = Date.ToLocalTime().ToLocalisableString(@"dd MMMM yyyy").ToUpper(), Font = OsuFont.Torus.With(size: TextSize, weight: FontWeight.SemiBold), Colour = colourProvider?.Content1 ?? Colour4.White, }, From 5467097387b97f0dbf2ae9defb7234368977e5c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Nov 2022 18:49:51 +0900 Subject: [PATCH 4/4] Fix adjacent day separators potentially being left behind after pending message resolution --- osu.Game/Overlays/Chat/DrawableChannel.cs | 36 ++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 3d2eafd973..65876fd7c5 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -141,27 +141,15 @@ namespace osu.Game.Overlays.Chat } var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); + int count = staleMessages.Length - Channel.MAX_HISTORY; if (count > 0) { - void expireAndAdjustScroll(Drawable d) - { - scroll.OffsetScrollPosition(-d.DrawHeight); - d.Expire(); - } - for (int i = 0; i < count; i++) expireAndAdjustScroll(staleMessages[i]); - // remove all adjacent day separators after stale message removal - for (int i = 0; i < ChatLineFlow.Count - 1; i++) - { - if (!(ChatLineFlow[i] is DaySeparator)) break; - if (!(ChatLineFlow[i + 1] is DaySeparator)) break; - - expireAndAdjustScroll(ChatLineFlow[i]); - } + removeAdjacentDaySeparators(); } // due to the scroll adjusts from old messages removal above, a scroll-to-end must be enforced, @@ -199,9 +187,29 @@ namespace osu.Game.Overlays.Chat ChatLineFlow.Remove(ds, true); ChatLineFlow.Add(CreateDaySeparator(message.Timestamp)); + + removeAdjacentDaySeparators(); } } + private void removeAdjacentDaySeparators() + { + // remove all adjacent day separators after stale message removal + for (int i = 0; i < ChatLineFlow.Count - 1; i++) + { + if (!(ChatLineFlow[i] is DaySeparator)) break; + if (!(ChatLineFlow[i + 1] is DaySeparator)) break; + + expireAndAdjustScroll(ChatLineFlow[i]); + } + } + + private void expireAndAdjustScroll(Drawable d) + { + scroll.OffsetScrollPosition(-d.DrawHeight); + d.Expire(); + } + private void messageRemoved(Message removed) => Schedule(() => { chatLines.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire();