mirror of
https://github.com/chylex/Discord-History-Tracker.git
synced 2024-11-25 14:42:44 +01:00
Compare commits
No commits in common. "3bf5acfa65a7f1cb92aaf7fb3bb6fdef186df5ad" and "d2934f4d6ae4a2d242e90a028219c5fb67fc97bd" have entirely different histories.
3bf5acfa65
...
d2934f4d6a
@ -1,6 +1,6 @@
|
|||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
namespace DHT.Desktop.Dialogs.Progress;
|
namespace DHT.Desktop.Dialogs.Progress;
|
||||||
|
|
||||||
sealed class ProgressItem : BaseModel {
|
sealed class ProgressItem : BaseModel {
|
||||||
private bool isVisible = false;
|
private bool isVisible = false;
|
||||||
|
@ -3,7 +3,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
|
|
||||||
namespace DHT.Desktop.Dialogs.TextBox;
|
namespace DHT.Desktop.Dialogs.TextBox;
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||||
public sealed partial class TextBoxDialog : Window {
|
public sealed partial class TextBoxDialog : Window {
|
||||||
|
@ -4,7 +4,7 @@ using System.ComponentModel;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
namespace DHT.Desktop.Dialogs.TextBox;
|
namespace DHT.Desktop.Dialogs.TextBox;
|
||||||
|
|
||||||
class TextBoxDialogModel : BaseModel {
|
class TextBoxDialogModel : BaseModel {
|
||||||
public string Title { get; init; } = "";
|
public string Title { get; init; } = "";
|
||||||
|
@ -3,7 +3,7 @@ using System.Collections;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
namespace DHT.Desktop.Dialogs.TextBox;
|
namespace DHT.Desktop.Dialogs.TextBox;
|
||||||
|
|
||||||
class TextBoxItem : BaseModel, INotifyDataErrorInfo {
|
class TextBoxItem : BaseModel, INotifyDataErrorInfo {
|
||||||
public string Title { get; init; } = "";
|
public string Title { get; init; } = "";
|
||||||
|
@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using DHT.Desktop.Common;
|
using DHT.Desktop.Common;
|
||||||
using DHT.Server;
|
|
||||||
using DHT.Server.Data.Filters;
|
using DHT.Server.Data.Filters;
|
||||||
using DHT.Server.Database;
|
using DHT.Server.Database;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
@ -48,7 +47,7 @@ sealed class AttachmentFilterPanelModel : BaseModel, IDisposable {
|
|||||||
|
|
||||||
public IEnumerable<Unit> Units => AllUnits;
|
public IEnumerable<Unit> Units => AllUnits;
|
||||||
|
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
private readonly string verb;
|
private readonly string verb;
|
||||||
|
|
||||||
private readonly AsyncValueComputer<long> matchingAttachmentCountComputer;
|
private readonly AsyncValueComputer<long> matchingAttachmentCountComputer;
|
||||||
@ -56,10 +55,10 @@ sealed class AttachmentFilterPanelModel : BaseModel, IDisposable {
|
|||||||
private long? totalAttachmentCount;
|
private long? totalAttachmentCount;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public AttachmentFilterPanelModel() : this(State.Dummy) {}
|
public AttachmentFilterPanelModel() : this(DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public AttachmentFilterPanelModel(State state, string verb = "Matches") {
|
public AttachmentFilterPanelModel(IDatabaseFile db, string verb = "Matches") {
|
||||||
this.state = state;
|
this.db = db;
|
||||||
this.verb = verb;
|
this.verb = verb;
|
||||||
|
|
||||||
this.matchingAttachmentCountComputer = AsyncValueComputer<long>.WithResultProcessor(SetAttachmentCounts).Build();
|
this.matchingAttachmentCountComputer = AsyncValueComputer<long>.WithResultProcessor(SetAttachmentCounts).Build();
|
||||||
@ -67,11 +66,11 @@ sealed class AttachmentFilterPanelModel : BaseModel, IDisposable {
|
|||||||
UpdateFilterStatistics();
|
UpdateFilterStatistics();
|
||||||
|
|
||||||
PropertyChanged += OnPropertyChanged;
|
PropertyChanged += OnPropertyChanged;
|
||||||
state.Db.Statistics.PropertyChanged += OnDbStatisticsChanged;
|
db.Statistics.PropertyChanged += OnDbStatisticsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
state.Db.Statistics.PropertyChanged -= OnDbStatisticsChanged;
|
db.Statistics.PropertyChanged -= OnDbStatisticsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) {
|
private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) {
|
||||||
@ -82,7 +81,7 @@ sealed class AttachmentFilterPanelModel : BaseModel, IDisposable {
|
|||||||
|
|
||||||
private void OnDbStatisticsChanged(object? sender, PropertyChangedEventArgs e) {
|
private void OnDbStatisticsChanged(object? sender, PropertyChangedEventArgs e) {
|
||||||
if (e.PropertyName == nameof(DatabaseStatistics.TotalAttachments)) {
|
if (e.PropertyName == nameof(DatabaseStatistics.TotalAttachments)) {
|
||||||
totalAttachmentCount = state.Db.Statistics.TotalAttachments;
|
totalAttachmentCount = db.Statistics.TotalAttachments;
|
||||||
UpdateFilterStatistics();
|
UpdateFilterStatistics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,7 +96,7 @@ sealed class AttachmentFilterPanelModel : BaseModel, IDisposable {
|
|||||||
else {
|
else {
|
||||||
matchingAttachmentCount = null;
|
matchingAttachmentCount = null;
|
||||||
UpdateFilterStatisticsText();
|
UpdateFilterStatisticsText();
|
||||||
matchingAttachmentCountComputer.Compute(() => state.Db.CountAttachments(filter));
|
matchingAttachmentCountComputer.Compute(() => db.CountAttachments(filter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ using Avalonia.Controls;
|
|||||||
using DHT.Desktop.Common;
|
using DHT.Desktop.Common;
|
||||||
using DHT.Desktop.Dialogs.CheckBox;
|
using DHT.Desktop.Dialogs.CheckBox;
|
||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Server;
|
|
||||||
using DHT.Server.Data;
|
using DHT.Server.Data;
|
||||||
using DHT.Server.Data.Filters;
|
using DHT.Server.Data.Filters;
|
||||||
using DHT.Server.Database;
|
using DHT.Server.Database;
|
||||||
@ -63,7 +62,7 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<ulong> IncludedChannels {
|
public HashSet<ulong> IncludedChannels {
|
||||||
get => includedChannels ?? state.Db.GetAllChannels().Select(static channel => channel.Id).ToHashSet();
|
get => includedChannels ?? db.GetAllChannels().Select(static channel => channel.Id).ToHashSet();
|
||||||
set => Change(ref includedChannels, value);
|
set => Change(ref includedChannels, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +72,7 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<ulong> IncludedUsers {
|
public HashSet<ulong> IncludedUsers {
|
||||||
get => includedUsers ?? state.Db.GetAllUsers().Select(static user => user.Id).ToHashSet();
|
get => includedUsers ?? db.GetAllUsers().Select(static user => user.Id).ToHashSet();
|
||||||
set => Change(ref includedUsers, value);
|
set => Change(ref includedUsers, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
private readonly string verb;
|
private readonly string verb;
|
||||||
|
|
||||||
private readonly AsyncValueComputer<long> exportedMessageCountComputer;
|
private readonly AsyncValueComputer<long> exportedMessageCountComputer;
|
||||||
@ -100,11 +99,11 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
private long? totalMessageCount;
|
private long? totalMessageCount;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public MessageFilterPanelModel() : this(null!, State.Dummy) {}
|
public MessageFilterPanelModel() : this(null!, DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public MessageFilterPanelModel(Window window, State state, string verb = "Matches") {
|
public MessageFilterPanelModel(Window window, IDatabaseFile db, string verb = "Matches") {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.state = state;
|
this.db = db;
|
||||||
this.verb = verb;
|
this.verb = verb;
|
||||||
|
|
||||||
this.exportedMessageCountComputer = AsyncValueComputer<long>.WithResultProcessor(SetExportedMessageCount).Build();
|
this.exportedMessageCountComputer = AsyncValueComputer<long>.WithResultProcessor(SetExportedMessageCount).Build();
|
||||||
@ -114,11 +113,11 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
UpdateUserFilterLabel();
|
UpdateUserFilterLabel();
|
||||||
|
|
||||||
PropertyChanged += OnPropertyChanged;
|
PropertyChanged += OnPropertyChanged;
|
||||||
state.Db.Statistics.PropertyChanged += OnDbStatisticsChanged;
|
db.Statistics.PropertyChanged += OnDbStatisticsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
state.Db.Statistics.PropertyChanged -= OnDbStatisticsChanged;
|
db.Statistics.PropertyChanged -= OnDbStatisticsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) {
|
private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) {
|
||||||
@ -137,7 +136,7 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
|
|
||||||
private void OnDbStatisticsChanged(object? sender, PropertyChangedEventArgs e) {
|
private void OnDbStatisticsChanged(object? sender, PropertyChangedEventArgs e) {
|
||||||
if (e.PropertyName == nameof(DatabaseStatistics.TotalMessages)) {
|
if (e.PropertyName == nameof(DatabaseStatistics.TotalMessages)) {
|
||||||
totalMessageCount = state.Db.Statistics.TotalMessages;
|
totalMessageCount = db.Statistics.TotalMessages;
|
||||||
UpdateFilterStatistics();
|
UpdateFilterStatistics();
|
||||||
}
|
}
|
||||||
else if (e.PropertyName == nameof(DatabaseStatistics.TotalChannels)) {
|
else if (e.PropertyName == nameof(DatabaseStatistics.TotalChannels)) {
|
||||||
@ -158,7 +157,7 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
else {
|
else {
|
||||||
exportedMessageCount = null;
|
exportedMessageCount = null;
|
||||||
UpdateFilterStatisticsText();
|
UpdateFilterStatisticsText();
|
||||||
exportedMessageCountComputer.Compute(() => state.Db.CountMessages(filter));
|
exportedMessageCountComputer.Compute(() => db.CountMessages(filter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,11 +175,11 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenChannelFilterDialog() {
|
public async void OpenChannelFilterDialog() {
|
||||||
var servers = state.Db.GetAllServers().ToDictionary(static server => server.Id);
|
var servers = db.GetAllServers().ToDictionary(static server => server.Id);
|
||||||
var items = new List<CheckBoxItem<ulong>>();
|
var items = new List<CheckBoxItem<ulong>>();
|
||||||
var included = IncludedChannels;
|
var included = IncludedChannels;
|
||||||
|
|
||||||
foreach (var channel in state.Db.GetAllChannels()) {
|
foreach (var channel in db.GetAllChannels()) {
|
||||||
var channelId = channel.Id;
|
var channelId = channel.Id;
|
||||||
var channelName = channel.Name;
|
var channelName = channel.Name;
|
||||||
|
|
||||||
@ -224,7 +223,7 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
var items = new List<CheckBoxItem<ulong>>();
|
var items = new List<CheckBoxItem<ulong>>();
|
||||||
var included = IncludedUsers;
|
var included = IncludedUsers;
|
||||||
|
|
||||||
foreach (var user in state.Db.GetAllUsers()) {
|
foreach (var user in db.GetAllUsers()) {
|
||||||
var name = user.Name;
|
var name = user.Name;
|
||||||
var discriminator = user.Discriminator;
|
var discriminator = user.Discriminator;
|
||||||
|
|
||||||
@ -241,13 +240,13 @@ sealed class MessageFilterPanelModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateChannelFilterLabel() {
|
private void UpdateChannelFilterLabel() {
|
||||||
long total = state.Db.Statistics.TotalChannels;
|
long total = db.Statistics.TotalChannels;
|
||||||
long included = FilterByChannel ? IncludedChannels.Count : total;
|
long included = FilterByChannel ? IncludedChannels.Count : total;
|
||||||
ChannelFilterLabel = "Selected " + included.Format() + " / " + total.Pluralize("channel") + ".";
|
ChannelFilterLabel = "Selected " + included.Format() + " / " + total.Pluralize("channel") + ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateUserFilterLabel() {
|
private void UpdateUserFilterLabel() {
|
||||||
long total = state.Db.Statistics.TotalUsers;
|
long total = db.Statistics.TotalUsers;
|
||||||
long included = FilterByUser ? IncludedUsers.Count : total;
|
long included = FilterByUser ? IncludedUsers.Count : total;
|
||||||
UserFilterLabel = "Selected " + included.Format() + " / " + total.Pluralize("user") + ".";
|
UserFilterLabel = "Selected " + included.Format() + " / " + total.Pluralize("user") + ".";
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ using System;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Desktop.Server;
|
using DHT.Desktop.Server;
|
||||||
using DHT.Server;
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Service;
|
using DHT.Server.Service;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ sealed class ServerConfigurationPanelModel : BaseModel, IDisposable {
|
|||||||
private readonly ServerManager serverManager;
|
private readonly ServerManager serverManager;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public ServerConfigurationPanelModel() : this(null!, new ServerManager(State.Dummy)) {}
|
public ServerConfigurationPanelModel() : this(null!, new ServerManager(DummyDatabaseFile.Instance)) {}
|
||||||
|
|
||||||
public ServerConfigurationPanelModel(Window window, ServerManager serverManager) {
|
public ServerConfigurationPanelModel(Window window, ServerManager serverManager) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
Width="800" Height="500"
|
Width="800" Height="500"
|
||||||
MinWidth="520" MinHeight="300"
|
MinWidth="520" MinHeight="300"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
Closing="OnClosing">
|
Closed="OnClosed">
|
||||||
|
|
||||||
<Design.DataContext>
|
<Design.DataContext>
|
||||||
<main:MainWindowModel />
|
<main:MainWindowModel />
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DHT.Desktop.Main.Pages;
|
using DHT.Desktop.Main.Pages;
|
||||||
using DHT.Utils.Logging;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main;
|
namespace DHT.Desktop.Main;
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
||||||
public sealed partial class MainWindow : Window {
|
public sealed partial class MainWindow : Window {
|
||||||
private static readonly Log Log = Log.ForType<MainWindow>();
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public MainWindow() {
|
public MainWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -24,24 +20,9 @@ public sealed partial class MainWindow : Window {
|
|||||||
DataContext = new MainWindowModel(this, args);
|
DataContext = new MainWindowModel(this, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OnClosing(object? sender, WindowClosingEventArgs e) {
|
public async void OnClosed(object? sender, EventArgs e) {
|
||||||
e.Cancel = true;
|
|
||||||
Closing -= OnClosing;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await Dispose();
|
|
||||||
} finally {
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Dispose() {
|
|
||||||
if (DataContext is MainWindowModel model) {
|
if (DataContext is MainWindowModel model) {
|
||||||
try {
|
await model.DisposeAsync();
|
||||||
await model.DisposeAsync();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Log.Error("Caught exception while disposing window: " + ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var temporaryFile in ViewerPageModel.TemporaryFiles) {
|
foreach (var temporaryFile in ViewerPageModel.TemporaryFiles) {
|
||||||
|
@ -7,7 +7,7 @@ using Avalonia.Controls;
|
|||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Desktop.Main.Screens;
|
using DHT.Desktop.Main.Screens;
|
||||||
using DHT.Desktop.Server;
|
using DHT.Desktop.Server;
|
||||||
using DHT.Server;
|
using DHT.Server.Database;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main;
|
namespace DHT.Desktop.Main;
|
||||||
@ -27,7 +27,7 @@ sealed class MainWindowModel : BaseModel, IAsyncDisposable {
|
|||||||
|
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
|
|
||||||
private State? state;
|
private IDatabaseFile? db;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public MainWindowModel() : this(null!, Arguments.Empty) {}
|
public MainWindowModel() : this(null!, Arguments.Empty) {}
|
||||||
@ -75,24 +75,21 @@ sealed class MainWindowModel : BaseModel, IAsyncDisposable {
|
|||||||
if (e.PropertyName == nameof(welcomeScreenModel.Db)) {
|
if (e.PropertyName == nameof(welcomeScreenModel.Db)) {
|
||||||
if (mainContentScreenModel != null) {
|
if (mainContentScreenModel != null) {
|
||||||
mainContentScreenModel.DatabaseClosed -= MainContentScreenModelOnDatabaseClosed;
|
mainContentScreenModel.DatabaseClosed -= MainContentScreenModelOnDatabaseClosed;
|
||||||
mainContentScreenModel.Dispose();
|
await mainContentScreenModel.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state != null) {
|
db?.Dispose();
|
||||||
await state.DisposeAsync();
|
db = welcomeScreenModel.Db;
|
||||||
}
|
|
||||||
|
|
||||||
state = welcomeScreenModel.Db == null ? null : new State(welcomeScreenModel.Db);
|
|
||||||
|
|
||||||
if (state == null) {
|
if (db == null) {
|
||||||
Title = DefaultTitle;
|
Title = DefaultTitle;
|
||||||
mainContentScreenModel = null;
|
mainContentScreenModel = null;
|
||||||
mainContentScreen = null;
|
mainContentScreen = null;
|
||||||
CurrentScreen = welcomeScreen;
|
CurrentScreen = welcomeScreen;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Title = Path.GetFileName(state.Db.Path) + " - " + DefaultTitle;
|
Title = Path.GetFileName(db.Path) + " - " + DefaultTitle;
|
||||||
mainContentScreenModel = new MainContentScreenModel(window, state);
|
mainContentScreenModel = new MainContentScreenModel(window, db);
|
||||||
await mainContentScreenModel.Initialize();
|
await mainContentScreenModel.Initialize();
|
||||||
mainContentScreenModel.DatabaseClosed += MainContentScreenModelOnDatabaseClosed;
|
mainContentScreenModel.DatabaseClosed += MainContentScreenModelOnDatabaseClosed;
|
||||||
mainContentScreen = new MainContentScreen { DataContext = mainContentScreenModel };
|
mainContentScreen = new MainContentScreen { DataContext = mainContentScreenModel };
|
||||||
@ -111,13 +108,13 @@ sealed class MainWindowModel : BaseModel, IAsyncDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public async ValueTask DisposeAsync() {
|
||||||
mainContentScreenModel?.Dispose();
|
welcomeScreenModel.Dispose();
|
||||||
|
|
||||||
if (state != null) {
|
if (mainContentScreenModel != null) {
|
||||||
await state.DisposeAsync();
|
await mainContentScreenModel.DisposeAsync();
|
||||||
state = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
welcomeScreenModel.Dispose();
|
db?.Dispose();
|
||||||
|
db = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ using Avalonia.Controls;
|
|||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Desktop.Main.Controls;
|
using DHT.Desktop.Main.Controls;
|
||||||
using DHT.Desktop.Server;
|
using DHT.Desktop.Server;
|
||||||
using DHT.Server;
|
using DHT.Server.Database;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main.Pages;
|
namespace DHT.Desktop.Main.Pages;
|
||||||
@ -12,14 +12,14 @@ sealed class AdvancedPageModel : BaseModel, IDisposable {
|
|||||||
public ServerConfigurationPanelModel ServerConfigurationModel { get; }
|
public ServerConfigurationPanelModel ServerConfigurationModel { get; }
|
||||||
|
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public AdvancedPageModel() : this(null!, State.Dummy, new ServerManager(State.Dummy)) {}
|
public AdvancedPageModel() : this(null!, DummyDatabaseFile.Instance, new ServerManager(DummyDatabaseFile.Instance)) {}
|
||||||
|
|
||||||
public AdvancedPageModel(Window window, State state, ServerManager serverManager) {
|
public AdvancedPageModel(Window window, IDatabaseFile db, ServerManager serverManager) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.state = state;
|
this.db = db;
|
||||||
|
|
||||||
ServerConfigurationModel = new ServerConfigurationPanelModel(window, serverManager);
|
ServerConfigurationModel = new ServerConfigurationPanelModel(window, serverManager);
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ sealed class AdvancedPageModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async void VacuumDatabase() {
|
public async void VacuumDatabase() {
|
||||||
state.Db.Vacuum();
|
db.Vacuum();
|
||||||
await Dialog.ShowOk(window, "Vacuum Database", "Done.");
|
await Dialog.ShowOk(window, "Vacuum Database", "Done.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DHT.Desktop.Common;
|
using DHT.Desktop.Common;
|
||||||
using DHT.Desktop.Main.Controls;
|
using DHT.Desktop.Main.Controls;
|
||||||
using DHT.Server;
|
|
||||||
using DHT.Server.Data;
|
using DHT.Server.Data;
|
||||||
using DHT.Server.Data.Aggregations;
|
using DHT.Server.Data.Aggregations;
|
||||||
using DHT.Server.Data.Filters;
|
using DHT.Server.Data.Filters;
|
||||||
@ -17,8 +16,8 @@ using DHT.Utils.Tasks;
|
|||||||
|
|
||||||
namespace DHT.Desktop.Main.Pages;
|
namespace DHT.Desktop.Main.Pages;
|
||||||
|
|
||||||
sealed class AttachmentsPageModel : BaseModel, IDisposable {
|
sealed class AttachmentsPageModel : BaseModel, IAsyncDisposable {
|
||||||
private static readonly DownloadItemFilter EnqueuedItemFilter = new () {
|
private static readonly DownloadItemFilter EnqueuedItemFilter = new() {
|
||||||
IncludeStatuses = new HashSet<DownloadStatus> {
|
IncludeStatuses = new HashSet<DownloadStatus> {
|
||||||
DownloadStatus.Enqueued,
|
DownloadStatus.Enqueued,
|
||||||
DownloadStatus.Downloading
|
DownloadStatus.Downloading
|
||||||
@ -27,7 +26,7 @@ sealed class AttachmentsPageModel : BaseModel, IDisposable {
|
|||||||
|
|
||||||
private bool isThreadDownloadButtonEnabled = true;
|
private bool isThreadDownloadButtonEnabled = true;
|
||||||
|
|
||||||
public string ToggleDownloadButtonText => IsDownloading ? "Stop Downloading" : "Start Downloading";
|
public string ToggleDownloadButtonText => downloader == null ? "Start Downloading" : "Stop Downloading";
|
||||||
|
|
||||||
public bool IsToggleDownloadButtonEnabled {
|
public bool IsToggleDownloadButtonEnabled {
|
||||||
get => isThreadDownloadButtonEnabled;
|
get => isThreadDownloadButtonEnabled;
|
||||||
@ -55,34 +54,34 @@ sealed class AttachmentsPageModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDownloading => state.Downloader.IsDownloading;
|
public bool IsDownloading => downloader != null;
|
||||||
public bool HasFailedDownloads => statisticsFailed.Items > 0;
|
public bool HasFailedDownloads => statisticsFailed.Items > 0;
|
||||||
|
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
private readonly AsyncValueComputer<DownloadStatusStatistics>.Single downloadStatisticsComputer;
|
private readonly AsyncValueComputer<DownloadStatusStatistics>.Single downloadStatisticsComputer;
|
||||||
|
private BackgroundDownloader? downloader;
|
||||||
|
|
||||||
private int doneItemsCount;
|
private int doneItemsCount;
|
||||||
private int initialFinishedCount;
|
private int initialFinishedCount;
|
||||||
private int? totalItemsToDownloadCount;
|
private int? totalItemsToDownloadCount;
|
||||||
|
|
||||||
public AttachmentsPageModel() : this(State.Dummy) {}
|
public AttachmentsPageModel() : this(DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public AttachmentsPageModel(State state) {
|
public AttachmentsPageModel(IDatabaseFile db) {
|
||||||
this.state = state;
|
this.db = db;
|
||||||
|
this.FilterModel = new AttachmentFilterPanelModel(db);
|
||||||
FilterModel = new AttachmentFilterPanelModel(state);
|
|
||||||
|
|
||||||
downloadStatisticsComputer = AsyncValueComputer<DownloadStatusStatistics>.WithResultProcessor(UpdateStatistics).WithOutdatedResults().BuildWithComputer(state.Db.GetDownloadStatusStatistics);
|
this.downloadStatisticsComputer = AsyncValueComputer<DownloadStatusStatistics>.WithResultProcessor(UpdateStatistics).WithOutdatedResults().BuildWithComputer(db.GetDownloadStatusStatistics);
|
||||||
downloadStatisticsComputer.Recompute();
|
this.downloadStatisticsComputer.Recompute();
|
||||||
|
|
||||||
state.Db.Statistics.PropertyChanged += OnDbStatisticsChanged;
|
db.Statistics.PropertyChanged += OnDbStatisticsChanged;
|
||||||
state.Downloader.OnItemFinished += DownloaderOnOnItemFinished;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public async ValueTask DisposeAsync() {
|
||||||
state.Db.Statistics.PropertyChanged -= OnDbStatisticsChanged;
|
db.Statistics.PropertyChanged -= OnDbStatisticsChanged;
|
||||||
state.Downloader.OnItemFinished -= DownloaderOnOnItemFinished;
|
|
||||||
FilterModel.Dispose();
|
FilterModel.Dispose();
|
||||||
|
await DisposeDownloader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDbStatisticsChanged(object? sender, PropertyChangedEventArgs e) {
|
private void OnDbStatisticsChanged(object? sender, PropertyChangedEventArgs e) {
|
||||||
@ -102,7 +101,7 @@ sealed class AttachmentsPageModel : BaseModel, IDisposable {
|
|||||||
private void EnqueueDownloadItems() {
|
private void EnqueueDownloadItems() {
|
||||||
var filter = FilterModel.CreateFilter();
|
var filter = FilterModel.CreateFilter();
|
||||||
filter.DownloadItemRule = AttachmentFilter.DownloadItemRules.OnlyNotPresent;
|
filter.DownloadItemRule = AttachmentFilter.DownloadItemRules.OnlyNotPresent;
|
||||||
state.Db.EnqueueDownloadItems(filter);
|
db.EnqueueDownloadItems(filter);
|
||||||
|
|
||||||
downloadStatisticsComputer.Recompute();
|
downloadStatisticsComputer.Recompute();
|
||||||
}
|
}
|
||||||
@ -147,24 +146,25 @@ sealed class AttachmentsPageModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnClickToggleDownload() {
|
public async Task OnClickToggleDownload() {
|
||||||
if (IsDownloading) {
|
if (downloader == null) {
|
||||||
|
initialFinishedCount = statisticsDownloaded.Items + statisticsFailed.Items;
|
||||||
|
EnqueueDownloadItems();
|
||||||
|
downloader = new BackgroundDownloader(db);
|
||||||
|
downloader.OnItemFinished += DownloaderOnOnItemFinished;
|
||||||
|
}
|
||||||
|
else {
|
||||||
IsToggleDownloadButtonEnabled = false;
|
IsToggleDownloadButtonEnabled = false;
|
||||||
await state.Downloader.Stop();
|
await DisposeDownloader();
|
||||||
downloadStatisticsComputer.Recompute();
|
downloadStatisticsComputer.Recompute();
|
||||||
IsToggleDownloadButtonEnabled = true;
|
IsToggleDownloadButtonEnabled = true;
|
||||||
|
|
||||||
state.Db.RemoveDownloadItems(EnqueuedItemFilter, FilterRemovalMode.RemoveMatching);
|
db.RemoveDownloadItems(EnqueuedItemFilter, FilterRemovalMode.RemoveMatching);
|
||||||
|
|
||||||
doneItemsCount = 0;
|
doneItemsCount = 0;
|
||||||
initialFinishedCount = 0;
|
initialFinishedCount = 0;
|
||||||
totalItemsToDownloadCount = null;
|
totalItemsToDownloadCount = null;
|
||||||
UpdateDownloadMessage();
|
UpdateDownloadMessage();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
initialFinishedCount = statisticsDownloaded.Items + statisticsFailed.Items;
|
|
||||||
await state.Downloader.Start();
|
|
||||||
EnqueueDownloadItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPropertyChanged(nameof(ToggleDownloadButtonText));
|
OnPropertyChanged(nameof(ToggleDownloadButtonText));
|
||||||
OnPropertyChanged(nameof(IsDownloading));
|
OnPropertyChanged(nameof(IsDownloading));
|
||||||
@ -179,13 +179,22 @@ sealed class AttachmentsPageModel : BaseModel, IDisposable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
state.Db.RemoveDownloadItems(allExceptFailedFilter, FilterRemovalMode.KeepMatching);
|
db.RemoveDownloadItems(allExceptFailedFilter, FilterRemovalMode.KeepMatching);
|
||||||
|
|
||||||
if (IsDownloading) {
|
if (IsDownloading) {
|
||||||
EnqueueDownloadItems();
|
EnqueueDownloadItems();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DisposeDownloader() {
|
||||||
|
if (downloader != null) {
|
||||||
|
downloader.OnItemFinished -= DownloaderOnOnItemFinished;
|
||||||
|
await downloader.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
downloader = null;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class StatisticsRow {
|
public sealed class StatisticsRow {
|
||||||
public string State { get; }
|
public string State { get; }
|
||||||
public int Items { get; set; }
|
public int Items { get; set; }
|
||||||
|
@ -14,7 +14,6 @@ using DHT.Desktop.Dialogs.File;
|
|||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Desktop.Dialogs.Progress;
|
using DHT.Desktop.Dialogs.Progress;
|
||||||
using DHT.Desktop.Dialogs.TextBox;
|
using DHT.Desktop.Dialogs.TextBox;
|
||||||
using DHT.Server;
|
|
||||||
using DHT.Server.Data;
|
using DHT.Server.Data;
|
||||||
using DHT.Server.Database;
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Database.Import;
|
using DHT.Server.Database.Import;
|
||||||
@ -22,7 +21,7 @@ using DHT.Server.Database.Sqlite;
|
|||||||
using DHT.Utils.Logging;
|
using DHT.Utils.Logging;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main.Pages;
|
namespace DHT.Desktop.Main.Pages;
|
||||||
|
|
||||||
sealed class DatabasePageModel : BaseModel {
|
sealed class DatabasePageModel : BaseModel {
|
||||||
private static readonly Log Log = Log.ForType<DatabasePageModel>();
|
private static readonly Log Log = Log.ForType<DatabasePageModel>();
|
||||||
@ -34,11 +33,11 @@ sealed class DatabasePageModel : BaseModel {
|
|||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public DatabasePageModel() : this(null!, State.Dummy) {}
|
public DatabasePageModel() : this(null!, DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public DatabasePageModel(Window window, State state) {
|
public DatabasePageModel(Window window, IDatabaseFile db) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.Db = state.Db;
|
this.Db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenDatabaseFolder() {
|
public async void OpenDatabaseFolder() {
|
||||||
|
@ -6,8 +6,8 @@ using System.Threading.Tasks;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Desktop.Dialogs.Progress;
|
using DHT.Desktop.Dialogs.Progress;
|
||||||
using DHT.Server;
|
|
||||||
using DHT.Server.Data;
|
using DHT.Server.Data;
|
||||||
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Service;
|
using DHT.Server.Service;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
|
|
||||||
@ -18,14 +18,14 @@ namespace DHT.Desktop.Main.Pages {
|
|||||||
public string GenerateMessages { get; set; } = "0";
|
public string GenerateMessages { get; set; } = "0";
|
||||||
|
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public DebugPageModel() : this(null!, State.Dummy) {}
|
public DebugPageModel() : this(null!, DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public DebugPageModel(Window window, State state) {
|
public DebugPageModel(Window window, IDatabaseFile db) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.state = state;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OnClickAddRandomDataToDatabase() {
|
public async void OnClickAddRandomDataToDatabase() {
|
||||||
@ -83,11 +83,11 @@ namespace DHT.Desktop.Main.Pages {
|
|||||||
Discriminator = rand.Next(0, 9999).ToString(),
|
Discriminator = rand.Next(0, 9999).ToString(),
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
state.Db.AddServer(server);
|
db.AddServer(server);
|
||||||
state.Db.AddUsers(users);
|
db.AddUsers(users);
|
||||||
|
|
||||||
foreach (var channel in channels) {
|
foreach (var channel in channels) {
|
||||||
state.Db.AddChannel(channel);
|
db.AddChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = DateTimeOffset.Now;
|
var now = DateTimeOffset.Now;
|
||||||
@ -117,7 +117,7 @@ namespace DHT.Desktop.Main.Pages {
|
|||||||
};
|
};
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
state.Db.AddMessages(messages);
|
db.AddMessages(messages);
|
||||||
|
|
||||||
messageCount -= BatchSize;
|
messageCount -= BatchSize;
|
||||||
await callback.Update("Adding messages in batches of " + BatchSize, ++batchIndex, batchCount);
|
await callback.Update("Adding messages in batches of " + BatchSize, ++batchIndex, batchCount);
|
||||||
|
@ -13,8 +13,8 @@ using DHT.Desktop.Dialogs.File;
|
|||||||
using DHT.Desktop.Dialogs.Message;
|
using DHT.Desktop.Dialogs.Message;
|
||||||
using DHT.Desktop.Main.Controls;
|
using DHT.Desktop.Main.Controls;
|
||||||
using DHT.Desktop.Server;
|
using DHT.Desktop.Server;
|
||||||
using DHT.Server;
|
|
||||||
using DHT.Server.Data.Filters;
|
using DHT.Server.Data.Filters;
|
||||||
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Database.Export;
|
using DHT.Server.Database.Export;
|
||||||
using DHT.Server.Database.Export.Strategy;
|
using DHT.Server.Database.Export.Strategy;
|
||||||
using DHT.Utils.Models;
|
using DHT.Utils.Models;
|
||||||
@ -38,16 +38,16 @@ sealed class ViewerPageModel : BaseModel, IDisposable {
|
|||||||
public MessageFilterPanelModel FilterModel { get; }
|
public MessageFilterPanelModel FilterModel { get; }
|
||||||
|
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public ViewerPageModel() : this(null!, State.Dummy) {}
|
public ViewerPageModel() : this(null!, DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public ViewerPageModel(Window window, State state) {
|
public ViewerPageModel(Window window, IDatabaseFile db) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.state = state;
|
this.db = db;
|
||||||
|
|
||||||
FilterModel = new MessageFilterPanelModel(window, state, "Will export");
|
FilterModel = new MessageFilterPanelModel(window, db, "Will export");
|
||||||
FilterModel.FilterPropertyChanged += OnFilterPropertyChanged;
|
FilterModel.FilterPropertyChanged += OnFilterPropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ sealed class ViewerPageModel : BaseModel, IDisposable {
|
|||||||
string jsonTempFile = path + ".tmp";
|
string jsonTempFile = path + ".tmp";
|
||||||
|
|
||||||
await using (var jsonStream = new FileStream(jsonTempFile, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) {
|
await using (var jsonStream = new FileStream(jsonTempFile, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) {
|
||||||
await ViewerJsonExport.Generate(jsonStream, strategy, state.Db, FilterModel.CreateFilter());
|
await ViewerJsonExport.Generate(jsonStream, strategy, db, FilterModel.CreateFilter());
|
||||||
|
|
||||||
char[] jsonBuffer = new char[Math.Min(32768, jsonStream.Position)];
|
char[] jsonBuffer = new char[Math.Min(32768, jsonStream.Position)];
|
||||||
jsonStream.Position = 0;
|
jsonStream.Position = 0;
|
||||||
@ -98,7 +98,7 @@ sealed class ViewerPageModel : BaseModel, IDisposable {
|
|||||||
|
|
||||||
public async void OnClickOpenViewer() {
|
public async void OnClickOpenViewer() {
|
||||||
string rootPath = Path.Combine(Path.GetTempPath(), "DiscordHistoryTracker");
|
string rootPath = Path.Combine(Path.GetTempPath(), "DiscordHistoryTracker");
|
||||||
string filenameBase = Path.GetFileNameWithoutExtension(state.Db.Path) + "-" + DateTime.Now.ToString("yyyy-MM-dd");
|
string filenameBase = Path.GetFileNameWithoutExtension(db.Path) + "-" + DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
string fullPath = Path.Combine(rootPath, filenameBase + ".html");
|
string fullPath = Path.Combine(rootPath, filenameBase + ".html");
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
@ -123,8 +123,8 @@ sealed class ViewerPageModel : BaseModel, IDisposable {
|
|||||||
string? path = await window.StorageProvider.SaveFile(new FilePickerSaveOptions {
|
string? path = await window.StorageProvider.SaveFile(new FilePickerSaveOptions {
|
||||||
Title = "Save Viewer",
|
Title = "Save Viewer",
|
||||||
FileTypeChoices = ViewerFileTypes,
|
FileTypeChoices = ViewerFileTypes,
|
||||||
SuggestedFileName = Path.GetFileNameWithoutExtension(state.Db.Path) + ".html",
|
SuggestedFileName = Path.GetFileNameWithoutExtension(db.Path) + ".html",
|
||||||
SuggestedStartLocation = await FileDialogs.GetSuggestedStartLocation(window, Path.GetDirectoryName(state.Db.Path)),
|
SuggestedStartLocation = await FileDialogs.GetSuggestedStartLocation(window, Path.GetDirectoryName(db.Path)),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
@ -136,13 +136,13 @@ sealed class ViewerPageModel : BaseModel, IDisposable {
|
|||||||
var filter = FilterModel.CreateFilter();
|
var filter = FilterModel.CreateFilter();
|
||||||
|
|
||||||
if (DatabaseToolFilterModeKeep) {
|
if (DatabaseToolFilterModeKeep) {
|
||||||
if (DialogResult.YesNo.Yes == await Dialog.ShowYesNo(window, "Keep Matching Messages in This Database", state.Db.CountMessages(filter).Pluralize("message") + " will be kept, and the rest will be removed from this database. This action cannot be undone. Proceed?")) {
|
if (DialogResult.YesNo.Yes == await Dialog.ShowYesNo(window, "Keep Matching Messages in This Database", db.CountMessages(filter).Pluralize("message") + " will be kept, and the rest will be removed from this database. This action cannot be undone. Proceed?")) {
|
||||||
state.Db.RemoveMessages(filter, FilterRemovalMode.KeepMatching);
|
db.RemoveMessages(filter, FilterRemovalMode.KeepMatching);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (DatabaseToolFilterModeRemove) {
|
else if (DatabaseToolFilterModeRemove) {
|
||||||
if (DialogResult.YesNo.Yes == await Dialog.ShowYesNo(window, "Remove Matching Messages in This Database", state.Db.CountMessages(filter).Pluralize("message") + " will be removed from this database. This action cannot be undone. Proceed?")) {
|
if (DialogResult.YesNo.Yes == await Dialog.ShowYesNo(window, "Remove Matching Messages in This Database", db.CountMessages(filter).Pluralize("message") + " will be removed from this database. This action cannot be undone. Proceed?")) {
|
||||||
state.Db.RemoveMessages(filter, FilterRemovalMode.RemoveMatching);
|
db.RemoveMessages(filter, FilterRemovalMode.RemoveMatching);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,13 @@ using DHT.Desktop.Dialogs.Message;
|
|||||||
using DHT.Desktop.Main.Controls;
|
using DHT.Desktop.Main.Controls;
|
||||||
using DHT.Desktop.Main.Pages;
|
using DHT.Desktop.Main.Pages;
|
||||||
using DHT.Desktop.Server;
|
using DHT.Desktop.Server;
|
||||||
using DHT.Server;
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Service;
|
using DHT.Server.Service;
|
||||||
using DHT.Utils.Logging;
|
using DHT.Utils.Logging;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main.Screens;
|
namespace DHT.Desktop.Main.Screens;
|
||||||
|
|
||||||
sealed class MainContentScreenModel : IDisposable {
|
sealed class MainContentScreenModel : IAsyncDisposable {
|
||||||
private static readonly Log Log = Log.ForType<MainContentScreenModel>();
|
private static readonly Log Log = Log.ForType<MainContentScreenModel>();
|
||||||
|
|
||||||
public DatabasePage DatabasePage { get; }
|
public DatabasePage DatabasePage { get; }
|
||||||
@ -53,37 +53,37 @@ sealed class MainContentScreenModel : IDisposable {
|
|||||||
private readonly ServerManager serverManager;
|
private readonly ServerManager serverManager;
|
||||||
|
|
||||||
[Obsolete("Designer")]
|
[Obsolete("Designer")]
|
||||||
public MainContentScreenModel() : this(null!, State.Dummy) {}
|
public MainContentScreenModel() : this(null!, DummyDatabaseFile.Instance) {}
|
||||||
|
|
||||||
public MainContentScreenModel(Window window, State state) {
|
public MainContentScreenModel(Window window, IDatabaseFile db) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.serverManager = new ServerManager(state);
|
this.serverManager = new ServerManager(db);
|
||||||
|
|
||||||
ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught;
|
ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught;
|
||||||
|
|
||||||
DatabasePageModel = new DatabasePageModel(window, state);
|
DatabasePageModel = new DatabasePageModel(window, db);
|
||||||
DatabasePage = new DatabasePage { DataContext = DatabasePageModel };
|
DatabasePage = new DatabasePage { DataContext = DatabasePageModel };
|
||||||
|
|
||||||
TrackingPageModel = new TrackingPageModel(window);
|
TrackingPageModel = new TrackingPageModel(window);
|
||||||
TrackingPage = new TrackingPage { DataContext = TrackingPageModel };
|
TrackingPage = new TrackingPage { DataContext = TrackingPageModel };
|
||||||
|
|
||||||
AttachmentsPageModel = new AttachmentsPageModel(state);
|
AttachmentsPageModel = new AttachmentsPageModel(db);
|
||||||
AttachmentsPage = new AttachmentsPage { DataContext = AttachmentsPageModel };
|
AttachmentsPage = new AttachmentsPage { DataContext = AttachmentsPageModel };
|
||||||
|
|
||||||
ViewerPageModel = new ViewerPageModel(window, state);
|
ViewerPageModel = new ViewerPageModel(window, db);
|
||||||
ViewerPage = new ViewerPage { DataContext = ViewerPageModel };
|
ViewerPage = new ViewerPage { DataContext = ViewerPageModel };
|
||||||
|
|
||||||
AdvancedPageModel = new AdvancedPageModel(window, state, serverManager);
|
AdvancedPageModel = new AdvancedPageModel(window, db, serverManager);
|
||||||
AdvancedPage = new AdvancedPage { DataContext = AdvancedPageModel };
|
AdvancedPage = new AdvancedPage { DataContext = AdvancedPageModel };
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
DebugPageModel = new DebugPageModel(window, state);
|
DebugPageModel = new DebugPageModel(window, db);
|
||||||
DebugPage = new DebugPage { DataContext = DebugPageModel };
|
DebugPage = new DebugPage { DataContext = DebugPageModel };
|
||||||
#else
|
#else
|
||||||
DebugPage = null;
|
DebugPage = null;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
StatusBarModel = new StatusBarModel(state.Db.Statistics);
|
StatusBarModel = new StatusBarModel(db.Statistics);
|
||||||
|
|
||||||
AdvancedPageModel.ServerConfigurationModel.ServerStatusChanged += OnServerStatusChanged;
|
AdvancedPageModel.ServerConfigurationModel.ServerStatusChanged += OnServerStatusChanged;
|
||||||
DatabaseClosed += OnDatabaseClosed;
|
DatabaseClosed += OnDatabaseClosed;
|
||||||
@ -97,9 +97,9 @@ sealed class MainContentScreenModel : IDisposable {
|
|||||||
serverManager.Launch();
|
serverManager.Launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public async ValueTask DisposeAsync() {
|
||||||
ServerLauncher.ServerManagementExceptionCaught -= ServerLauncherOnServerManagementExceptionCaught;
|
ServerLauncher.ServerManagementExceptionCaught -= ServerLauncherOnServerManagementExceptionCaught;
|
||||||
AttachmentsPageModel.Dispose();
|
await AttachmentsPageModel.DisposeAsync();
|
||||||
ViewerPageModel.Dispose();
|
ViewerPageModel.Dispose();
|
||||||
serverManager.Dispose();
|
serverManager.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using DHT.Server;
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Service;
|
using DHT.Server.Service;
|
||||||
|
|
||||||
namespace DHT.Desktop.Server;
|
namespace DHT.Desktop.Server;
|
||||||
@ -12,10 +12,10 @@ sealed class ServerManager : IDisposable {
|
|||||||
|
|
||||||
public bool IsRunning => ServerLauncher.IsRunning;
|
public bool IsRunning => ServerLauncher.IsRunning;
|
||||||
|
|
||||||
private readonly State state;
|
private readonly IDatabaseFile db;
|
||||||
|
|
||||||
public ServerManager(State state) {
|
public ServerManager(IDatabaseFile db) {
|
||||||
if (state != State.Dummy) {
|
if (db != DummyDatabaseFile.Instance) {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
throw new InvalidOperationException("Only one instance of ServerManager can exist at the same time!");
|
throw new InvalidOperationException("Only one instance of ServerManager can exist at the same time!");
|
||||||
}
|
}
|
||||||
@ -23,11 +23,11 @@ sealed class ServerManager : IDisposable {
|
|||||||
instance = this;
|
instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = state;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Launch() {
|
public void Launch() {
|
||||||
ServerLauncher.Relaunch(Port, Token, state.Db);
|
ServerLauncher.Relaunch(Port, Token, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Relaunch(ushort port, string token) {
|
public void Relaunch(ushort port, string token) {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
namespace DHT.Server.Database.Export;
|
namespace DHT.Server.Database.Export;
|
||||||
|
|
||||||
readonly record struct Snowflake(ulong Id);
|
readonly record struct Snowflake(ulong Id);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace DHT.Server.Database.Import;
|
namespace DHT.Server.Database.Import;
|
||||||
|
|
||||||
sealed class DiscordEmbedLegacyJson {
|
sealed class DiscordEmbedLegacyJson {
|
||||||
public required string Url { get; init; }
|
public required string Url { get; init; }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace DHT.Server.Database.Sqlite;
|
namespace DHT.Server.Database.Sqlite;
|
||||||
|
|
||||||
public interface ISchemaUpgradeCallbacks {
|
public interface ISchemaUpgradeCallbacks {
|
||||||
Task<bool> CanUpgrade();
|
Task<bool> CanUpgrade();
|
||||||
|
@ -11,14 +11,14 @@ using DHT.Utils.Tasks;
|
|||||||
|
|
||||||
namespace DHT.Server.Download;
|
namespace DHT.Server.Download;
|
||||||
|
|
||||||
sealed class DownloaderTask : BaseModel {
|
public sealed class BackgroundDownloader : BaseModel {
|
||||||
private static readonly Log Log = Log.ForType<DownloaderTask>();
|
private static readonly Log Log = Log.ForType<BackgroundDownloader>();
|
||||||
|
|
||||||
private const int DownloadTasks = 4;
|
private const int DownloadTasks = 4;
|
||||||
private const int QueueSize = 25;
|
private const int QueueSize = 25;
|
||||||
private const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
private const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
||||||
|
|
||||||
internal event EventHandler<DownloadItem>? OnItemFinished;
|
public event EventHandler<DownloadItem>? OnItemFinished;
|
||||||
|
|
||||||
private readonly Channel<DownloadItem> downloadQueue = Channel.CreateBounded<DownloadItem>(new BoundedChannelOptions(QueueSize) {
|
private readonly Channel<DownloadItem> downloadQueue = Channel.CreateBounded<DownloadItem>(new BoundedChannelOptions(QueueSize) {
|
||||||
SingleReader = false,
|
SingleReader = false,
|
||||||
@ -34,7 +34,7 @@ sealed class DownloaderTask : BaseModel {
|
|||||||
private readonly Task queueWriterTask;
|
private readonly Task queueWriterTask;
|
||||||
private readonly Task[] downloadTasks;
|
private readonly Task[] downloadTasks;
|
||||||
|
|
||||||
internal DownloaderTask(IDatabaseFile db) {
|
public BackgroundDownloader(IDatabaseFile db) {
|
||||||
this.cancellationToken = cancellationTokenSource.Token;
|
this.cancellationToken = cancellationTokenSource.Token;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.queueWriterTask = Task.Run(RunQueueWriterTask);
|
this.queueWriterTask = Task.Run(RunQueueWriterTask);
|
||||||
@ -56,7 +56,7 @@ sealed class DownloaderTask : BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunDownloadTask(int taskIndex) {
|
private async Task RunDownloadTask(int taskIndex) {
|
||||||
var log = Log.ForType<DownloaderTask>("Task " + taskIndex);
|
var log = Log.ForType<BackgroundDownloader>("Task " + taskIndex);
|
||||||
|
|
||||||
var client = new HttpClient();
|
var client = new HttpClient();
|
||||||
client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent);
|
client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent);
|
||||||
@ -87,7 +87,7 @@ sealed class DownloaderTask : BaseModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task Stop() {
|
public async Task Stop() {
|
||||||
try {
|
try {
|
||||||
await cancellationTokenSource.CancelAsync();
|
await cancellationTokenSource.CancelAsync();
|
||||||
} catch (Exception) {
|
} catch (Exception) {
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Frozen;
|
using System.Collections.Frozen;
|
||||||
|
|
||||||
namespace DHT.Server.Download;
|
namespace DHT.Server.Download;
|
||||||
|
|
||||||
static class DiscordCdn {
|
static class DiscordCdn {
|
||||||
private static FrozenSet<string> CdnHosts { get; } = new [] {
|
private static FrozenSet<string> CdnHosts { get; } = new [] {
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DHT.Server.Database;
|
|
||||||
|
|
||||||
namespace DHT.Server.Download;
|
|
||||||
|
|
||||||
public sealed class Downloader {
|
|
||||||
private DownloaderTask? current;
|
|
||||||
public bool IsDownloading => current != null;
|
|
||||||
|
|
||||||
public event EventHandler<DownloadItem>? OnItemFinished;
|
|
||||||
|
|
||||||
private readonly IDatabaseFile db;
|
|
||||||
private readonly SemaphoreSlim semaphore = new (1, 1);
|
|
||||||
|
|
||||||
internal Downloader(IDatabaseFile db) {
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Start() {
|
|
||||||
await semaphore.WaitAsync();
|
|
||||||
try {
|
|
||||||
if (current == null) {
|
|
||||||
current = new DownloaderTask(db);
|
|
||||||
current.OnItemFinished += DelegateOnItemFinished;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Stop() {
|
|
||||||
await semaphore.WaitAsync();
|
|
||||||
try {
|
|
||||||
if (current != null) {
|
|
||||||
await current.Stop();
|
|
||||||
current.OnItemFinished -= DelegateOnItemFinished;
|
|
||||||
current = null;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DelegateOnItemFinished(object? sender, DownloadItem e) {
|
|
||||||
OnItemFinished?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ using DHT.Utils.Logging;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
|
|
||||||
namespace DHT.Server.Service.Middlewares;
|
namespace DHT.Server.Service.Middlewares;
|
||||||
|
|
||||||
sealed class ServerLoggingMiddleware {
|
sealed class ServerLoggingMiddleware {
|
||||||
private static readonly Log Log = Log.ForType<ServerLoggingMiddleware>();
|
private static readonly Log Log = Log.ForType<ServerLoggingMiddleware>();
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DHT.Server.Database;
|
|
||||||
using DHT.Server.Download;
|
|
||||||
|
|
||||||
namespace DHT.Server;
|
|
||||||
|
|
||||||
public sealed class State : IAsyncDisposable {
|
|
||||||
public static State Dummy { get; } = new (DummyDatabaseFile.Instance);
|
|
||||||
|
|
||||||
public IDatabaseFile Db { get; }
|
|
||||||
public Downloader Downloader { get; }
|
|
||||||
|
|
||||||
public State(IDatabaseFile db) {
|
|
||||||
Db = db;
|
|
||||||
Downloader = new Downloader(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
|
||||||
await Downloader.Stop();
|
|
||||||
Db.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace DHT.Utils.Logging;
|
namespace DHT.Utils.Logging;
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
public static partial class WindowsConsole {
|
public static partial class WindowsConsole {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace DHT.Utils.Tasks;
|
namespace DHT.Utils.Tasks;
|
||||||
|
|
||||||
public static class TaskExtensions {
|
public static class TaskExtensions {
|
||||||
public static async Task WaitIgnoringCancellation(this Task task) {
|
public static async Task WaitIgnoringCancellation(this Task task) {
|
||||||
|
@ -5,7 +5,7 @@ using DHT.Utils;
|
|||||||
[assembly: AssemblyFileVersion(Version.Tag)]
|
[assembly: AssemblyFileVersion(Version.Tag)]
|
||||||
[assembly: AssemblyInformationalVersion(Version.Tag)]
|
[assembly: AssemblyInformationalVersion(Version.Tag)]
|
||||||
|
|
||||||
namespace DHT.Utils;
|
namespace DHT.Utils;
|
||||||
|
|
||||||
static class Version {
|
static class Version {
|
||||||
public const string Tag = "39.1.0.0";
|
public const string Tag = "39.1.0.0";
|
||||||
|
Loading…
Reference in New Issue
Block a user