feat: add database details section to settings
Shows path to database (click to copy), database size, database log size, message count. Also adds a Ctrl+Shift button to wipe the database permanently. This is performed by clearing the Messages collection and then rebuilding the database, which brings it down to around 48KB on my machine (even with many messages).
This commit is contained in:
Generated
+90
@@ -1634,6 +1634,33 @@ namespace ChatTwo.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Clear the message history database.
|
||||
/// </summary>
|
||||
internal static string Options_ClearDatabase_Button {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_ClearDatabase_Button", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Successfully cleared the chat database.
|
||||
/// </summary>
|
||||
internal static string Options_ClearDatabase_Success {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_ClearDatabase_Success", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Removes all message history. Cannot be restored. Hold Ctrl+Shift to click..
|
||||
/// </summary>
|
||||
internal static string Options_ClearDatabase_Tooltip {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_ClearDatabase_Tooltip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Replace consecutive duplicate messages with a counter appended to the first instance of the message..
|
||||
/// </summary>
|
||||
@@ -1688,6 +1715,69 @@ namespace ChatTwo.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Click to copy database directory path.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_CopyConfigPath {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_CopyConfigPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copied database directory path to clipboard.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_CopyConfigPathNotification {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_CopyConfigPathNotification", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Database details:.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_Heading {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_Heading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log size: {0}.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_LogSize {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_LogSize", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Stored messages: {0:N0}/{1:N0}.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_MessageCount {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_MessageCount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Path: {0}.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_Path {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_Path", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Size: {0}.
|
||||
/// </summary>
|
||||
internal static string Options_Database_Metadata_Size {
|
||||
get {
|
||||
return ResourceManager.GetString("Options_Database_Metadata_Size", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Database.
|
||||
/// </summary>
|
||||
|
||||
@@ -952,4 +952,34 @@
|
||||
<data name="Options_HideInLoadingScreens_Description" xml:space="preserve">
|
||||
<value>Hide {0} during loading screens.</value>
|
||||
</data>
|
||||
<data name="Options_ClearDatabase_Button">
|
||||
<value>Clear the message history database</value>
|
||||
</data>
|
||||
<data name="Options_ClearDatabase_Success">
|
||||
<value>Successfully cleared the chat database</value>
|
||||
</data>
|
||||
<data name="Options_ClearDatabase_Tooltip">
|
||||
<value>Removes all message history. Cannot be restored. Hold Ctrl+Shift to click.</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_CopyConfigPath">
|
||||
<value>Click to copy database directory path</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_CopyConfigPathNotification">
|
||||
<value>Copied database directory path to clipboard</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_Heading">
|
||||
<value>Database details:</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_LogSize">
|
||||
<value>Log size: {0}</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_MessageCount">
|
||||
<value>Stored messages: {0:N0}/{1:N0}</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_Path">
|
||||
<value>Path: {0}</value>
|
||||
</data>
|
||||
<data name="Options_Database_Metadata_Size">
|
||||
<value>Size: {0}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
+32
-6
@@ -140,9 +140,6 @@ internal class Store : IDisposable {
|
||||
bson => DateTime.UnixEpoch.AddMilliseconds(bson.AsInt64)
|
||||
);
|
||||
Database = Connect();
|
||||
Messages.EnsureIndex(msg => msg.Date);
|
||||
Messages.EnsureIndex(msg => msg.SortCode);
|
||||
Messages.EnsureIndex(msg => msg.ExtraChatChannel);
|
||||
|
||||
Plugin.ChatGui.ChatMessageUnhandled += ChatMessage;
|
||||
Plugin.Framework.Update += GetMessageInfo;
|
||||
@@ -159,17 +156,26 @@ internal class Store : IDisposable {
|
||||
Database.Dispose();
|
||||
}
|
||||
|
||||
private ILiteDatabase Connect() {
|
||||
internal static string DatabasePath()
|
||||
{
|
||||
var dir = Plugin.Interface.ConfigDirectory;
|
||||
dir.Create();
|
||||
return Path.Join(dir.FullName, "chat.db");
|
||||
}
|
||||
|
||||
var dbPath = Path.Join(dir.FullName, "chat.db");
|
||||
private LiteDatabase Connect() {
|
||||
var dbPath = DatabasePath();
|
||||
var connection = Plugin.Config.SharedMode ? "shared" : "direct";
|
||||
var connString = $"Filename='{dbPath}';Connection={connection}";
|
||||
return new LiteDatabase(connString, BsonMapper.Global) {
|
||||
var conn = new LiteDatabase(connString, BsonMapper.Global) {
|
||||
CheckpointSize = 1_000,
|
||||
Timeout = TimeSpan.FromSeconds(1),
|
||||
};
|
||||
var messages = conn.GetCollection<Message>("messages");
|
||||
messages.EnsureIndex(msg => msg.Date);
|
||||
messages.EnsureIndex(msg => msg.SortCode);
|
||||
messages.EnsureIndex(msg => msg.ExtraChatChannel);
|
||||
return conn;
|
||||
}
|
||||
|
||||
internal void Reconnect() {
|
||||
@@ -177,6 +183,26 @@ internal class Store : IDisposable {
|
||||
Database = Connect();
|
||||
}
|
||||
|
||||
internal void ClearDatabase()
|
||||
{
|
||||
Messages.DeleteAll();
|
||||
Database.Rebuild();
|
||||
}
|
||||
|
||||
internal static long DatabaseSize()
|
||||
{
|
||||
var dbPath = DatabasePath();
|
||||
return !File.Exists(dbPath) ? 0 : new FileInfo(dbPath).Length;
|
||||
}
|
||||
|
||||
internal static long DatabaseLogSize()
|
||||
{
|
||||
var dbLogPath = Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat-log.db");
|
||||
return !File.Exists(dbLogPath) ? 0 : new FileInfo(dbLogPath).Length;
|
||||
}
|
||||
|
||||
internal int MessageCount() => Messages.Count();
|
||||
|
||||
private void Logout() {
|
||||
LastContentId = 0;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class SettingsWindow : Window, IUiComponent
|
||||
new Ui.SettingsTabs.Fonts(Mutable),
|
||||
new ChatColours(Mutable, Plugin),
|
||||
new Tabs(Plugin, Mutable),
|
||||
new Database(Mutable, Plugin.Store),
|
||||
new Database(Mutable, Plugin),
|
||||
new Miscellaneous(Mutable),
|
||||
new About(),
|
||||
};
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
using ChatTwo.Resources;
|
||||
using ChatTwo.Util;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace ChatTwo.Ui.SettingsTabs;
|
||||
|
||||
internal sealed class Database : ISettingsTab {
|
||||
private Configuration Mutable { get; }
|
||||
private Store Store { get; }
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
public string Name => Language.Options_Database_Tab + "###tabs-database";
|
||||
|
||||
internal Database(Configuration mutable, Store store) {
|
||||
Store = store;
|
||||
internal Database(Configuration mutable, Plugin plugin) {
|
||||
Plugin = plugin;
|
||||
Mutable = mutable;
|
||||
}
|
||||
|
||||
private bool _showAdvanced;
|
||||
|
||||
private long _databaseLastRefreshTicks;
|
||||
private long _databaseSize;
|
||||
private long _databaseLogSize;
|
||||
private int _databaseMessageCount;
|
||||
|
||||
public void Draw(bool changed) {
|
||||
if (changed) {
|
||||
_showAdvanced = ImGui.GetIO().KeyShift;
|
||||
@@ -46,18 +52,88 @@ internal sealed class Database : ISettingsTab {
|
||||
);
|
||||
ImGuiUtil.WarningText(string.Format(Language.Options_SharedMode_Warning, Plugin.PluginName));
|
||||
|
||||
ImGui.Spacing();
|
||||
ImGui.Separator();
|
||||
ImGui.Spacing();
|
||||
|
||||
if (_showAdvanced && ImGui.TreeNodeEx(Language.Options_Database_Advanced)) {
|
||||
ImGui.Text(Language.Options_Database_Metadata_Heading);
|
||||
var style = ImGui.GetStyle();
|
||||
ImGui.Indent(style.IndentSpacing);
|
||||
|
||||
// Refresh the database size and message count every 5 seconds to avoid
|
||||
// constant stat calls and spamming the database.
|
||||
if (_databaseLastRefreshTicks + 5 * 1000 < Environment.TickCount64)
|
||||
{
|
||||
_databaseSize = Store.DatabaseSize();
|
||||
_databaseLogSize = Store.DatabaseLogSize();
|
||||
_databaseMessageCount = Plugin.Store.MessageCount();
|
||||
_databaseLastRefreshTicks = Environment.TickCount64;
|
||||
}
|
||||
|
||||
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_Path, Store.DatabasePath()));
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
// Copy the directory path instead of the file path so people can
|
||||
// paste it into their file explorer.
|
||||
var path = Path.GetDirectoryName(Store.DatabasePath());
|
||||
ImGui.SetClipboardText(path);
|
||||
WrapperUtil.AddNotification(Language.Options_Database_Metadata_CopyConfigPathNotification, NotificationType.Info);
|
||||
}
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text(Language.Options_Database_Metadata_CopyConfigPath);
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_Size, StringUtil.BytesToString(_databaseSize)));
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text(_databaseSize.ToString("N0") + "B");
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_LogSize, StringUtil.BytesToString(_databaseLogSize)));
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text(_databaseLogSize.ToString("N0") + "B");
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_MessageCount, _databaseMessageCount, Store.MessagesLimit));
|
||||
|
||||
if (ImGuiUtil.CtrlShiftButton(Language.Options_ClearDatabase_Button, Language.Options_ClearDatabase_Tooltip))
|
||||
{
|
||||
Plugin.Log.Warning("Clearing database");
|
||||
Plugin.Store.ClearDatabase();
|
||||
foreach (var tab in Plugin.Config.Tabs)
|
||||
{
|
||||
tab.Clear();
|
||||
}
|
||||
// Refresh on next draw
|
||||
_databaseLastRefreshTicks = 0;
|
||||
WrapperUtil.AddNotification(Language.Options_ClearDatabase_Success, NotificationType.Info);
|
||||
}
|
||||
|
||||
ImGui.Unindent(style.IndentSpacing);
|
||||
ImGui.Spacing();
|
||||
|
||||
if (_showAdvanced && ImGui.TreeNodeEx(Language.Options_Database_Advanced))
|
||||
{
|
||||
ImGui.PushTextWrapPos();
|
||||
ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
|
||||
|
||||
if (ImGui.Button("Checkpoint")) {
|
||||
Store.Database.Checkpoint();
|
||||
if (ImGuiUtil.CtrlShiftButton("Checkpoint", "Ctrl+Shift: Database.Checkpoint()"))
|
||||
{
|
||||
Plugin.Store.Database.Checkpoint();
|
||||
}
|
||||
|
||||
if (ImGui.Button("Rebuild")) {
|
||||
Store.Database.Rebuild();
|
||||
if (ImGuiUtil.CtrlShiftButton("Rebuild", "Ctrl+Shift: Database.Rebuild()"))
|
||||
{
|
||||
Plugin.Store.Database.Rebuild();
|
||||
}
|
||||
|
||||
ImGui.PopTextWrapPos();
|
||||
|
||||
@@ -234,6 +234,22 @@ internal static class ImGuiUtil {
|
||||
return r;
|
||||
}
|
||||
|
||||
internal static bool CtrlShiftButton(string label, string tooltip = "")
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
var ctrlShiftHeld = io.KeyCtrl && io.KeyShift;
|
||||
if (!ctrlShiftHeld) ImGui.BeginDisabled();
|
||||
var ret = ImGui.Button(label) && ctrlShiftHeld;
|
||||
if (!ctrlShiftHeld) ImGui.EndDisabled();
|
||||
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) {
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.TextUnformatted(tooltip);
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static bool TryToImGui(this VirtualKey key, out ImGuiKey result) {
|
||||
result = key switch {
|
||||
VirtualKey.NO_KEY => ImGuiKey.None,
|
||||
|
||||
@@ -10,4 +10,16 @@ internal static class StringUtil {
|
||||
bytes[^1] = 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Taken from https://stackoverflow.com/a/4975942
|
||||
internal static String BytesToString(long byteCount) {
|
||||
string[] suf = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; // Longs run out around EB
|
||||
if (byteCount == 0)
|
||||
return "0" + suf[0];
|
||||
|
||||
var bytes = Math.Abs(byteCount);
|
||||
var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
|
||||
var num = Math.Round(bytes / Math.Pow(1024, place), 1);
|
||||
return (Math.Sign(byteCount) * num).ToString("N0") + suf[place];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user