From 88fbb24ff0a4513f3146cb10fde9f765c94c7af5 Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 22 May 2024 19:25:36 +0200 Subject: [PATCH] - Migrate DB to v2 (Channel) - Add channel selection to DBViewer --- ChatTwo/Code/ChatSource.cs | 2 +- ChatTwo/Message.cs | 31 ++++++----- ChatTwo/MessageStore.cs | 34 ++++++++---- ChatTwo/Resources/Language.Designer.cs | 9 ++++ ChatTwo/Resources/Language.resx | 3 ++ ChatTwo/Ui/DbViewer.cs | 73 +++++++++++++++++++++++--- ChatTwo/Util/ImGuiUtil.cs | 7 +++ ChatTwo/Util/TabsUtil.cs | 49 +++++++++++++++++ 8 files changed, 176 insertions(+), 32 deletions(-) diff --git a/ChatTwo/Code/ChatSource.cs b/ChatTwo/Code/ChatSource.cs index 456bfa6..d087bcf 100755 --- a/ChatTwo/Code/ChatSource.cs +++ b/ChatTwo/Code/ChatSource.cs @@ -2,7 +2,7 @@ namespace ChatTwo.Code; [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] [Flags] -internal enum ChatSource : ushort +public enum ChatSource : ushort { Self = 2, PartyMember = 4, diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index d56bec6..e411185 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -12,45 +12,48 @@ using Lumina.Excel.GeneratedSheets; namespace ChatTwo; -internal class SortCode { +internal class SortCode +{ internal ChatType Type { get; } internal ChatSource Source { get; } [BsonCtor] // Used by LegacyMessageImporter - public SortCode(ChatType type, ChatSource source) { + public SortCode(ChatType type, ChatSource source) + { Type = type; Source = source; } - internal SortCode(uint raw) { + internal SortCode(uint raw) + { Type = (ChatType)(raw >> 16); Source = (ChatSource)(raw & 0xFFFF); } - internal uint Encode() { + internal uint Encode() + { return ((uint) Type << 16) | (uint) Source; } - private bool Equals(SortCode other) { + private bool Equals(SortCode other) + { return Type == other.Type && Source == other.Source; } - public override bool Equals(object? obj) { - if (ReferenceEquals(null, obj)) { + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; - } - if (ReferenceEquals(this, obj)) { + if (ReferenceEquals(this, obj)) return true; - } return obj.GetType() == GetType() && Equals((SortCode) obj); } - public override int GetHashCode() { - unchecked { - return ((int) Type * 397) ^ (int) Source; - } + public override int GetHashCode() + { + unchecked { return ((int) Type * 397) ^ (int) Source; } } } diff --git a/ChatTwo/MessageStore.cs b/ChatTwo/MessageStore.cs index 8345424..7456a65 100644 --- a/ChatTwo/MessageStore.cs +++ b/ChatTwo/MessageStore.cs @@ -167,12 +167,17 @@ internal class MessageStore : IDisposable var userVersion = Convert.ToInt32(cmd.ExecuteScalar()); var migrationsToDo = new List(); - if (userVersion <= 0) + switch (userVersion) { - migrationsToDo.Add(Migrate0); - // Migration support was only added in version 1. Migrate0 is - // idempotent. - migrationsToDo.Add(Migrate1); + case <= 0: + migrationsToDo.Add(Migrate0); + // Migration support was only added in version 1. Migrate0 is + // idempotent. + migrationsToDo.Add(Migrate1); + break; + case 1: + migrationsToDo.Add(Migrate2); + break; } foreach (var migration in migrationsToDo) @@ -213,6 +218,17 @@ internal class MessageStore : IDisposable SetMigrationVersion(1); } + private void Migrate2() + { + Connection.Execute(@" + -- Migration 2: Add Channel generated column + ALTER TABLE messages ADD COLUMN Channel INTEGER GENERATED ALWAYS AS (Code & 0x7f) VIRTUAL; + CREATE INDEX IF NOT EXISTS idx_messages_channel ON messages (Channel); + "); + + SetMigrationVersion(2); + } + private void SetMigrationVersion(int version) { var cmd = Connection.CreateCommand(); @@ -373,14 +389,14 @@ internal class MessageStore : IDisposable cmd.ExecuteNonQuery(); } - internal long CountDateRange(DateTime after, DateTime before, ulong? receiver = null) + internal long CountDateRange(DateTime after, DateTime before, uint[] channels, ulong? receiver = null) { List whereClauses = ["deleted = false"]; if (receiver != null) whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Date BETWEEN $After AND $Before"); - whereClauses.Add("Code != 72"); + whereClauses.Add($"Channel IN ({string.Join(", ", channels)})"); var whereClause = "WHERE " + string.Join(" AND ", whereClauses); @@ -402,14 +418,14 @@ internal class MessageStore : IDisposable return (long) cmd.ExecuteScalar()!; } - internal MessageEnumerator GetDateRange(DateTime after, DateTime before, ulong? receiver = null, int page = 0) + internal MessageEnumerator GetDateRange(DateTime after, DateTime before, uint[] channels, ulong? receiver = null, int page = 0) { List whereClauses = ["deleted = false"]; if (receiver != null) whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Date BETWEEN $After AND $Before"); - whereClauses.Add("Code != 72"); + whereClauses.Add($"Channel IN ({string.Join(", ", channels)})"); var whereClause = "WHERE " + string.Join(" AND ", whereClauses); diff --git a/ChatTwo/Resources/Language.Designer.cs b/ChatTwo/Resources/Language.Designer.cs index 562ced0..f4ee12a 100755 --- a/ChatTwo/Resources/Language.Designer.cs +++ b/ChatTwo/Resources/Language.Designer.cs @@ -1634,6 +1634,15 @@ namespace ChatTwo.Resources { } } + /// + /// Looks up a localized string similar to Chat Type. + /// + internal static string DbViewer_TableField_Type { + get { + return ResourceManager.GetString("DbViewer_TableField_Type", resourceCulture); + } + } + /// /// Looks up a localized string similar to Chinese (full). /// diff --git a/ChatTwo/Resources/Language.resx b/ChatTwo/Resources/Language.resx index db41bd2..2b39046 100644 --- a/ChatTwo/Resources/Language.resx +++ b/ChatTwo/Resources/Language.resx @@ -1168,6 +1168,9 @@ Date + + Chat Type + Sender diff --git a/ChatTwo/Ui/DbViewer.cs b/ChatTwo/Ui/DbViewer.cs index ac66fb3..c02a6cd 100644 --- a/ChatTwo/Ui/DbViewer.cs +++ b/ChatTwo/Ui/DbViewer.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.Globalization; using System.Numerics; +using ChatTwo.Code; using ChatTwo.Resources; using ChatTwo.Util; using Dalamud.Interface; @@ -24,10 +25,11 @@ public class DbViewer : Window private int CurrentPage = 1; private string SimpleSearchTerm = ""; private bool OnlyCurrentCharacter = true; + private readonly Dictionary ChatCodes; private bool IsProcessing; private long ProcessingStart = Environment.TickCount64; - private (DateTime Min, DateTime Max, int Page, bool Local) LastProcessed; + private (DateTime Min, DateTime Max, int Page, bool Local, int ChannelCount) LastProcessed; private string MinDateString = ""; private string MaxDateString = ""; @@ -42,11 +44,12 @@ public class DbViewer : Window public DbViewer(Plugin plugin) : base("DBViewer###chat2-dbviewer") { Plugin = plugin; + ChatCodes = TabsUtil.MostlyPlayer; DateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; DateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern; - LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter); + LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count); DateReset(); SizeConstraints = new WindowSizeConstraints @@ -88,6 +91,8 @@ public class DbViewer : Window if (ImGuiUtil.IconButton(FontAwesomeIcon.Recycle)) DateReset(); ImGuiUtil.DrawArrows(ref CurrentPage, 1, totalPages, spacing); + ImGui.SameLine(0, spacing); + ChannelSelection(); var skipText = Language.DbViewer_CharacterOption; var textLength = ImGui.GetTextLineHeight() + ImGui.CalcTextSize(skipText).X + ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetStyle().FramePadding.X * 2; @@ -107,7 +112,7 @@ public class DbViewer : Window if (DateWidget.Validate(MinimalDate, ref AfterDate, ref BeforeDate)) DateRefresh(); - if (!IsProcessing && LastProcessed != (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter)) + if (!IsProcessing && LastProcessed != (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count)) { // Page hasn't changed, so we reset it back to 1 if (LastProcessed.Page == CurrentPage) @@ -116,17 +121,18 @@ public class DbViewer : Window AdjustDates(); IsProcessing = true; ProcessingStart = Environment.TickCount64 + 1_000; // + 1 second - LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter); + LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count); Task.Run(() => { try { ulong? character = OnlyCurrentCharacter ? Plugin.ClientState.LocalContentId : null; + var channels = ChatCodes.Select(c => (uint) c.Key).ToArray(); // We only want to fetch count if this is the first page if (CurrentPage == 1) - Count = Plugin.MessageManager.Store.CountDateRange(AfterDate, BeforeDate, character); - Messages = Plugin.MessageManager.Store.GetDateRange(AfterDate, BeforeDate, character, CurrentPage - 1).ToArray(); + Count = Plugin.MessageManager.Store.CountDateRange(AfterDate, BeforeDate, channels, character); + Messages = Plugin.MessageManager.Store.GetDateRange(AfterDate, BeforeDate, channels, character, CurrentPage - 1).ToArray(); Filter(); } @@ -153,11 +159,13 @@ public class DbViewer : Window if (!child.Success) return; - using var table = ImRaii.Table("##messageHistory", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable); + using var table = ImRaii.Table("##messageHistory", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable); if (!table.Success) return; - ImGui.TableSetupColumn(Language.DbViewer_TableField_Date, ImGuiTableColumnFlags.WidthFixed); + var columnWidth = ImGui.CalcTextSize(Language.DbViewer_TableField_Type); + ImGui.TableSetupColumn(Language.DbViewer_TableField_Date, ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize); + ImGui.TableSetupColumn(Language.DbViewer_TableField_Type, ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize, columnWidth.X); ImGui.TableSetupColumn(Language.DbViewer_TableField_Sender); ImGui.TableSetupColumn(Language.DbViewer_TableField_Content); @@ -167,6 +175,14 @@ public class DbViewer : Window ImGui.TableNextColumn(); ImGui.TextUnformatted(message.Date.ToLocalTime().ToString(DateTimeFormat)); + ImGui.TableNextColumn(); + var pos = ImGui.GetCursorPos(); + ImGuiUtil.CenterText($"{message.Code.Raw}"); + ImGui.SetCursorPos(pos); + ImGui.Dummy(columnWidth); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip(message.Code.Type.Name()); + ImGui.TableNextColumn(); Plugin.ChatLogWindow.DrawChunks(message.Sender); @@ -175,6 +191,47 @@ public class DbViewer : Window } } + private void ChannelSelection() + { + const string addTabPopup = "add-channel-popup"; + + if (ImGui.Button("Channels")) + ImGui.OpenPopup(addTabPopup); + + using var popup = ImRaii.Popup(addTabPopup); + if (!popup.Success) + return; + + using var channelNode = ImRaii.TreeNode(Language.Options_Tabs_Channels); + if (!channelNode.Success) + return; + + foreach (var (header, types) in ChatTypeExt.SortOrder) + { + using var headerNode = ImRaii.TreeNode(header); + if (!headerNode.Success) + continue; + + foreach (var type in types) + { + if (type.IsGm()) + continue; + + var enabled = ChatCodes.ContainsKey(type); + if (ImGui.Checkbox($"##{type.Name()}", ref enabled)) + { + if (enabled) + ChatCodes[type] = ChatSourceExt.All; + else + ChatCodes.Remove(type); + } + + ImGui.SameLine(); + ImGui.TextUnformatted(type.Name()); + } + } + } + private void Filter() { if (SimpleSearchTerm == "") diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 3f33483..7e75060 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -317,6 +317,13 @@ internal static class ImGuiUtil } } + public static void CenterText(string text, float indent = 0.0f) + { + indent *= ImGuiHelpers.GlobalScale; + ImGui.SameLine(((ImGui.GetContentRegionAvail().X - ImGui.CalcTextSize(text).X) * 0.5f) + indent); + ImGui.TextUnformatted(text); + } + internal static bool TryToImGui(this VirtualKey key, out ImGuiKey result) { result = key switch { diff --git a/ChatTwo/Util/TabsUtil.cs b/ChatTwo/Util/TabsUtil.cs index ce1ae56..1f9cb2c 100755 --- a/ChatTwo/Util/TabsUtil.cs +++ b/ChatTwo/Util/TabsUtil.cs @@ -73,4 +73,53 @@ internal static class TabsUtil { [ChatType.NpcDialogue] = ChatSourceExt.All, }, }; + + internal static Dictionary MostlyPlayer => new() + { + // Special + [ChatType.Debug] = ChatSourceExt.All, + [ChatType.Urgent] = ChatSourceExt.All, + [ChatType.Notice] = ChatSourceExt.All, + // Chat + [ChatType.Say] = ChatSourceExt.All, + [ChatType.Yell] = ChatSourceExt.All, + [ChatType.Shout] = ChatSourceExt.All, + [ChatType.TellIncoming] = ChatSourceExt.All, + [ChatType.TellOutgoing] = ChatSourceExt.All, + [ChatType.Party] = ChatSourceExt.All, + [ChatType.CrossParty] = ChatSourceExt.All, + [ChatType.Alliance] = ChatSourceExt.All, + [ChatType.FreeCompany] = ChatSourceExt.All, + [ChatType.PvpTeam] = ChatSourceExt.All, + [ChatType.CrossLinkshell1] = ChatSourceExt.All, + [ChatType.CrossLinkshell2] = ChatSourceExt.All, + [ChatType.CrossLinkshell3] = ChatSourceExt.All, + [ChatType.CrossLinkshell4] = ChatSourceExt.All, + [ChatType.CrossLinkshell5] = ChatSourceExt.All, + [ChatType.CrossLinkshell6] = ChatSourceExt.All, + [ChatType.CrossLinkshell7] = ChatSourceExt.All, + [ChatType.CrossLinkshell8] = ChatSourceExt.All, + [ChatType.Linkshell1] = ChatSourceExt.All, + [ChatType.Linkshell2] = ChatSourceExt.All, + [ChatType.Linkshell3] = ChatSourceExt.All, + [ChatType.Linkshell4] = ChatSourceExt.All, + [ChatType.Linkshell5] = ChatSourceExt.All, + [ChatType.Linkshell6] = ChatSourceExt.All, + [ChatType.Linkshell7] = ChatSourceExt.All, + [ChatType.Linkshell8] = ChatSourceExt.All, + [ChatType.NoviceNetwork] = ChatSourceExt.All, + [ChatType.StandardEmote] = ChatSourceExt.All, + [ChatType.CustomEmote] = ChatSourceExt.All, + // Announcements + [ChatType.System] = ChatSourceExt.All, + [ChatType.Error] = ChatSourceExt.All, + [ChatType.Echo] = ChatSourceExt.All, + [ChatType.NoviceNetworkSystem] = ChatSourceExt.All, + [ChatType.FreeCompanyAnnouncement] = ChatSourceExt.All, + [ChatType.PvpTeamAnnouncement] = ChatSourceExt.All, + [ChatType.FreeCompanyLoginLogout] = ChatSourceExt.All, + [ChatType.PvpTeamLoginLogout] = ChatSourceExt.All, + [ChatType.RandomNumber] = ChatSourceExt.All, + [ChatType.MessageBook] = ChatSourceExt.All, + }; }