Reorganize namespaces and move some classes to a separate Utils project

This commit is contained in:
chylex 2022-02-20 20:37:48 +01:00
parent 77aa15e557
commit 849ef18adb
No known key found for this signature in database
GPG Key ID: 4DE42C8F19A80548
36 changed files with 177 additions and 101 deletions

View File

@ -4,9 +4,9 @@
<option name="projectPerEditor">
<map>
<entry key="Desktop/App.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Dialogs/CheckBoxDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Dialogs/MessageDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Dialogs/ProgressDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Dialogs/CheckBox/CheckBoxDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Dialogs/Message/MessageDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Dialogs/Progress/ProgressDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/AboutWindow.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Controls/FilterPanel.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Controls/StatusBar.axaml" value="Desktop/Desktop.csproj" />

View File

@ -1,8 +1,10 @@
using System;
using DHT.Server.Logging;
using DHT.Utils.Logging;
namespace DHT.Desktop {
public class Arguments {
private static readonly Log Log = Log.ForType<Arguments>();
public static Arguments Empty => new(Array.Empty<string>());
public string? DatabaseFile { get; }

View File

@ -3,14 +3,16 @@ using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Dialogs.Message;
using DHT.Server.Database;
using DHT.Server.Database.Exceptions;
using DHT.Server.Database.Sqlite;
using DHT.Server.Logging;
using DHT.Utils.Logging;
namespace DHT.Desktop.Common {
public static class DatabaseGui {
private static readonly Log Log = Log.ForType(typeof(DatabaseGui));
private const string DatabaseFileInitialName = "archive.dht";
private static readonly List<FileDialogFilter> DatabaseFileDialogFilter = new() {

View File

@ -34,7 +34,15 @@
<DependentUpon>MainWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Dialogs\MessageDialog.axaml.cs">
<Compile Update="Dialogs\CheckBox\CheckBoxDialog.axaml.cs">
<DependentUpon>CheckBoxDialog.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Dialogs\Progress\ProgressDialog.axaml.cs">
<DependentUpon>ProgressDialog.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Dialogs\Message\MessageDialog.axaml.cs">
<DependentUpon>MessageDialog.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>

View File

@ -2,16 +2,16 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dialogs="clr-namespace:DHT.Desktop.Dialogs"
xmlns:namespace="clr-namespace:DHT.Desktop.Dialogs.CheckBox"
mc:Ignorable="d" d:DesignWidth="500"
x:Class="DHT.Desktop.Dialogs.CheckBoxDialog"
x:Class="DHT.Desktop.Dialogs.CheckBox.CheckBoxDialog"
Title="{Binding Title}"
Icon="avares://DiscordHistoryTracker/Resources/icon.ico"
Width="500" SizeToContent="Height" CanResize="False"
WindowStartupLocation="CenterOwner">
<Window.DataContext>
<dialogs:CheckBoxDialogModel />
<namespace:CheckBoxDialogModel />
</Window.DataContext>
<Window.Styles>

View File

@ -2,8 +2,9 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using DHT.Desktop.Dialogs.Message;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.CheckBox {
public class CheckBoxDialog : Window {
public CheckBoxDialog() {
InitializeComponent();

View File

@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using DHT.Desktop.Models;
using DHT.Utils.Models;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.CheckBox {
public class CheckBoxDialogModel : BaseModel {
public string Title { get; init; } = "";

View File

@ -1,6 +1,6 @@
using DHT.Desktop.Models;
using DHT.Utils.Models;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.CheckBox {
public class CheckBoxItem : BaseModel {
public string Title { get; init; } = "";
public object? Item { get; init; } = null;

View File

@ -2,7 +2,7 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Threading;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Message {
public static class Dialog {
public static async Task ShowOk(Window owner, string title, string message) {
if (!Dispatcher.UIThread.CheckAccess()) {

View File

@ -1,6 +1,6 @@
using System;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Message {
public static class DialogResult {
public enum All {
Ok,

View File

@ -2,16 +2,16 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dialogs="clr-namespace:DHT.Desktop.Dialogs"
xmlns:namespace="clr-namespace:DHT.Desktop.Dialogs.Message"
mc:Ignorable="d" d:DesignWidth="500"
x:Class="DHT.Desktop.Dialogs.MessageDialog"
x:Class="DHT.Desktop.Dialogs.Message.MessageDialog"
Title="{Binding Title}"
Icon="avares://DiscordHistoryTracker/Resources/icon.ico"
Width="500" SizeToContent="Height" CanResize="False"
WindowStartupLocation="CenterOwner">
<Window.DataContext>
<dialogs:MessageDialogModel />
<namespace:MessageDialogModel />
</Window.DataContext>
<Window.Styles>

View File

@ -3,7 +3,7 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Message {
public class MessageDialog : Window {
public MessageDialog() {
InitializeComponent();

View File

@ -1,4 +1,4 @@
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Message {
public class MessageDialogModel {
public string Title { get; init; } = "";
public string Message { get; init; } = "";

View File

@ -1,6 +1,6 @@
using System.Threading.Tasks;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Progress {
public interface IProgressCallback {
Task Update(string message, int finishedItems, int totalItems);
}

View File

@ -2,9 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dialogs="clr-namespace:DHT.Desktop.Dialogs"
xmlns:namespace="clr-namespace:DHT.Desktop.Dialogs.Progress"
mc:Ignorable="d" d:DesignWidth="500"
x:Class="DHT.Desktop.Dialogs.ProgressDialog"
x:Class="DHT.Desktop.Dialogs.Progress.ProgressDialog"
Title="{Binding Title}"
Icon="avares://DiscordHistoryTracker/Resources/icon.ico"
Opened="Loaded"
@ -13,7 +13,7 @@
WindowStartupLocation="CenterOwner">
<Window.DataContext>
<dialogs:ProgressDialogModel />
<namespace:ProgressDialogModel />
</Window.DataContext>
<Window.Styles>

View File

@ -5,7 +5,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Progress {
public class ProgressDialog : Window {
private bool isFinished = false;

View File

@ -2,9 +2,9 @@ using System;
using System.Threading.Tasks;
using Avalonia.Threading;
using DHT.Desktop.Common;
using DHT.Desktop.Models;
using DHT.Utils.Models;
namespace DHT.Desktop.Dialogs {
namespace DHT.Desktop.Dialogs.Progress {
public class ProgressDialogModel : BaseModel {
public string Title { get; init; } = "";

View File

@ -6,11 +6,12 @@ using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Common;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Models;
using DHT.Desktop.Dialogs.CheckBox;
using DHT.Desktop.Dialogs.Message;
using DHT.Server.Data;
using DHT.Server.Data.Filters;
using DHT.Server.Database;
using DHT.Utils.Models;
namespace DHT.Desktop.Main.Controls {
public class FilterPanelModel : BaseModel {

View File

@ -1,6 +1,6 @@
using System;
using DHT.Desktop.Models;
using DHT.Server.Database;
using DHT.Utils.Models;
namespace DHT.Desktop.Main.Controls {
public class StatusBarModel : BaseModel {

View File

@ -4,10 +4,10 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Pages;
using DHT.Desktop.Models;
using DHT.Server.Database;
using DHT.Utils.Models;
namespace DHT.Desktop.Main {
public class MainWindowModel : BaseModel {

View File

@ -5,14 +5,17 @@ using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Common;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Models;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Dialogs.Progress;
using DHT.Server.Database;
using DHT.Server.Logging;
using DHT.Server.Service;
using DHT.Utils.Logging;
using DHT.Utils.Models;
namespace DHT.Desktop.Main.Pages {
public class DatabasePageModel : BaseModel {
private static readonly Log Log = Log.ForType<DatabasePageModel>();
public IDatabaseFile Db { get; }
public event EventHandler? DatabaseClosed;

View File

@ -3,16 +3,18 @@ using System.Threading.Tasks;
using System.Web;
using Avalonia;
using Avalonia.Controls;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Controls;
using DHT.Desktop.Models;
using DHT.Desktop.Resources;
using DHT.Server.Database;
using DHT.Server.Logging;
using DHT.Server.Service;
using DHT.Utils.Logging;
using DHT.Utils.Models;
using static DHT.Desktop.Program;
namespace DHT.Desktop.Main.Pages {
public class TrackingPageModel : BaseModel, IDisposable {
private static readonly Log Log = Log.ForType<TrackingPageModel>();
internal static string ServerPort { get; set; } = ServerUtils.FindAvailablePort(50000, 60000).ToString();
internal static string ServerToken { get; set; } = ServerUtils.GenerateRandomToken(20);
@ -106,12 +108,12 @@ namespace DHT.Desktop.Main.Pages {
}
public async Task OnClickCopyTrackingScript() {
string bootstrap = await ResourceLoader.ReadTextAsync("Tracker/bootstrap.js");
string bootstrap = await Resources.ReadTextAsync("Tracker/bootstrap.js");
string script = bootstrap.Replace("= 0; /*[PORT]*/", "= " + ServerPort + ";")
.Replace("/*[TOKEN]*/", HttpUtility.JavaScriptStringEncode(ServerToken))
.Replace("/*[IMPORTS]*/", await ResourceLoader.ReadJoinedAsync("Tracker/scripts/", '\n'))
.Replace("/*[CSS-CONTROLLER]*/", await ResourceLoader.ReadTextAsync("Tracker/styles/controller.css"))
.Replace("/*[CSS-SETTINGS]*/", await ResourceLoader.ReadTextAsync("Tracker/styles/settings.css"));
.Replace("/*[IMPORTS]*/", await Resources.ReadJoinedAsync("Tracker/scripts/", '\n'))
.Replace("/*[CSS-CONTROLLER]*/", await Resources.ReadTextAsync("Tracker/styles/controller.css"))
.Replace("/*[CSS-SETTINGS]*/", await Resources.ReadTextAsync("Tracker/styles/settings.css"));
await Application.Current.Clipboard.SetTextAsync(script);
}

View File

@ -7,13 +7,13 @@ using System.Threading.Tasks;
using System.Web;
using Avalonia.Controls;
using DHT.Desktop.Common;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Controls;
using DHT.Desktop.Models;
using DHT.Desktop.Resources;
using DHT.Server.Data.Filters;
using DHT.Server.Database;
using DHT.Server.Database.Export;
using DHT.Utils.Models;
using static DHT.Desktop.Program;
namespace DHT.Desktop.Main.Pages {
public class ViewerPageModel : BaseModel {
@ -65,10 +65,10 @@ namespace DHT.Desktop.Main.Pages {
private async Task<string> GenerateViewerContents() {
string json = ViewerJsonExport.Generate(db, FilterModel.CreateFilter());
string index = await ResourceLoader.ReadTextAsync("Viewer/index.html");
string viewer = index.Replace("/*[JS]*/", await ResourceLoader.ReadJoinedAsync("Viewer/scripts/", '\n'))
.Replace("/*[CSS]*/", await ResourceLoader.ReadJoinedAsync("Viewer/styles/", '\n'))
string index = await Resources.ReadTextAsync("Viewer/index.html");
string viewer = index.Replace("/*[JS]*/", await Resources.ReadJoinedAsync("Viewer/scripts/", '\n'))
.Replace("/*[CSS]*/", await Resources.ReadJoinedAsync("Viewer/styles/", '\n'))
.Replace("/*[ARCHIVE]*/", HttpUtility.JavaScriptStringEncode(json));
return viewer;
}

View File

@ -3,9 +3,9 @@ using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Common;
using DHT.Desktop.Dialogs;
using DHT.Desktop.Models;
using DHT.Desktop.Dialogs.Message;
using DHT.Server.Database;
using DHT.Utils.Models;
namespace DHT.Desktop.Main {
public class WelcomeScreenModel : BaseModel {

View File

@ -1,14 +1,18 @@
using System.Globalization;
using System.Reflection;
using Avalonia;
using DHT.Utils.Resources;
namespace DHT.Desktop {
internal static class Program {
public static string Version { get; }
public static CultureInfo Culture { get; }
public static ResourceLoader Resources { get; }
static Program() {
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "";
var assembly = Assembly.GetExecutingAssembly();
Version = assembly.GetName().Version?.ToString() ?? "";
while (Version.EndsWith(".0")) {
Version = Version[..^2];
}
@ -18,6 +22,8 @@ namespace DHT.Desktop {
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
Resources = new ResourceLoader(assembly);
}
public static void Main(string[] args) {

View File

@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop", "Desktop\Desktop.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{7F94B470-B06F-43C0-9655-6592A9AE2D92}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "Utils\Utils.csproj", "{7EEC1C05-1D21-4B39-B1D6-CF51F3795629}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -18,5 +20,9 @@ Global
{7F94B470-B06F-43C0-9655-6592A9AE2D92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F94B470-B06F-43C0-9655-6592A9AE2D92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F94B470-B06F-43C0-9655-6592A9AE2D92}.Release|Any CPU.Build.0 = Release|Any CPU
{7EEC1C05-1D21-4B39-B1D6-CF51F3795629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EEC1C05-1D21-4B39-B1D6-CF51F3795629}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EEC1C05-1D21-4B39-B1D6-CF51F3795629}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EEC1C05-1D21-4B39-B1D6-CF51F3795629}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text;
using System.Threading.Tasks;
using DHT.Server.Collections;
using DHT.Server.Data;
using DHT.Server.Data.Filters;
using DHT.Utils.Collections;
using Microsoft.Data.Sqlite;
namespace DHT.Server.Database.Sqlite {

View File

@ -3,13 +3,15 @@ using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using DHT.Server.Database;
using DHT.Server.Logging;
using DHT.Server.Service;
using DHT.Utils.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
namespace DHT.Server.Endpoints {
public abstract class BaseEndpoint {
private static readonly Log Log = Log.ForType<BaseEndpoint>();
protected IDatabaseFile Db { get; }
private readonly ServerParameters parameters;

View File

@ -1,31 +0,0 @@
using System;
using System.Diagnostics;
namespace DHT.Server.Logging {
public static class Log {
private static void LogLevel(ConsoleColor color, string level, string text) {
Console.ForegroundColor = color;
foreach (string line in text.Replace("\r", "").Split('\n')) {
string formatted = $"[{level}] {line}";
Console.WriteLine(formatted);
Trace.WriteLine(formatted);
}
}
public static void Info(string message) {
LogLevel(ConsoleColor.Blue, "INFO", message);
}
public static void Warn(string message) {
LogLevel(ConsoleColor.Yellow, "WARN", message);
}
public static void Error(string message) {
LogLevel(ConsoleColor.Red, "ERROR", message);
}
public static void Error(Exception e) {
LogLevel(ConsoleColor.Red, "ERROR", e.ToString());
}
}
}

View File

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>DHT.Server</RootNamespace>
@ -13,7 +12,6 @@
<FileVersion>$(Version)</FileVersion>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>none</DebugType>
@ -24,4 +22,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Utils\Utils.csproj" />
</ItemGroup>
</Project>

View File

@ -3,7 +3,7 @@ using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using DHT.Server.Database;
using DHT.Server.Logging;
using DHT.Utils.Logging;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
@ -11,6 +11,8 @@ using Microsoft.Extensions.DependencyInjection;
namespace DHT.Server.Service {
public static class ServerLauncher {
private static readonly Log Log = Log.ForType(typeof(ServerLauncher));
private static IWebHost? Server { get; set; } = null;
public static bool IsRunning { get; private set; }

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace DHT.Server.Collections {
namespace DHT.Utils.Collections {
public class MultiDictionary<TKey, TValue> where TKey : notnull {
private readonly Dictionary<TKey, List<TValue>> dict = new();

45
app/Utils/Logging/Log.cs Normal file
View File

@ -0,0 +1,45 @@
using System;
using System.Diagnostics;
namespace DHT.Utils.Logging {
public sealed class Log {
public static Log ForType<T>() {
return ForType(typeof(T));
}
public static Log ForType(Type type) {
return new Log(type.Name);
}
private readonly string tag;
private Log(string tag) {
this.tag = tag;
}
private void LogLevel(ConsoleColor color, string level, string text) {
Console.ForegroundColor = color;
foreach (string line in text.Replace("\r", "").Split('\n')) {
string formatted = $"[{level}] [{tag}] {line}";
Console.WriteLine(formatted);
Trace.WriteLine(formatted);
}
}
public void Info(string message) {
LogLevel(ConsoleColor.Blue, "INFO", message);
}
public void Warn(string message) {
LogLevel(ConsoleColor.Yellow, "WARN", message);
}
public void Error(string message) {
LogLevel(ConsoleColor.Red, "ERROR", message);
}
public void Error(Exception e) {
LogLevel(ConsoleColor.Red, "ERROR", e.ToString());
}
}
}

View File

@ -3,7 +3,7 @@ using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace DHT.Desktop.Models {
namespace DHT.Utils.Models {
public abstract class BaseModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;

View File

@ -4,11 +4,16 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DHT.Desktop.Resources {
public static class ResourceLoader {
private static Stream GetEmbeddedStream(string filename) {
namespace DHT.Utils.Resources {
public sealed class ResourceLoader {
private readonly Assembly assembly;
public ResourceLoader(Assembly assembly) {
this.assembly = assembly;
}
private Stream GetEmbeddedStream(string filename) {
Stream? stream = null;
Assembly assembly = Assembly.GetExecutingAssembly();
foreach (var embeddedName in assembly.GetManifestResourceNames()) {
if (embeddedName.Replace('\\', '/') == filename) {
stream = assembly.GetManifestResourceStream(embeddedName);
@ -19,19 +24,18 @@ namespace DHT.Desktop.Resources {
return stream ?? throw new ArgumentException("Missing embedded resource: " + filename);
}
private static async Task<string> ReadTextAsync(Stream stream) {
private async Task<string> ReadTextAsync(Stream stream) {
using var reader = new StreamReader(stream, Encoding.UTF8);
return await reader.ReadToEndAsync();
}
public static async Task<string> ReadTextAsync(string filename) {
public async Task<string> ReadTextAsync(string filename) {
return await ReadTextAsync(GetEmbeddedStream(filename));
}
public static async Task<string> ReadJoinedAsync(string path, char separator) {
public async Task<string> ReadJoinedAsync(string path, char separator) {
StringBuilder joined = new();
Assembly assembly = Assembly.GetExecutingAssembly();
foreach (var embeddedName in assembly.GetManifestResourceNames()) {
if (embeddedName.Replace('\\', '/').StartsWith(path)) {
joined.Append(await ReadTextAsync(assembly.GetManifestResourceStream(embeddedName)!)).Append(separator);

22
app/Utils/Utils.csproj Normal file
View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>DHT.Utils</RootNamespace>
<Nullable>enable</Nullable>
<PackageId>DiscordHistoryTrackerUtils</PackageId>
<Authors>chylex</Authors>
<Company>DiscordHistoryTracker</Company>
<Product>DiscordHistoryTrackerUtils</Product>
<Version>33.1.0.0</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="10.3.0" />
</ItemGroup>
</Project>