feat: add duplicate message collapsing
This commit is contained in:
parent
771e7d787b
commit
02118163ac
|
@ -22,6 +22,20 @@ internal abstract class Chunk {
|
||||||
ChunkSource.Content => this.Message?.ContentSource,
|
ChunkSource.Content => this.Message?.ContentSource,
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get some basic text for use in generating hashes.
|
||||||
|
/// </summary>
|
||||||
|
internal string StringValue() {
|
||||||
|
switch (this) {
|
||||||
|
case TextChunk text:
|
||||||
|
return text.Content;
|
||||||
|
case IconChunk icon:
|
||||||
|
return icon.Icon.ToString();
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum ChunkSource {
|
internal enum ChunkSource {
|
||||||
|
|
|
@ -35,6 +35,7 @@ internal class Configuration : IPluginConfiguration {
|
||||||
public bool FilterIncludePreviousSessions;
|
public bool FilterIncludePreviousSessions;
|
||||||
public bool SharedMode;
|
public bool SharedMode;
|
||||||
public bool SortAutoTranslate;
|
public bool SortAutoTranslate;
|
||||||
|
public bool CollapseDuplicateMessages;
|
||||||
|
|
||||||
public bool FontsEnabled = true;
|
public bool FontsEnabled = true;
|
||||||
public ExtraGlyphRanges ExtraGlyphRanges = 0;
|
public ExtraGlyphRanges ExtraGlyphRanges = 0;
|
||||||
|
@ -71,6 +72,7 @@ internal class Configuration : IPluginConfiguration {
|
||||||
this.FilterIncludePreviousSessions = other.FilterIncludePreviousSessions;
|
this.FilterIncludePreviousSessions = other.FilterIncludePreviousSessions;
|
||||||
this.SharedMode = other.SharedMode;
|
this.SharedMode = other.SharedMode;
|
||||||
this.SortAutoTranslate = other.SortAutoTranslate;
|
this.SortAutoTranslate = other.SortAutoTranslate;
|
||||||
|
this.CollapseDuplicateMessages = other.CollapseDuplicateMessages;
|
||||||
this.FontsEnabled = other.FontsEnabled;
|
this.FontsEnabled = other.FontsEnabled;
|
||||||
this.ExtraGlyphRanges = other.ExtraGlyphRanges;
|
this.ExtraGlyphRanges = other.ExtraGlyphRanges;
|
||||||
this.FontSize = other.FontSize;
|
this.FontSize = other.FontSize;
|
||||||
|
|
|
@ -15,6 +15,28 @@ internal class SortCode {
|
||||||
|
|
||||||
public SortCode() {
|
public SortCode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool Equals(SortCode other) {
|
||||||
|
return this.Type == other.Type && this.Source == other.Source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) {
|
||||||
|
if (ReferenceEquals(null, obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(this, obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj.GetType() == this.GetType() && this.Equals((SortCode) obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() {
|
||||||
|
unchecked {
|
||||||
|
return ((int) this.Type * 397) ^ (int) this.Source;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Message {
|
internal class Message {
|
||||||
|
@ -38,6 +60,8 @@ internal class Message {
|
||||||
internal SeString ContentSource { get; }
|
internal SeString ContentSource { get; }
|
||||||
|
|
||||||
internal SortCode SortCode { get; }
|
internal SortCode SortCode { get; }
|
||||||
|
|
||||||
|
internal int Hash { get; }
|
||||||
|
|
||||||
internal Message(ulong receiver, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource) {
|
internal Message(ulong receiver, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource) {
|
||||||
this.Receiver = receiver;
|
this.Receiver = receiver;
|
||||||
|
@ -48,6 +72,7 @@ internal class Message {
|
||||||
this.SenderSource = senderSource;
|
this.SenderSource = senderSource;
|
||||||
this.ContentSource = contentSource;
|
this.ContentSource = contentSource;
|
||||||
this.SortCode = new SortCode(this.Code.Type, this.Code.Source);
|
this.SortCode = new SortCode(this.Code.Type, this.Code.Source);
|
||||||
|
this.Hash = this.GenerateHash();
|
||||||
|
|
||||||
foreach (var chunk in sender.Concat(content)) {
|
foreach (var chunk in sender.Concat(content)) {
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
|
@ -65,9 +90,16 @@ internal class Message {
|
||||||
this.SenderSource = BsonMapper.Global.Deserialize<SeString>(senderSource);
|
this.SenderSource = BsonMapper.Global.Deserialize<SeString>(senderSource);
|
||||||
this.ContentSource = BsonMapper.Global.Deserialize<SeString>(contentSource);
|
this.ContentSource = BsonMapper.Global.Deserialize<SeString>(contentSource);
|
||||||
this.SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
this.SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
||||||
|
this.Hash = this.GenerateHash();
|
||||||
|
|
||||||
foreach (var chunk in this.Sender.Concat(this.Content)) {
|
foreach (var chunk in this.Sender.Concat(this.Content)) {
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GenerateHash() {
|
||||||
|
return this.SortCode.GetHashCode()
|
||||||
|
^ string.Join("", this.Sender.Select(c => c.StringValue())).GetHashCode()
|
||||||
|
^ string.Join("", this.Content.Select(c => c.StringValue())).GetHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -839,4 +839,10 @@
|
||||||
<data name="ChatType_GmNoviceNetwork" xml:space="preserve">
|
<data name="ChatType_GmNoviceNetwork" xml:space="preserve">
|
||||||
<value>Novice Network (GM)</value>
|
<value>Novice Network (GM)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_CollapseDuplicateMessages_Name" xml:space="preserve">
|
||||||
|
<value>Collapse duplicate messages</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_CollapseDuplicateMessages_Description" xml:space="preserve">
|
||||||
|
<value>Only show the first message and show a counter at the end of messages that have been repeated.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -671,12 +671,50 @@ internal sealed class ChatLog : IUiComponent {
|
||||||
|
|
||||||
var lastPos = ImGui.GetCursorPosY();
|
var lastPos = ImGui.GetCursorPosY();
|
||||||
var lastTimestamp = string.Empty;
|
var lastTimestamp = string.Empty;
|
||||||
foreach (var message in tab.Messages) {
|
int? lastMessageHash = null;
|
||||||
|
var sameCount = 0;
|
||||||
|
for (var i = 0; i < tab.Messages.Count; i++) {
|
||||||
|
var message = tab.Messages[i];
|
||||||
|
|
||||||
if (reset) {
|
if (reset) {
|
||||||
message.Height = null;
|
message.Height = null;
|
||||||
message.IsVisible = false;
|
message.IsVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.Ui.Plugin.Config.CollapseDuplicateMessages) {
|
||||||
|
var messageHash = message.Hash;
|
||||||
|
var same = lastMessageHash == messageHash;
|
||||||
|
if (same) {
|
||||||
|
sameCount += 1;
|
||||||
|
|
||||||
|
if (i != tab.Messages.Count - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sameCount > 0) {
|
||||||
|
ImGui.SameLine();
|
||||||
|
this.DrawChunks(
|
||||||
|
new[] {
|
||||||
|
new TextChunk(ChunkSource.None, null, $" ({sameCount + 1}x)") {
|
||||||
|
FallbackColour = ChatType.System,
|
||||||
|
Italic = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
handler,
|
||||||
|
ImGui.GetContentRegionAvail().X
|
||||||
|
);
|
||||||
|
sameCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMessageHash = messageHash;
|
||||||
|
|
||||||
|
if (same && i == tab.Messages.Count - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// go to next row
|
// go to next row
|
||||||
if (table) {
|
if (table) {
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
@ -924,13 +962,13 @@ internal sealed class ChatLog : IUiComponent {
|
||||||
this._popOutDocked.Clear();
|
this._popOutDocked.Clear();
|
||||||
this._popOutDocked.AddRange(Enumerable.Repeat(false, this.Ui.Plugin.Config.Tabs.Count));
|
this._popOutDocked.AddRange(Enumerable.Repeat(false, this.Ui.Plugin.Config.Tabs.Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < this.Ui.Plugin.Config.Tabs.Count; i++) {
|
for (var i = 0; i < this.Ui.Plugin.Config.Tabs.Count; i++) {
|
||||||
var tab = this.Ui.Plugin.Config.Tabs[i];
|
var tab = this.Ui.Plugin.Config.Tabs[i];
|
||||||
if (!tab.PopOut) {
|
if (!tab.PopOut) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.DrawPopOut(tab, i);
|
this.DrawPopOut(tab, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,9 @@ internal sealed class Display : ISettingsTab {
|
||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGuiUtil.OptionCheckbox(ref this.Mutable.CollapseDuplicateMessages, Language.Options_CollapseDuplicateMessages_Name, Language.Options_CollapseDuplicateMessages_Description);
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.ShowNoviceNetwork, Language.Options_ShowNoviceNetwork_Name, Language.Options_ShowNoviceNetwork_Description);
|
ImGuiUtil.OptionCheckbox(ref this.Mutable.ShowNoviceNetwork, Language.Options_ShowNoviceNetwork_Name, Language.Options_ShowNoviceNetwork_Description);
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue