mirror of
https://github.com/chylex/Discord-History-Tracker.git
synced 2025-09-17 16:24:47 +02:00
Compare commits
4 Commits
737ab43ea4
...
v44.0
Author | SHA1 | Date | |
---|---|---|---|
bfac9b91d9
|
|||
4215dc76f4
|
|||
86adda610b
|
|||
f1c4cd040e
|
9
app/Desktop/Common/SystemUtils.cs
Normal file
9
app/Desktop/Common/SystemUtils.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace DHT.Desktop.Common;
|
||||||
|
|
||||||
|
static class SystemUtils {
|
||||||
|
public static void OpenUrl(string url) {
|
||||||
|
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
|
||||||
|
}
|
||||||
|
}
|
@@ -1,45 +1,41 @@
|
|||||||
using System.Diagnostics;
|
using DHT.Desktop.Common;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main;
|
namespace DHT.Desktop.Main;
|
||||||
|
|
||||||
sealed class AboutWindowModel {
|
sealed class AboutWindowModel {
|
||||||
public void ShowOfficialWebsite() {
|
public void ShowOfficialWebsite() {
|
||||||
OpenUrl("https://dht.chylex.com");
|
SystemUtils.OpenUrl(Program.Website);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowIssueTracker() {
|
public void ShowIssueTracker() {
|
||||||
OpenUrl("https://github.com/chylex/Discord-History-Tracker/issues");
|
SystemUtils.OpenUrl("https://github.com/chylex/Discord-History-Tracker/issues");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowSourceCode() {
|
public void ShowSourceCode() {
|
||||||
OpenUrl("https://github.com/chylex/Discord-History-Tracker");
|
SystemUtils.OpenUrl("https://github.com/chylex/Discord-History-Tracker");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLibraryNetCore() {
|
public void ShowLibraryNetCore() {
|
||||||
OpenUrl("https://github.com/dotnet/core");
|
SystemUtils.OpenUrl("https://github.com/dotnet/core");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLibraryAvalonia() {
|
public void ShowLibraryAvalonia() {
|
||||||
OpenUrl("https://www.nuget.org/packages/Avalonia");
|
SystemUtils.OpenUrl("https://www.nuget.org/packages/Avalonia");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLibraryCommunityToolkit() {
|
public void ShowLibraryCommunityToolkit() {
|
||||||
OpenUrl("https://github.com/CommunityToolkit/dotnet");
|
SystemUtils.OpenUrl("https://github.com/CommunityToolkit/dotnet");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLibrarySqlite() {
|
public void ShowLibrarySqlite() {
|
||||||
OpenUrl("https://www.sqlite.org");
|
SystemUtils.OpenUrl("https://www.sqlite.org");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLibrarySqliteAdoNet() {
|
public void ShowLibrarySqliteAdoNet() {
|
||||||
OpenUrl("https://www.nuget.org/packages/Microsoft.Data.Sqlite");
|
SystemUtils.OpenUrl("https://www.nuget.org/packages/Microsoft.Data.Sqlite");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLibraryRxNet() {
|
public void ShowLibraryRxNet() {
|
||||||
OpenUrl("https://github.com/dotnet/reactive");
|
SystemUtils.OpenUrl("https://github.com/dotnet/reactive");
|
||||||
}
|
|
||||||
|
|
||||||
private static void OpenUrl(string url) {
|
|
||||||
Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
@@ -52,10 +51,7 @@ sealed partial class ViewerPageModel : ObservableObject, IDisposable {
|
|||||||
string serverUrl = "http://127.0.0.1:" + ServerConfiguration.Port;
|
string serverUrl = "http://127.0.0.1:" + ServerConfiguration.Port;
|
||||||
string serverToken = ServerConfiguration.Token;
|
string serverToken = ServerConfiguration.Token;
|
||||||
string sessionId = state.ViewerSessions.Register(new ViewerSession(FilterModel.CreateFilter())).ToString();
|
string sessionId = state.ViewerSessions.Register(new ViewerSession(FilterModel.CreateFilter())).ToString();
|
||||||
|
SystemUtils.OpenUrl(serverUrl + "/viewer/?token=" + HttpUtility.UrlEncode(serverToken) + "&session=" + HttpUtility.UrlEncode(sessionId));
|
||||||
Process.Start(new ProcessStartInfo(serverUrl + "/viewer/?token=" + HttpUtility.UrlEncode(serverToken) + "&session=" + HttpUtility.UrlEncode(sessionId)) {
|
|
||||||
UseShellExecute = true
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
await Dialog.ShowOk(window, "Open Viewer", "Could not open viewer: " + e.Message);
|
await Dialog.ShowOk(window, "Open Viewer", "Could not open viewer: " + e.Message);
|
||||||
}
|
}
|
||||||
|
@@ -22,20 +22,21 @@
|
|||||||
<Setter Property="HorizontalAlignment" Value="Center" />
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="Button">
|
<Style Selector="Grid#ButtonPanel > Button">
|
||||||
<Setter Property="Margin" Value="5 0" />
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
|
|
||||||
<Panel Name="RootPanel">
|
<Panel Name="RootPanel">
|
||||||
<StackPanel Margin="42">
|
<StackPanel Margin="42 30">
|
||||||
<TextBlock Text="{Binding Version, StringFormat=Discord History Tracker v{0}}" FontSize="25" Margin="0 0 0 30" HorizontalAlignment="Center" />
|
<TextBlock Text="{Binding Version, StringFormat=Discord History Tracker v{0}}" FontSize="25" Margin="0 0 0 25" HorizontalAlignment="Center" />
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<Grid Name="ButtonPanel" RowDefinitions="Auto,12,Auto,12,Auto" ColumnDefinitions="*,12,*" Margin="12 0" HorizontalAlignment="Stretch">
|
||||||
<Button Command="{Binding OpenOrCreateDatabase}" IsEnabled="{Binding IsOpenOrCreateDatabaseButtonEnabled}">Open or Create Database</Button>
|
<Button Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Command="{Binding OpenOrCreateDatabase}" IsEnabled="{Binding IsOpenOrCreateDatabaseButtonEnabled}">Open or Create Database</Button>
|
||||||
<Button Command="{Binding ShowAboutDialog}">About</Button>
|
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Command="{Binding CheckUpdates}">Check For Updates</Button>
|
||||||
<Button Command="{Binding Exit}">Exit</Button>
|
<Button Grid.Row="4" Grid.Column="0" Command="{Binding ShowAboutDialog}">About</Button>
|
||||||
</StackPanel>
|
<Button Grid.Row="4" Grid.Column="2" Command="{Binding Exit}">Exit</Button>
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Panel>
|
</Panel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
@@ -10,17 +12,20 @@ using DHT.Desktop.Dialogs.Progress;
|
|||||||
using DHT.Server.Data.Settings;
|
using DHT.Server.Data.Settings;
|
||||||
using DHT.Server.Database;
|
using DHT.Server.Database;
|
||||||
using DHT.Server.Database.Sqlite.Schema;
|
using DHT.Server.Database.Sqlite.Schema;
|
||||||
|
using DHT.Utils.Logging;
|
||||||
|
|
||||||
namespace DHT.Desktop.Main.Screens;
|
namespace DHT.Desktop.Main.Screens;
|
||||||
|
|
||||||
sealed partial class WelcomeScreenModel : ObservableObject {
|
sealed partial class WelcomeScreenModel : ObservableObject {
|
||||||
|
private static readonly Log Log = Log.ForType<WelcomeScreenModel>();
|
||||||
|
|
||||||
public string Version => Program.Version;
|
public string Version => Program.Version;
|
||||||
|
|
||||||
[ObservableProperty(Setter = Access.Private)]
|
[ObservableProperty(Setter = Access.Private)]
|
||||||
private bool isOpenOrCreateDatabaseButtonEnabled = true;
|
private bool isOpenOrCreateDatabaseButtonEnabled = true;
|
||||||
|
|
||||||
public event EventHandler<IDatabaseFile>? DatabaseSelected;
|
public event EventHandler<IDatabaseFile>? DatabaseSelected;
|
||||||
|
|
||||||
private readonly Window window;
|
private readonly Window window;
|
||||||
|
|
||||||
private string? dbFilePath;
|
private string? dbFilePath;
|
||||||
@@ -46,28 +51,22 @@ sealed partial class WelcomeScreenModel : ObservableObject {
|
|||||||
|
|
||||||
public async Task OpenOrCreateDatabaseFromPath(string path) {
|
public async Task OpenOrCreateDatabaseFromPath(string path) {
|
||||||
dbFilePath = path;
|
dbFilePath = path;
|
||||||
|
|
||||||
bool isNew = !File.Exists(path);
|
bool isNew = !File.Exists(path);
|
||||||
|
|
||||||
var db = await DatabaseGui.TryOpenOrCreateDatabaseFromPath(path, window, new SchemaUpgradeCallbacks(window));
|
var db = await DatabaseGui.TryOpenOrCreateDatabaseFromPath(path, window, new SchemaUpgradeCallbacks(window));
|
||||||
if (db == null) {
|
if (db == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNew && await Dialog.ShowYesNo(window, "Automatic Downloads", "Do you want to automatically download files hosted on Discord? You can change this later in the Downloads tab.") == DialogResult.YesNo.Yes) {
|
if (isNew && await Dialog.ShowYesNo(window, "Automatic Downloads", "Do you want to automatically download files hosted on Discord? You can change this later in the Downloads tab.") == DialogResult.YesNo.Yes) {
|
||||||
await db.Settings.Set(SettingsKey.DownloadsAutoStart, true);
|
await db.Settings.Set(SettingsKey.DownloadsAutoStart, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseSelected?.Invoke(this, db);
|
DatabaseSelected?.Invoke(this, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class SchemaUpgradeCallbacks : ISchemaUpgradeCallbacks {
|
private sealed class SchemaUpgradeCallbacks(Window window) : ISchemaUpgradeCallbacks {
|
||||||
private readonly Window window;
|
|
||||||
|
|
||||||
public SchemaUpgradeCallbacks(Window window) {
|
|
||||||
this.window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> CanUpgrade() {
|
public async Task<bool> CanUpgrade() {
|
||||||
return DialogResult.YesNo.Yes == await DatabaseGui.ShowCanUpgradeDatabaseDialog(window);
|
return DialogResult.YesNo.Yes == await DatabaseGui.ShowCanUpgradeDatabaseDialog(window);
|
||||||
}
|
}
|
||||||
@@ -80,20 +79,12 @@ sealed partial class WelcomeScreenModel : ObservableObject {
|
|||||||
await doUpgrade(reporter);
|
await doUpgrade(reporter);
|
||||||
await Task.Delay(TimeSpan.FromMilliseconds(600));
|
await Task.Delay(TimeSpan.FromMilliseconds(600));
|
||||||
}
|
}
|
||||||
|
|
||||||
await new ProgressDialog { DataContext = new ProgressDialogModel("Upgrading Database", StartUpgrade, progressItems: 3) }.ShowProgressDialog(window);
|
await new ProgressDialog { DataContext = new ProgressDialogModel("Upgrading Database", StartUpgrade, progressItems: 3) }.ShowProgressDialog(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ProgressReporter : ISchemaUpgradeCallbacks.IProgressReporter {
|
private sealed class ProgressReporter(int versionSteps, IReadOnlyList<IProgressCallback> callbacks) : ISchemaUpgradeCallbacks.IProgressReporter {
|
||||||
private readonly IReadOnlyList<IProgressCallback> callbacks;
|
|
||||||
|
|
||||||
private readonly int versionSteps;
|
|
||||||
private int versionProgress = 0;
|
private int versionProgress = 0;
|
||||||
|
|
||||||
public ProgressReporter(int versionSteps, IReadOnlyList<IProgressCallback> callbacks) {
|
|
||||||
this.callbacks = callbacks;
|
|
||||||
this.versionSteps = versionSteps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task NextVersion() {
|
public async Task NextVersion() {
|
||||||
await callbacks[0].Update("Upgrading schema version...", versionProgress++, versionSteps);
|
await callbacks[0].Update("Upgrading schema version...", versionProgress++, versionSteps);
|
||||||
@@ -118,6 +109,53 @@ sealed partial class WelcomeScreenModel : ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task CheckUpdates() {
|
||||||
|
Version? latestVersion = await ProgressDialog.ShowIndeterminate<Version?>(window, "Check Updates", "Checking for updates...", async _ => {
|
||||||
|
var client = new HttpClient(new SocketsHttpHandler {
|
||||||
|
AutomaticDecompression = DecompressionMethods.None,
|
||||||
|
AllowAutoRedirect = false,
|
||||||
|
UseCookies = false
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
client.MaxResponseContentBufferSize = 1024;
|
||||||
|
client.DefaultRequestHeaders.UserAgent.ParseAdd("DiscordHistoryTracker/" + Program.Version);
|
||||||
|
|
||||||
|
string response;
|
||||||
|
try {
|
||||||
|
response = await client.GetStringAsync(Program.Website + "/version");
|
||||||
|
} catch (TaskCanceledException e) when (e.InnerException is TimeoutException) {
|
||||||
|
await Dialog.ShowOk(window, "Check Updates", "Request timed out.");
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.Error(e);
|
||||||
|
await Dialog.ShowOk(window, "Check Updates", "Error checking for updates: " + e.Message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!System.Version.TryParse(response, out var latestVersion)) {
|
||||||
|
await Dialog.ShowOk(window, "Check Updates", "Server returned an invalid response.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestVersion;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (latestVersion == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Program.AssemblyVersion >= latestVersion) {
|
||||||
|
await Dialog.ShowOk(window, "Check Updates", "You are using the latest version.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await Dialog.ShowYesNo(window, "Check Updates", "A newer version is available: v" + Program.VersionToString(latestVersion) + "\nVisit the official website and close the app?") == DialogResult.YesNo.Yes) {
|
||||||
|
SystemUtils.OpenUrl(Program.Website);
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ShowAboutDialog() {
|
public async Task ShowAboutDialog() {
|
||||||
await new AboutWindow { DataContext = new AboutWindowModel() }.ShowDialog(window);
|
await new AboutWindow { DataContext = new AboutWindowModel() }.ShowDialog(window);
|
||||||
}
|
}
|
||||||
|
@@ -9,17 +9,18 @@ namespace DHT.Desktop;
|
|||||||
|
|
||||||
static class Program {
|
static class Program {
|
||||||
public static string Version { get; }
|
public static string Version { get; }
|
||||||
|
public static Version AssemblyVersion { get; }
|
||||||
public static CultureInfo Culture { get; }
|
public static CultureInfo Culture { get; }
|
||||||
public static ResourceLoader Resources { get; }
|
public static ResourceLoader Resources { get; }
|
||||||
public static Arguments Arguments { get; }
|
public static Arguments Arguments { get; }
|
||||||
|
|
||||||
|
public const string Website = "https://dht.chylex.com";
|
||||||
|
|
||||||
static Program() {
|
static Program() {
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
|
||||||
Version = assembly.GetName().Version?.ToString() ?? "";
|
AssemblyVersion = assembly.GetName().Version ?? new Version(0, 0, 0, 0);
|
||||||
while (Version.EndsWith(".0")) {
|
Version = VersionToString(AssemblyVersion);
|
||||||
Version = Version[..^2];
|
|
||||||
}
|
|
||||||
|
|
||||||
Culture = CultureInfo.CurrentCulture;
|
Culture = CultureInfo.CurrentCulture;
|
||||||
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
|
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
@@ -30,6 +31,16 @@ static class Program {
|
|||||||
Resources = new ResourceLoader(assembly);
|
Resources = new ResourceLoader(assembly);
|
||||||
Arguments = new Arguments(Environment.GetCommandLineArgs());
|
Arguments = new Arguments(Environment.GetCommandLineArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string VersionToString(Version version) {
|
||||||
|
string versionStr = version.ToString();
|
||||||
|
|
||||||
|
while (versionStr.EndsWith(".0")) {
|
||||||
|
versionStr = versionStr[..^2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionStr;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Main(string[] args) {
|
public static void Main(string[] args) {
|
||||||
if (Arguments.Console && OperatingSystem.IsWindows()) {
|
if (Arguments.Console && OperatingSystem.IsWindows()) {
|
||||||
|
@@ -117,24 +117,25 @@ class DISCORD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getMessagesFromSelectedChannel() {
|
||||||
|
const channelId = this.#getCurrentlySelectedChannelId();
|
||||||
|
return channelId ? this.#getMessages(channelId) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the provided function with a list of messages whenever the currently loaded messages change.
|
* Calls the provided function with a list of messages whenever the currently loaded messages change.
|
||||||
|
* @param callback {function(server: ?DiscordGuild, channel: DiscordChannel, messages: Array<DiscordMessage>, hasMoreBefore: boolean)}
|
||||||
*/
|
*/
|
||||||
static setupMessageCallback(callback) {
|
static setupMessageCallback(callback) {
|
||||||
const previousMessages = new Set();
|
const previousMessages = new Set();
|
||||||
|
|
||||||
const onMessageElementsChanged = force => {
|
const onMessageElementsChanged = force => {
|
||||||
const channelId = this.#getCurrentlySelectedChannelId();
|
const messages = this.getMessagesFromSelectedChannel();
|
||||||
if (!channelId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = this.#getMessages(channelId);
|
|
||||||
if (!messages || !messages.ready || messages.loadingMore) {
|
if (!messages || !messages.ready || messages.loadingMore) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channel = this.#channelStore.getChannel(channelId);
|
const channel = this.#channelStore.getChannel(messages.channelId);
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -286,7 +286,7 @@ const STATE = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (msg.reactions.length > 0) {
|
if (msg.reactions.length > 0) {
|
||||||
obj.reactions = msg.reactions.map(reaction => {
|
obj.reactions = msg.reactions.filter(reaction => reaction.count > 0).map(reaction => {
|
||||||
const emoji = reaction.emoji;
|
const emoji = reaction.emoji;
|
||||||
|
|
||||||
const mapped = {
|
const mapped = {
|
||||||
|
@@ -73,6 +73,7 @@
|
|||||||
/**
|
/**
|
||||||
* @name MessageData
|
* @name MessageData
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
|
* @property {String} channelId
|
||||||
* @property {Boolean} ready
|
* @property {Boolean} ready
|
||||||
* @property {Boolean} loadingMore
|
* @property {Boolean} loadingMore
|
||||||
* @property {Boolean} hasMoreAfter
|
* @property {Boolean} hasMoreAfter
|
||||||
|
@@ -8,5 +8,5 @@ using DHT.Utils;
|
|||||||
namespace DHT.Utils;
|
namespace DHT.Utils;
|
||||||
|
|
||||||
static class Version {
|
static class Version {
|
||||||
public const string Tag = "43.1.0.0";
|
public const string Tag = "44.0.0.0";
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user