Merge pull request #27

feat: add LiteDB => Sqlite importer
This commit is contained in:
Infi
2024-04-21 21:04:11 +02:00
committed by GitHub
11 changed files with 808 additions and 14 deletions
+201
View File
@@ -0,0 +1,201 @@
using System.Numerics;
using ChatTwo.Resources;
using ChatTwo.Util;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Interface.Windowing;
using ImGuiNET;
namespace ChatTwo.Ui;
internal class LegacyMesasgeImporterWindow : Window
{
private readonly MessageStore _store;
private LegacyMessageImporterEligibility Eligibility { get; set; }
private LegacyMessageImporter? Importer { get; set; }
internal LegacyMesasgeImporterWindow(MessageStore store) : base("Chat 2 Legacy Importer###chat2-legacy-importer")
{
_store = store;
Eligibility = LegacyMessageImporterEligibility.CheckEligibility();
LogAndNotify();
}
public void Dispose()
{
Importer?.Dispose();
}
private void LogAndNotify()
{
Plugin.Log.Info(
$"[Migration] Checked migration eligibility: {Eligibility.Status} - '{Eligibility.AdditionalIneligibilityInfo}'");
switch (Eligibility.Status)
{
case LegacyMessageImporterEligibilityStatus.Eligible:
{
var notification = Plugin.Notification.AddNotification(new Notification
{
Type = NotificationType.Info,
// The user needs to dismiss this for it to go away.
InitialDuration = TimeSpan.FromHours(6),
Title = "Chat 2 Migration",
Content = "Import messages from old database into new database? Click for more information.",
});
// TODO: clicking does not dismiss
notification.Click += _ => IsOpen = true;
break;
}
case LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed:
{
var notification = Plugin.Notification.AddNotification(new Notification
{
Type = NotificationType.Warning,
InitialDuration = TimeSpan.FromMinutes(1),
Title = "Chat Two Migration",
Content =
"Migration is not possible because the old database could not be opened. Click for more information."
});
// TODO: clicking does not dismiss
notification.Click += _ => IsOpen = true;
break;
}
}
}
public override void Draw()
{
if (Importer != null)
{
DrawImportStatus();
return;
}
if (Eligibility.Status == LegacyMessageImporterEligibilityStatus.Eligible)
DrawEligible();
else
DrawIneligible();
}
private void DrawEligible()
{
// TODO: pretty
ImGui.Text("Import database messages from legacy LiteDB database to Sqlite database?");
ImGui.Text($"Message count: {Eligibility.MessageCount}");
ImGui.Text($"Database size: {Eligibility.DatabaseSizeBytes}");
if (ImGui.Button("Yes, import messages"))
{
// Next draw call will run DrawImportStatus().
Importer = Eligibility.StartImport(_store);
return;
}
ImGui.SameLine();
if (ImGuiUtil.CtrlShiftButton("No, do not import messages",
"Ctrl+Shift: renames old database to avoid prompting again"))
{
Eligibility.RenameOldDatabase();
IsOpen = false;
}
}
private void DrawIneligible()
{
// TODO: pretty
ImGui.Text("Your legacy LiteDB database is not eligible for import:");
switch (Eligibility.Status)
{
case LegacyMessageImporterEligibilityStatus.IneligibleOriginalDbNotExists:
ImGui.Text("The old database could not be found.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleMigrationDbExists:
ImGui.Text("The migration process was already started.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed:
ImGui.Text("The old database could not be opened.");
break;
case LegacyMessageImporterEligibilityStatus.IneligibleNoMessages:
ImGui.Text("The old database contains no messages.");
break;
case LegacyMessageImporterEligibilityStatus.Eligible:
default:
throw new ArgumentOutOfRangeException();
}
if (!string.IsNullOrWhiteSpace(Eligibility.AdditionalIneligibilityInfo))
ImGui.Text(Eligibility.AdditionalIneligibilityInfo);
// LiteDB failures notify the user, so give them a chance to rename the
// database to avoid prompting again.
if (Eligibility.Status == LegacyMessageImporterEligibilityStatus.IneligibleLiteDbFailed)
{
if (ImGuiUtil.CtrlShiftButton("Rename old database",
"Ctrl+Shift: rename old database to avoid import prompt in the future"))
{
Eligibility.RenameOldDatabase();
// TODO: notify success as this changes the status
}
}
}
private void DrawImportStatus()
{
// TODO: pretty
if (Importer == null)
return;
var importStart = Importer.ImportStart;
var importEnd = Importer.ImportComplete;
var total = Importer.ImportCount;
var successful = Importer!.SuccessfulMessages;
var failed = Importer.FailedMessages;
var remaining = Importer.RemainingMessages;
if (importEnd != null)
{
ImGui.Text($"Completed migration in {Duration(importStart, importEnd.Value)}");
ImGui.Text($"Successfully imported: {successful} messages");
ImGui.Text($"Failed to import: {failed} messages");
ImGui.Text($"Unaccounted for: {remaining}");
ImGui.Text("See logs for more details: /xllog");
return;
}
// TODO: implement Importer.MaxMessageRate slider in UI, values 0 (infinity) => 10000
ImGui.Text($"Importing messages... {Importer.Progress:P}%");
ImGui.Text($"Duration: {Duration(importStart, Environment.TickCount64)}");
ImGui.Text($"Successfully imported: {successful} messages");
ImGui.Text($"Failed to import: {failed} messages");
ImGui.Text($"Progress: {Importer.ProcessedMessages}/{total} messages");
ImGui.Text($"Remaining: {remaining} messages");
ImGui.Text($"Messages per second: {Importer.CurrentMessageRate}");
ImGui.Text($"Estimated time remaining: {Importer.EstimatedTimeRemaining}");
ImGui.Text("See logs for more details: /xllog");
// TODO: this doesn't render properly
ImGui.ProgressBar(Importer.Progress, new Vector2(0.0f, 0.0f), $"{Importer.Progress:P}%");
if (ImGuiUtil.CtrlShiftButton("Cancel import", "Ctrl+Shift: cancel import and close window"))
{
// TODO: This currently crashes the whole game because we don't ask
// the importer thread to stop and wait for it to stop before
// disposing it.
// See LegacyMessageImporter.Dispose() for more details.
/*
Importer.Dispose();
Importer = null;
Eligibility = LegacyMessageImporterEligibility.CheckEligibility();
*/
}
}
private static TimeSpan Duration(long startTicks, long endTicks)
{
return endTicks < startTicks ? TimeSpan.Zero : TimeSpan.FromTicks(endTicks - startTicks);
}
}