Db: Migrate ImportHistory to sqlite
This commit is contained in:
parent
7e2ccd3b42
commit
c7d5aa1eaa
@ -7,6 +7,8 @@ using System.Text.Json;
|
|||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
using NJson = Newtonsoft.Json;
|
using NJson = Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Pal.Client.Configuration
|
namespace Pal.Client.Configuration
|
||||||
@ -14,12 +16,14 @@ namespace Pal.Client.Configuration
|
|||||||
internal sealed class ConfigurationManager
|
internal sealed class ConfigurationManager
|
||||||
{
|
{
|
||||||
private readonly DalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
public event EventHandler<IPalacePalConfiguration>? Saved;
|
public event EventHandler<IPalacePalConfiguration>? Saved;
|
||||||
|
|
||||||
public ConfigurationManager(DalamudPluginInterface pluginInterface)
|
public ConfigurationManager(DalamudPluginInterface pluginInterface, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
|
||||||
Migrate();
|
Migrate();
|
||||||
}
|
}
|
||||||
@ -61,6 +65,26 @@ namespace Pal.Client.Configuration
|
|||||||
var v7 = MigrateToV7(configurationV1);
|
var v7 = MigrateToV7(configurationV1);
|
||||||
Save(v7, queue: false);
|
Save(v7, queue: false);
|
||||||
|
|
||||||
|
using (var scope = _serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
dbContext.Imports.RemoveRange(dbContext.Imports);
|
||||||
|
|
||||||
|
foreach (var importHistory in configurationV1.ImportHistory)
|
||||||
|
{
|
||||||
|
PluginLog.Information($"Migrating import {importHistory.Id}");
|
||||||
|
dbContext.Imports.Add(new ImportHistory
|
||||||
|
{
|
||||||
|
Id = importHistory.Id,
|
||||||
|
RemoteUrl = importHistory.RemoteUrl?.Replace(".μ.tv", ".liza.sh"),
|
||||||
|
ExportedAt = importHistory.ExportedAt,
|
||||||
|
ImportedAt = importHistory.ImportedAt
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
File.Move(_pluginInterface.ConfigFile.FullName, _pluginInterface.ConfigFile.FullName + ".old", true);
|
File.Move(_pluginInterface.ConfigFile.FullName, _pluginInterface.ConfigFile.FullName + ".old", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,6 @@ namespace Pal.Client.Configuration
|
|||||||
DeepDungeonConfiguration DeepDungeons { get; set; }
|
DeepDungeonConfiguration DeepDungeons { get; set; }
|
||||||
RendererConfiguration Renderer { get; set; }
|
RendererConfiguration Renderer { get; set; }
|
||||||
|
|
||||||
[Obsolete]
|
|
||||||
List<ConfigurationV1.ImportHistoryEntry> ImportHistory { get; }
|
|
||||||
|
|
||||||
IAccountConfiguration CreateAccount(string server, Guid accountId);
|
IAccountConfiguration CreateAccount(string server, Guid accountId);
|
||||||
IAccountConfiguration? FindAccount(string server);
|
IAccountConfiguration? FindAccount(string server);
|
||||||
void RemoveAccount(string server);
|
void RemoveAccount(string server);
|
||||||
|
12
Pal.Client/Database/ImportHistory.cs
Normal file
12
Pal.Client/Database/ImportHistory.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Pal.Client.Database
|
||||||
|
{
|
||||||
|
internal sealed class ImportHistory
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string? RemoteUrl { get; set; }
|
||||||
|
public DateTime ExportedAt { get; set; }
|
||||||
|
public DateTime ImportedAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
45
Pal.Client/Database/Migrations/20230216154417_AddImportHistory.Designer.cs
generated
Normal file
45
Pal.Client/Database/Migrations/20230216154417_AddImportHistory.Designer.cs
generated
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Pal.Client.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PalClientContext))]
|
||||||
|
[Migration("20230216154417_AddImportHistory")]
|
||||||
|
partial class AddImportHistory
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.3");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Pal.Client.Database.ImportHistory", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExportedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ImportedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RemoteUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Imports");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Pal.Client.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddImportHistory : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Imports",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
RemoteUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ExportedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
ImportedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Imports", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Imports");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Pal.Client.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PalClientContext))]
|
||||||
|
partial class PalClientContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.3");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Pal.Client.Database.ImportHistory", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExportedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ImportedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RemoteUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Imports");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Pal.Client/Database/PalClientContext.cs
Normal file
14
Pal.Client/Database/PalClientContext.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Pal.Client.Database
|
||||||
|
{
|
||||||
|
internal class PalClientContext : DbContext
|
||||||
|
{
|
||||||
|
public DbSet<ImportHistory> Imports { get; set; } = null!;
|
||||||
|
|
||||||
|
public PalClientContext(DbContextOptions<PalClientContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Pal.Client/Database/PalClientContextFactory.cs
Normal file
20
Pal.Client/Database/PalClientContextFactory.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#if EF
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
|
namespace Pal.Client.Database
|
||||||
|
{
|
||||||
|
internal sealed class PalClientContextFactory : IDesignTimeDbContextFactory<PalClientContext>
|
||||||
|
{
|
||||||
|
public PalClientContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var optionsBuilder =
|
||||||
|
new DbContextOptionsBuilder<PalClientContext>().UseSqlite(
|
||||||
|
$"Data Source={Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "pluginConfigs", "Palace Pal", "palace-pal.data.sqlite3")}");
|
||||||
|
return new PalClientContext(optionsBuilder.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
58
Pal.Client/DependencyInjection/ImportService.cs
Normal file
58
Pal.Client/DependencyInjection/ImportService.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
namespace Pal.Client.DependencyInjection
|
||||||
|
{
|
||||||
|
internal sealed class ImportService
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public ImportService(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(ImportHistory history)
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
dbContext.Imports.Add(history);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImportHistory? FindLast()
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
return dbContext.Imports.OrderByDescending(x => x.ImportedAt).ThenBy(x => x.Id).FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ImportHistory> FindForServer(string server)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(server))
|
||||||
|
return new();
|
||||||
|
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
return dbContext.Imports.Where(x => x.RemoteUrl == server).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAllByIds(List<Guid> ids)
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
dbContext.RemoveRange(dbContext.Imports.Where(x => ids.Contains(x.Id)));
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveById(Guid id)
|
||||||
|
=> RemoveAllByIds(new List<Guid> { id });
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,8 @@
|
|||||||
using System.Globalization;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState;
|
using Dalamud.Game.ClientState;
|
||||||
@ -7,17 +11,22 @@ using Dalamud.Game.ClientState.Objects;
|
|||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.Logging;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Pal.Client.Commands;
|
using Pal.Client.Commands;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.Net;
|
using Pal.Client.Net;
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
using Pal.Client.Rendering;
|
using Pal.Client.Rendering;
|
||||||
using Pal.Client.Scheduled;
|
using Pal.Client.Scheduled;
|
||||||
using Pal.Client.Windows;
|
using Pal.Client.Windows;
|
||||||
|
|
||||||
namespace Pal.Client.DependencyInjection
|
namespace Pal.Client
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DI-aware Plugin.
|
/// DI-aware Plugin.
|
||||||
@ -25,6 +34,8 @@ namespace Pal.Client.DependencyInjection
|
|||||||
// ReSharper disable once UnusedType.Global
|
// ReSharper disable once UnusedType.Global
|
||||||
internal sealed class DependencyInjectionContext : IDalamudPlugin
|
internal sealed class DependencyInjectionContext : IDalamudPlugin
|
||||||
{
|
{
|
||||||
|
private readonly string _sqliteConnectionString;
|
||||||
|
private readonly CancellationTokenSource _initCts = new();
|
||||||
private ServiceProvider? _serviceProvider;
|
private ServiceProvider? _serviceProvider;
|
||||||
|
|
||||||
public string Name => Localization.Palace_Pal;
|
public string Name => Localization.Palace_Pal;
|
||||||
@ -39,6 +50,9 @@ namespace Pal.Client.DependencyInjection
|
|||||||
CommandManager commandManager,
|
CommandManager commandManager,
|
||||||
DataManager dataManager)
|
DataManager dataManager)
|
||||||
{
|
{
|
||||||
|
PluginLog.Information("Building service container");
|
||||||
|
|
||||||
|
CancellationToken token = _initCts.Token;
|
||||||
IServiceCollection services = new ServiceCollection();
|
IServiceCollection services = new ServiceCollection();
|
||||||
|
|
||||||
// dalamud
|
// dalamud
|
||||||
@ -54,6 +68,11 @@ namespace Pal.Client.DependencyInjection
|
|||||||
services.AddSingleton(dataManager);
|
services.AddSingleton(dataManager);
|
||||||
services.AddSingleton(new WindowSystem(typeof(DependencyInjectionContext).AssemblyQualifiedName));
|
services.AddSingleton(new WindowSystem(typeof(DependencyInjectionContext).AssemblyQualifiedName));
|
||||||
|
|
||||||
|
// EF core
|
||||||
|
_sqliteConnectionString =
|
||||||
|
$"Data Source={Path.Join(pluginInterface.GetPluginConfigDirectory(), "palace-pal.data.sqlite3")}";
|
||||||
|
services.AddDbContext<PalClientContext>(o => o.UseSqlite(_sqliteConnectionString));
|
||||||
|
|
||||||
// plugin-specific
|
// plugin-specific
|
||||||
services.AddSingleton<Plugin>();
|
services.AddSingleton<Plugin>();
|
||||||
services.AddSingleton<DebugState>();
|
services.AddSingleton<DebugState>();
|
||||||
@ -64,11 +83,12 @@ namespace Pal.Client.DependencyInjection
|
|||||||
services.AddTransient<RepoVerification>();
|
services.AddTransient<RepoVerification>();
|
||||||
services.AddSingleton<PalCommand>();
|
services.AddSingleton<PalCommand>();
|
||||||
|
|
||||||
// territory handling
|
// territory & marker related services
|
||||||
services.AddSingleton<TerritoryState>();
|
services.AddSingleton<TerritoryState>();
|
||||||
services.AddSingleton<FrameworkService>();
|
services.AddSingleton<FrameworkService>();
|
||||||
services.AddSingleton<ChatService>();
|
services.AddSingleton<ChatService>();
|
||||||
services.AddSingleton<FloorService>();
|
services.AddSingleton<FloorService>();
|
||||||
|
services.AddSingleton<ImportService>();
|
||||||
|
|
||||||
// windows & related services
|
// windows & related services
|
||||||
services.AddSingleton<AgreementWindow>();
|
services.AddSingleton<AgreementWindow>();
|
||||||
@ -97,7 +117,7 @@ namespace Pal.Client.DependencyInjection
|
|||||||
ValidateScopes = true,
|
ValidateScopes = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// initialize plugin
|
|
||||||
#if RELEASE
|
#if RELEASE
|
||||||
// You're welcome to remove this code in your fork, but please make sure that:
|
// You're welcome to remove this code in your fork, but please make sure that:
|
||||||
// - none of the links accessible within FFXIV open the original repo (e.g. in the plugin installer), and
|
// - none of the links accessible within FFXIV open the original repo (e.g. in the plugin installer), and
|
||||||
@ -108,8 +128,33 @@ namespace Pal.Client.DependencyInjection
|
|||||||
_serviceProvider.GetService<RepoVerification>();
|
_serviceProvider.GetService<RepoVerification>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// This is not ideal as far as loading the plugin goes, because there's no way to check for errors and
|
||||||
|
// tell Dalamud that no, the plugin isn't ready -- so the plugin will count as properly initialized,
|
||||||
|
// even if it's not.
|
||||||
|
//
|
||||||
|
// There's 2-3 seconds of slowdown primarily caused by the sqlite init, but that needs to happen for
|
||||||
|
// config stuff.
|
||||||
|
PluginLog.Information("Service container built, triggering async init");
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PluginLog.Information("Starting async init");
|
||||||
|
|
||||||
|
// initialize database
|
||||||
|
await using (var scope = _serviceProvider.CreateAsyncScope())
|
||||||
|
{
|
||||||
|
PluginLog.Log("Loading database & running migrations");
|
||||||
|
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
await dbContext.Database.MigrateAsync();
|
||||||
|
|
||||||
|
PluginLog.Log("Completed database migrations");
|
||||||
|
}
|
||||||
|
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// set up legacy services
|
// set up legacy services
|
||||||
LocalState.PluginInterface = pluginInterface;
|
LocalState.PluginConfigDirectory = pluginInterface.GetPluginConfigDirectory();
|
||||||
LocalState.Mode = _serviceProvider.GetRequiredService<IPalacePalConfiguration>().Mode;
|
LocalState.Mode = _serviceProvider.GetRequiredService<IPalacePalConfiguration>().Mode;
|
||||||
|
|
||||||
// windows that have logic to open on startup
|
// windows that have logic to open on startup
|
||||||
@ -121,11 +166,23 @@ namespace Pal.Client.DependencyInjection
|
|||||||
_serviceProvider.GetRequiredService<FrameworkService>();
|
_serviceProvider.GetRequiredService<FrameworkService>();
|
||||||
_serviceProvider.GetRequiredService<ChatService>();
|
_serviceProvider.GetRequiredService<ChatService>();
|
||||||
|
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
_serviceProvider.GetRequiredService<Plugin>();
|
_serviceProvider.GetRequiredService<Plugin>();
|
||||||
|
|
||||||
|
PluginLog.Information("Async init complete");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
PluginLog.Error(e, "Async load failed");
|
||||||
|
chatGui.PrintError($"Async loading failed: {e}");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_initCts.Cancel();
|
||||||
|
|
||||||
// ensure we're not calling dispose recursively on ourselves
|
// ensure we're not calling dispose recursively on ourselves
|
||||||
if (_serviceProvider != null)
|
if (_serviceProvider != null)
|
||||||
{
|
{
|
||||||
@ -133,6 +190,10 @@ namespace Pal.Client.DependencyInjection
|
|||||||
_serviceProvider = null;
|
_serviceProvider = null;
|
||||||
|
|
||||||
serviceProvider.Dispose();
|
serviceProvider.Dispose();
|
||||||
|
|
||||||
|
// ensure we're not keeping the file open longer than the plugin is loaded
|
||||||
|
using (SqliteConnection sqliteConnection = new(_sqliteConnectionString))
|
||||||
|
SqliteConnection.ClearPool(sqliteConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,6 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ namespace Pal.Client
|
|||||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new() { IncludeFields = true };
|
private static readonly JsonSerializerOptions JsonSerializerOptions = new() { IncludeFields = true };
|
||||||
private const int CurrentVersion = 4;
|
private const int CurrentVersion = 4;
|
||||||
|
|
||||||
internal static DalamudPluginInterface PluginInterface { get; set; }
|
internal static string PluginConfigDirectory { get; set; } = null!;
|
||||||
internal static EMode Mode { get; set; }
|
internal static EMode Mode { get; set; }
|
||||||
|
|
||||||
public uint TerritoryType { get; set; }
|
public uint TerritoryType { get; set; }
|
||||||
@ -126,7 +125,7 @@ namespace Pal.Client
|
|||||||
|
|
||||||
public string GetSaveLocation() => GetSaveLocation(TerritoryType);
|
public string GetSaveLocation() => GetSaveLocation(TerritoryType);
|
||||||
|
|
||||||
private static string GetSaveLocation(uint territoryType) => Path.Join(PluginInterface.GetPluginConfigDirectory(), $"{territoryType}.json");
|
private static string GetSaveLocation(uint territoryType) => Path.Join(PluginConfigDirectory, $"{territoryType}.json");
|
||||||
|
|
||||||
public static void ForEach(Action<LocalState> action)
|
public static void ForEach(Action<LocalState> action)
|
||||||
{
|
{
|
||||||
|
@ -30,10 +30,6 @@
|
|||||||
|
|
||||||
<ItemGroup Condition="'$(Configuration)' == 'Release' And Exists('Certificate.pfx')">
|
<ItemGroup Condition="'$(Configuration)' == 'Release' And Exists('Certificate.pfx')">
|
||||||
<EmbeddedResource Include="Certificate.pfx" />
|
<EmbeddedResource Include="Certificate.pfx" />
|
||||||
<EmbeddedResource Update="Properties\Localization.resx">
|
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
|
||||||
<LastGenOutput>Localization.Designer.cs</LastGenOutput>
|
|
||||||
</EmbeddedResource>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -68,35 +64,39 @@
|
|||||||
<!--You may need to adjust these paths yourself. These point to a Dalamud assembly in AppData.-->
|
<!--You may need to adjust these paths yourself. These point to a Dalamud assembly in AppData.-->
|
||||||
<Reference Include="Dalamud">
|
<Reference Include="Dalamud">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="ImGui.NET">
|
<Reference Include="ImGui.NET">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="ImGuiScene">
|
<Reference Include="ImGuiScene">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Lumina">
|
<Reference Include="Lumina">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Lumina.Excel">
|
<Reference Include="Lumina.Excel">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Newtonsoft.Json.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="FFXIVClientStructs">
|
<Reference Include="FFXIVClientStructs">
|
||||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
|
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
|
||||||
<Private>false</Private>
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Update="Properties\Localization.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Localization.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
<Compile Update="Properties\Localization.Designer.cs">
|
<Compile Update="Properties\Localization.Designer.cs">
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
@ -104,8 +104,11 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
|
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin" Condition="'$(Configuration)' == 'Release'">
|
||||||
<Exec Command="rename "$(OutDir)$(AssemblyName)\latest.zip" "$(AssemblyName)-$(Version).zip"" />
|
<Exec Command="rename "$(OutDir)$(AssemblyName)\latest.zip" "$(AssemblyName)-$(Version).zip"" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="Clean">
|
||||||
|
<RemoveDir Directories="dist" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -5,7 +5,6 @@ using Pal.Client.Windows;
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Logging;
|
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
using ECommons;
|
using ECommons;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -31,8 +30,6 @@ namespace Pal.Client
|
|||||||
IPalacePalConfiguration configuration,
|
IPalacePalConfiguration configuration,
|
||||||
RenderAdapter renderAdapter)
|
RenderAdapter renderAdapter)
|
||||||
{
|
{
|
||||||
PluginLog.Information("Initializing Palace Pal");
|
|
||||||
|
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
15
Pal.Client/README.md
Normal file
15
Pal.Client/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Palace Pal
|
||||||
|
|
||||||
|
## Client Build Notes
|
||||||
|
|
||||||
|
### Database Migrations
|
||||||
|
|
||||||
|
Since EF core needs all dll files to be present, including Dalamud ones,
|
||||||
|
there's a special `EF` configuration that exempts them from setting
|
||||||
|
`<Private>false</Private>` during the build.
|
||||||
|
|
||||||
|
To use with `dotnet ef` commands, specify it as `-c EF`, for example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
dotnet ef migrations add MigrationName --configuration EF
|
||||||
|
```
|
@ -7,10 +7,11 @@ using System.Linq;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Database;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
|
using Pal.Client.Windows;
|
||||||
|
|
||||||
namespace Pal.Client.Scheduled
|
namespace Pal.Client.Scheduled
|
||||||
{
|
{
|
||||||
@ -30,17 +31,20 @@ namespace Pal.Client.Scheduled
|
|||||||
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedImport>
|
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedImport>
|
||||||
{
|
{
|
||||||
private readonly ChatGui _chatGui;
|
private readonly ChatGui _chatGui;
|
||||||
private readonly IPalacePalConfiguration _configuration;
|
|
||||||
private readonly ConfigurationManager _configurationManager;
|
|
||||||
private readonly FloorService _floorService;
|
private readonly FloorService _floorService;
|
||||||
|
private readonly ImportService _importService;
|
||||||
|
private readonly ConfigWindow _configWindow;
|
||||||
|
|
||||||
public Handler(ChatGui chatGui, IPalacePalConfiguration configuration,
|
public Handler(
|
||||||
ConfigurationManager configurationManager, FloorService floorService)
|
ChatGui chatGui,
|
||||||
|
FloorService floorService,
|
||||||
|
ImportService importService,
|
||||||
|
ConfigWindow configWindow)
|
||||||
{
|
{
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
_configuration = configuration;
|
|
||||||
_configurationManager = configurationManager;
|
|
||||||
_floorService = floorService;
|
_floorService = floorService;
|
||||||
|
_importService = importService;
|
||||||
|
_configWindow = configWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(QueuedImport import, ref bool recreateLayout, ref bool saveMarkers)
|
protected override void Run(QueuedImport import, ref bool recreateLayout, ref bool saveMarkers)
|
||||||
@ -53,11 +57,9 @@ namespace Pal.Client.Scheduled
|
|||||||
if (!Validate(import))
|
if (!Validate(import))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var oldExportIds = string.IsNullOrEmpty(import.Export.ServerUrl)
|
List<Guid> oldExportIds = _importService.FindForServer(import.Export.ServerUrl)
|
||||||
? _configuration.ImportHistory.Where(x => x.RemoteUrl == import.Export.ServerUrl)
|
|
||||||
.Select(x => x.Id)
|
.Select(x => x.Id)
|
||||||
.Where(x => x != Guid.Empty).ToList()
|
.ToList();
|
||||||
: new List<Guid>();
|
|
||||||
|
|
||||||
foreach (var remoteFloor in import.Export.Floors)
|
foreach (var remoteFloor in import.Export.Floors)
|
||||||
{
|
{
|
||||||
@ -70,17 +72,19 @@ namespace Pal.Client.Scheduled
|
|||||||
localState.Save();
|
localState.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
_configuration.ImportHistory.RemoveAll(hist =>
|
_importService.RemoveAllByIds(oldExportIds);
|
||||||
oldExportIds.Contains(hist.Id) || hist.Id == import.ExportId);
|
_importService.RemoveById(import.ExportId);
|
||||||
_configuration.ImportHistory.Add(new ConfigurationV1.ImportHistoryEntry
|
_importService.Add(new ImportHistory
|
||||||
{
|
{
|
||||||
Id = import.ExportId,
|
Id = import.ExportId,
|
||||||
RemoteUrl = import.Export.ServerUrl,
|
RemoteUrl = import.Export.ServerUrl,
|
||||||
ExportedAt = import.Export.CreatedAt.ToDateTime(),
|
ExportedAt = import.Export.CreatedAt.ToDateTime(),
|
||||||
ImportedAt = DateTime.UtcNow,
|
ImportedAt = DateTime.UtcNow,
|
||||||
});
|
});
|
||||||
_configurationManager.Save(_configuration);
|
_configWindow.UpdateLastImport();
|
||||||
|
|
||||||
|
PluginLog.Information(
|
||||||
|
$"Imported {import.ExportId} for {import.ImportedTraps} traps, {import.ImportedHoardCoffers} hoard coffers");
|
||||||
_chatGui.PalMessage(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
|
_chatGui.PalMessage(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
|
||||||
import.ImportedHoardCoffers));
|
import.ImportedHoardCoffers));
|
||||||
}
|
}
|
||||||
@ -101,9 +105,9 @@ namespace Pal.Client.Scheduled
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Guid.TryParse(import.Export.ExportId, out Guid exportId) || import.ExportId == Guid.Empty)
|
if (!Guid.TryParse(import.Export.ExportId, out Guid exportId) || exportId == Guid.Empty)
|
||||||
{
|
{
|
||||||
PluginLog.Error("Import: Invalid export id");
|
PluginLog.Error($"Import: Invalid export id ({import.Export.ExportId})");
|
||||||
_chatGui.PalError(Localization.Error_ImportFailed_InvalidFile);
|
_chatGui.PalError(Localization.Error_ImportFailed_InvalidFile);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Windows;
|
||||||
using Pal.Common;
|
using Pal.Common;
|
||||||
|
|
||||||
namespace Pal.Client.Scheduled
|
namespace Pal.Client.Scheduled
|
||||||
@ -17,13 +18,15 @@ namespace Pal.Client.Scheduled
|
|||||||
|
|
||||||
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedUndoImport>
|
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedUndoImport>
|
||||||
{
|
{
|
||||||
private readonly IPalacePalConfiguration _configuration;
|
private readonly ImportService _importService;
|
||||||
private readonly FloorService _floorService;
|
private readonly FloorService _floorService;
|
||||||
|
private readonly ConfigWindow _configWindow;
|
||||||
|
|
||||||
public Handler(IPalacePalConfiguration configuration, FloorService floorService)
|
public Handler(ImportService importService, FloorService floorService, ConfigWindow configWindow)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_importService = importService;
|
||||||
_floorService = floorService;
|
_floorService = floorService;
|
||||||
|
_configWindow = configWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(QueuedUndoImport queued, ref bool recreateLayout, ref bool saveMarkers)
|
protected override void Run(QueuedUndoImport queued, ref bool recreateLayout, ref bool saveMarkers)
|
||||||
@ -38,7 +41,8 @@ namespace Pal.Client.Scheduled
|
|||||||
localState.Save();
|
localState.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
_configuration.ImportHistory.RemoveAll(hist => hist.Id == queued.ExportId);
|
_importService.RemoveById(queued.ExportId);
|
||||||
|
_configWindow.UpdateLastImport();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ using System.Threading.Tasks;
|
|||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.Database;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ namespace Pal.Client.Windows
|
|||||||
private readonly DebugState _debugState;
|
private readonly DebugState _debugState;
|
||||||
private readonly ChatGui _chatGui;
|
private readonly ChatGui _chatGui;
|
||||||
private readonly RemoteApi _remoteApi;
|
private readonly RemoteApi _remoteApi;
|
||||||
|
private readonly ImportService _importService;
|
||||||
|
|
||||||
private int _mode;
|
private int _mode;
|
||||||
private int _renderer;
|
private int _renderer;
|
||||||
@ -55,6 +57,7 @@ namespace Pal.Client.Windows
|
|||||||
private string? _saveExportDialogStartPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
private string? _saveExportDialogStartPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||||
private readonly FileDialogManager _importDialog;
|
private readonly FileDialogManager _importDialog;
|
||||||
private readonly FileDialogManager _exportDialog;
|
private readonly FileDialogManager _exportDialog;
|
||||||
|
private ImportHistory? _lastImport;
|
||||||
|
|
||||||
private CancellationTokenSource? _testConnectionCts;
|
private CancellationTokenSource? _testConnectionCts;
|
||||||
|
|
||||||
@ -68,7 +71,8 @@ namespace Pal.Client.Windows
|
|||||||
FloorService floorService,
|
FloorService floorService,
|
||||||
DebugState debugState,
|
DebugState debugState,
|
||||||
ChatGui chatGui,
|
ChatGui chatGui,
|
||||||
RemoteApi remoteApi)
|
RemoteApi remoteApi,
|
||||||
|
ImportService importService)
|
||||||
: base(WindowId)
|
: base(WindowId)
|
||||||
{
|
{
|
||||||
_windowSystem = windowSystem;
|
_windowSystem = windowSystem;
|
||||||
@ -81,6 +85,7 @@ namespace Pal.Client.Windows
|
|||||||
_debugState = debugState;
|
_debugState = debugState;
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
_remoteApi = remoteApi;
|
_remoteApi = remoteApi;
|
||||||
|
_importService = importService;
|
||||||
|
|
||||||
LanguageChanged();
|
LanguageChanged();
|
||||||
|
|
||||||
@ -117,6 +122,8 @@ namespace Pal.Client.Windows
|
|||||||
_hoardConfig = new ConfigurableMarker(_configuration.DeepDungeons.HoardCoffers);
|
_hoardConfig = new ConfigurableMarker(_configuration.DeepDungeons.HoardCoffers);
|
||||||
_silverConfig = new ConfigurableMarker(_configuration.DeepDungeons.SilverCoffers);
|
_silverConfig = new ConfigurableMarker(_configuration.DeepDungeons.SilverCoffers);
|
||||||
_connectionText = null;
|
_connectionText = null;
|
||||||
|
|
||||||
|
UpdateLastImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnClose()
|
public override void OnClose()
|
||||||
@ -278,13 +285,14 @@ namespace Pal.Client.Windows
|
|||||||
DoImport(_openImportPath);
|
DoImport(_openImportPath);
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
var importHistory = _configuration.ImportHistory.OrderByDescending(x => x.ImportedAt)
|
ImportHistory? importHistory = _lastImport;
|
||||||
.ThenBy(x => x.Id).FirstOrDefault();
|
|
||||||
if (importHistory != null)
|
if (importHistory != null)
|
||||||
{
|
{
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.TextWrapped(string.Format(Localization.Config_UndoImportExplanation1,
|
ImGui.TextWrapped(string.Format(Localization.Config_UndoImportExplanation1,
|
||||||
importHistory.ImportedAt, importHistory.RemoteUrl, importHistory.ExportedAt));
|
importHistory.ImportedAt.ToLocalTime(),
|
||||||
|
importHistory.RemoteUrl,
|
||||||
|
importHistory.ExportedAt.ToUniversalTime()));
|
||||||
ImGui.TextWrapped(Localization.Config_UndoImportExplanation2);
|
ImGui.TextWrapped(Localization.Config_UndoImportExplanation2);
|
||||||
if (ImGui.Button(Localization.Config_UndoImport))
|
if (ImGui.Button(Localization.Config_UndoImport))
|
||||||
UndoImport(importHistory.Id);
|
UndoImport(importHistory.Id);
|
||||||
@ -466,6 +474,11 @@ namespace Pal.Client.Windows
|
|||||||
_frameworkService.EarlyEventQueue.Enqueue(new QueuedUndoImport(importId));
|
_frameworkService.EarlyEventQueue.Enqueue(new QueuedUndoImport(importId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void UpdateLastImport()
|
||||||
|
{
|
||||||
|
_lastImport = _importService.FindLast();
|
||||||
|
}
|
||||||
|
|
||||||
private void DoExport(string destinationPath)
|
private void DoExport(string destinationPath)
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
|
Loading…
Reference in New Issue
Block a user