mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-07-10 16:01:55 +02:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc35f25293 | |||
| 0e3969ca2d | |||
| 91b7486f45 | |||
| b7548756a8 | |||
| 8f9c0f8660 | |||
| b12be796c7 | |||
| 7a69c87b56 | |||
| 9e7ad4d85c | |||
| 0fb4bea64d | |||
| d8abd011e5 | |||
| 9a5697b5b5 | |||
| 886b8442d3 | |||
| bedf7de8c6 | |||
| 5ef329b63c | |||
| 060e55a0d7 | |||
| 61444aad30 | |||
| 9b3421e8a5 |
@@ -1,51 +0,0 @@
|
|||||||
# Install .NET runtime
|
|
||||||
|
|
||||||
**.NET 8.0 Runtime** is required by **DiscordChatExporter**.
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> Installing the **.NET Runtime** is not required if you running **DiscordChatExporter** using [Docker](Docker.md).
|
|
||||||
|
|
||||||
## Windows
|
|
||||||
|
|
||||||
Both the GUI and the CLI flavors of **DiscordChatExporter** for Windows come with a bootstrapper that should automatically install all the required prerequisites. Simply run the application and follow the presented instructions.
|
|
||||||
|
|
||||||
If, for some reason, you need to install the runtime manually, use one of the download links below:
|
|
||||||
|
|
||||||
- [.NET 8.0 Runtime for **Windows x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-x86-installer)
|
|
||||||
- [.NET 8.0 Runtime for **Windows x86**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-x64-installer)
|
|
||||||
- [.NET 8.0 Runtime for **Windows arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-windows-arm64-installer)
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> [How can I tell if my computer is running a 32-bit (x86) or a 64-bit (x64) version of Windows?](https://support.microsoft.com/help/15056/windows-32-64-bit-faq)
|
|
||||||
|
|
||||||
## macOS
|
|
||||||
|
|
||||||
Use one of the download links below:
|
|
||||||
|
|
||||||
- [.NET 8.0 Runtime for **macOS x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-x64-installer)
|
|
||||||
- [.NET 8.0 Runtime for **macOS arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-arm64-installer)
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> [Is my Mac using an Intel processor (x64) or Apple Silicon (arm64)?](https://support.apple.com/HT211814)
|
|
||||||
|
|
||||||
## Linux
|
|
||||||
|
|
||||||
Installing the .NET Runtime works differently depending on which Linux distribution you use. Check out the instructions for your distro:
|
|
||||||
|
|
||||||
- [Ubuntu](https://docs.microsoft.com/dotnet/core/install/linux-ubuntu)
|
|
||||||
- [Debian](https://docs.microsoft.com/dotnet/core/install/linux-debian)
|
|
||||||
- [Fedora](https://docs.microsoft.com/dotnet/core/install/linux-fedora)
|
|
||||||
|
|
||||||
For other distros, please check the _'Install on Linux'_ menu on the left of [this page](https://docs.microsoft.com/dotnet/core/install/linux).
|
|
||||||
|
|
||||||
## Verify installation (optional)
|
|
||||||
|
|
||||||
You can check which version of **.NET Runtime** is installed by running the following command in a terminal:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ dotnet --info
|
|
||||||
|
|
||||||
.NET runtimes installed:
|
|
||||||
Microsoft.NETCore.App 7.0.x [C:\path\to\dotnet\shared\Microsoft.NETCore.App]
|
|
||||||
Microsoft.WindowsDesktop.App 7.0.x [C:\path\to\dotnet\shared\Microsoft.WindowsDesktop.App]
|
|
||||||
```
|
|
||||||
@@ -35,13 +35,13 @@ You can open `.html` files with a web browser, such as Google Chrome.
|
|||||||
|
|
||||||
> **Warning**:
|
> **Warning**:
|
||||||
> If a picture is deleted, or if a user changes its avatar, the respective images will no longer be displayed.
|
> If a picture is deleted, or if a user changes its avatar, the respective images will no longer be displayed.
|
||||||
> Export using the "Download referenced assets" (`--media`) option to avoid this.
|
> Export using the "Download assets" (`--media`) option to avoid this.
|
||||||
|
|
||||||
### Plain Text
|
### Plain Text
|
||||||
|
|
||||||
<img src="https://i.imgur.com/PbUyRXD.png" height="400"/>
|
<img src="https://i.imgur.com/PbUyRXD.png" height="400"/>
|
||||||
|
|
||||||
The Plain Text format is the best option for archiving due to its small size.
|
The Plain Text format formats messages as plain text, and has the smallest size.
|
||||||
You can open `.txt` files with a text editor, such as Notepad.
|
You can open `.txt` files with a text editor, such as Notepad.
|
||||||
|
|
||||||
### JSON
|
### JSON
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
## Installation & Usage
|
## Installation & Usage
|
||||||
|
|
||||||
- [Get .NET Runtime](Dotnet.md)
|
|
||||||
- Getting started:
|
- Getting started:
|
||||||
- [Using the GUI](Using-the-GUI.md)
|
- [Using the GUI](Using-the-GUI.md)
|
||||||
- [Using the CLI](Using-the-CLI.md)
|
- [Using the CLI](Using-the-CLI.md)
|
||||||
@@ -28,6 +27,5 @@
|
|||||||
- [General questions](Troubleshooting.md#general)
|
- [General questions](Troubleshooting.md#general)
|
||||||
- [First steps help](Troubleshooting.md#first-steps)
|
- [First steps help](Troubleshooting.md#first-steps)
|
||||||
- [It's crashing/failing](Troubleshooting.md#DCE-is-crashingfailing)
|
- [It's crashing/failing](Troubleshooting.md#DCE-is-crashingfailing)
|
||||||
- [.NET Core Runtime is required](Troubleshooting.md#net-core-runtime-is-required)
|
|
||||||
- [Errors](Troubleshooting.md#errors)
|
- [Errors](Troubleshooting.md#errors)
|
||||||
- [**More help**](Troubleshooting.md)
|
- [**More help**](Troubleshooting.md)
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# Scheduling exports with Cron
|
# Scheduling exports with Cron
|
||||||
|
|
||||||
Make sure you already have **DiscordChatExporter.CLI** downloaded and **.NET Runtime** installed.
|
|
||||||
|
|
||||||
## Creating the script
|
## Creating the script
|
||||||
|
|
||||||
1. Open Terminal and create a new text file with `nano /path/to/DiscordChatExporter/cron.sh`
|
1. Open Terminal and create a new text file with `nano /path/to/DiscordChatExporter/cron.sh`
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# Scheduling exports on macOS
|
# Scheduling exports on macOS
|
||||||
|
|
||||||
Make sure you already have **DiscordChatExporter.CLI** downloaded and **.NET Runtime** installed.
|
|
||||||
|
|
||||||
## Creating the script
|
## Creating the script
|
||||||
|
|
||||||
1. Open TextEdit.app and create a new file
|
1. Open TextEdit.app and create a new file
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
# Scheduling exports on Windows
|
# Scheduling exports on Windows
|
||||||
|
|
||||||
We'll be using [DiscordChatExporter CLI](https://github.com/Tyrrrz/DiscordChatExporter/releases/latest), PowerShell, and Task Scheduler.
|
|
||||||
Make sure you already have **DiscordChatExporter.CLI** downloaded and **.NET Runtime** installed.
|
|
||||||
|
|
||||||
## Creating the script
|
## Creating the script
|
||||||
|
|
||||||
1. Open a text editor such as Notepad and paste:
|
1. Open a text editor such as Notepad and paste:
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ Check the following page: [Obtaining token](Token-and-IDs.md)
|
|||||||
|
|
||||||
### When I open DCE a black window pops up quickly or nothing shows up
|
### When I open DCE a black window pops up quickly or nothing shows up
|
||||||
|
|
||||||
If you have [.NET Runtime correctly installed](Dotnet.md), you might have downloaded the CLI flavor, try [downloading the GUI](Getting-started.md#gui-or-cli) instead.
|
You might have downloaded the CLI flavor of the app, which is meant to be run in a terminal. Try [downloading the GUI](Getting-started.md#gui-or-cli) instead if that's what you want.
|
||||||
|
|
||||||
### How can I set DCE to export automatically at certain times?
|
### How can I set DCE to export automatically at certain times?
|
||||||
|
|
||||||
@@ -84,16 +84,6 @@ Check the following pages to learn how to schedule **DiscordChatExporter.CLI** r
|
|||||||
|
|
||||||
Try opening it with a different program, try partitioning or use a different file format, like `PlainText`.
|
Try opening it with a different program, try partitioning or use a different file format, like `PlainText`.
|
||||||
|
|
||||||
### DCE is crashing/failing
|
|
||||||
|
|
||||||
Check the following page: [Installing .NET Core Runtime](Dotnet.md)
|
|
||||||
|
|
||||||
If you already have .NET Core installed, please check if your problem is a [known issue](https://github.com/Tyrrrz/DiscordChatExporter/issues?q=is%3Aissue) before [opening a bug report](https://github.com/Tyrrrz/DiscordChatExporter/issues/new).
|
|
||||||
|
|
||||||
### .NET Core Runtime is required
|
|
||||||
|
|
||||||
Check the following page: [Installing .NET Core Runtime](Dotnet.md)
|
|
||||||
|
|
||||||
### I see messages in the export, but they have no content
|
### I see messages in the export, but they have no content
|
||||||
|
|
||||||
Your bot is missing the 'Message Content Intent'. Go to the [Discord Developer Portal](https://discord.com/developers/applications), navigate to the 'Bot' section and enable it.
|
Your bot is missing the 'Message Content Intent'. Go to the [Discord Developer Portal](https://discord.com/developers/applications), navigate to the 'Bot' section and enable it.
|
||||||
|
|||||||
+3
-10
@@ -1,24 +1,17 @@
|
|||||||
# Using the CLI
|
# Using the CLI
|
||||||
|
|
||||||
## Guide
|
|
||||||
|
|
||||||
> **Note**:
|
|
||||||
> Make sure you have [.NET Core installed](Dotnet.md) before attempting to run the commands below.
|
|
||||||
> **Docker** users, please refer to the [Docker usage instructions](Docker.md).
|
|
||||||
|
|
||||||
## Step 1
|
## Step 1
|
||||||
|
|
||||||
After extracting the `.zip`, open Command Prompt, aka `cmd` (`Terminal` on **macOS** and **Linux**).
|
After extracting the `.zip`, open Command Prompt, aka `cmd` (`Terminal` on **macOS** and **Linux**).
|
||||||
|
|
||||||
## Step 2
|
## Step 2
|
||||||
|
|
||||||
Change the current directory to DCE's folder with `cd C:\path\to\directory`, then press ENTER to run the command.
|
Change the current directory to DCE's folder with `cd C:\path\to\DiscordChatExporter` (`cd /path/to/DiscordChatExporter` on **MacOS** and **Linux**), then press ENTER to run the command.
|
||||||
|
|
||||||
**Windows** users can quickly get the directory's path by clicking the address bar while inside the folder.
|
**Windows** users can quickly get the folder's path by clicking the address bar while inside the folder.
|
||||||

|

|
||||||
|
|
||||||
**macOS** users can select the `.exe`, hit Command+I (⌘I), and copy what's after `Where:` to get the directory
|
**macOS** users can press Command+Option+C (⌘⌥C) while inside the folder (or selecting it) to copy its path to the clipboard.
|
||||||

|
|
||||||
|
|
||||||
You can also drag and drop the folder on **every platform**.
|
You can also drag and drop the folder on **every platform**.
|
||||||

|

|
||||||
|
|||||||
+11
-18
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
|
|
||||||
After extracting the `.zip`, open `DiscordChatExporter.exe`
|
After extracting the `.zip`, run `DiscordChatExporter.exe` (Windows), or `DiscordChatExporter` (Mac OS and Linux).
|
||||||
|
|
||||||
### Step 2
|
### Step 2
|
||||||
|
|
||||||
@@ -61,37 +61,30 @@ In this screen you can customize the following:
|
|||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
- **Auto-update**
|
- **Auto-update** - Perform automatic updates on every launch.
|
||||||
Perform automatic updates on every launch.
|
|
||||||
Default: Enabled
|
Default: Enabled
|
||||||
|
|
||||||
> **Note**:
|
> **Note**:
|
||||||
> Keep this option enabled to receive the latest features and bug fixes!
|
> Keep this option enabled to receive the latest features and bug fixes!
|
||||||
|
|
||||||
- **Dark mode**
|
- **Dark mode** - Use darker colors in the UI (User Interface).
|
||||||
Use darker colors in the UI (User Interface).
|
|
||||||
Default: Disabled
|
Default: Disabled
|
||||||
|
|
||||||
- **Persist token**
|
- **Persist token** - Persist last used token between sessions.
|
||||||
Persist last used token between sessions.
|
|
||||||
Default: Enabled
|
Default: Enabled
|
||||||
|
|
||||||
- **Show threads**
|
- **Show threads** - Controls whether threads are shown in the channel list.
|
||||||
Controls whether threads are shown in the channel list.
|
|
||||||
Default: none
|
Default: none
|
||||||
|
|
||||||
- **Locale**
|
- **Locale** - Customize how dates are formatted in the exported files.
|
||||||
Customize how dates are formatted in the exported files.
|
|
||||||
|
|
||||||
- **Date format**
|
- **Date format** - Customize how dates are formatted in the exported files in the settings menu ().
|
||||||
Customize how dates are formatted in the exported files in the settings menu ().
|
|
||||||
|
|
||||||
- **Parallel limit**
|
- **Parallel limit** - The number of channels that will be exported at the same time.
|
||||||
The number of channels that will be exported at the same time.
|
|
||||||
Default: 1
|
Default: 1
|
||||||
|
|
||||||
- **Normalize to UTC**
|
|
||||||
Convert all dates to UTC before exporting.
|
|
||||||
|
|
||||||
> **Note**:
|
> **Note**:
|
||||||
> Try to keep this number low so that your account doesn't get flagged.
|
> Try to keep this number low so that your account doesn't get flagged.
|
||||||
|
|
||||||
|
- **Normalize to UTC** - Convert all dates to UTC before exporting.
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have sponsored this project
|
- label: I have sponsored this project
|
||||||
required: false
|
required: false
|
||||||
|
- label: I have not read any of the above and just checked all the boxes to submit the issue
|
||||||
|
required: false
|
||||||
|
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ jobs:
|
|||||||
# GUI assets aren't suffixed, unlike the CLI assets
|
# GUI assets aren't suffixed, unlike the CLI assets
|
||||||
asset: DiscordChatExporter
|
asset: DiscordChatExporter
|
||||||
|
|
||||||
# Need to run on Windows because of DotnetRuntimeBootstrapper's dependency on Ressy
|
runs-on: ${{ startsWith(matrix.rid, 'win-') && 'windows-latest' || startsWith(matrix.rid, 'osx-') && 'macos-latest' || 'ubuntu-latest' }}
|
||||||
runs-on: windows-latest
|
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -124,7 +123,7 @@ jobs:
|
|||||||
--output ${{ matrix.app }}/bin/publish/
|
--output ${{ matrix.app }}/bin/publish/
|
||||||
--configuration Release
|
--configuration Release
|
||||||
--runtime ${{ matrix.rid }}
|
--runtime ${{ matrix.rid }}
|
||||||
--no-self-contained
|
--self-contained
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
|
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
|
||||||
@@ -196,7 +195,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ${{ matrix.asset }}.${{ matrix.rid }}
|
name: ${{ matrix.asset }}.${{ matrix.rid }}
|
||||||
path: ${{ matrix.app }}/
|
path: ${{ matrix.app }}/
|
||||||
|
|
||||||
|
- name: Set permissions
|
||||||
|
if: ${{ !startsWith(matrix.rid, 'win') }}
|
||||||
|
run: chmod +x ${{ matrix.app }}/${{ matrix.asset }}
|
||||||
|
|
||||||
- name: Create package
|
- name: Create package
|
||||||
# Change into the artifacts directory to avoid including the directory itself in the zip archive
|
# Change into the artifacts directory to avoid including the directory itself in the zip archive
|
||||||
working-directory: ${{ matrix.app }}/
|
working-directory: ${{ matrix.app }}/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<ILLinkTreatWarningsAsErrors>false</ILLinkTreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
<PackageReference Include="xunit" Version="2.8.0" />
|
<PackageReference Include="xunit" Version="2.8.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0" PrivateAssets="all" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<NoWarn>$(NoWarn);IL2104</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CliFx" Version="2.3.5" />
|
<PackageReference Include="CliFx" Version="2.3.5" />
|
||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.28.2" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="0.28.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="Deorcify" Version="1.0.2" PrivateAssets="all" />
|
<PackageReference Include="Deorcify" Version="1.0.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="DotnetRuntimeBootstrapper" Version="2.5.4" PrivateAssets="all" />
|
|
||||||
<PackageReference Include="Gress" Version="2.1.1" />
|
<PackageReference Include="Gress" Version="2.1.1" />
|
||||||
<PackageReference Include="Spectre.Console" Version="0.49.1" />
|
<PackageReference Include="Spectre.Console" Version="0.49.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,3 +1,37 @@
|
|||||||
using CliFx;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx;
|
||||||
|
using DiscordChatExporter.Cli.Commands;
|
||||||
|
using DiscordChatExporter.Cli.Commands.Converters;
|
||||||
|
|
||||||
return await new CliApplicationBuilder().AddCommandsFromThisAssembly().Build().RunAsync(args);
|
namespace DiscordChatExporter.Cli;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
// Explicit references because CliFx relies on reflection and we're publishing with trimming enabled
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportAllCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportChannelsCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportDirectMessagesCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ExportGuildCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GetChannelsCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GetDirectChannelsCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GetGuildsCommand))]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(GuideCommand))]
|
||||||
|
[DynamicDependency(
|
||||||
|
DynamicallyAccessedMemberTypes.All,
|
||||||
|
typeof(ThreadInclusionModeBindingConverter)
|
||||||
|
)]
|
||||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TruthyBooleanBindingConverter))]
|
||||||
|
public static async Task<int> Main(string[] args) =>
|
||||||
|
await new CliApplicationBuilder()
|
||||||
|
.AddCommand<ExportAllCommand>()
|
||||||
|
.AddCommand<ExportChannelsCommand>()
|
||||||
|
.AddCommand<ExportDirectMessagesCommand>()
|
||||||
|
.AddCommand<ExportGuildCommand>()
|
||||||
|
.AddCommand<GetChannelsCommand>()
|
||||||
|
.AddCommand<GetDirectChannelsCommand>()
|
||||||
|
.AddCommand<GetGuildsCommand>()
|
||||||
|
.AddCommand<GuideCommand>()
|
||||||
|
.Build()
|
||||||
|
.RunAsync(args);
|
||||||
|
}
|
||||||
|
|||||||
@@ -676,10 +676,14 @@ public class DiscordClient(string token)
|
|||||||
.SetQueryParameter("after", currentAfter.ToString())
|
.SetQueryParameter("after", currentAfter.ToString())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var response = await GetJsonResponseAsync(url, cancellationToken);
|
// Can be null on reactions with an emoji that has been deleted (?)
|
||||||
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1226
|
||||||
|
var response = await TryGetJsonResponseAsync(url, cancellationToken);
|
||||||
|
if (response is null)
|
||||||
|
yield break;
|
||||||
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
foreach (var userJson in response.EnumerateArray())
|
foreach (var userJson in response.Value.EnumerateArray())
|
||||||
{
|
{
|
||||||
var user = User.Parse(userJson);
|
var user = User.Parse(userJson);
|
||||||
yield return user;
|
yield return user;
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.28.2" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="0.28.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="Gress" Version="2.1.1" />
|
<PackageReference Include="Gress" Version="2.1.1" />
|
||||||
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
||||||
<PackageReference Include="Polly" Version="8.3.1" />
|
<PackageReference Include="Polly" Version="8.4.0" />
|
||||||
<PackageReference Include="RazorBlade" Version="0.6.0" />
|
<PackageReference Include="RazorBlade" Version="0.6.0" />
|
||||||
<PackageReference Include="Superpower" Version="3.0.0" />
|
<PackageReference Include="Superpower" Version="3.0.0" />
|
||||||
<PackageReference Include="WebMarkupMin.Core" Version="2.16.0" />
|
<PackageReference Include="WebMarkupMin.Core" Version="2.16.0" />
|
||||||
<PackageReference Include="YoutubeExplode" Version="6.3.14" />
|
<PackageReference Include="YoutubeExplode" Version="6.3.16" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -7,13 +7,18 @@
|
|||||||
xmlns:materialAssists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles"
|
xmlns:materialAssists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles"
|
||||||
xmlns:materialControls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
|
xmlns:materialControls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
|
||||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles">
|
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles"
|
||||||
|
ActualThemeVariantChanged="Application_OnActualThemeVariantChanged">
|
||||||
<Application.DataTemplates>
|
<Application.DataTemplates>
|
||||||
<framework:ViewManager />
|
<framework:ViewManager />
|
||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<materialStyles:MaterialTheme />
|
<!-- This theme is used as a stub to pre-load default resources, the actual colors are set through code -->
|
||||||
|
<materialStyles:MaterialTheme
|
||||||
|
BaseTheme="Light"
|
||||||
|
PrimaryColor="Grey"
|
||||||
|
SecondaryColor="DeepOrange" />
|
||||||
<materialIcons:MaterialIconStyles />
|
<materialIcons:MaterialIconStyles />
|
||||||
<dialogHostAvalonia:DialogHostStyles />
|
<dialogHostAvalonia:DialogHostStyles />
|
||||||
|
|
||||||
@@ -121,7 +126,7 @@
|
|||||||
|
|
||||||
<!-- Toggle switch -->
|
<!-- Toggle switch -->
|
||||||
<Style Selector="ToggleSwitch">
|
<Style Selector="ToggleSwitch">
|
||||||
<Setter Property="materialAssists:ToggleSwitchAssist.SwitchThumbOffBackground" Value="{DynamicResource MaterialBackgroundBrush}" />
|
<Setter Property="materialAssists:ToggleSwitchAssist.SwitchThumbOffBackground" Value="{DynamicResource ToggleBackgroundBrush}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- Tooltip -->
|
<!-- Tooltip -->
|
||||||
@@ -132,4 +137,17 @@
|
|||||||
<Setter Property="TextElement.FontStretch" Value="Normal" />
|
<Setter Property="TextElement.FontStretch" Value="Normal" />
|
||||||
</Style>
|
</Style>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
|
<ResourceDictionary x:Key="Default">
|
||||||
|
<SolidColorBrush x:Key="ToggleBackgroundBrush" Color="#FFFFFF" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
<ResourceDictionary x:Key="Dark">
|
||||||
|
<SolidColorBrush x:Key="ToggleBackgroundBrush" Color="#8E8E8E" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
@@ -7,6 +7,8 @@ using Avalonia.Media;
|
|||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using DiscordChatExporter.Gui.Framework;
|
using DiscordChatExporter.Gui.Framework;
|
||||||
using DiscordChatExporter.Gui.Services;
|
using DiscordChatExporter.Gui.Services;
|
||||||
|
using DiscordChatExporter.Gui.Utils;
|
||||||
|
using DiscordChatExporter.Gui.Utils.Extensions;
|
||||||
using DiscordChatExporter.Gui.ViewModels;
|
using DiscordChatExporter.Gui.ViewModels;
|
||||||
using DiscordChatExporter.Gui.ViewModels.Components;
|
using DiscordChatExporter.Gui.ViewModels.Components;
|
||||||
using DiscordChatExporter.Gui.ViewModels.Dialogs;
|
using DiscordChatExporter.Gui.ViewModels.Dialogs;
|
||||||
@@ -16,9 +18,12 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace DiscordChatExporter.Gui;
|
namespace DiscordChatExporter.Gui;
|
||||||
|
|
||||||
public partial class App : Application, IDisposable
|
public class App : Application, IDisposable
|
||||||
{
|
{
|
||||||
|
private readonly DisposableCollector _eventRoot = new();
|
||||||
|
|
||||||
private readonly ServiceProvider _services;
|
private readonly ServiceProvider _services;
|
||||||
|
private readonly SettingsService _settingsService;
|
||||||
private readonly MainViewModel _mainViewModel;
|
private readonly MainViewModel _mainViewModel;
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
@@ -43,17 +48,54 @@ public partial class App : Application, IDisposable
|
|||||||
services.AddTransient<SettingsViewModel>();
|
services.AddTransient<SettingsViewModel>();
|
||||||
|
|
||||||
_services = services.BuildServiceProvider(true);
|
_services = services.BuildServiceProvider(true);
|
||||||
|
_settingsService = _services.GetRequiredService<SettingsService>();
|
||||||
_mainViewModel = _services.GetRequiredService<ViewModelManager>().CreateMainViewModel();
|
_mainViewModel = _services.GetRequiredService<ViewModelManager>().CreateMainViewModel();
|
||||||
|
|
||||||
|
// Re-initialize the theme when the user changes it
|
||||||
|
_eventRoot.Add(
|
||||||
|
_settingsService.WatchProperty(
|
||||||
|
o => o.Theme,
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
RequestedThemeVariant = _settingsService.Theme switch
|
||||||
|
{
|
||||||
|
ThemeVariant.Light => Avalonia.Styling.ThemeVariant.Light,
|
||||||
|
ThemeVariant.Dark => Avalonia.Styling.ThemeVariant.Dark,
|
||||||
|
_ => Avalonia.Styling.ThemeVariant.Default
|
||||||
|
};
|
||||||
|
|
||||||
|
InitializeTheme();
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
// Increase maximum concurrent connections
|
// Increase maximum concurrent connections
|
||||||
ServicePointManager.DefaultConnectionLimit = 20;
|
ServicePointManager.DefaultConnectionLimit = 20;
|
||||||
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InitializeTheme()
|
||||||
|
{
|
||||||
|
var actualTheme = RequestedThemeVariant?.Key switch
|
||||||
|
{
|
||||||
|
"Light" => PlatformThemeVariant.Light,
|
||||||
|
"Dark" => PlatformThemeVariant.Dark,
|
||||||
|
_ => PlatformSettings?.GetColorValues().ThemeVariant ?? PlatformThemeVariant.Light
|
||||||
|
};
|
||||||
|
|
||||||
|
this.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme =
|
||||||
|
actualTheme == PlatformThemeVariant.Light
|
||||||
|
? Theme.Create(Theme.Light, Color.Parse("#343838"), Color.Parse("#F9A825"))
|
||||||
|
: Theme.Create(Theme.Dark, Color.Parse("#E8E8E8"), Color.Parse("#F9A825"));
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
@@ -61,50 +103,20 @@ public partial class App : Application, IDisposable
|
|||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
|
||||||
// Set custom theme colors
|
// Set up custom theme colors
|
||||||
SetDefaultTheme();
|
InitializeTheme();
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
_settingsService.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => _services.Dispose();
|
private void Application_OnActualThemeVariantChanged(object? sender, EventArgs args) =>
|
||||||
}
|
// Re-initialize the theme when the system theme changes
|
||||||
|
InitializeTheme();
|
||||||
|
|
||||||
public partial class App
|
public void Dispose()
|
||||||
{
|
|
||||||
public static void SetLightTheme()
|
|
||||||
{
|
{
|
||||||
if (Current is null)
|
_eventRoot.Dispose();
|
||||||
return;
|
_services.Dispose();
|
||||||
|
|
||||||
Current.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
|
|
||||||
Theme.Light,
|
|
||||||
Color.Parse("#343838"),
|
|
||||||
Color.Parse("#F9A825")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetDarkTheme()
|
|
||||||
{
|
|
||||||
if (Current is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Current.LocateMaterialTheme<MaterialThemeBase>().CurrentTheme = Theme.Create(
|
|
||||||
Theme.Dark,
|
|
||||||
Color.Parse("#E8E8E8"),
|
|
||||||
Color.Parse("#F9A825")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetDefaultTheme()
|
|
||||||
{
|
|
||||||
if (Current is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var isDarkModeEnabledByDefault =
|
|
||||||
Current.PlatformSettings?.GetColorValues().ThemeVariant == PlatformThemeVariant.Dark;
|
|
||||||
|
|
||||||
if (isDarkModeEnabledByDefault)
|
|
||||||
SetDarkTheme();
|
|
||||||
else
|
|
||||||
SetLightTheme();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class LocaleToDisplayNameStringConverter : IValueConverter
|
|||||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||||
value is string locale && !string.IsNullOrWhiteSpace(locale)
|
value is string locale && !string.IsNullOrWhiteSpace(locale)
|
||||||
? CultureInfo.GetCultureInfo(locale).DisplayName
|
? CultureInfo.GetCultureInfo(locale).DisplayName
|
||||||
: "System default";
|
: "System";
|
||||||
|
|
||||||
public object ConvertBack(
|
public object ConvertBack(
|
||||||
object? value,
|
object? value,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<AssemblyName>DiscordChatExporter</AssemblyName>
|
<AssemblyName>DiscordChatExporter</AssemblyName>
|
||||||
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -15,14 +17,13 @@
|
|||||||
<PackageReference Include="Avalonia" Version="11.0.10" />
|
<PackageReference Include="Avalonia" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" Condition="'$(Configuration)' == 'Debug'" />
|
||||||
<PackageReference Include="Cogwheel" Version="2.0.4" />
|
<PackageReference Include="Cogwheel" Version="2.1.0" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.28.2" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="0.28.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="Deorcify" Version="1.0.2" PrivateAssets="all" />
|
<PackageReference Include="Deorcify" Version="1.0.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="DialogHost.Avalonia" Version="0.7.7" />
|
<PackageReference Include="DialogHost.Avalonia" Version="0.7.7" />
|
||||||
<PackageReference Include="DotnetRuntimeBootstrapper" Version="2.5.4" PrivateAssets="all" />
|
|
||||||
<PackageReference Include="Gress" Version="2.1.1" />
|
<PackageReference Include="Gress" Version="2.1.1" />
|
||||||
<PackageReference Include="Material.Avalonia" Version="3.5.0" />
|
<PackageReference Include="Material.Avalonia" Version="3.6.0" />
|
||||||
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.9" />
|
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
<PackageReference Include="Onova" Version="2.6.11" />
|
<PackageReference Include="Onova" Version="2.6.11" />
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace DiscordChatExporter.Gui.Framework;
|
||||||
|
|
||||||
|
public enum ThemeVariant
|
||||||
|
{
|
||||||
|
System,
|
||||||
|
Light,
|
||||||
|
Dark
|
||||||
|
}
|
||||||
@@ -1,25 +1,31 @@
|
|||||||
using System;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
|
using DiscordChatExporter.Gui.ViewModels;
|
||||||
|
using DiscordChatExporter.Gui.ViewModels.Components;
|
||||||
|
using DiscordChatExporter.Gui.ViewModels.Dialogs;
|
||||||
|
using DiscordChatExporter.Gui.Views;
|
||||||
|
using DiscordChatExporter.Gui.Views.Components;
|
||||||
|
using DiscordChatExporter.Gui.Views.Dialogs;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Gui.Framework;
|
namespace DiscordChatExporter.Gui.Framework;
|
||||||
|
|
||||||
public partial class ViewManager
|
public partial class ViewManager
|
||||||
{
|
{
|
||||||
|
private Control? TryCreateView(ViewModelBase viewModel) =>
|
||||||
|
viewModel switch
|
||||||
|
{
|
||||||
|
MainViewModel => new MainView(),
|
||||||
|
DashboardViewModel => new DashboardView(),
|
||||||
|
ExportSetupViewModel => new ExportSetupView(),
|
||||||
|
MessageBoxViewModel => new MessageBoxView(),
|
||||||
|
SettingsViewModel => new SettingsView(),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
public Control? TryBindView(ViewModelBase viewModel)
|
public Control? TryBindView(ViewModelBase viewModel)
|
||||||
{
|
{
|
||||||
var name = viewModel
|
var view = TryCreateView(viewModel);
|
||||||
.GetType()
|
if (view is null)
|
||||||
.FullName?.Replace("ViewModel", "View", StringComparison.Ordinal);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var type = Type.GetType(name);
|
|
||||||
if (type is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (Activator.CreateInstance(type) is not Control view)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
view.DataContext ??= viewModel;
|
view.DataContext ??= viewModel;
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ public static class Program
|
|||||||
|
|
||||||
public static string ProjectUrl { get; } = "https://github.com/Tyrrrz/DiscordChatExporter";
|
public static string ProjectUrl { get; } = "https://github.com/Tyrrrz/DiscordChatExporter";
|
||||||
|
|
||||||
public static string LatestReleaseUrl { get; } = ProjectUrl + "/releases/latest";
|
|
||||||
|
|
||||||
public static string DocumentationUrl { get; } = ProjectUrl + "/tree/master/.docs";
|
public static string DocumentationUrl { get; } = ProjectUrl + "/tree/master/.docs";
|
||||||
|
|
||||||
public static AppBuilder BuildAvaloniaApp() =>
|
public static AppBuilder BuildAvaloniaApp() =>
|
||||||
|
|||||||
@@ -1,79 +1,133 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Avalonia;
|
using System.Text.Json.Serialization;
|
||||||
using Avalonia.Platform;
|
|
||||||
using Cogwheel;
|
using Cogwheel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using DiscordChatExporter.Core.Exporting;
|
using DiscordChatExporter.Core.Exporting;
|
||||||
|
using DiscordChatExporter.Gui.Framework;
|
||||||
using DiscordChatExporter.Gui.Models;
|
using DiscordChatExporter.Gui.Models;
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace DiscordChatExporter.Gui.Services;
|
namespace DiscordChatExporter.Gui.Services;
|
||||||
|
|
||||||
|
// Can't use [ObservableProperty] here because System.Text.Json's source generator doesn't see
|
||||||
|
// the generated properties.
|
||||||
[INotifyPropertyChanged]
|
[INotifyPropertyChanged]
|
||||||
public partial class SettingsService()
|
public partial class SettingsService()
|
||||||
: SettingsBase(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Settings.dat"))
|
: SettingsBase(
|
||||||
|
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Settings.dat"),
|
||||||
|
SerializerContext.Default
|
||||||
|
)
|
||||||
{
|
{
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isUkraineSupportMessageEnabled = true;
|
private bool _isUkraineSupportMessageEnabled = true;
|
||||||
|
public bool IsUkraineSupportMessageEnabled
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isAutoUpdateEnabled = true;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isDarkModeEnabled;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isTokenPersisted = true;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private ThreadInclusionMode _threadInclusionMode;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _locale;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _isUtcNormalizationEnabled;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private int _parallelLimit = 1;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private Version? _lastAppVersion;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _lastToken;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private ExportFormat _lastExportFormat = ExportFormat.HtmlDark;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _lastPartitionLimitValue;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _lastMessageFilterValue;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _lastShouldFormatMarkdown = true;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _lastShouldDownloadAssets;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool _lastShouldReuseAssets;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string? _lastAssetsDirPath;
|
|
||||||
|
|
||||||
public override void Reset()
|
|
||||||
{
|
{
|
||||||
base.Reset();
|
get => _isUkraineSupportMessageEnabled;
|
||||||
|
set => SetProperty(ref _isUkraineSupportMessageEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the dark mode setting separately because its default value is evaluated dynamically
|
private ThemeVariant _theme;
|
||||||
// and cannot be set by the field initializer.
|
public ThemeVariant Theme
|
||||||
IsDarkModeEnabled =
|
{
|
||||||
Application.Current?.PlatformSettings?.GetColorValues().ThemeVariant
|
get => _theme;
|
||||||
== PlatformThemeVariant.Dark;
|
set => SetProperty(ref _theme, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isAutoUpdateEnabled = true;
|
||||||
|
public bool IsAutoUpdateEnabled
|
||||||
|
{
|
||||||
|
get => _isAutoUpdateEnabled;
|
||||||
|
set => SetProperty(ref _isAutoUpdateEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isTokenPersisted = true;
|
||||||
|
public bool IsTokenPersisted
|
||||||
|
{
|
||||||
|
get => _isTokenPersisted;
|
||||||
|
set => SetProperty(ref _isTokenPersisted, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThreadInclusionMode _threadInclusionMode;
|
||||||
|
public ThreadInclusionMode ThreadInclusionMode
|
||||||
|
{
|
||||||
|
get => _threadInclusionMode;
|
||||||
|
set => SetProperty(ref _threadInclusionMode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _locale;
|
||||||
|
public string? Locale
|
||||||
|
{
|
||||||
|
get => _locale;
|
||||||
|
set => SetProperty(ref _locale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isUtcNormalizationEnabled;
|
||||||
|
public bool IsUtcNormalizationEnabled
|
||||||
|
{
|
||||||
|
get => _isUtcNormalizationEnabled;
|
||||||
|
set => SetProperty(ref _isUtcNormalizationEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _parallelLimit = 1;
|
||||||
|
public int ParallelLimit
|
||||||
|
{
|
||||||
|
get => _parallelLimit;
|
||||||
|
set => SetProperty(ref _parallelLimit, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _lastToken;
|
||||||
|
public string? LastToken
|
||||||
|
{
|
||||||
|
get => _lastToken;
|
||||||
|
set => SetProperty(ref _lastToken, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExportFormat _lastExportFormat = ExportFormat.HtmlDark;
|
||||||
|
public ExportFormat LastExportFormat
|
||||||
|
{
|
||||||
|
get => _lastExportFormat;
|
||||||
|
set => SetProperty(ref _lastExportFormat, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _lastPartitionLimitValue;
|
||||||
|
public string? LastPartitionLimitValue
|
||||||
|
{
|
||||||
|
get => _lastPartitionLimitValue;
|
||||||
|
set => SetProperty(ref _lastPartitionLimitValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _lastMessageFilterValue;
|
||||||
|
public string? LastMessageFilterValue
|
||||||
|
{
|
||||||
|
get => _lastMessageFilterValue;
|
||||||
|
set => SetProperty(ref _lastMessageFilterValue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _lastShouldFormatMarkdown = true;
|
||||||
|
public bool LastShouldFormatMarkdown
|
||||||
|
{
|
||||||
|
get => _lastShouldFormatMarkdown;
|
||||||
|
set => SetProperty(ref _lastShouldFormatMarkdown, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _lastShouldDownloadAssets;
|
||||||
|
public bool LastShouldDownloadAssets
|
||||||
|
{
|
||||||
|
get => _lastShouldDownloadAssets;
|
||||||
|
set => SetProperty(ref _lastShouldDownloadAssets, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _lastShouldReuseAssets;
|
||||||
|
public bool LastShouldReuseAssets
|
||||||
|
{
|
||||||
|
get => _lastShouldReuseAssets;
|
||||||
|
set => SetProperty(ref _lastShouldReuseAssets, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? _lastAssetsDirPath;
|
||||||
|
public string? LastAssetsDirPath
|
||||||
|
{
|
||||||
|
get => _lastAssetsDirPath;
|
||||||
|
set => SetProperty(ref _lastAssetsDirPath, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Save()
|
public override void Save()
|
||||||
@@ -88,3 +142,9 @@ public partial class SettingsService()
|
|||||||
LastToken = lastToken;
|
LastToken = lastToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public partial class SettingsService
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(SettingsService))]
|
||||||
|
private partial class SerializerContext : JsonSerializerContext;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using DiscordChatExporter.Gui.Framework;
|
using DiscordChatExporter.Gui.Framework;
|
||||||
using DiscordChatExporter.Gui.Models;
|
using DiscordChatExporter.Gui.Models;
|
||||||
@@ -23,25 +22,27 @@ public class SettingsViewModel : DialogViewModelBase
|
|||||||
_eventRoot.Add(_settingsService.WatchAllProperties(OnAllPropertiesChanged));
|
_eventRoot.Add(_settingsService.WatchAllProperties(OnAllPropertiesChanged));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ThemeVariant> AvailableThemes { get; } = Enum.GetValues<ThemeVariant>();
|
||||||
|
|
||||||
|
public ThemeVariant Theme
|
||||||
|
{
|
||||||
|
get => _settingsService.Theme;
|
||||||
|
set => _settingsService.Theme = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsAutoUpdateEnabled
|
public bool IsAutoUpdateEnabled
|
||||||
{
|
{
|
||||||
get => _settingsService.IsAutoUpdateEnabled;
|
get => _settingsService.IsAutoUpdateEnabled;
|
||||||
set => _settingsService.IsAutoUpdateEnabled = value;
|
set => _settingsService.IsAutoUpdateEnabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDarkModeEnabled
|
|
||||||
{
|
|
||||||
get => _settingsService.IsDarkModeEnabled;
|
|
||||||
set => _settingsService.IsDarkModeEnabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsTokenPersisted
|
public bool IsTokenPersisted
|
||||||
{
|
{
|
||||||
get => _settingsService.IsTokenPersisted;
|
get => _settingsService.IsTokenPersisted;
|
||||||
set => _settingsService.IsTokenPersisted = value;
|
set => _settingsService.IsTokenPersisted = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<ThreadInclusionMode> AvailableThreadInclusions { get; } =
|
public IReadOnlyList<ThreadInclusionMode> AvailableThreadInclusionModes { get; } =
|
||||||
Enum.GetValues<ThreadInclusionMode>();
|
Enum.GetValues<ThreadInclusionMode>();
|
||||||
|
|
||||||
public ThreadInclusionMode ThreadInclusionMode
|
public ThreadInclusionMode ThreadInclusionMode
|
||||||
|
|||||||
@@ -79,36 +79,8 @@ public partial class MainViewModel(
|
|||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task InitializeAsync()
|
private async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
// Reset settings (needed to resolve the default dark mode setting)
|
|
||||||
settingsService.Reset();
|
|
||||||
|
|
||||||
// Load settings
|
|
||||||
settingsService.Load();
|
|
||||||
|
|
||||||
// Set the correct theme
|
|
||||||
if (settingsService.IsDarkModeEnabled)
|
|
||||||
App.SetDarkTheme();
|
|
||||||
else
|
|
||||||
App.SetLightTheme();
|
|
||||||
|
|
||||||
await ShowUkraineSupportMessageAsync();
|
await ShowUkraineSupportMessageAsync();
|
||||||
await CheckForUpdatesAsync();
|
await CheckForUpdatesAsync();
|
||||||
|
|
||||||
// App has just been updated, display the changelog
|
|
||||||
if (
|
|
||||||
settingsService.LastAppVersion is not null
|
|
||||||
&& settingsService.LastAppVersion != Program.Version
|
|
||||||
)
|
|
||||||
{
|
|
||||||
snackbarManager.Notify(
|
|
||||||
$"Successfully updated to {Program.Name} v{Program.VersionString}",
|
|
||||||
"WHAT'S NEW",
|
|
||||||
() => ProcessEx.StartShellExecute(Program.LatestReleaseUrl)
|
|
||||||
);
|
|
||||||
|
|
||||||
settingsService.LastAppVersion = Program.Version;
|
|
||||||
settingsService.Save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
|
|||||||
@@ -9,11 +9,8 @@
|
|||||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:materialStyles="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
|
xmlns:materialStyles="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
|
||||||
x:Name="UserControl"
|
x:Name="UserControl"
|
||||||
|
x:DataType="components:DashboardViewModel"
|
||||||
Loaded="UserControl_OnLoaded">
|
Loaded="UserControl_OnLoaded">
|
||||||
<Design.DataContext>
|
|
||||||
<components:DashboardViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
|
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<StackPanel
|
<StackPanel
|
||||||
@@ -95,6 +92,130 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</Style>
|
</Style>
|
||||||
</Panel.Styles>
|
</Panel.Styles>
|
||||||
|
<!-- Guilds and channels -->
|
||||||
|
<Grid ColumnDefinitions="Auto,*" IsVisible="{Binding !!AvailableGuilds.Count}">
|
||||||
|
<!-- Guilds -->
|
||||||
|
<Border
|
||||||
|
Grid.Column="0"
|
||||||
|
BorderBrush="{DynamicResource MaterialDividerBrush}"
|
||||||
|
BorderThickness="0,0,1,0">
|
||||||
|
<ListBox
|
||||||
|
x:Name="AvailableGuildsListBox"
|
||||||
|
ItemsSource="{Binding AvailableGuilds}"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
||||||
|
SelectedItem="{Binding SelectedGuild}"
|
||||||
|
SelectionChanged="AvailableGuildsListBox_OnSelectionChanged"
|
||||||
|
SelectionMode="Single">
|
||||||
|
<ListBox.Styles>
|
||||||
|
<Style Selector="ListBox">
|
||||||
|
<Style Selector="^ ListBoxItem">
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="Cursor" Value="Hand" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</ListBox.Styles>
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Panel Background="Transparent" ToolTip.Tip="{Binding Name}">
|
||||||
|
<!-- Guild icon placeholder -->
|
||||||
|
<Ellipse
|
||||||
|
Width="48"
|
||||||
|
Height="48"
|
||||||
|
Margin="12"
|
||||||
|
Fill="{DynamicResource MaterialDividerBrush}" />
|
||||||
|
|
||||||
|
<!-- Guild icon -->
|
||||||
|
<Ellipse
|
||||||
|
Width="48"
|
||||||
|
Height="48"
|
||||||
|
Margin="12">
|
||||||
|
<Ellipse.Fill>
|
||||||
|
<ImageBrush asyncImageLoader:ImageBrushLoader.Source="{Binding IconUrl}" />
|
||||||
|
</Ellipse.Fill>
|
||||||
|
</Ellipse>
|
||||||
|
</Panel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Channels -->
|
||||||
|
<Border Grid.Column="1">
|
||||||
|
<TreeView
|
||||||
|
x:Name="AvailableChannelsTreeView"
|
||||||
|
ItemsSource="{Binding AvailableChannels}"
|
||||||
|
SelectedItems="{Binding SelectedChannels}"
|
||||||
|
SelectionChanged="AvailableChannelsTreeView_OnSelectionChanged"
|
||||||
|
SelectionMode="Multiple"
|
||||||
|
TextSearch.Text="Name">
|
||||||
|
<TreeView.Styles>
|
||||||
|
<Style Selector="TreeView">
|
||||||
|
<Style Selector="^ TreeViewItem">
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="Cursor" Value="Hand" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</TreeView.Styles>
|
||||||
|
<TreeView.ItemTemplate>
|
||||||
|
<TreeDataTemplate ItemsSource="{Binding Children}">
|
||||||
|
<Grid
|
||||||
|
Background="Transparent"
|
||||||
|
Classes.category="{Binding Channel.IsCategory}"
|
||||||
|
ColumnDefinitions="Auto,*,Auto">
|
||||||
|
<Grid.Styles>
|
||||||
|
<Style Selector="Grid">
|
||||||
|
<Style Selector="^:not(.category)">
|
||||||
|
<Setter Property="ToolTip.Tip">
|
||||||
|
<Template>
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="Last message sent:" />
|
||||||
|
<Run FontWeight="SemiBold" Text="{Binding Channel.LastMessageId, Converter={x:Static converters:SnowflakeToTimestampStringConverter.Instance}, TargetNullValue=never, Mode=OneWay}" />
|
||||||
|
</TextBlock>
|
||||||
|
</Template>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</Grid.Styles>
|
||||||
|
|
||||||
|
<!-- Channel icon -->
|
||||||
|
<materialIcons:MaterialIcon
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,4,0"
|
||||||
|
Classes.voice="{Binding Channel.IsVoice}"
|
||||||
|
IsVisible="{Binding !Channel.IsCategory}">
|
||||||
|
<materialIcons:MaterialIcon.Styles>
|
||||||
|
<Style Selector="materialIcons|MaterialIcon">
|
||||||
|
<Setter Property="Kind" Value="Pound" />
|
||||||
|
|
||||||
|
<Style Selector="^.voice">
|
||||||
|
<Setter Property="Kind" Value="VolumeHigh" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</materialIcons:MaterialIcon.Styles>
|
||||||
|
</materialIcons:MaterialIcon>
|
||||||
|
|
||||||
|
<!-- Channel name -->
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,12"
|
||||||
|
FontSize="14"
|
||||||
|
Text="{Binding Channel.Name, Mode=OneWay}" />
|
||||||
|
|
||||||
|
<!-- Checkmark -->
|
||||||
|
<materialIcons:MaterialIcon
|
||||||
|
Grid.Column="2"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
Margin="16,0"
|
||||||
|
IsVisible="{Binding $parent[TreeViewItem].IsSelected}"
|
||||||
|
Kind="Check" />
|
||||||
|
</Grid>
|
||||||
|
</TreeDataTemplate>
|
||||||
|
</TreeView.ItemTemplate>
|
||||||
|
</TreeView>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<!-- Placeholder / usage instructions -->
|
<!-- Placeholder / usage instructions -->
|
||||||
<Panel IsVisible="{Binding !AvailableGuilds.Count}">
|
<Panel IsVisible="{Binding !AvailableGuilds.Count}">
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
@@ -215,131 +336,6 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<!-- Guilds and channels -->
|
|
||||||
<Grid ColumnDefinitions="Auto,*" IsVisible="{Binding !!AvailableGuilds.Count}">
|
|
||||||
<!-- Guilds -->
|
|
||||||
<Border
|
|
||||||
Grid.Column="0"
|
|
||||||
BorderBrush="{DynamicResource MaterialDividerBrush}"
|
|
||||||
BorderThickness="0,0,1,0">
|
|
||||||
<ListBox
|
|
||||||
x:Name="AvailableGuildsListBox"
|
|
||||||
ItemsSource="{Binding AvailableGuilds}"
|
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
|
||||||
SelectedItem="{Binding SelectedGuild}"
|
|
||||||
SelectionChanged="AvailableGuildsListBox_OnSelectionChanged"
|
|
||||||
SelectionMode="Single">
|
|
||||||
<ListBox.Styles>
|
|
||||||
<Style Selector="ListBox">
|
|
||||||
<Style Selector="^ ListBoxItem">
|
|
||||||
<Setter Property="Padding" Value="0" />
|
|
||||||
<Setter Property="Cursor" Value="Hand" />
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
</ListBox.Styles>
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Panel Background="Transparent" ToolTip.Tip="{Binding Name}">
|
|
||||||
<!-- Guild icon placeholder -->
|
|
||||||
<Ellipse
|
|
||||||
Width="48"
|
|
||||||
Height="48"
|
|
||||||
Margin="12"
|
|
||||||
Fill="{DynamicResource MaterialDividerBrush}" />
|
|
||||||
|
|
||||||
<!-- Guild icon -->
|
|
||||||
<Ellipse
|
|
||||||
Width="48"
|
|
||||||
Height="48"
|
|
||||||
Margin="12">
|
|
||||||
<Ellipse.Fill>
|
|
||||||
<ImageBrush asyncImageLoader:ImageBrushLoader.Source="{Binding IconUrl}" />
|
|
||||||
</Ellipse.Fill>
|
|
||||||
</Ellipse>
|
|
||||||
</Panel>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Channels -->
|
|
||||||
<Border Grid.Column="1">
|
|
||||||
<TreeView
|
|
||||||
x:Name="AvailableChannelsTreeView"
|
|
||||||
AutoScrollToSelectedItem="False"
|
|
||||||
ItemsSource="{Binding AvailableChannels}"
|
|
||||||
SelectedItems="{Binding SelectedChannels}"
|
|
||||||
SelectionChanged="AvailableChannelsTreeView_OnSelectionChanged"
|
|
||||||
SelectionMode="Multiple"
|
|
||||||
TextSearch.Text="Name">
|
|
||||||
<TreeView.Styles>
|
|
||||||
<Style Selector="TreeView">
|
|
||||||
<Style Selector="^ TreeViewItem">
|
|
||||||
<Setter Property="Padding" Value="0" />
|
|
||||||
<Setter Property="Cursor" Value="Hand" />
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
</TreeView.Styles>
|
|
||||||
<TreeView.ItemTemplate>
|
|
||||||
<TreeDataTemplate ItemsSource="{Binding Children}">
|
|
||||||
<Grid
|
|
||||||
Background="Transparent"
|
|
||||||
Classes.category="{Binding Channel.IsCategory}"
|
|
||||||
ColumnDefinitions="Auto,*,Auto">
|
|
||||||
<Grid.Styles>
|
|
||||||
<Style Selector="Grid">
|
|
||||||
<Style Selector="^:not(.category)">
|
|
||||||
<Setter Property="ToolTip.Tip">
|
|
||||||
<Template>
|
|
||||||
<TextBlock>
|
|
||||||
<Run Text="Last message sent:" />
|
|
||||||
<Run FontWeight="SemiBold" Text="{Binding Channel.LastMessageId, Converter={x:Static converters:SnowflakeToTimestampStringConverter.Instance}, TargetNullValue=never, Mode=OneWay}" />
|
|
||||||
</TextBlock>
|
|
||||||
</Template>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
</Grid.Styles>
|
|
||||||
|
|
||||||
<!-- Channel icon -->
|
|
||||||
<materialIcons:MaterialIcon
|
|
||||||
Grid.Column="0"
|
|
||||||
Margin="0,0,4,0"
|
|
||||||
Classes.voice="{Binding Channel.IsVoice}"
|
|
||||||
IsVisible="{Binding !Channel.IsCategory}">
|
|
||||||
<materialIcons:MaterialIcon.Styles>
|
|
||||||
<Style Selector="materialIcons|MaterialIcon">
|
|
||||||
<Setter Property="Kind" Value="Pound" />
|
|
||||||
|
|
||||||
<Style Selector="^.voice">
|
|
||||||
<Setter Property="Kind" Value="VolumeHigh" />
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
</materialIcons:MaterialIcon.Styles>
|
|
||||||
</materialIcons:MaterialIcon>
|
|
||||||
|
|
||||||
<!-- Channel name -->
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
Margin="0,12"
|
|
||||||
FontSize="14"
|
|
||||||
Text="{Binding Channel.Name, Mode=OneWay}" />
|
|
||||||
|
|
||||||
<!-- Checkmark -->
|
|
||||||
<materialIcons:MaterialIcon
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="24"
|
|
||||||
Height="24"
|
|
||||||
Margin="16,0"
|
|
||||||
IsVisible="{Binding $parent[TreeViewItem].IsSelected}"
|
|
||||||
Kind="Check" />
|
|
||||||
</Grid>
|
|
||||||
</TreeDataTemplate>
|
|
||||||
</TreeView.ItemTemplate>
|
|
||||||
</TreeView>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Export button -->
|
<!-- Export button -->
|
||||||
<Button
|
<Button
|
||||||
Width="56"
|
Width="56"
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="DiscordChatExporter.Gui.Views.Controls.HyperLink"
|
x:Class="DiscordChatExporter.Gui.Views.Controls.HyperLink"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:controls="clr-namespace:DiscordChatExporter.Gui.Views.Controls">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="TextBlock"
|
x:Name="TextBlock"
|
||||||
Cursor="Hand"
|
Cursor="Hand"
|
||||||
Foreground="{DynamicResource MaterialSecondaryDarkBrush}"
|
Foreground="{DynamicResource MaterialSecondaryDarkBrush}"
|
||||||
PointerReleased="TextBlock_OnPointerReleased"
|
PointerReleased="TextBlock_OnPointerReleased"
|
||||||
Text="{Binding $parent[UserControl].Text, Mode=OneWay}">
|
Text="{Binding $parent[controls:HyperLink].Text, Mode=OneWay}">
|
||||||
<TextBlock.Styles>
|
<TextBlock.Styles>
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
<Style Selector="^:pointerover">
|
<Style Selector="^:pointerover">
|
||||||
|
|||||||
@@ -10,11 +10,8 @@
|
|||||||
xmlns:utils="clr-namespace:DiscordChatExporter.Gui.Utils"
|
xmlns:utils="clr-namespace:DiscordChatExporter.Gui.Utils"
|
||||||
x:Name="UserControl"
|
x:Name="UserControl"
|
||||||
Width="380"
|
Width="380"
|
||||||
|
x:DataType="dialogs:ExportSetupViewModel"
|
||||||
Loaded="UserControl_OnLoaded">
|
Loaded="UserControl_OnLoaded">
|
||||||
<Design.DataContext>
|
|
||||||
<dialogs:ExportSetupViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*,Auto">
|
<Grid RowDefinitions="Auto,*,Auto">
|
||||||
<!-- Guild/channel info -->
|
<!-- Guild/channel info -->
|
||||||
<Grid
|
<Grid
|
||||||
@@ -40,7 +37,7 @@
|
|||||||
FontWeight="Light"
|
FontWeight="Light"
|
||||||
IsVisible="{Binding !IsSingleChannel}"
|
IsVisible="{Binding !IsSingleChannel}"
|
||||||
TextTrimming="CharacterEllipsis">
|
TextTrimming="CharacterEllipsis">
|
||||||
<Run Text="{Binding Channels.Count, Mode=OneWay}" />
|
<Run Text="{Binding Channels.Count, FallbackValue=0, Mode=OneWay}" />
|
||||||
<Run Text="channels selected" />
|
<Run Text="channels selected" />
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:dialogs="clr-namespace:DiscordChatExporter.Gui.ViewModels.Dialogs"
|
xmlns:dialogs="clr-namespace:DiscordChatExporter.Gui.ViewModels.Dialogs"
|
||||||
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
||||||
Width="500">
|
Width="500"
|
||||||
<Design.DataContext>
|
x:DataType="dialogs:MessageBoxViewModel">
|
||||||
<dialogs:MessageBoxViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*,Auto">
|
<Grid RowDefinitions="Auto,*,Auto">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:converters="clr-namespace:DiscordChatExporter.Gui.Converters"
|
xmlns:converters="clr-namespace:DiscordChatExporter.Gui.Converters"
|
||||||
xmlns:dialogs="clr-namespace:DiscordChatExporter.Gui.ViewModels.Dialogs"
|
xmlns:dialogs="clr-namespace:DiscordChatExporter.Gui.ViewModels.Dialogs"
|
||||||
Width="380">
|
Width="380"
|
||||||
<Design.DataContext>
|
x:DataType="dialogs:SettingsViewModel">
|
||||||
<dialogs:SettingsViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*,Auto">
|
<Grid RowDefinitions="Auto,*,Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
@@ -24,6 +21,19 @@
|
|||||||
BorderThickness="0,1">
|
BorderThickness="0,1">
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
|
<!-- Theme -->
|
||||||
|
<DockPanel
|
||||||
|
Margin="16,8"
|
||||||
|
LastChildFill="False"
|
||||||
|
ToolTip.Tip="Preferred user interface theme">
|
||||||
|
<TextBlock DockPanel.Dock="Left" Text="Theme" />
|
||||||
|
<ComboBox
|
||||||
|
Width="150"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
ItemsSource="{Binding AvailableThemes}"
|
||||||
|
SelectedItem="{Binding Theme}" />
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
<!-- Auto-updates -->
|
<!-- Auto-updates -->
|
||||||
<DockPanel
|
<DockPanel
|
||||||
Margin="16,8"
|
Margin="16,8"
|
||||||
@@ -35,19 +45,6 @@
|
|||||||
<ToggleSwitch DockPanel.Dock="Right" IsChecked="{Binding IsAutoUpdateEnabled}" />
|
<ToggleSwitch DockPanel.Dock="Right" IsChecked="{Binding IsAutoUpdateEnabled}" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
<!-- Dark mode -->
|
|
||||||
<DockPanel
|
|
||||||
Margin="16,8"
|
|
||||||
LastChildFill="False"
|
|
||||||
ToolTip.Tip="Use darker colors in the UI">
|
|
||||||
<TextBlock DockPanel.Dock="Left" Text="Dark mode" />
|
|
||||||
<ToggleSwitch
|
|
||||||
x:Name="DarkModeToggleSwitch"
|
|
||||||
DockPanel.Dock="Right"
|
|
||||||
IsChecked="{Binding IsDarkModeEnabled}"
|
|
||||||
IsCheckedChanged="DarkModeToggleSwitch_OnIsCheckedChanged" />
|
|
||||||
</DockPanel>
|
|
||||||
|
|
||||||
<!-- Persist token -->
|
<!-- Persist token -->
|
||||||
<DockPanel
|
<DockPanel
|
||||||
Margin="16,8"
|
Margin="16,8"
|
||||||
@@ -66,7 +63,7 @@
|
|||||||
<ComboBox
|
<ComboBox
|
||||||
Width="150"
|
Width="150"
|
||||||
DockPanel.Dock="Right"
|
DockPanel.Dock="Right"
|
||||||
ItemsSource="{Binding AvailableThreadInclusions}"
|
ItemsSource="{Binding AvailableThreadInclusionModes}"
|
||||||
SelectedItem="{Binding ThreadInclusionMode}" />
|
SelectedItem="{Binding ThreadInclusionMode}" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Windows;
|
using DiscordChatExporter.Gui.Framework;
|
||||||
using Avalonia.Interactivity;
|
|
||||||
using DiscordChatExporter.Gui.Framework;
|
|
||||||
using DiscordChatExporter.Gui.ViewModels.Dialogs;
|
using DiscordChatExporter.Gui.ViewModels.Dialogs;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Gui.Views.Dialogs;
|
namespace DiscordChatExporter.Gui.Views.Dialogs;
|
||||||
@@ -8,20 +6,4 @@ namespace DiscordChatExporter.Gui.Views.Dialogs;
|
|||||||
public partial class SettingsView : UserControl<SettingsViewModel>
|
public partial class SettingsView : UserControl<SettingsViewModel>
|
||||||
{
|
{
|
||||||
public SettingsView() => InitializeComponent();
|
public SettingsView() => InitializeComponent();
|
||||||
|
|
||||||
private void DarkModeToggleSwitch_OnIsCheckedChanged(object? sender, RoutedEventArgs args)
|
|
||||||
{
|
|
||||||
if (DarkModeToggleSwitch.IsChecked is true)
|
|
||||||
{
|
|
||||||
App.SetDarkTheme();
|
|
||||||
}
|
|
||||||
else if (DarkModeToggleSwitch.IsChecked is false)
|
|
||||||
{
|
|
||||||
App.SetLightTheme();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.SetDefaultTheme();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,10 @@
|
|||||||
Height="625"
|
Height="625"
|
||||||
MinWidth="600"
|
MinWidth="600"
|
||||||
MinHeight="400"
|
MinHeight="400"
|
||||||
|
x:DataType="viewModels:MainViewModel"
|
||||||
Icon="/favicon.ico"
|
Icon="/favicon.ico"
|
||||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
RenderOptions.BitmapInterpolationMode="HighQuality"
|
||||||
WindowStartupLocation="CenterScreen">
|
WindowStartupLocation="CenterScreen">
|
||||||
<Design.DataContext>
|
|
||||||
<viewModels:MainViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
|
|
||||||
<dialogHostAvalonia:DialogHost
|
<dialogHostAvalonia:DialogHost
|
||||||
x:Name="DialogHost"
|
x:Name="DialogHost"
|
||||||
CloseOnClickAway="False"
|
CloseOnClickAway="False"
|
||||||
|
|||||||
@@ -23,8 +23,7 @@
|
|||||||
**DiscordChatExporter** is an application that can be used to export message history from any [Discord](https://discord.com) channel to a file.
|
**DiscordChatExporter** is an application that can be used to export message history from any [Discord](https://discord.com) channel to a file.
|
||||||
It works with direct messages, group messages, and server channels, and supports Discord's dialect of markdown as well as most other rich media features.
|
It works with direct messages, group messages, and server channels, and supports Discord's dialect of markdown as well as most other rich media features.
|
||||||
|
|
||||||
<!-- Can't use a relative link here due to a bug in markdown parsing -->
|
> ❔ If you have questions or issues, **please refer to the [docs](.docs)**.
|
||||||
> ❔ If you have questions or issues, **please refer to the [docs](https://github.com/Tyrrrz/DiscordChatExporter/tree/master/.docs)**.
|
|
||||||
|
|
||||||
> 💬 If you want to chat, **join my [Discord server](https://discord.gg/2SUWKFnHSm)**.
|
> 💬 If you want to chat, **join my [Discord server](https://discord.gg/2SUWKFnHSm)**.
|
||||||
|
|
||||||
@@ -51,17 +50,8 @@ To learn more about the war and how you can help, [click here](https://tyrrrz.me
|
|||||||
- 📦 [AUR](https://aur.archlinux.org/packages/discord-chat-exporter-cli): `discord-chat-exporter-cli`
|
- 📦 [AUR](https://aur.archlinux.org/packages/discord-chat-exporter-cli): `discord-chat-exporter-cli`
|
||||||
- 📦 [Nix](https://search.nixos.org/packages?query=discordchatexporter-cli): `discordchatexporter-cli`
|
- 📦 [Nix](https://search.nixos.org/packages?query=discordchatexporter-cli): `discordchatexporter-cli`
|
||||||
|
|
||||||
> **Important**:
|
> **Note**:
|
||||||
> To run **DiscordChatExporter**, you need to make sure that the **.NET 8.0 Runtime** is installed.
|
> If you're unsure which build is right for your system, consult with [this page](https://useragent.cc) to determine your OS and CPU architecture.
|
||||||
> You can download it here:
|
|
||||||
>
|
|
||||||
> - [.NET 8.0 Runtime for **macOS x64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-x64-installer)
|
|
||||||
> - [.NET 8.0 Runtime for **macOS arm64**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.0-macos-arm64-installer)
|
|
||||||
> - [.NET 8.0 Runtime for **Linux**](https://learn.microsoft.com/dotnet/core/install/linux) (find the correct download for your distro)
|
|
||||||
> - On Windows, the runtime should be installed automatically when you run the application for the first time
|
|
||||||
>
|
|
||||||
> This should not be necessary if you install **DiscordChatExporter** using a package manager, as it should take care of the dependencies for you.
|
|
||||||
> This is also not necessary if you are running **DiscordChatExporter** via Docker, because the image already contains the runtime.
|
|
||||||
|
|
||||||
> **Note**:
|
> **Note**:
|
||||||
> AUR and Nix packages linked above are maintained by the community.
|
> AUR and Nix packages linked above are maintained by the community.
|
||||||
@@ -69,8 +59,7 @@ To learn more about the war and how you can help, [click here](https://tyrrrz.me
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Graphical and command-line interfaces
|
- Cross-platform graphical and command-line interfaces
|
||||||
- Fully cross-platform
|
|
||||||
- Authentication via either a user or a bot token
|
- Authentication via either a user or a bot token
|
||||||
- Multiple output formats: HTML (dark/light), TXT, CSV, JSON
|
- Multiple output formats: HTML (dark/light), TXT, CSV, JSON
|
||||||
- Support for markdown, attachments, embeds, emoji, and other rich media features
|
- Support for markdown, attachments, embeds, emoji, and other rich media features
|
||||||
|
|||||||
Reference in New Issue
Block a user