1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-01 17:34:10 +02:00

Address inspections

This commit is contained in:
chylex 2022-02-05 22:45:44 +01:00
parent c4aa62fc3a
commit 933e0e54df
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
46 changed files with 140 additions and 132 deletions

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace TweetLib.Browser.CEF.Data { namespace TweetLib.Browser.CEF.Data {
public abstract class ContextMenuActionRegistry<T> { abstract class ContextMenuActionRegistry<T> {
private readonly Dictionary<T, Action> actions = new (); private readonly Dictionary<T, Action> actions = new ();
protected abstract T NextId(int n); protected abstract T NextId(int n);

View File

@ -28,7 +28,7 @@ public abstract class ContextMenuLogic {
206 // AddToDictionary 206 // AddToDictionary
}; };
protected sealed class ContextMenuActionRegistry : ContextMenuActionRegistry<int> { private protected sealed class ContextMenuActionRegistry : ContextMenuActionRegistry<int> {
private const int CommandUserFirst = 26500; private const int CommandUserFirst = 26500;
protected override int NextId(int n) { protected override int NextId(int n) {

View File

@ -30,19 +30,16 @@ public bool OnBeforeResourceLoad(TRequest request, IDisposable callback) {
if (resourceRequestHandler != null) { if (resourceRequestHandler != null) {
var result = resourceRequestHandler.Handle(requestAdapter.GetUrl(request), requestAdapter.GetResourceType(request)); var result = resourceRequestHandler.Handle(requestAdapter.GetUrl(request), requestAdapter.GetResourceType(request));
switch (result) { if (result is RequestHandleResult.Redirect redirect) {
case RequestHandleResult.Redirect redirect: requestAdapter.SetUrl(request, redirect.Url);
requestAdapter.SetUrl(request, redirect.Url); }
break; else if (result is RequestHandleResult.Process process) {
requestAdapter.SetHeader(request, "Accept-Encoding", "identity");
case RequestHandleResult.Process process: responseProcessors[requestAdapter.GetIdentifier(request)] = process.Processor;
requestAdapter.SetHeader(request, "Accept-Encoding", "identity"); }
responseProcessors[requestAdapter.GetIdentifier(request)] = process.Processor; else if (result == RequestHandleResult.Cancel) {
break; callback.Dispose();
return false;
case RequestHandleResult.Cancel:
callback.Dispose();
return false;
} }
} }

View File

@ -6,11 +6,11 @@
using TweetLib.Browser.Request; using TweetLib.Browser.Request;
namespace TweetLib.Browser.CEF.Logic { namespace TweetLib.Browser.CEF.Logic {
internal abstract class SchemeResourceVisitor { abstract class SchemeResourceVisitor {
protected static readonly SchemeResource.Status FileIsEmpty = new (HttpStatusCode.NoContent, "File is empty."); protected static readonly SchemeResource.Status FileIsEmpty = new (HttpStatusCode.NoContent, "File is empty.");
} }
internal sealed class SchemeResourceVisitor<TResourceHandler> : SchemeResourceVisitor, ISchemeResourceVisitor<TResourceHandler> { sealed class SchemeResourceVisitor<TResourceHandler> : SchemeResourceVisitor, ISchemeResourceVisitor<TResourceHandler> {
private readonly IResourceHandlerFactory<TResourceHandler> factory; private readonly IResourceHandlerFactory<TResourceHandler> factory;
public SchemeResourceVisitor(IResourceHandlerFactory<TResourceHandler> factory) { public SchemeResourceVisitor(IResourceHandlerFactory<TResourceHandler> factory) {

View File

@ -1,8 +0,0 @@
using System.Net;
namespace TweetLib.Browser.Interfaces {
public interface IResourceProvider<T> {
T Status(HttpStatusCode code, string message);
T File(byte[] contents, string extension);
}
}

View File

@ -1,7 +1,9 @@
using TweetLib.Browser.Interfaces; using TweetLib.Browser.Interfaces;
namespace TweetLib.Browser.Request { namespace TweetLib.Browser.Request {
public abstract class RequestHandleResult { public class RequestHandleResult {
public static RequestHandleResult Cancel { get; } = new ();
private RequestHandleResult() {} private RequestHandleResult() {}
public sealed class Redirect : RequestHandleResult { public sealed class Redirect : RequestHandleResult {
@ -19,11 +21,5 @@ public Process(IResponseProcessor processor) {
Processor = processor; Processor = processor;
} }
} }
public sealed class Cancel : RequestHandleResult {
public static Cancel Instance { get; } = new ();
private Cancel() {}
}
} }
} }

View File

@ -17,22 +17,23 @@ public static class App {
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
public static readonly bool IsPortable = Setup.IsPortable; public static readonly bool IsPortable = Setup.IsPortable;
public static readonly string ResourcesPath = Path.Combine(ProgramPath, "resources"); internal static readonly string ResourcesPath = Path.Combine(ProgramPath, "resources");
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins"); internal static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
public static readonly string GuidePath = Path.Combine(ProgramPath, "guide"); internal static readonly string GuidePath = Path.Combine(ProgramPath, "guide");
public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataFolder(); public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataFolder();
public static readonly string LogoPath = Path.Combine(ResourcesPath, "images/logo.png");
public static Logger Logger { get; } = new (Path.Combine(StoragePath, "TD_Log.txt"), Setup.IsDebugLogging); public static Logger Logger { get; } = new (Path.Combine(StoragePath, "TD_Log.txt"), Setup.IsDebugLogging);
public static ConfigManager ConfigManager { get; } = Setup.CreateConfigManager(StoragePath); public static ConfigManager ConfigManager { get; } = Setup.CreateConfigManager(StoragePath);
public static IAppErrorHandler ErrorHandler { get; } = Validate(Builder.ErrorHandler, nameof(Builder.ErrorHandler)); public static IAppErrorHandler ErrorHandler { get; } = Validate(Builder.ErrorHandler, nameof(Builder.ErrorHandler));
public static IAppSystemHandler SystemHandler { get; } = Validate(Builder.SystemHandler, nameof(Builder.SystemHandler)); public static IAppSystemHandler SystemHandler { get; } = Validate(Builder.SystemHandler, nameof(Builder.SystemHandler));
public static IAppMessageDialogs MessageDialogs { get; } = Validate(Builder.MessageDialogs, nameof(Builder.MessageDialogs));
public static IAppFileDialogs? FileDialogs { get; } = Builder.FileDialogs; internal static IAppMessageDialogs MessageDialogs { get; } = Validate(Builder.MessageDialogs, nameof(Builder.MessageDialogs));
internal static IAppFileDialogs? FileDialogs { get; } = Builder.FileDialogs;
internal static IAppUserConfiguration UserConfiguration => ConfigManager.User; internal static IAppUserConfiguration UserConfiguration => ConfigManager.User;
internal static IAppSystemConfiguration SystemConfiguration => ConfigManager.System;
private static string GetDataFolder() { private static string GetDataFolder() {
string? custom = Setup.CustomDataFolder; string? custom = Setup.CustomDataFolder;
@ -75,7 +76,7 @@ internal static void Launch() {
WebUtils.DefaultUserAgent = Lib.BrandName + " " + Version.Tag; WebUtils.DefaultUserAgent = Lib.BrandName + " " + Version.Tag;
if (SystemConfiguration.UseSystemProxyForAllConnections) { if (ConfigManager.System.UseSystemProxyForAllConnections) {
WebUtils.EnableSystemProxy(); WebUtils.EnableSystemProxy();
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using TweetLib.Utils.Data; using TweetLib.Utils.Data;
@ -46,7 +47,7 @@ public int GetDisplayDuration(int value) {
return 2000 + Math.Max(1000, value * characters); return 2000 + Math.Max(1000, value * characters);
} }
public string GenerateHtml(string bodyClasses, string? headLayout, string? customStyles, IEnumerable<InjectedString> injections, string[] scripts) { // TODO internal string GenerateHtml(string bodyClasses, string? headLayout, string? customStyles, IEnumerable<InjectedString> injections, string[] scripts) { // TODO
headLayout ??= DefaultHeadLayout; headLayout ??= DefaultHeadLayout;
customStyles ??= string.Empty; customStyles ??= string.Empty;
@ -71,12 +72,7 @@ public string GenerateHtml(string bodyClasses, string? headLayout, string? custo
build.Append("<tweetduck-script-placeholder></body></html>"); build.Append("<tweetduck-script-placeholder></body></html>");
string result = build.ToString(); string result = build.ToString();
return injections.Aggregate(result, static (current, injection) => injection.InjectInto(current)).Replace("<tweetduck-script-placeholder>", GenerateScripts(scripts));
foreach (var injection in injections) {
result = injection.InjectInto(result);
}
return result.Replace("<tweetduck-script-placeholder>", GenerateScripts(scripts));
} }
private string GenerateScripts(string[] scripts) { private string GenerateScripts(string[] scripts) {

View File

@ -13,7 +13,7 @@ internal static void SetNotificationLayout(string? fontSize, string? headLayout)
} }
public static string? FontSize { get; private set; } public static string? FontSize { get; private set; }
public static string? HeadLayout { get; private set; } private static string? HeadLayout { get; set; }
private NotificationBrowser(IBrowserComponent browserComponent, Func<NotificationBrowser, BrowserSetup> setup) : base(browserComponent, setup) {} private NotificationBrowser(IBrowserComponent browserComponent, Func<NotificationBrowser, BrowserSetup> setup) : base(browserComponent, setup) {}

View File

@ -7,7 +7,7 @@ namespace TweetLib.Core.Features.Plugins.Config {
public sealed class PluginConfig : IConfigObject<PluginConfig> { public sealed class PluginConfig : IConfigObject<PluginConfig> {
internal IEnumerable<string> DisabledPlugins => disabled; internal IEnumerable<string> DisabledPlugins => disabled;
public event EventHandler<PluginChangedStateEventArgs>? PluginChangedState; internal event EventHandler<PluginChangedStateEventArgs>? PluginChangedState;
private readonly HashSet<string> defaultDisabled; private readonly HashSet<string> defaultDisabled;
private readonly HashSet<string> disabled; private readonly HashSet<string> disabled;

View File

@ -6,12 +6,11 @@
namespace TweetLib.Core.Features.Plugins.Config { namespace TweetLib.Core.Features.Plugins.Config {
sealed class PluginConfigInstance : IConfigInstance { sealed class PluginConfigInstance : IConfigInstance {
public PluginConfig Instance { get; } private readonly PluginConfig instance;
private readonly string filename; private readonly string filename;
public PluginConfigInstance(string filename, PluginConfig instance) { public PluginConfigInstance(string filename, PluginConfig instance) {
this.Instance = instance; this.instance = instance;
this.filename = filename; this.filename = filename;
} }
@ -27,7 +26,7 @@ public void Load() {
newDisabled.Add(line); newDisabled.Add(line);
} }
Instance.Reset(newDisabled); instance.Reset(newDisabled);
} }
} catch (FileNotFoundException) { } catch (FileNotFoundException) {
// ignore // ignore
@ -43,7 +42,7 @@ public void Save() {
using var writer = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8); using var writer = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8);
writer.WriteLine("#Disabled"); writer.WriteLine("#Disabled");
foreach (string identifier in Instance.DisabledPlugins) { foreach (string identifier in instance.DisabledPlugins) {
writer.WriteLine(identifier); writer.WriteLine(identifier);
} }
} catch (Exception e) { } catch (Exception e) {
@ -58,7 +57,7 @@ public void Reload() {
public void Reset() { public void Reset() {
try { try {
File.Delete(filename); File.Delete(filename);
Instance.ResetToDefault(); instance.ResetToDefault();
} catch (Exception e) { } catch (Exception e) {
OnException("Could not delete the plugin configuration file.", e); OnException("Could not delete the plugin configuration file.", e);
return; return;

View File

@ -2,12 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace TweetLib.Core.Features.Plugins.Enums { namespace TweetLib.Core.Features.Plugins.Enums {
public enum PluginEnvironment { enum PluginEnvironment {
Browser, Browser,
Notification Notification
} }
internal static class PluginEnvironments { static class PluginEnvironments {
public static IEnumerable<PluginEnvironment> All { get; } = new PluginEnvironment[] { public static IEnumerable<PluginEnvironment> All { get; } = new PluginEnvironment[] {
PluginEnvironment.Browser, PluginEnvironment.Browser,
PluginEnvironment.Notification PluginEnvironment.Notification

View File

@ -7,7 +7,7 @@ public enum PluginGroup {
Custom Custom
} }
internal static class PluginGroups { static class PluginGroups {
public static IEnumerable<PluginGroup> All { get; } = new PluginGroup[] { public static IEnumerable<PluginGroup> All { get; } = new PluginGroup[] {
PluginGroup.Official, PluginGroup.Official,
PluginGroup.Custom PluginGroup.Custom

View File

@ -1,7 +1,7 @@
using System; using System;
namespace TweetLib.Core.Features.Plugins.Events { namespace TweetLib.Core.Features.Plugins.Events {
public sealed class PluginChangedStateEventArgs : EventArgs { internal sealed class PluginChangedStateEventArgs : EventArgs {
public Plugin Plugin { get; } public Plugin Plugin { get; }
public bool IsEnabled { get; } public bool IsEnabled { get; }

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace TweetLib.Core.Features.Plugins.Events { namespace TweetLib.Core.Features.Plugins.Events {
public sealed class PluginErrorEventArgs : EventArgs { internal sealed class PluginErrorEventArgs : EventArgs {
public bool HasErrors => Errors.Count > 0; public bool HasErrors => Errors.Count > 0;
public IList<string> Errors { get; } public IList<string> Errors { get; }

View File

@ -17,31 +17,32 @@ public sealed class Plugin {
public string Author { get; } public string Author { get; }
public string Version { get; } public string Version { get; }
public string Website { get; } public string Website { get; }
public string ConfigFile { get; }
public string ConfigDefault { get; }
public Version RequiredVersion { get; } public Version RequiredVersion { get; }
public bool CanRun { get; } public bool CanRun { get; }
public bool HasConfig { internal bool HasConfig {
get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0; get => configFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, configFile).Length > 0;
} }
public string ConfigPath { internal string ConfigPath {
get => HasConfig ? Path.Combine(GetPluginFolder(PluginFolder.Data), ConfigFile) : string.Empty; get => HasConfig ? Path.Combine(GetPluginFolder(PluginFolder.Data), configFile) : string.Empty;
} }
public bool HasDefaultConfig { private bool HasDefaultConfig {
get => ConfigDefault.Length > 0 && GetFullPathIfSafe(PluginFolder.Root, ConfigDefault).Length > 0; get => configDefault.Length > 0 && GetFullPathIfSafe(PluginFolder.Root, configDefault).Length > 0;
} }
public string DefaultConfigPath { private string DefaultConfigPath {
get => HasDefaultConfig ? Path.Combine(GetPluginFolder(PluginFolder.Root), ConfigDefault) : string.Empty; get => HasDefaultConfig ? Path.Combine(GetPluginFolder(PluginFolder.Root), configDefault) : string.Empty;
} }
private readonly string pathRoot; private readonly string pathRoot;
private readonly string pathData; private readonly string pathData;
private readonly ISet<PluginEnvironment> environments; private readonly ISet<PluginEnvironment> environments;
private readonly string configFile;
private readonly string configDefault;
private Plugin(PluginGroup group, string identifier, string pathRoot, string pathData, Builder builder) { private Plugin(PluginGroup group, string identifier, string pathRoot, string pathData, Builder builder) {
this.pathRoot = pathRoot; this.pathRoot = pathRoot;
@ -56,18 +57,18 @@ private Plugin(PluginGroup group, string identifier, string pathRoot, string pat
this.Author = builder.Author; this.Author = builder.Author;
this.Version = builder.Version; this.Version = builder.Version;
this.Website = builder.Website; this.Website = builder.Website;
this.ConfigFile = builder.ConfigFile; this.configFile = builder.ConfigFile;
this.ConfigDefault = builder.ConfigDefault; this.configDefault = builder.ConfigDefault;
this.RequiredVersion = builder.RequiredVersion; this.RequiredVersion = builder.RequiredVersion;
this.CanRun = AppVersion >= RequiredVersion; this.CanRun = AppVersion >= RequiredVersion;
} }
public bool HasEnvironment(PluginEnvironment environment) { internal bool HasEnvironment(PluginEnvironment environment) {
return environments.Contains(environment); return environments.Contains(environment);
} }
public string GetScriptPath(PluginEnvironment environment) { internal string GetScriptPath(PluginEnvironment environment) {
return environments.Contains(environment) ? Path.Combine(pathRoot, environment.GetPluginScriptFile()) : string.Empty; return environments.Contains(environment) ? Path.Combine(pathRoot, environment.GetPluginScriptFile()) : string.Empty;
} }
@ -79,7 +80,7 @@ public string GetPluginFolder(PluginFolder folder) {
}; };
} }
public string GetFullPathIfSafe(PluginFolder folder, string relativePath) { internal string GetFullPathIfSafe(PluginFolder folder, string relativePath) {
string rootFolder = GetPluginFolder(folder); string rootFolder = GetPluginFolder(folder);
return FileUtils.ResolveRelativePathSafely(rootFolder, relativePath); return FileUtils.ResolveRelativePathSafely(rootFolder, relativePath);
} }
@ -98,7 +99,7 @@ public override bool Equals(object obj) {
// Builder // Builder
public sealed class Builder { internal sealed class Builder {
private static readonly Version DefaultRequiredVersion = new (0, 0, 0, 0); private static readonly Version DefaultRequiredVersion = new (0, 0, 0, 0);
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;

View File

@ -7,7 +7,7 @@
using TweetLib.Utils.Data; using TweetLib.Utils.Data;
namespace TweetLib.Core.Features.Plugins { namespace TweetLib.Core.Features.Plugins {
internal static class PluginLoader { static class PluginLoader {
private static readonly string[] EndTag = { "[END]" }; private static readonly string[] EndTag = { "[END]" };
public static IEnumerable<Result<Plugin>> AllInFolder(string pluginFolder, string pluginDataFolder, PluginGroup group) { public static IEnumerable<Result<Plugin>> AllInFolder(string pluginFolder, string pluginDataFolder, PluginGroup group) {
@ -38,7 +38,7 @@ public static IEnumerable<Result<Plugin>> AllInFolder(string pluginFolder, strin
} }
} }
public static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group) { private static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group) {
Plugin.Builder builder = new Plugin.Builder(group, name, pathRoot, pathData); Plugin.Builder builder = new Plugin.Builder(group, name, pathRoot, pathData);
foreach (var environment in Directory.EnumerateFiles(pathRoot, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).Select(EnvironmentFromFileName)) { foreach (var environment in Directory.EnumerateFiles(pathRoot, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).Select(EnvironmentFromFileName)) {

View File

@ -18,15 +18,15 @@ public sealed class PluginManager {
public string PluginFolder { get; } public string PluginFolder { get; }
public string PluginDataFolder { get; } public string PluginDataFolder { get; }
public event EventHandler<PluginErrorEventArgs>? Reloaded; internal event EventHandler<PluginErrorEventArgs>? Reloaded;
public event EventHandler<PluginErrorEventArgs>? Executed; internal event EventHandler<PluginErrorEventArgs>? Executed;
internal readonly PluginBridge bridge; internal readonly PluginBridge bridge;
private IScriptExecutor? browserExecutor; private IScriptExecutor? browserExecutor;
private readonly HashSet<Plugin> plugins = new (); private readonly HashSet<Plugin> plugins = new ();
public PluginManager(PluginConfig config, string pluginFolder, string pluginDataFolder) { internal PluginManager(PluginConfig config, string pluginFolder, string pluginDataFolder) {
this.Config = config; this.Config = config;
this.Config.PluginChangedState += Config_PluginChangedState; this.Config.PluginChangedState += Config_PluginChangedState;
this.PluginFolder = pluginFolder; this.PluginFolder = pluginFolder;

View File

@ -3,7 +3,7 @@
using TweetLib.Core.Features.Plugins.Enums; using TweetLib.Core.Features.Plugins.Enums;
namespace TweetLib.Core.Features.Plugins { namespace TweetLib.Core.Features.Plugins {
internal static class PluginScriptGenerator { static class PluginScriptGenerator {
public static string GenerateConfig(PluginConfig config) { public static string GenerateConfig(PluginConfig config) {
return "window.TD_PLUGINS_DISABLE = [" + string.Join(",", config.DisabledPlugins.Select(static id => '"' + id + '"')) + "]"; return "window.TD_PLUGINS_DISABLE = [" + string.Join(",", config.DisabledPlugins.Select(static id => '"' + id + '"')) + "]";
} }

View File

@ -3,8 +3,8 @@
using TweetLib.Core.Features.Twitter; using TweetLib.Core.Features.Twitter;
namespace TweetLib.Core.Features { namespace TweetLib.Core.Features {
public static class PropertyObjectScript { static class PropertyObjectScript {
public enum Environment { internal enum Environment {
Browser, Browser,
Notification Notification
} }

View File

@ -247,10 +247,10 @@ private sealed class ResourceRequestHandler : BaseResourceRequestHandler {
return new RequestHandleResult.Process(VendorScriptProcessor.Instance); return new RequestHandleResult.Process(VendorScriptProcessor.Instance);
case ResourceType.Script when url.Contains("analytics."): case ResourceType.Script when url.Contains("analytics."):
return RequestHandleResult.Cancel.Instance; return RequestHandleResult.Cancel;
case ResourceType.Xhr when url.Contains(UrlVersionCheck): case ResourceType.Xhr when url.Contains(UrlVersionCheck):
return RequestHandleResult.Cancel.Instance; return RequestHandleResult.Cancel;
case ResourceType.Xhr when url.Contains("://api.twitter.com/") && url.Contains("include_entities=1") && !url.Contains("&include_ext_has_nft_avatar=1"): case ResourceType.Xhr when url.Contains("://api.twitter.com/") && url.Contains("include_entities=1") && !url.Contains("&include_ext_has_nft_avatar=1"):
return new RequestHandleResult.Redirect(url.Replace("include_entities=1", "include_entities=1&include_ext_has_nft_avatar=1")); return new RequestHandleResult.Redirect(url.Replace("include_entities=1", "include_entities=1&include_ext_has_nft_avatar=1"));

View File

@ -1,9 +1,11 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TweetLib.Utils.Static; using TweetLib.Utils.Static;
namespace TweetLib.Core.Features.Twitter { namespace TweetLib.Core.Features.Twitter {
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
public static class TwitterUrls { public static class TwitterUrls {
public const string TweetDeck = "https://tweetdeck.twitter.com"; public const string TweetDeck = "https://tweetdeck.twitter.com";
private const string TwitterTrackingUrl = "t.co"; private const string TwitterTrackingUrl = "t.co";

View File

@ -4,8 +4,7 @@
namespace TweetLib.Core.Systems.Configuration { namespace TweetLib.Core.Systems.Configuration {
sealed class FileConfigInstance<T> : IConfigInstance where T : IConfigObject<T> { sealed class FileConfigInstance<T> : IConfigInstance where T : IConfigObject<T> {
public T Instance { get; } private readonly T instance;
private readonly SimpleObjectSerializer<T> serializer; private readonly SimpleObjectSerializer<T> serializer;
private readonly string filenameMain; private readonly string filenameMain;
@ -13,7 +12,7 @@ sealed class FileConfigInstance<T> : IConfigInstance where T : IConfigObject<T>
private readonly string identifier; private readonly string identifier;
public FileConfigInstance(string filename, T instance, string identifier, TypeConverterRegistry converterRegistry) { public FileConfigInstance(string filename, T instance, string identifier, TypeConverterRegistry converterRegistry) {
this.Instance = instance; this.instance = instance;
this.serializer = new SimpleObjectSerializer<T>(converterRegistry); this.serializer = new SimpleObjectSerializer<T>(converterRegistry);
this.filenameMain = filename ?? throw new ArgumentNullException(nameof(filename), "Config file name must not be null!"); this.filenameMain = filename ?? throw new ArgumentNullException(nameof(filename), "Config file name must not be null!");
@ -22,7 +21,7 @@ public FileConfigInstance(string filename, T instance, string identifier, TypeCo
} }
private void LoadInternal(bool backup) { private void LoadInternal(bool backup) {
serializer.Read(backup ? filenameBackup : filenameMain, Instance); serializer.Read(backup ? filenameBackup : filenameMain, instance);
} }
public void Load() { public void Load() {
@ -64,7 +63,7 @@ public void Save() {
File.Move(filenameMain, filenameBackup); File.Move(filenameMain, filenameBackup);
} }
serializer.Write(filenameMain, Instance); serializer.Write(filenameMain, instance);
} catch (SerializationSoftException e) { } catch (SerializationSoftException e) {
OnException($"{e.Errors.Count} error{(e.Errors.Count == 1 ? " was" : "s were")} encountered while saving the configuration file for {identifier}.", 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) { } catch (Exception e) {
@ -77,7 +76,7 @@ public void Reload() {
LoadInternal(false); LoadInternal(false);
} catch (FileNotFoundException) { } catch (FileNotFoundException) {
try { try {
serializer.Write(filenameMain, Instance.ConstructWithDefaults()); serializer.Write(filenameMain, instance.ConstructWithDefaults());
LoadInternal(false); LoadInternal(false);
} catch (Exception e) { } catch (Exception e) {
OnException($"Could not regenerate the configuration file for {identifier}.", e); OnException($"Could not regenerate the configuration file for {identifier}.", e);

View File

@ -1,5 +1,5 @@
namespace TweetLib.Core.Systems.Dialogs { namespace TweetLib.Core.Systems.Dialogs {
public static class Dialogs { internal static class Dialogs {
public const string OK = "OK"; public const string OK = "OK";
} }
} }

View File

@ -5,7 +5,7 @@ public sealed class FileDialogFilter {
public string Name { get; } public string Name { get; }
public IReadOnlyList<string> Extensions { get; } public IReadOnlyList<string> Extensions { get; }
public FileDialogFilter(string name, params string[] extensions) { internal FileDialogFilter(string name, params string[] extensions) {
Name = name; Name = name;
Extensions = extensions; Extensions = extensions;
} }

View File

@ -99,7 +99,11 @@ private LockResult DetermineLockingProcessOrFail(Exception originalException) {
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
byte[] bytes = new byte[sizeof(int)]; byte[] bytes = new byte[sizeof(int)];
fileStream.Read(bytes, 0, bytes.Length);
if (fileStream.Read(bytes, 0, bytes.Length) != bytes.Length) {
throw new IOException("Read fewer bytes than requested!");
}
pid = BitConverter.ToInt32(bytes, 0); pid = BitConverter.ToInt32(bytes, 0);
} }

View File

@ -10,7 +10,7 @@ public enum UpdateDownloadStatus {
public static class UpdateDownloadStatusExtensions { public static class UpdateDownloadStatusExtensions {
public static bool IsFinished(this UpdateDownloadStatus status, bool canRetry) { public static bool IsFinished(this UpdateDownloadStatus status, bool canRetry) {
return status == UpdateDownloadStatus.AssetMissing || status == UpdateDownloadStatus.Done || (status == UpdateDownloadStatus.Failed && !canRetry); return status is UpdateDownloadStatus.AssetMissing or UpdateDownloadStatus.Done || (status == UpdateDownloadStatus.Failed && !canRetry);
} }
} }
} }

View File

@ -66,7 +66,7 @@ internal void BeginSilentDownload() {
internal void DeleteInstaller() { internal void DeleteInstaller() {
DownloadStatus = UpdateDownloadStatus.None; DownloadStatus = UpdateDownloadStatus.None;
if (currentDownload != null && currentDownload.IsBusy) { if (currentDownload is { IsBusy: true }) {
currentDownload.CancelAsync(); // deletes file when cancelled currentDownload.CancelAsync(); // deletes file when cancelled
return; return;
} }

View File

@ -12,7 +12,7 @@ public static CommandLineArgs FromStringArray(char entryChar, string[] array) {
return args; return args;
} }
public static void ReadStringArray(char entryChar, string[] array, CommandLineArgs targetArgs) { private static void ReadStringArray(char entryChar, string[] array, CommandLineArgs targetArgs) {
for (int index = 0; index < array.Length; index++) { for (int index = 0; index < array.Length; index++) {
string entry = array[index]; string entry = array[index];

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
namespace TweetLib.Utils.Collections { namespace TweetLib.Utils.Collections {
@ -8,6 +9,8 @@ namespace TweetLib.Utils.Collections {
/// <typeparam name="K1">The type of the outer key.</typeparam> /// <typeparam name="K1">The type of the outer key.</typeparam>
/// <typeparam name="K2">The type of the inner key.</typeparam> /// <typeparam name="K2">The type of the inner key.</typeparam>
/// <typeparam name="V">The type of the values.</typeparam> /// <typeparam name="V">The type of the values.</typeparam>
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public sealed class TwoKeyDictionary<K1, K2, V> { public sealed class TwoKeyDictionary<K1, K2, V> {
private readonly Dictionary<K1, Dictionary<K2, V>> dict; private readonly Dictionary<K1, Dictionary<K2, V>> dict;
private readonly int innerCapacity; private readonly int innerCapacity;

View File

@ -30,10 +30,13 @@ public string InjectInto(string targetHTML) {
case Position.Before: case Position.Before:
cutIndex = index; cutIndex = index;
break; break;
case Position.After: case Position.After:
cutIndex = index + search.Length; cutIndex = index + search.Length;
break; break;
default: return targetHTML;
default:
return targetHTML;
} }
return targetHTML.Insert(cutIndex, html); return targetHTML.Insert(cutIndex, html);

View File

@ -21,6 +21,15 @@ private static string JoinIdentifier(string[] identifier) {
return string.Join(KeySeparator.ToString(), identifier.Select(ValidateIdentifier)); return string.Join(KeySeparator.ToString(), identifier.Select(ValidateIdentifier));
} }
private static void ReadExactly(Stream stream, byte[] buffer) {
var length = buffer.Length;
var read = stream.Read(buffer, 0, length);
if (read != length) {
throw new IOException("Read fewer bytes than requested: " + read + " < " + length);
}
}
private readonly Stream stream; private readonly Stream stream;
public CombinedFileStream(Stream stream) { public CombinedFileStream(Stream stream) {
@ -79,13 +88,13 @@ private void WriteStreamImpl(string identifier, Stream sourceStream) {
} }
byte[] name = new byte[nameLength]; byte[] name = new byte[nameLength];
stream.Read(name, 0, nameLength); ReadExactly(stream, name);
byte[] contentLength = new byte[4]; byte[] contentLength = new byte[4];
stream.Read(contentLength, 0, 4); ReadExactly(stream, contentLength);
byte[] contents = new byte[BitConverter.ToInt32(contentLength, 0)]; byte[] contents = new byte[BitConverter.ToInt32(contentLength, 0)];
stream.Read(contents, 0, contents.Length); ReadExactly(stream, contents);
return new Entry(Encoding.UTF8.GetString(name), contents); return new Entry(Encoding.UTF8.GetString(name), contents);
} }
@ -98,10 +107,10 @@ private void WriteStreamImpl(string identifier, Stream sourceStream) {
} }
byte[] name = new byte[nameLength]; byte[] name = new byte[nameLength];
stream.Read(name, 0, nameLength); ReadExactly(stream, name);
byte[] contentLength = new byte[4]; byte[] contentLength = new byte[4];
stream.Read(contentLength, 0, 4); ReadExactly(stream, contentLength);
stream.Position += BitConverter.ToInt32(contentLength, 0); stream.Position += BitConverter.ToInt32(contentLength, 0);
@ -120,9 +129,7 @@ void IDisposable.Dispose() {
public sealed class Entry { public sealed class Entry {
private string Identifier { get; } private string Identifier { get; }
public string KeyName { public string KeyName => StringUtils.ExtractBefore(Identifier, KeySeparator);
get { return StringUtils.ExtractBefore(Identifier, KeySeparator); }
}
public string[] KeyValue { public string[] KeyValue {
get { get {

View File

@ -9,7 +9,7 @@ public void Register(Type type, ITypeConverter converter) {
converters[type] = converter; converters[type] = converter;
} }
public ITypeConverter? TryGet(Type type) { internal ITypeConverter? TryGet(Type type) {
return converters.TryGetValue(type, out var converter) ? converter : null; return converters.TryGetValue(type, out var converter) ? converter : null;
} }
} }

View File

@ -77,7 +77,7 @@ public FormPlayer(IntPtr handle, int dpi, int volume, string url, string token)
Marshal.ReleaseComObject(media); Marshal.ReleaseComObject(media);
return $"{(progress / 60).ToString("00")}:{(progress % 60).ToString("00")}"; return $"{(progress / 60):00}:{(progress % 60):00}";
}); });
labelTooltip.AttachTooltip(trackBarVolume, false, args => $"Volume : {trackBarVolume.Value}%"); labelTooltip.AttachTooltip(trackBarVolume, false, args => $"Volume : {trackBarVolume.Value}%");

View File

@ -8,8 +8,8 @@ namespace TweetDuck.Video {
static class Program { static class Program {
// referenced in VideoPlayer // referenced in VideoPlayer
// set by task manager -- public const int "CodeProcessKilled" = 1; // set by task manager -- public const int "CodeProcessKilled" = 1;
public const int CodeInvalidArgs = 2; private const int CodeInvalidArgs = 2;
public const int CodeLaunchFail = 3; private const int CodeLaunchFail = 3;
public const int CodeMediaError = 4; public const int CodeMediaError = 4;
public const int CodeOwnerGone = 5; public const int CodeOwnerGone = 5;
public const int CodeUserRequested = 6; public const int CodeUserRequested = 6;

View File

@ -18,14 +18,17 @@ private static NotificationBrowser CreateBrowserImpl(IBrowserComponent browserCo
protected override FormBorderStyle NotificationBorderStyle { protected override FormBorderStyle NotificationBorderStyle {
get { get {
var style = base.NotificationBorderStyle;
if (Config.NotificationSize == DesktopNotification.Size.Custom) { if (Config.NotificationSize == DesktopNotification.Size.Custom) {
switch (base.NotificationBorderStyle) { return style switch {
case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable; FormBorderStyle.FixedSingle => FormBorderStyle.Sizable,
case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow; FormBorderStyle.FixedToolWindow => FormBorderStyle.SizableToolWindow,
} _ => style
};
} }
return base.NotificationBorderStyle; return style;
} }
} }

View File

@ -48,7 +48,8 @@ private static int FontSizeLevel {
private readonly int timerBarHeight; private readonly int timerBarHeight;
protected int timeLeft, totalTime; private int timeLeft;
protected int totalTime;
protected bool pausedDuringNotification; protected bool pausedDuringNotification;
private readonly NativeMethods.HookProc mouseHookDelegate; private readonly NativeMethods.HookProc mouseHookDelegate;

View File

@ -48,7 +48,7 @@ public bool HasNotifications {
private readonly ContextMenu contextMenu; private readonly ContextMenu contextMenu;
private bool hasNotifications; private bool hasNotifications;
public TrayIcon() { private TrayIcon() {
InitializeComponent(); InitializeComponent();
this.contextMenu = new ContextMenu(); this.contextMenu = new ContextMenu();

View File

@ -32,7 +32,7 @@ public static CommandLineArgs GetCurrentClean() {
return args; return args;
} }
public static CommandLineArgs GetCurrentForInstaller() { private static CommandLineArgs GetCurrentForInstaller() {
CommandLineArgs args = GetCurrentClean(); CommandLineArgs args = GetCurrentClean();
args.AddFlag(ArgUpdated); args.AddFlag(ArgUpdated);
return args; return args;

View File

@ -11,6 +11,7 @@
using TweetLib.Utils.Data; using TweetLib.Utils.Data;
namespace TweetDuck.Configuration { namespace TweetDuck.Configuration {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
sealed class UserConfig : BaseConfig<UserConfig>, IAppUserConfiguration { sealed class UserConfig : BaseConfig<UserConfig>, IAppUserConfiguration {
public bool FirstRun { get; set; } = true; public bool FirstRun { get; set; } = true;

View File

@ -1,7 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Management; using TweetDuck.Management;
using TweetLib.Core; using TweetLib.Core;
@ -22,7 +21,7 @@ public FormAbout() {
labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, Lib.IssueTrackerUrl)); labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, Lib.IssueTrackerUrl));
try { try {
pictureLogo.Image = Image.FromFile(Path.Combine(App.ResourcesPath, "images/logo.png")); pictureLogo.Image = Image.FromFile(App.LogoPath);
} catch (Exception) { } catch (Exception) {
// ignore // ignore
} }

View File

@ -16,7 +16,7 @@ sealed partial class FormPlugins : Form, FormManager.IAppDialog {
private readonly PluginManager pluginManager; private readonly PluginManager pluginManager;
public FormPlugins() { private FormPlugins() {
InitializeComponent(); InitializeComponent();
Text = Program.BrandName + " Plugins"; Text = Program.BrandName + " Plugins";

View File

@ -24,7 +24,7 @@ public TabSettingsSounds(Action playSoundNotification) {
tbCustomSound.Text = Config.NotificationSoundPath; tbCustomSound.Text = Config.NotificationSoundPath;
tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty); tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
NativeMethods.SendMessage(tbCustomSound.Handle, NativeMethods.EM_SETCUEBANNER, 0, "(default TweetDeck sound)"); NativeMethods.SetCueBanner(tbCustomSound, "(default TweetDeck sound)");
} }
public override void OnReady() { public override void OnReady() {

View File

@ -16,7 +16,7 @@ sealed partial class PluginControl : UserControl {
private int nextHeight; private int nextHeight;
public PluginControl() { private PluginControl() {
InitializeComponent(); InitializeComponent();
} }

View File

@ -71,7 +71,7 @@ private static void Main() {
ErrorHandler = reporter, ErrorHandler = reporter,
SystemHandler = new SystemHandler(), SystemHandler = new SystemHandler(),
MessageDialogs = new MessageDialogs(), MessageDialogs = new MessageDialogs(),
FileDialogs = new FileDialogs(), FileDialogs = new FileDialogs()
}); });
errorReporter = reporter; errorReporter = reporter;

View File

@ -8,16 +8,16 @@ namespace TweetDuck.Utils {
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
static class NativeMethods { static class NativeMethods {
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF); private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1); public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
public const int HWND_TOPMOST = -1; public const int HWND_TOPMOST = -1;
public const uint SWP_NOACTIVATE = 0x0010; public const uint SWP_NOACTIVATE = 0x0010;
public const int WS_DISABLED = 0x08000000;
public const int GWL_STYLE = -16;
public const int SB_HORZ = 0; public const int SB_HORZ = 0;
public const int EM_SETCUEBANNER = 0x1501;
private const int WS_DISABLED = 0x08000000;
private const int GWL_STYLE = -16;
private const int EM_SETCUEBANNER = 0x1501;
public const int WM_MOUSE_LL = 14; public const int WM_MOUSE_LL = 14;
public const int WM_MOUSEWHEEL = 0x020A; public const int WM_MOUSEWHEEL = 0x020A;
@ -57,10 +57,10 @@ private struct MSLLHOOKSTRUCT {
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags); private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); private static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam); private static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern uint RegisterWindowMessage(string messageName); public static extern uint RegisterWindowMessage(string messageName);
@ -100,6 +100,10 @@ public static void SetFormDisabled(Form form, bool disabled) {
} }
} }
public static void SetCueBanner(Control control, string cueText) {
SendMessage(control.Handle, EM_SETCUEBANNER, 0, cueText);
}
public static void BroadcastMessage(uint msg, uint wParam, int lParam) { public static void BroadcastMessage(uint msg, uint wParam, int lParam) {
PostMessage(HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam)); PostMessage(HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
} }