1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-07 08:34:06 +02:00

Continue moving config, plugin, and update classes to TweetLib.Core

This commit is contained in:
chylex 2019-05-26 14:05:03 +02:00
parent 77030c9b23
commit 864ff6206d
16 changed files with 159 additions and 140 deletions

View File

@ -1,8 +1,8 @@
using System;
using System.Drawing;
using TweetDuck.Configuration.Instance;
using TweetDuck.Data;
using TweetLib.Core.Features.Configuration;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Serialization.Converters;
using TweetLib.Core.Utils;
@ -16,7 +16,7 @@ sealed class ConfigManager : IConfigManager{
private readonly FileConfigInstance<UserConfig> infoUser;
private readonly FileConfigInstance<SystemConfig> infoSystem;
private readonly PluginConfigInstance infoPlugins;
private readonly PluginConfigInstance<PluginConfig> infoPlugins;
private readonly IConfigInstance<BaseConfig>[] infoList;
@ -28,7 +28,7 @@ public ConfigManager(){
infoList = new IConfigInstance<BaseConfig>[]{
infoUser = new FileConfigInstance<UserConfig>(Program.UserConfigFilePath, User, "program options"),
infoSystem = new FileConfigInstance<SystemConfig>(Program.SystemConfigFilePath, System, "system options"),
infoPlugins = new PluginConfigInstance(Program.PluginConfigFilePath, Plugins)
infoPlugins = new PluginConfigInstance<PluginConfig>(Program.PluginConfigFilePath, Plugins)
};
// TODO refactor further

View File

@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TweetLib.Core.Features.Configuration;
namespace TweetDuck.Configuration.Instance{
class PluginConfigInstance : IConfigInstance<PluginConfig>{
public PluginConfig Instance { get; }
private readonly string filename;
public PluginConfigInstance(string filename, PluginConfig instance){
this.filename = filename;
this.Instance = instance;
}
public void Load(){
try{
using(StreamReader reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
string line = reader.ReadLine();
if (line == "#Disabled"){
HashSet<string> newDisabled = new HashSet<string>();
while((line = reader.ReadLine()) != null){
newDisabled.Add(line);
}
Instance.ReloadSilently(newDisabled);
}
}
}catch(FileNotFoundException){
}catch(DirectoryNotFoundException){
}catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
}
}
public void Save(){
try{
using(StreamWriter writer = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
writer.WriteLine("#Disabled");
foreach(string identifier in Instance.DisabledPlugins){
writer.WriteLine(identifier);
}
}
}catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
}
}
public void Reload(){
Load();
}
public void Reset(){
try{
File.Delete(filename);
Instance.ReloadSilently(Instance.ConstructWithDefaults<PluginConfig>().DisabledPlugins);
}catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not delete the plugin configuration file.", true, e);
return;
}
Reload();
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using TweetLib.Core.Features.Configuration;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetDuck.Configuration{
@ -11,11 +12,30 @@ sealed class PluginConfig : BaseConfig, IPluginConfig{
"official/reply-account"
};
// CONFIGURATION
// CONFIGURATION DATA
public IEnumerable<string> DisabledPlugins => disabled;
private readonly HashSet<string> disabled = new HashSet<string>(DefaultDisabled);
// EVENTS
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
// END OF CONFIG
public PluginConfig(IConfigManager configManager) : base(configManager){}
protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
return new PluginConfig(configManager);
}
// INTERFACE IMPLEMENTATION
IEnumerable<string> IPluginConfig.DisabledPlugins => disabled;
void IPluginConfig.Reset(IEnumerable<string> newDisabledPlugins){
disabled.Clear();
disabled.UnionWith(newDisabledPlugins);
}
public void SetEnabled(Plugin plugin, bool enabled){
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
@ -27,20 +47,5 @@ public void SetEnabled(Plugin plugin, bool enabled){
public bool IsEnabled(Plugin plugin){
return !disabled.Contains(plugin.Identifier);
}
public void ReloadSilently(IEnumerable<string> newDisabled){
disabled.Clear();
disabled.UnionWith(newDisabled);
}
private readonly HashSet<string> disabled = new HashSet<string>(DefaultDisabled);
// END OF CONFIG
public PluginConfig(IConfigManager configManager) : base(configManager){}
protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
return new PluginConfig(configManager);
}
}
}

View File

@ -2,7 +2,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Updates;
using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core.Bridge{

View File

@ -1,5 +1,6 @@
using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge;
@ -72,7 +73,7 @@ public FormBrowser(){
this.notification = new FormNotificationTweet(this, plugins);
this.notification.Show();
this.updates = new UpdateHandler(Program.InstallerPath);
this.updates = new UpdateHandler(new UpdateCheckClient(Program.InstallerPath), TaskScheduler.FromCurrentSynchronizationContext());
this.updates.CheckFinished += updates_CheckFinished;
this.updateBridge = new UpdateBridge(updates, this);

View File

@ -10,7 +10,7 @@
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Updates;
using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core.Other{
sealed partial class FormSettings : Form, FormManager.IAppDialog{

View File

@ -6,7 +6,6 @@
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils;
using TweetDuck.Updates;
using TweetLib.Core.Features.Updates;
using TweetLib.Core.Utils;

View File

@ -9,6 +9,7 @@
using TweetDuck.Resources;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Events;

View File

@ -54,9 +54,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\Arguments.cs" />
<Compile Include="Configuration\Instance\FileConfigInstance.cs" />
<Compile Include="Configuration\ConfigManager.cs" />
<Compile Include="Configuration\Instance\PluginConfigInstance.cs" />
<Compile Include="Configuration\LockManager.cs" />
<Compile Include="Configuration\SystemConfig.cs" />
<Compile Include="Configuration\UserConfig.cs" />
@ -274,7 +272,6 @@
<Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\NativeMethods.cs" />
<Compile Include="Updates\UpdateCheckClient.cs" />
<Compile Include="Updates\UpdateHandler.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\ScriptLoader.cs" />

View File

@ -11,7 +11,7 @@
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
namespace TweetDuck.Updates{
sealed class UpdateCheckClient{
sealed class UpdateCheckClient : IUpdateCheckClient{
private const string ApiLatestRelease = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
private const string UpdaterAssetName = "TweetDuck.Update.exe";
@ -21,7 +21,9 @@ public UpdateCheckClient(string installerFolder){
this.installerFolder = installerFolder;
}
public Task<UpdateInfo> Check(){
bool IUpdateCheckClient.CanCheck => Program.Config.User.EnableUpdateCheck;
Task<UpdateInfo> IUpdateCheckClient.Check(){
TaskCompletionSource<UpdateInfo> result = new TaskCompletionSource<UpdateInfo>();
WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentVanilla);
@ -67,10 +69,9 @@ string AssetDownloadUrl(JsonObject obj){
private static Exception ExpandWebException(Exception e){
if (e is WebException we && we.Response is HttpWebResponse response){
try{
using(Stream stream = response.GetResponseStream())
using(StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(response.CharacterSet ?? "utf-8"))){
return new Reporter.ExpandedLogException(e, reader.ReadToEnd());
}
using var stream = response.GetResponseStream();
using var reader = new StreamReader(stream, Encoding.GetEncoding(response.CharacterSet ?? "utf-8"));
return new Reporter.ExpandedLogException(e, reader.ReadToEnd());
}catch{
// whatever
}

View File

@ -1,23 +1,20 @@
using System;
using System.IO;
using TweetLib.Core.Features.Configuration;
using TweetLib.Core.Serialization;
namespace TweetDuck.Configuration.Instance{
sealed class FileConfigInstance<T> : IConfigInstance<T> where T : BaseConfig{
private const string ErrorTitle = "Configuration Error";
namespace TweetLib.Core.Features.Configuration{
public sealed class FileConfigInstance<T> : IConfigInstance<T> where T : BaseConfig{
public T Instance { get; }
public FileSerializer<T> Serializer { get; }
private readonly string filenameMain;
private readonly string filenameBackup;
private readonly string errorIdentifier;
private readonly string identifier;
public FileConfigInstance(string filename, T instance, string errorIdentifier){
public FileConfigInstance(string filename, T instance, string identifier){
this.filenameMain = filename;
this.filenameBackup = filename+".bak";
this.errorIdentifier = errorIdentifier;
this.filenameBackup = filename + ".bak";
this.identifier = identifier;
this.Instance = instance;
this.Serializer = new FileSerializer<T>();
@ -28,14 +25,14 @@ private void LoadInternal(bool backup){
}
public void Load(){
Exception firstException = null;
Exception? firstException = null;
for(int attempt = 0; attempt < 2; attempt++){
try{
LoadInternal(attempt > 0);
if (firstException != null){ // silently log exception that caused a backup restore
Program.Reporter.LogImportant(firstException.ToString());
App.ErrorHandler.Log(firstException.ToString());
}
return;
@ -50,13 +47,13 @@ public void Load(){
}
if (firstException is FormatException){
Program.Reporter.HandleException(ErrorTitle, "The configuration file for "+errorIdentifier+" is outdated or corrupted. If you continue, your "+errorIdentifier+" will be reset.", true, firstException);
OnException($"The configuration file for {identifier} is outdated or corrupted. If you continue, your {identifier} will be reset.", firstException);
}
else if (firstException is SerializationSoftException sse){
Program.Reporter.HandleException(ErrorTitle, $"{sse.Errors.Count} error{(sse.Errors.Count == 1 ? " was" : "s were")} encountered while loading the configuration file for "+errorIdentifier+". If you continue, some of your "+errorIdentifier+" will be reset.", true, firstException);
OnException($"{sse.Errors.Count} error{(sse.Errors.Count == 1 ? " was" : "s were")} encountered while loading the configuration file for {identifier}. If you continue, some of your {identifier} will be reset.", firstException);
}
else if (firstException != null){
Program.Reporter.HandleException(ErrorTitle, "Could not open the configuration file for "+errorIdentifier+". If you continue, your "+errorIdentifier+" will be reset.", true, firstException);
OnException($"Could not open the configuration file for {identifier}. If you continue, your {identifier} will be reset.", firstException);
}
}
@ -69,9 +66,9 @@ public void Save(){
Serializer.Write(filenameMain, Instance);
}catch(SerializationSoftException e){
Program.Reporter.HandleException(ErrorTitle, $"{e.Errors.Count} error{(e.Errors.Count == 1 ? " was" : "s were")} encountered while saving the configuration file for "+errorIdentifier+".", true, e);
OnException($"{e.Errors.Count} error{(e.Errors.Count == 1 ? " was" : "s were")} encountered while saving the configuration file for {identifier}.", e);
}catch(Exception e){
Program.Reporter.HandleException(ErrorTitle, "Could not save the configuration file for "+errorIdentifier+".", true, e);
OnException($"Could not save the configuration file for {identifier}.", e);
}
}
@ -83,10 +80,10 @@ public void Reload(){
Serializer.Write(filenameMain, Instance.ConstructWithDefaults<T>());
LoadInternal(false);
}catch(Exception e){
Program.Reporter.HandleException(ErrorTitle, "Could not regenerate the configuration file for "+errorIdentifier+".", true, e);
OnException($"Could not regenerate the configuration file for {identifier}.", e);
}
}catch(Exception e){
Program.Reporter.HandleException(ErrorTitle, "Could not reload the configuration file for "+errorIdentifier+".", true, e);
OnException($"Could not reload the configuration file for {identifier}.", e);
}
}
@ -95,11 +92,15 @@ public void Reset(){
File.Delete(filenameMain);
File.Delete(filenameBackup);
}catch(Exception e){
Program.Reporter.HandleException(ErrorTitle, "Could not delete configuration files to reset "+errorIdentifier+".", true, e);
OnException($"Could not delete configuration files to reset {identifier}.", e);
return;
}
Reload();
}
private static void OnException(string message, Exception e){
App.ErrorHandler.HandleException("Configuration Error", message, true, e);
}
}
}

View File

@ -2,11 +2,12 @@
using System.Collections.Generic;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetLib.Core.Features.Plugins{
namespace TweetLib.Core.Features.Plugins.Config{
public interface IPluginConfig{
IEnumerable<string> DisabledPlugins { get; }
event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
IEnumerable<string> DisabledPlugins { get; }
void Reset(IEnumerable<string> newDisabledPlugins);
void SetEnabled(Plugin plugin, bool enabled);
bool IsEnabled(Plugin plugin);

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TweetLib.Core.Features.Configuration;
namespace TweetLib.Core.Features.Plugins.Config{
public sealed class PluginConfigInstance<T> : IConfigInstance<T> where T : BaseConfig, IPluginConfig{
public T Instance { get; }
private readonly string filename;
public PluginConfigInstance(string filename, T instance){
this.filename = filename;
this.Instance = instance;
}
public void Load(){
try{
using var reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8);
string line = reader.ReadLine();
if (line == "#Disabled"){
HashSet<string> newDisabled = new HashSet<string>();
while((line = reader.ReadLine()) != null){
newDisabled.Add(line);
}
Instance.Reset(newDisabled);
}
}catch(FileNotFoundException){
}catch(DirectoryNotFoundException){
}catch(Exception e){
OnException("Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", e);
}
}
public void Save(){
try{
using var writer = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8);
writer.WriteLine("#Disabled");
foreach(string identifier in Instance.DisabledPlugins){
writer.WriteLine(identifier);
}
}catch(Exception e){
OnException("Could not save the plugin configuration file.", e);
}
}
public void Reload(){
Load();
}
public void Reset(){
try{
File.Delete(filename);
Instance.Reset(Instance.ConstructWithDefaults<T>().DisabledPlugins);
}catch(Exception e){
OnException("Could not delete the plugin configuration file.", e);
return;
}
Reload();
}
private static void OnException(string message, Exception e){
App.ErrorHandler.HandleException("Plugin Configuration Error", message, true, e);
}
}
}

View File

@ -1,4 +1,5 @@
using System.Linq;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetLib.Core.Features.Plugins{

View File

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace TweetLib.Core.Features.Updates{
public interface IUpdateCheckClient{
bool CanCheck { get; }
Task<UpdateInfo> Check();
}
}

View File

@ -1,35 +1,38 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Updates;
using Timer = System.Windows.Forms.Timer;
using Timer = System.Timers.Timer;
namespace TweetDuck.Updates{
sealed class UpdateHandler : IDisposable{
namespace TweetLib.Core.Features.Updates{
public sealed class UpdateHandler : IDisposable{
public const int CheckCodeUpdatesDisabled = -1;
private readonly UpdateCheckClient client;
private readonly IUpdateCheckClient client;
private readonly TaskScheduler scheduler;
private readonly Timer timer;
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
private ushort lastEventId;
public UpdateHandler(string installerFolder){
this.client = new UpdateCheckClient(installerFolder);
this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
public UpdateHandler(IUpdateCheckClient client, TaskScheduler scheduler){
this.client = client;
this.scheduler = scheduler;
this.timer = new Timer();
this.timer.Tick += timer_Tick;
this.timer = new Timer{
AutoReset = false,
Enabled = false
};
this.timer.Elapsed += timer_Elapsed;
}
public void Dispose(){
timer.Dispose();
}
private void timer_Tick(object sender, EventArgs e){
timer.Stop();
private void timer_Elapsed(object sender, ElapsedEventArgs e){
Check(false);
}
@ -40,9 +43,9 @@ public void StartTimer(){
timer.Stop();
if (Program.Config.User.EnableUpdateCheck){
if (client.CanCheck){
DateTime now = DateTime.Now;
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
TimeSpan nextHour = now.AddSeconds(60 * (60 - now.Minute) - now.Second) - now;
if (nextHour.TotalMinutes < 15){
nextHour = nextHour.Add(TimeSpan.FromHours(1));
@ -54,7 +57,7 @@ public void StartTimer(){
}
public int Check(bool force){
if (Program.Config.User.EnableUpdateCheck || force){
if (client.CanCheck || force){
int nextEventId = unchecked(++lastEventId);
Task<UpdateInfo> checkTask = client.Check();