mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 19:32:10 +02:00
Compare commits
1 Commits
1.18
...
random_wip
Author | SHA1 | Date | |
---|---|---|---|
d6a14edcdf |
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,4 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
patreon: chylex
|
|
||||||
ko_fi: chylex
|
|
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetLib.Core.Collections;
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
static class Arguments{
|
static class Arguments{
|
||||||
@@ -22,8 +22,8 @@ namespace TweetDuck.Configuration{
|
|||||||
return Current.HasFlag(flag);
|
return Current.HasFlag(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetValue(string key){
|
public static string GetValue(string key, string defaultValue){
|
||||||
return Current.GetValue(key);
|
return Current.GetValue(key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CommandLineArgs GetCurrentClean(){
|
public static CommandLineArgs GetCurrentClean(){
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using TweetDuck.Configuration.Instance;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetLib.Core.Features.Configuration;
|
using TweetDuck.Data.Serialization;
|
||||||
using TweetLib.Core.Features.Plugins.Config;
|
|
||||||
using TweetLib.Core.Serialization.Converters;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class ConfigManager : IConfigManager{
|
sealed class ConfigManager{
|
||||||
public UserConfig User { get; }
|
public UserConfig User { get; }
|
||||||
public SystemConfig System { get; }
|
public SystemConfig System { get; }
|
||||||
public PluginConfig Plugins { get; }
|
public PluginConfig Plugins { get; }
|
||||||
@@ -16,7 +16,7 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
private readonly FileConfigInstance<UserConfig> infoUser;
|
private readonly FileConfigInstance<UserConfig> infoUser;
|
||||||
private readonly FileConfigInstance<SystemConfig> infoSystem;
|
private readonly FileConfigInstance<SystemConfig> infoSystem;
|
||||||
private readonly PluginConfigInstance<PluginConfig> infoPlugins;
|
private readonly PluginConfigInstance infoPlugins;
|
||||||
|
|
||||||
private readonly IConfigInstance<BaseConfig>[] infoList;
|
private readonly IConfigInstance<BaseConfig>[] infoList;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ namespace TweetDuck.Configuration{
|
|||||||
infoList = new IConfigInstance<BaseConfig>[]{
|
infoList = new IConfigInstance<BaseConfig>[]{
|
||||||
infoUser = new FileConfigInstance<UserConfig>(Program.UserConfigFilePath, User, "program options"),
|
infoUser = new FileConfigInstance<UserConfig>(Program.UserConfigFilePath, User, "program options"),
|
||||||
infoSystem = new FileConfigInstance<SystemConfig>(Program.SystemConfigFilePath, System, "system options"),
|
infoSystem = new FileConfigInstance<SystemConfig>(Program.SystemConfigFilePath, System, "system options"),
|
||||||
infoPlugins = new PluginConfigInstance<PluginConfig>(Program.PluginConfigFilePath, Plugins)
|
infoPlugins = new PluginConfigInstance(Program.PluginConfigFilePath, Plugins)
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO refactor further
|
// TODO refactor further
|
||||||
@@ -70,13 +70,59 @@ namespace TweetDuck.Configuration{
|
|||||||
infoPlugins.Reload();
|
infoPlugins.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IConfigManager.TriggerProgramRestartRequested(){
|
private void TriggerProgramRestartRequested(){
|
||||||
ProgramRestartRequested?.Invoke(this, EventArgs.Empty);
|
ProgramRestartRequested?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
IConfigInstance<BaseConfig> IConfigManager.GetInstanceInfo(BaseConfig instance){
|
private IConfigInstance<BaseConfig> GetInstanceInfo(BaseConfig instance){
|
||||||
Type instanceType = instance.GetType();
|
Type instanceType = instance.GetType();
|
||||||
return Array.Find(infoList, info => info.Instance.GetType() == instanceType); // TODO handle null
|
return Array.Find(infoList, info => info.Instance.GetType() == instanceType); // TODO handle null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class BaseConfig{
|
||||||
|
private readonly ConfigManager configManager;
|
||||||
|
|
||||||
|
protected BaseConfig(ConfigManager configManager){
|
||||||
|
this.configManager = configManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Management
|
||||||
|
|
||||||
|
public void Save(){
|
||||||
|
configManager.GetInstanceInfo(this).Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reload(){
|
||||||
|
configManager.GetInstanceInfo(this).Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset(){
|
||||||
|
configManager.GetInstanceInfo(this).Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construction methods
|
||||||
|
|
||||||
|
public T ConstructWithDefaults<T>() where T : BaseConfig{
|
||||||
|
return ConstructWithDefaults(configManager) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BaseConfig ConstructWithDefaults(ConfigManager configManager);
|
||||||
|
|
||||||
|
// Utility methods
|
||||||
|
|
||||||
|
protected void UpdatePropertyWithEvent<T>(ref T field, T value, EventHandler eventHandler){
|
||||||
|
if (!EqualityComparer<T>.Default.Equals(field, value)){
|
||||||
|
field = value;
|
||||||
|
eventHandler?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void UpdatePropertyWithRestartRequest<T>(ref T field, T value){
|
||||||
|
if (!EqualityComparer<T>.Default.Equals(field, value)){
|
||||||
|
field = value;
|
||||||
|
configManager.TriggerProgramRestartRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetLib.Core.Serialization;
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
|
namespace TweetDuck.Configuration.Instance{
|
||||||
|
sealed class FileConfigInstance<T> : IConfigInstance<T> where T : ConfigManager.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 T Instance { get; }
|
||||||
public FileSerializer<T> Serializer { get; }
|
public FileSerializer<T> Serializer { get; }
|
||||||
|
|
||||||
private readonly string filenameMain;
|
private readonly string filenameMain;
|
||||||
private readonly string filenameBackup;
|
private readonly string filenameBackup;
|
||||||
private readonly string identifier;
|
private readonly string errorIdentifier;
|
||||||
|
|
||||||
public FileConfigInstance(string filename, T instance, string identifier){
|
public FileConfigInstance(string filename, T instance, string errorIdentifier){
|
||||||
this.filenameMain = filename;
|
this.filenameMain = filename;
|
||||||
this.filenameBackup = filename + ".bak";
|
this.filenameBackup = filename+".bak";
|
||||||
this.identifier = identifier;
|
this.errorIdentifier = errorIdentifier;
|
||||||
|
|
||||||
this.Instance = instance;
|
this.Instance = instance;
|
||||||
this.Serializer = new FileSerializer<T>();
|
this.Serializer = new FileSerializer<T>();
|
||||||
@@ -25,14 +27,14 @@ namespace TweetLib.Core.Features.Configuration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Load(){
|
public void Load(){
|
||||||
Exception? firstException = null;
|
Exception firstException = null;
|
||||||
|
|
||||||
for(int attempt = 0; attempt < 2; attempt++){
|
for(int attempt = 0; attempt < 2; attempt++){
|
||||||
try{
|
try{
|
||||||
LoadInternal(attempt > 0);
|
LoadInternal(attempt > 0);
|
||||||
|
|
||||||
if (firstException != null){ // silently log exception that caused a backup restore
|
if (firstException != null){ // silently log exception that caused a backup restore
|
||||||
App.ErrorHandler.Log(firstException.ToString());
|
Program.Reporter.LogImportant(firstException.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -47,13 +49,13 @@ namespace TweetLib.Core.Features.Configuration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (firstException is FormatException){
|
if (firstException is FormatException){
|
||||||
OnException($"The configuration file for {identifier} is outdated or corrupted. If you continue, your {identifier} will be reset.", firstException);
|
Program.Reporter.HandleException(ErrorTitle, "The configuration file for "+errorIdentifier+" is outdated or corrupted. If you continue, your "+errorIdentifier+" will be reset.", true, firstException);
|
||||||
}
|
}
|
||||||
else if (firstException is SerializationSoftException sse){
|
else if (firstException is SerializationSoftException sse){
|
||||||
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);
|
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);
|
||||||
}
|
}
|
||||||
else if (firstException != null){
|
else if (firstException != null){
|
||||||
OnException($"Could not open the configuration file for {identifier}. If you continue, your {identifier} will be reset.", firstException);
|
Program.Reporter.HandleException(ErrorTitle, "Could not open the configuration file for "+errorIdentifier+". If you continue, your "+errorIdentifier+" will be reset.", true, firstException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,9 +68,9 @@ namespace TweetLib.Core.Features.Configuration{
|
|||||||
|
|
||||||
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);
|
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);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
OnException($"Could not save the configuration file for {identifier}.", e);
|
Program.Reporter.HandleException(ErrorTitle, "Could not save the configuration file for "+errorIdentifier+".", true, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +82,10 @@ namespace TweetLib.Core.Features.Configuration{
|
|||||||
Serializer.Write(filenameMain, Instance.ConstructWithDefaults<T>());
|
Serializer.Write(filenameMain, Instance.ConstructWithDefaults<T>());
|
||||||
LoadInternal(false);
|
LoadInternal(false);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
OnException($"Could not regenerate the configuration file for {identifier}.", e);
|
Program.Reporter.HandleException(ErrorTitle, "Could not regenerate the configuration file for "+errorIdentifier+".", true, e);
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
OnException($"Could not reload the configuration file for {identifier}.", e);
|
Program.Reporter.HandleException(ErrorTitle, "Could not reload the configuration file for "+errorIdentifier+".", true, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,15 +94,11 @@ namespace TweetLib.Core.Features.Configuration{
|
|||||||
File.Delete(filenameMain);
|
File.Delete(filenameMain);
|
||||||
File.Delete(filenameBackup);
|
File.Delete(filenameBackup);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
OnException($"Could not delete configuration files to reset {identifier}.", e);
|
Program.Reporter.HandleException(ErrorTitle, "Could not delete configuration files to reset "+errorIdentifier+".", true, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnException(string message, Exception e){
|
|
||||||
App.ErrorHandler.HandleException("Configuration Error", message, true, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
namespace TweetLib.Core.Features.Configuration{
|
namespace TweetDuck.Configuration.Instance{
|
||||||
public interface IConfigInstance<out T>{
|
interface IConfigInstance<out T>{
|
||||||
T Instance { get; }
|
T Instance { get; }
|
||||||
|
|
||||||
void Save();
|
void Save();
|
69
Configuration/Instance/PluginConfigInstance.cs
Normal file
69
Configuration/Instance/PluginConfigInstance.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,42 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using TweetLib.Core.Features.Configuration;
|
using TweetDuck.Plugins;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetLib.Core.Features.Plugins.Config;
|
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class PluginConfig : BaseConfig, IPluginConfig{
|
sealed class PluginConfig : ConfigManager.BaseConfig, IPluginConfig{
|
||||||
private static readonly string[] DefaultDisabled = {
|
private static readonly string[] DefaultDisabled = {
|
||||||
"official/clear-columns",
|
"official/clear-columns",
|
||||||
"official/reply-account"
|
"official/reply-account"
|
||||||
};
|
};
|
||||||
|
|
||||||
// CONFIGURATION DATA
|
// CONFIGURATION
|
||||||
|
|
||||||
private readonly HashSet<string> disabled = new HashSet<string>(DefaultDisabled);
|
public IEnumerable<string> DisabledPlugins => disabled;
|
||||||
|
|
||||||
// EVENTS
|
|
||||||
|
|
||||||
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
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){
|
public void SetEnabled(Plugin plugin, bool enabled){
|
||||||
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
|
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
|
||||||
PluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
PluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
||||||
@@ -47,5 +26,20 @@ namespace TweetDuck.Configuration{
|
|||||||
public bool IsEnabled(Plugin plugin){
|
public bool IsEnabled(Plugin plugin){
|
||||||
return !disabled.Contains(plugin.Identifier);
|
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(ConfigManager configManager) : base(configManager){}
|
||||||
|
|
||||||
|
protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){
|
||||||
|
return new PluginConfig(configManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
using TweetLib.Core.Features.Configuration;
|
namespace TweetDuck.Configuration{
|
||||||
|
sealed class SystemConfig : ConfigManager.BaseConfig{
|
||||||
namespace TweetDuck.Configuration{
|
|
||||||
sealed class SystemConfig : BaseConfig{
|
|
||||||
|
|
||||||
// CONFIGURATION DATA
|
// CONFIGURATION DATA
|
||||||
|
|
||||||
@@ -19,9 +17,9 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
// END OF CONFIG
|
// END OF CONFIG
|
||||||
|
|
||||||
public SystemConfig(IConfigManager configManager) : base(configManager){}
|
public SystemConfig(ConfigManager configManager) : base(configManager){}
|
||||||
|
|
||||||
protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
|
protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){
|
||||||
return new SystemConfig(configManager);
|
return new SystemConfig(configManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,10 +5,9 @@ using TweetDuck.Core.Notification;
|
|||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetLib.Core.Features.Configuration;
|
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class UserConfig : BaseConfig{
|
sealed class UserConfig : ConfigManager.BaseConfig{
|
||||||
|
|
||||||
// CONFIGURATION DATA
|
// CONFIGURATION DATA
|
||||||
|
|
||||||
@@ -19,7 +18,6 @@ namespace TweetDuck.Configuration{
|
|||||||
public Size PluginsWindowSize { get; set; } = Size.Empty;
|
public Size PluginsWindowSize { get; set; } = Size.Empty;
|
||||||
|
|
||||||
public bool ExpandLinksOnHover { get; set; } = true;
|
public bool ExpandLinksOnHover { get; set; } = true;
|
||||||
public bool FocusDmInput { get; set; } = true;
|
|
||||||
public bool OpenSearchInFirstColumn { get; set; } = true;
|
public bool OpenSearchInFirstColumn { get; set; } = true;
|
||||||
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
|
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
|
||||||
public bool BestImageQuality { get; set; } = true;
|
public bool BestImageQuality { get; set; } = true;
|
||||||
@@ -136,9 +134,9 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
// END OF CONFIG
|
// END OF CONFIG
|
||||||
|
|
||||||
public UserConfig(IConfigManager configManager) : base(configManager){}
|
public UserConfig(ConfigManager configManager) : base(configManager){}
|
||||||
|
|
||||||
protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
|
protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){
|
||||||
return new UserConfig(configManager);
|
return new UserConfig(configManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,6 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover));
|
build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover));
|
||||||
|
|
||||||
if (environment == Environment.Browser){
|
if (environment == Environment.Browser){
|
||||||
build.Append("x.focusDmInput=").Append(Bool(config.FocusDmInput));
|
|
||||||
build.Append("x.openSearchInFirstColumn=").Append(Bool(config.OpenSearchInFirstColumn));
|
build.Append("x.openSearchInFirstColumn=").Append(Bool(config.OpenSearchInFirstColumn));
|
||||||
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(config.KeepLikeFollowDialogsOpen));
|
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(config.KeepLikeFollowDialogsOpen));
|
||||||
build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications));
|
build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications));
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Windows.Forms;
|
||||||
using System.Windows.Forms;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
@@ -7,7 +6,6 @@ using TweetDuck.Core.Other;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
|
||||||
class TweetDeckBridge{
|
class TweetDeckBridge{
|
||||||
public static string FontSize { get; private set; }
|
public static string FontSize { get; private set; }
|
||||||
public static string NotificationHeadLayout { get; private set; }
|
public static string NotificationHeadLayout { get; private set; }
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
|
||||||
class UpdateBridge{
|
class UpdateBridge{
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private readonly Control sync;
|
private readonly Control sync;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
@@ -15,10 +14,9 @@ using TweetDuck.Core.Other.Analytics;
|
|||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
|
||||||
using TweetLib.Core.Features.Updates;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
||||||
@@ -73,7 +71,7 @@ namespace TweetDuck.Core{
|
|||||||
this.notification = new FormNotificationTweet(this, plugins);
|
this.notification = new FormNotificationTweet(this, plugins);
|
||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
|
|
||||||
this.updates = new UpdateHandler(new UpdateCheckClient(Program.InstallerPath), TaskScheduler.FromCurrentSynchronizationContext());
|
this.updates = new UpdateHandler(Program.InstallerPath);
|
||||||
this.updates.CheckFinished += updates_CheckFinished;
|
this.updates.CheckFinished += updates_CheckFinished;
|
||||||
|
|
||||||
this.updateBridge = new UpdateBridge(updates, this);
|
this.updateBridge = new UpdateBridge(updates, this);
|
||||||
|
@@ -17,6 +17,8 @@ namespace TweetDuck.Core{
|
|||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool HasAnyDialogs => Application.OpenForms.OfType<IAppDialog>().Any();
|
||||||
|
|
||||||
public static void CloseAllDialogs(){
|
public static void CloseAllDialogs(){
|
||||||
foreach(IAppDialog dialog in Application.OpenForms.OfType<IAppDialog>().Reverse()){
|
foreach(IAppDialog dialog in Application.OpenForms.OfType<IAppDialog>().Reverse()){
|
||||||
((Form)dialog).Close();
|
((Form)dialog).Close();
|
||||||
|
@@ -12,7 +12,6 @@ using TweetDuck.Core.Notification;
|
|||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Other.Analytics;
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
abstract class ContextMenuBase : IContextMenuHandler{
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetLib.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Management{
|
namespace TweetDuck.Core.Management{
|
||||||
sealed class ContextInfo{
|
sealed class ContextInfo{
|
||||||
@@ -107,7 +107,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
private string unsafeLinkUrl = string.Empty;
|
private string unsafeLinkUrl = string.Empty;
|
||||||
private string mediaUrl = string.Empty;
|
private string mediaUrl = string.Empty;
|
||||||
|
|
||||||
private ChirpInfo chirp = default;
|
private ChirpInfo chirp = default(ChirpInfo);
|
||||||
|
|
||||||
public void AddContext(IContextMenuParams parameters){
|
public void AddContext(IContextMenuParams parameters){
|
||||||
ContextMenuType flags = parameters.TypeFlags;
|
ContextMenuType flags = parameters.TypeFlags;
|
||||||
|
@@ -3,10 +3,9 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetLib.Core.Data;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetLib.Core.Features.Plugins;
|
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Management{
|
namespace TweetDuck.Core.Management{
|
||||||
sealed class ProfileManager{
|
sealed class ProfileManager{
|
||||||
|
@@ -33,7 +33,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
screen = Screen.AllScreens[Config.NotificationDisplay-1];
|
screen = Screen.AllScreens[Config.NotificationDisplay-1];
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
screen = Screen.FromControl(owner);
|
screen = Screen.FromControl(owner); // TODO may be disposed?
|
||||||
}
|
}
|
||||||
|
|
||||||
int edgeDist = Config.NotificationEdgeDistance;
|
int edgeDist = Config.NotificationEdgeDistance;
|
||||||
|
@@ -6,10 +6,10 @@ using TweetDuck.Core.Bridge;
|
|||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetLib.Core.Data;
|
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
abstract partial class FormNotificationMain : FormNotificationBase{
|
abstract partial class FormNotificationMain : FormNotificationBase{
|
||||||
|
@@ -6,9 +6,9 @@ using CefSharp;
|
|||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetLib.Core.Data;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification.Screenshot{
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification.Screenshot{
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
|
||||||
sealed class ScreenshotBridge{
|
sealed class ScreenshotBridge{
|
||||||
private readonly Control owner;
|
private readonly Control owner;
|
||||||
|
|
||||||
|
@@ -2,8 +2,7 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using TweetLib.Core.Serialization;
|
using TweetDuck.Data.Serialization;
|
||||||
using TweetLib.Core.Serialization.Converters;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Analytics{
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
|
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
|
||||||
|
@@ -9,8 +9,6 @@ using System.Timers;
|
|||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetLib.Core;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Analytics{
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
sealed class AnalyticsManager : IDisposable{
|
sealed class AnalyticsManager : IDisposable{
|
||||||
@@ -82,7 +80,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
private void SetLastDataCollectionTime(DateTime dt, string message = null){
|
private void SetLastDataCollectionTime(DateTime dt, string message = null){
|
||||||
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
|
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
|
||||||
File.LastCollectionVersion = Program.VersionTag;
|
File.LastCollectionVersion = Program.VersionTag;
|
||||||
File.LastCollectionMessage = message ?? dt.ToString("g", Lib.Culture);
|
File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture);
|
||||||
|
|
||||||
File.Save();
|
File.Save();
|
||||||
RestartTimer();
|
RestartTimer();
|
||||||
@@ -119,7 +117,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
System.Diagnostics.Debugger.Break();
|
System.Diagnostics.Debugger.Break();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WebUtils.NewClient(BrowserUtils.UserAgentVanilla).UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
|
BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
|
||||||
}).ContinueWith(task => browser.InvokeAsyncSafe(() => {
|
}).ContinueWith(task => browser.InvokeAsyncSafe(() => {
|
||||||
if (task.Status == TaskStatus.RanToCompletion){
|
if (task.Status == TaskStatus.RanToCompletion){
|
||||||
SetLastDataCollectionTime(DateTime.Now);
|
SetLastDataCollectionTime(DateTime.Now);
|
||||||
|
@@ -11,10 +11,7 @@ using System.Text.RegularExpressions;
|
|||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetLib.Core;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetLib.Core.Features.Plugins;
|
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Analytics{
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
static class AnalyticsReportGenerator{
|
static class AnalyticsReportGenerator{
|
||||||
@@ -30,7 +27,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
{ "System Edition" , SystemEdition },
|
{ "System Edition" , SystemEdition },
|
||||||
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
|
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
|
||||||
{ "System Build" , SystemBuild },
|
{ "System Build" , SystemBuild },
|
||||||
{ "System Locale" , Lib.Culture.Name.ToLower() },
|
{ "System Locale" , Program.Culture.Name.ToLower() },
|
||||||
0,
|
0,
|
||||||
{ "RAM" , Exact(RamSize) },
|
{ "RAM" , Exact(RamSize) },
|
||||||
{ "GPU" , GpuVendor },
|
{ "GPU" , GpuVendor },
|
||||||
|
@@ -6,7 +6,6 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Controls;
|
using TweetDuck.Plugins.Controls;
|
||||||
using TweetLib.Core.Features.Plugins;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
sealed partial class FormPlugins : Form, FormManager.IAppDialog{
|
sealed partial class FormPlugins : Form, FormManager.IAppDialog{
|
||||||
|
@@ -10,7 +10,7 @@ using TweetDuck.Core.Other.Settings;
|
|||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
sealed partial class FormSettings : Form, FormManager.IAppDialog{
|
sealed partial class FormSettings : Form, FormManager.IAppDialog{
|
||||||
@@ -195,7 +195,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
private sealed class SettingsTab{
|
private sealed class SettingsTab{
|
||||||
public Button Button { get; }
|
public Button Button { get; }
|
||||||
|
|
||||||
public BaseTabSettings Control => control ??= constructor();
|
public BaseTabSettings Control => control ?? (control = constructor());
|
||||||
public bool IsInitialized => control != null;
|
public bool IsInitialized => control != null;
|
||||||
|
|
||||||
private readonly Func<BaseTabSettings> constructor;
|
private readonly Func<BaseTabSettings> constructor;
|
||||||
|
@@ -5,6 +5,8 @@ using TweetDuck.Core.Other.Analytics;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsAnalytics : Form{
|
sealed partial class DialogSettingsAnalytics : Form{
|
||||||
|
public string CefArgs => textBoxReport.Text;
|
||||||
|
|
||||||
public DialogSettingsAnalytics(AnalyticsReport report){
|
public DialogSettingsAnalytics(AnalyticsReport report){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Collections;
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsCefArgs : Form{
|
sealed partial class DialogSettingsCefArgs : Form{
|
||||||
|
@@ -4,8 +4,8 @@ using System.IO;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsManage : Form{
|
sealed partial class DialogSettingsManage : Form{
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetLib.Core.Collections;
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
sealed partial class DialogSettingsRestart : Form{
|
sealed partial class DialogSettingsRestart : Form{
|
||||||
@@ -18,7 +18,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
tbDataFolder.Enabled = false;
|
tbDataFolder.Enabled = false;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder) ?? string.Empty;
|
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty);
|
||||||
tbDataFolder.TextChanged += control_Change;
|
tbDataFolder.TextChanged += control_Change;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
71
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
71
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -39,7 +39,6 @@
|
|||||||
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
|
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
|
||||||
this.labelUpdates = new System.Windows.Forms.Label();
|
this.labelUpdates = new System.Windows.Forms.Label();
|
||||||
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
|
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
|
||||||
this.checkFocusDmInput = new System.Windows.Forms.CheckBox();
|
|
||||||
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
|
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
|
||||||
this.labelTray = new System.Windows.Forms.Label();
|
this.labelTray = new System.Windows.Forms.Label();
|
||||||
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
||||||
@@ -83,11 +82,11 @@
|
|||||||
//
|
//
|
||||||
this.checkUpdateNotifications.AutoSize = true;
|
this.checkUpdateNotifications.AutoSize = true;
|
||||||
this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 409);
|
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 393);
|
||||||
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||||
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
||||||
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
|
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
|
||||||
this.checkUpdateNotifications.TabIndex = 14;
|
this.checkUpdateNotifications.TabIndex = 13;
|
||||||
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
||||||
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -95,12 +94,12 @@
|
|||||||
//
|
//
|
||||||
this.btnCheckUpdates.AutoSize = true;
|
this.btnCheckUpdates.AutoSize = true;
|
||||||
this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 433);
|
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 417);
|
||||||
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
||||||
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
|
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
|
||||||
this.btnCheckUpdates.TabIndex = 15;
|
this.btnCheckUpdates.TabIndex = 14;
|
||||||
this.btnCheckUpdates.Text = "Check Updates Now";
|
this.btnCheckUpdates.Text = "Check Updates Now";
|
||||||
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -120,11 +119,11 @@
|
|||||||
//
|
//
|
||||||
this.checkBestImageQuality.AutoSize = true;
|
this.checkBestImageQuality.AutoSize = true;
|
||||||
this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 122);
|
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 98);
|
||||||
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
||||||
this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19);
|
this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19);
|
||||||
this.checkBestImageQuality.TabIndex = 5;
|
this.checkBestImageQuality.TabIndex = 4;
|
||||||
this.checkBestImageQuality.Text = "Best Image Quality";
|
this.checkBestImageQuality.Text = "Best Image Quality";
|
||||||
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -132,11 +131,11 @@
|
|||||||
//
|
//
|
||||||
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
||||||
this.checkOpenSearchInFirstColumn.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkOpenSearchInFirstColumn.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 74);
|
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 50);
|
||||||
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
||||||
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(245, 19);
|
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(245, 19);
|
||||||
this.checkOpenSearchInFirstColumn.TabIndex = 3;
|
this.checkOpenSearchInFirstColumn.TabIndex = 2;
|
||||||
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
||||||
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -159,11 +158,11 @@
|
|||||||
//
|
//
|
||||||
this.labelZoom.AutoSize = true;
|
this.labelZoom.AutoSize = true;
|
||||||
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelZoom.Location = new System.Drawing.Point(3, 179);
|
this.labelZoom.Location = new System.Drawing.Point(3, 155);
|
||||||
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelZoom.Name = "labelZoom";
|
this.labelZoom.Name = "labelZoom";
|
||||||
this.labelZoom.Size = new System.Drawing.Size(39, 15);
|
this.labelZoom.Size = new System.Drawing.Size(39, 15);
|
||||||
this.labelZoom.TabIndex = 7;
|
this.labelZoom.TabIndex = 6;
|
||||||
this.labelZoom.Text = "Zoom";
|
this.labelZoom.Text = "Zoom";
|
||||||
//
|
//
|
||||||
// zoomUpdateTimer
|
// zoomUpdateTimer
|
||||||
@@ -186,21 +185,21 @@
|
|||||||
//
|
//
|
||||||
this.panelZoom.Controls.Add(this.trackBarZoom);
|
this.panelZoom.Controls.Add(this.trackBarZoom);
|
||||||
this.panelZoom.Controls.Add(this.labelZoomValue);
|
this.panelZoom.Controls.Add(this.labelZoomValue);
|
||||||
this.panelZoom.Location = new System.Drawing.Point(0, 195);
|
this.panelZoom.Location = new System.Drawing.Point(0, 171);
|
||||||
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
|
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
|
||||||
this.panelZoom.Name = "panelZoom";
|
this.panelZoom.Name = "panelZoom";
|
||||||
this.panelZoom.Size = new System.Drawing.Size(300, 35);
|
this.panelZoom.Size = new System.Drawing.Size(300, 35);
|
||||||
this.panelZoom.TabIndex = 8;
|
this.panelZoom.TabIndex = 7;
|
||||||
//
|
//
|
||||||
// checkAnimatedAvatars
|
// checkAnimatedAvatars
|
||||||
//
|
//
|
||||||
this.checkAnimatedAvatars.AutoSize = true;
|
this.checkAnimatedAvatars.AutoSize = true;
|
||||||
this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 146);
|
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 122);
|
||||||
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
||||||
this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19);
|
this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19);
|
||||||
this.checkAnimatedAvatars.TabIndex = 6;
|
this.checkAnimatedAvatars.TabIndex = 5;
|
||||||
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
||||||
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -208,11 +207,11 @@
|
|||||||
//
|
//
|
||||||
this.labelUpdates.AutoSize = true;
|
this.labelUpdates.AutoSize = true;
|
||||||
this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelUpdates.Location = new System.Drawing.Point(0, 383);
|
this.labelUpdates.Location = new System.Drawing.Point(0, 367);
|
||||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1);
|
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
|
||||||
this.labelUpdates.Name = "labelUpdates";
|
this.labelUpdates.Name = "labelUpdates";
|
||||||
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
|
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
|
||||||
this.labelUpdates.TabIndex = 13;
|
this.labelUpdates.TabIndex = 12;
|
||||||
this.labelUpdates.Text = "UPDATES";
|
this.labelUpdates.Text = "UPDATES";
|
||||||
//
|
//
|
||||||
// flowPanelLeft
|
// flowPanelLeft
|
||||||
@@ -221,7 +220,6 @@
|
|||||||
| System.Windows.Forms.AnchorStyles.Left)));
|
| System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.flowPanelLeft.Controls.Add(this.labelUI);
|
this.flowPanelLeft.Controls.Add(this.labelUI);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkExpandLinks);
|
this.flowPanelLeft.Controls.Add(this.checkExpandLinks);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkFocusDmInput);
|
|
||||||
this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn);
|
this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
|
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
|
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
|
||||||
@@ -242,27 +240,15 @@
|
|||||||
this.flowPanelLeft.TabIndex = 0;
|
this.flowPanelLeft.TabIndex = 0;
|
||||||
this.flowPanelLeft.WrapContents = false;
|
this.flowPanelLeft.WrapContents = false;
|
||||||
//
|
//
|
||||||
// checkFocusDmInput
|
|
||||||
//
|
|
||||||
this.checkFocusDmInput.AutoSize = true;
|
|
||||||
this.checkFocusDmInput.Font = new System.Drawing.Font("Segoe UI", 9F);
|
|
||||||
this.checkFocusDmInput.Location = new System.Drawing.Point(6, 50);
|
|
||||||
this.checkFocusDmInput.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
|
||||||
this.checkFocusDmInput.Name = "checkFocusDmInput";
|
|
||||||
this.checkFocusDmInput.Size = new System.Drawing.Size(282, 19);
|
|
||||||
this.checkFocusDmInput.TabIndex = 2;
|
|
||||||
this.checkFocusDmInput.Text = "Focus Input Field When Opening Direct Message";
|
|
||||||
this.checkFocusDmInput.UseVisualStyleBackColor = true;
|
|
||||||
//
|
|
||||||
// checkKeepLikeFollowDialogsOpen
|
// checkKeepLikeFollowDialogsOpen
|
||||||
//
|
//
|
||||||
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
|
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
|
||||||
this.checkKeepLikeFollowDialogsOpen.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkKeepLikeFollowDialogsOpen.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 98);
|
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 74);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
|
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
|
||||||
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(190, 19);
|
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(190, 19);
|
||||||
this.checkKeepLikeFollowDialogsOpen.TabIndex = 4;
|
this.checkKeepLikeFollowDialogsOpen.TabIndex = 3;
|
||||||
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
|
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
|
||||||
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
|
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -270,11 +256,11 @@
|
|||||||
//
|
//
|
||||||
this.labelTray.AutoSize = true;
|
this.labelTray.AutoSize = true;
|
||||||
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelTray.Location = new System.Drawing.Point(0, 255);
|
this.labelTray.Location = new System.Drawing.Point(0, 236);
|
||||||
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
|
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
|
||||||
this.labelTray.Name = "labelTray";
|
this.labelTray.Name = "labelTray";
|
||||||
this.labelTray.Size = new System.Drawing.Size(99, 19);
|
this.labelTray.Size = new System.Drawing.Size(99, 19);
|
||||||
this.labelTray.TabIndex = 9;
|
this.labelTray.TabIndex = 8;
|
||||||
this.labelTray.Text = "SYSTEM TRAY";
|
this.labelTray.Text = "SYSTEM TRAY";
|
||||||
//
|
//
|
||||||
// comboBoxTrayType
|
// comboBoxTrayType
|
||||||
@@ -282,32 +268,32 @@
|
|||||||
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.comboBoxTrayType.FormattingEnabled = true;
|
this.comboBoxTrayType.FormattingEnabled = true;
|
||||||
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 279);
|
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 260);
|
||||||
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
|
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
|
||||||
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
||||||
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
|
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
|
||||||
this.comboBoxTrayType.TabIndex = 10;
|
this.comboBoxTrayType.TabIndex = 9;
|
||||||
//
|
//
|
||||||
// labelTrayIcon
|
// labelTrayIcon
|
||||||
//
|
//
|
||||||
this.labelTrayIcon.AutoSize = true;
|
this.labelTrayIcon.AutoSize = true;
|
||||||
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelTrayIcon.Location = new System.Drawing.Point(3, 314);
|
this.labelTrayIcon.Location = new System.Drawing.Point(3, 295);
|
||||||
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
||||||
this.labelTrayIcon.Name = "labelTrayIcon";
|
this.labelTrayIcon.Name = "labelTrayIcon";
|
||||||
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
|
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
|
||||||
this.labelTrayIcon.TabIndex = 11;
|
this.labelTrayIcon.TabIndex = 10;
|
||||||
this.labelTrayIcon.Text = "Tray Icon";
|
this.labelTrayIcon.Text = "Tray Icon";
|
||||||
//
|
//
|
||||||
// checkTrayHighlight
|
// checkTrayHighlight
|
||||||
//
|
//
|
||||||
this.checkTrayHighlight.AutoSize = true;
|
this.checkTrayHighlight.AutoSize = true;
|
||||||
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 335);
|
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 316);
|
||||||
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||||
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
||||||
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
|
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
|
||||||
this.checkTrayHighlight.TabIndex = 12;
|
this.checkTrayHighlight.TabIndex = 11;
|
||||||
this.checkTrayHighlight.Text = "Enable Highlight";
|
this.checkTrayHighlight.Text = "Enable Highlight";
|
||||||
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -562,6 +548,5 @@
|
|||||||
private System.Windows.Forms.Label labelTranslationTarget;
|
private System.Windows.Forms.Label labelTranslationTarget;
|
||||||
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
|
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
|
||||||
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
|
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
|
||||||
private System.Windows.Forms.CheckBox checkFocusDmInput;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
||||||
@@ -35,7 +34,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
// user interface
|
// user interface
|
||||||
|
|
||||||
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
||||||
toolTip.SetToolTip(checkFocusDmInput, "Places cursor into Direct Message input\r\nfield when opening a conversation.");
|
|
||||||
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
||||||
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
|
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
|
||||||
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
||||||
@@ -44,7 +42,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
|
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
|
||||||
|
|
||||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||||
checkFocusDmInput.Checked = Config.FocusDmInput;
|
|
||||||
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
||||||
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
|
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
|
||||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
@@ -130,7 +127,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
||||||
checkFocusDmInput.CheckedChanged += checkFocusDmInput_CheckedChanged;
|
|
||||||
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
||||||
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
|
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
|
||||||
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||||
@@ -164,10 +160,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFocusDmInput_CheckedChanged(object sender, EventArgs e){
|
|
||||||
Config.FocusDmInput = checkFocusDmInput.Checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
||||||
}
|
}
|
||||||
|
@@ -12,8 +12,8 @@ using TweetDuck.Core.Handling.General;
|
|||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
sealed class TweetDeckBrowser : IDisposable{
|
sealed class TweetDeckBrowser : IDisposable{
|
||||||
|
@@ -3,16 +3,16 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
static class BrowserUtils{
|
static class BrowserUtils{
|
||||||
public static string UserAgentVanilla => Program.BrandName + " " + Application.ProductVersion;
|
public static string UserAgentVanilla => Program.BrandName+" "+Application.ProductVersion;
|
||||||
public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + Cef.ChromiumVersion + " Safari/537.36";
|
public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"+Cef.ChromiumVersion+" Safari/537.36";
|
||||||
|
|
||||||
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
||||||
|
|
||||||
@@ -74,11 +74,29 @@ namespace TweetDuck.Core.Utils{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string TwitterTrackingUrl = "t.co";
|
||||||
|
|
||||||
|
public enum UrlCheckResult{
|
||||||
|
Invalid, Tracking, Fine
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UrlCheckResult CheckUrl(string url){
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||||
|
string scheme = uri.Scheme;
|
||||||
|
|
||||||
|
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
|
||||||
|
return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UrlCheckResult.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
public static void OpenExternalBrowser(string url){
|
public static void OpenExternalBrowser(string url){
|
||||||
if (string.IsNullOrWhiteSpace(url))return;
|
if (string.IsNullOrWhiteSpace(url))return;
|
||||||
|
|
||||||
switch(UrlUtils.Check(url)){
|
switch(CheckUrl(url)){
|
||||||
case UrlUtils.CheckResult.Fine:
|
case UrlCheckResult.Fine:
|
||||||
if (FormGuide.CheckGuideUrl(url, out string hash)){
|
if (FormGuide.CheckGuideUrl(url, out string hash)){
|
||||||
FormGuide.Show(hash);
|
FormGuide.Show(hash);
|
||||||
}
|
}
|
||||||
@@ -99,9 +117,9 @@ namespace TweetDuck.Core.Utils{
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UrlUtils.CheckResult.Tracking:
|
case UrlCheckResult.Tracking:
|
||||||
if (Config.IgnoreTrackingUrlWarning){
|
if (Config.IgnoreTrackingUrlWarning){
|
||||||
goto case UrlUtils.CheckResult.Fine;
|
goto case UrlCheckResult.Fine;
|
||||||
}
|
}
|
||||||
|
|
||||||
using(FormMessage form = new FormMessage("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, MessageBoxIcon.Warning)){
|
using(FormMessage form = new FormMessage("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, MessageBoxIcon.Warning)){
|
||||||
@@ -117,13 +135,13 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result == DialogResult.Ignore || result == DialogResult.Yes){
|
if (result == DialogResult.Ignore || result == DialogResult.Yes){
|
||||||
goto case UrlUtils.CheckResult.Fine;
|
goto case UrlCheckResult.Fine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UrlUtils.CheckResult.Invalid:
|
case UrlCheckResult.Invalid:
|
||||||
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -156,10 +174,50 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetFileNameFromUrl(string url){
|
||||||
|
string file = Path.GetFileName(new Uri(url).AbsolutePath);
|
||||||
|
return string.IsNullOrEmpty(file) ? null : file;
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetErrorName(CefErrorCode code){
|
public static string GetErrorName(CefErrorCode code){
|
||||||
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static WebClient CreateWebClient(){
|
||||||
|
WindowsUtils.EnsureTLS12();
|
||||||
|
|
||||||
|
WebClient client = new WebClient{ Proxy = null };
|
||||||
|
client.Headers[HttpRequestHeader.UserAgent] = UserAgentVanilla;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebClient DownloadFileAsync(string url, string target, string cookie, Action onSuccess, Action<Exception> onFailure){
|
||||||
|
WebClient client = CreateWebClient();
|
||||||
|
|
||||||
|
if (cookie != null){
|
||||||
|
client.Headers[HttpRequestHeader.Cookie] = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.DownloadFileCompleted += (sender, args) => {
|
||||||
|
if (args.Cancelled){
|
||||||
|
try{
|
||||||
|
File.Delete(target);
|
||||||
|
}catch{
|
||||||
|
// didn't want it deleted anyways
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (args.Error != null){
|
||||||
|
onFailure?.Invoke(args.Error);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
onSuccess?.Invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
client.DownloadFileAsync(new Uri(url), target);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
public static int Scale(int baseValue, double scaleFactor){
|
public static int Scale(int baseValue, double scaleFactor){
|
||||||
return (int)Math.Round(baseValue*scaleFactor);
|
return (int)Math.Round(baseValue*scaleFactor);
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,8 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace TweetLib.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
public static class LocaleUtils{
|
static class LocaleUtils{
|
||||||
// https://cs.chromium.org/chromium/src/third_party/hunspell_dictionaries/
|
// https://cs.chromium.org/chromium/src/third_party/hunspell_dictionaries/
|
||||||
public static IEnumerable<Item> SpellCheckLanguages { get; } = new List<string>{
|
public static IEnumerable<Item> SpellCheckLanguages { get; } = new List<string>{
|
||||||
"af-ZA", "bg-BG", "ca-ES", "cs-CZ", "da-DK", "de-DE",
|
"af-ZA", "bg-BG", "ca-ES", "cs-CZ", "da-DK", "de-DE",
|
||||||
@@ -30,19 +30,11 @@ namespace TweetLib.Core.Utils{
|
|||||||
|
|
||||||
public sealed class Item : IComparable<Item>{
|
public sealed class Item : IComparable<Item>{
|
||||||
public string Code { get; }
|
public string Code { get; }
|
||||||
|
public CultureInfo Info { get; }
|
||||||
|
|
||||||
private string Name => info?.NativeName ?? Code;
|
public Item(string code, string alt = null){
|
||||||
|
|
||||||
private readonly CultureInfo? info;
|
|
||||||
|
|
||||||
public Item(string code, string? alt = null){
|
|
||||||
this.Code = code;
|
this.Code = code;
|
||||||
|
this.Info = CultureInfo.GetCultureInfo(alt ?? code);
|
||||||
try{
|
|
||||||
this.info = CultureInfo.GetCultureInfo(alt ?? code);
|
|
||||||
}catch(CultureNotFoundException){
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj){
|
public override bool Equals(object obj){
|
||||||
@@ -54,16 +46,12 @@ namespace TweetLib.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString(){
|
public override string ToString(){
|
||||||
if (info == null){
|
string capitalizedName = Info.TextInfo.ToTitleCase(Info.NativeName);
|
||||||
return Code;
|
return Info.DisplayName == Info.NativeName ? capitalizedName : $"{capitalizedName}, {Info.DisplayName}";
|
||||||
}
|
|
||||||
|
|
||||||
string capitalizedName = info.TextInfo.ToTitleCase(info.NativeName);
|
|
||||||
return info.DisplayName == info.NativeName ? capitalizedName : $"{capitalizedName}, {info.DisplayName}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CompareTo(Item other){
|
public int CompareTo(Item other){
|
||||||
return string.Compare(Name, other.Name, false, CultureInfo.InvariantCulture);
|
return string.Compare(Info.NativeName, other.Info.NativeName, false, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,8 +2,8 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace TweetLib.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
public static class StringUtils{
|
static class StringUtils{
|
||||||
public static readonly string[] EmptyArray = new string[0];
|
public static readonly string[] EmptyArray = new string[0];
|
||||||
|
|
||||||
public static string ExtractBefore(string str, char search, int startIndex = 0){
|
public static string ExtractBefore(string str, char search, int startIndex = 0){
|
||||||
@@ -23,7 +23,7 @@ namespace TweetLib.Core.Utils{
|
|||||||
return Regex.Replace(str, @"[a-zA-Z]", match => {
|
return Regex.Replace(str, @"[a-zA-Z]", match => {
|
||||||
int code = match.Value[0];
|
int code = match.Value[0];
|
||||||
int start = code <= 90 ? 65 : 97;
|
int start = code <= 90 ? 65 : 97;
|
||||||
return ((char)(start + (code - start + 13) % 26)).ToString();
|
return ((char)(start+(code-start+13)%26)).ToString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@@ -8,9 +8,7 @@ using TweetDuck.Core.Management;
|
|||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
using Cookie = CefSharp.Cookie;
|
using Cookie = CefSharp.Cookie;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
@@ -73,7 +71,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static string GetImageFileName(string url){
|
public static string GetImageFileName(string url){
|
||||||
return UrlUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ViewImage(string url, ImageQuality quality){
|
public static void ViewImage(string url, ImageQuality quality){
|
||||||
@@ -90,7 +88,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
|
|
||||||
string file = Path.Combine(BrowserCache.CacheFolder, GetImageFileName(url) ?? Path.GetRandomFileName());
|
string file = Path.Combine(BrowserCache.CacheFolder, GetImageFileName(url) ?? Path.GetRandomFileName());
|
||||||
|
|
||||||
if (FileUtils.FileExistsAndNotEmpty(file)){
|
if (WindowsUtils.FileExistsAndNotEmpty(file)){
|
||||||
ViewImageInternal(file);
|
ViewImageInternal(file);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -145,7 +143,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void DownloadVideo(string url, string username){
|
public static void DownloadVideo(string url, string username){
|
||||||
string filename = UrlUtils.GetFileNameFromUrl(url);
|
string filename = BrowserUtils.GetFileNameFromUrl(url);
|
||||||
string ext = Path.GetExtension(filename);
|
string ext = Path.GetExtension(filename);
|
||||||
|
|
||||||
using(SaveFileDialog dialog = new SaveFileDialog{
|
using(SaveFileDialog dialog = new SaveFileDialog{
|
||||||
@@ -180,10 +178,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentChrome);
|
BrowserUtils.DownloadFileAsync(url, target, cookieStr, onSuccess, onFailure);
|
||||||
client.Headers[HttpRequestHeader.Cookie] = cookieStr;
|
|
||||||
client.DownloadFileCompleted += WebUtils.FileDownloadCallback(target, onSuccess, onFailure);
|
|
||||||
client.DownloadFileAsync(new Uri(url), target);
|
|
||||||
}, scheduler);
|
}, scheduler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -15,6 +16,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
|
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
|
||||||
|
|
||||||
private static readonly bool IsWindows8OrNewer;
|
private static readonly bool IsWindows8OrNewer;
|
||||||
|
private static bool HasMicrosoftBeenBroughtTo2008Yet;
|
||||||
|
|
||||||
public static int CurrentProcessID { get; }
|
public static int CurrentProcessID { get; }
|
||||||
public static bool ShouldAvoidToolWindow { get; }
|
public static bool ShouldAvoidToolWindow { get; }
|
||||||
@@ -31,6 +33,47 @@ namespace TweetDuck.Core.Utils{
|
|||||||
ShouldAvoidToolWindow = IsWindows8OrNewer;
|
ShouldAvoidToolWindow = IsWindows8OrNewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EnsureTLS12(){
|
||||||
|
if (!HasMicrosoftBeenBroughtTo2008Yet){
|
||||||
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||||
|
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
|
||||||
|
HasMicrosoftBeenBroughtTo2008Yet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateDirectoryForFile(string file){
|
||||||
|
string dir = Path.GetDirectoryName(file);
|
||||||
|
|
||||||
|
if (dir == null){
|
||||||
|
throw new ArgumentException("Invalid file path: "+file);
|
||||||
|
}
|
||||||
|
else if (dir.Length > 0){
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CheckFolderWritePermission(string path){
|
||||||
|
string testFile = Path.Combine(path, ".test");
|
||||||
|
|
||||||
|
try{
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
|
||||||
|
using(File.Create(testFile)){}
|
||||||
|
File.Delete(testFile);
|
||||||
|
return true;
|
||||||
|
}catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool FileExistsAndNotEmpty(string path){
|
||||||
|
try{
|
||||||
|
return new FileInfo(path).Length > 0;
|
||||||
|
}catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
||||||
try{
|
try{
|
||||||
using(Process.Start(new ProcessStartInfo{
|
using(Process.Start(new ProcessStartInfo{
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TweetLib.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetLib.Core.Data{
|
namespace TweetDuck.Data{
|
||||||
public sealed class CombinedFileStream : IDisposable{
|
sealed class CombinedFileStream : IDisposable{
|
||||||
private const char KeySeparator = '|';
|
public const char KeySeparator = '|';
|
||||||
|
|
||||||
private readonly Stream stream;
|
private readonly Stream stream;
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ namespace TweetLib.Core.Data{
|
|||||||
stream.Write(contents, 0, contents.Length);
|
stream.Write(contents, 0, contents.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry? ReadFile(){
|
public Entry ReadFile(){
|
||||||
int nameLength = stream.ReadByte();
|
int nameLength = stream.ReadByte();
|
||||||
|
|
||||||
if (nameLength == -1){
|
if (nameLength == -1){
|
||||||
@@ -64,7 +64,7 @@ namespace TweetLib.Core.Data{
|
|||||||
return new Entry(Encoding.UTF8.GetString(name), contents);
|
return new Entry(Encoding.UTF8.GetString(name), contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? SkipFile(){
|
public string SkipFile(){
|
||||||
int nameLength = stream.ReadByte();
|
int nameLength = stream.ReadByte();
|
||||||
|
|
||||||
if (nameLength == -1){
|
if (nameLength == -1){
|
||||||
@@ -120,7 +120,7 @@ namespace TweetLib.Core.Data{
|
|||||||
|
|
||||||
public void WriteToFile(string path, bool createDirectory){
|
public void WriteToFile(string path, bool createDirectory){
|
||||||
if (createDirectory){
|
if (createDirectory){
|
||||||
FileUtils.CreateDirectoryForFile(path);
|
WindowsUtils.CreateDirectoryForFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllBytes(path, contents);
|
File.WriteAllBytes(path, contents);
|
@@ -2,8 +2,8 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace TweetLib.Core.Collections{
|
namespace TweetDuck.Data{
|
||||||
public sealed class CommandLineArgs{
|
sealed class CommandLineArgs{
|
||||||
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
||||||
CommandLineArgs args = new CommandLineArgs();
|
CommandLineArgs args = new CommandLineArgs();
|
||||||
ReadStringArray(entryChar, array, args);
|
ReadStringArray(entryChar, array, args);
|
||||||
@@ -15,7 +15,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
string entry = array[index];
|
string entry = array[index];
|
||||||
|
|
||||||
if (entry.Length > 0 && entry[0] == entryChar){
|
if (entry.Length > 0 && entry[0] == entryChar){
|
||||||
if (index < array.Length - 1){
|
if (index < array.Length-1){
|
||||||
string potentialValue = array[index+1];
|
string potentialValue = array[index+1];
|
||||||
|
|
||||||
if (potentialValue.Length > 0 && potentialValue[0] == entryChar){
|
if (potentialValue.Length > 0 && potentialValue[0] == entryChar){
|
||||||
@@ -52,7 +52,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
key = matchValue.Substring(0, indexEquals).TrimStart('-');
|
key = matchValue.Substring(0, indexEquals).TrimStart('-');
|
||||||
value = matchValue.Substring(indexEquals + 1).Trim('"');
|
value = matchValue.Substring(indexEquals+1).Trim('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.Length != 0){
|
if (key.Length != 0){
|
||||||
@@ -66,7 +66,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
private readonly HashSet<string> flags = new HashSet<string>();
|
private readonly HashSet<string> flags = new HashSet<string>();
|
||||||
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
||||||
|
|
||||||
public int Count => flags.Count + values.Count;
|
public int Count => flags.Count+values.Count;
|
||||||
|
|
||||||
public void AddFlag(string flag){
|
public void AddFlag(string flag){
|
||||||
flags.Add(flag.ToLower());
|
flags.Add(flag.ToLower());
|
||||||
@@ -84,8 +84,12 @@ namespace TweetLib.Core.Collections{
|
|||||||
values[key.ToLower()] = value;
|
values[key.ToLower()] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? GetValue(string key){
|
public bool HasValue(string key){
|
||||||
return values.TryGetValue(key.ToLower(), out string val) ? val : null;
|
return values.ContainsKey(key.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetValue(string key, string defaultValue){
|
||||||
|
return values.TryGetValue(key.ToLower(), out string val) ? val : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveValue(string key){
|
public void RemoveValue(string key){
|
||||||
@@ -99,7 +103,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
copy.AddFlag(flag);
|
copy.AddFlag(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var kvp in values){
|
foreach(KeyValuePair<string, string> kvp in values){
|
||||||
copy.SetValue(kvp.Key, kvp.Value);
|
copy.SetValue(kvp.Key, kvp.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +115,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
target[flag] = "1";
|
target[flag] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var kvp in values){
|
foreach(KeyValuePair<string, string> kvp in values){
|
||||||
target[kvp.Key] = kvp.Value;
|
target[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,11 +127,11 @@ namespace TweetLib.Core.Collections{
|
|||||||
build.Append(flag).Append(' ');
|
build.Append(flag).Append(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var kvp in values){
|
foreach(KeyValuePair<string, string> kvp in values){
|
||||||
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
|
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Length == 0 ? string.Empty : build.Remove(build.Length - 1, 1).ToString();
|
return build.Length == 0 ? string.Empty : build.Remove(build.Length-1, 1).ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetLib.Core.Data{
|
namespace TweetDuck.Data{
|
||||||
public sealed class InjectedHTML{
|
sealed class InjectedHTML{
|
||||||
public enum Position{
|
public enum Position{
|
||||||
Before, After
|
Before, After
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ namespace TweetLib.Core.Data{
|
|||||||
|
|
||||||
switch(position){
|
switch(position){
|
||||||
case Position.Before: cutIndex = index; break;
|
case Position.Before: cutIndex = index; break;
|
||||||
case Position.After: cutIndex = index + search.Length; break;
|
case Position.After: cutIndex = index+search.Length; break;
|
||||||
default: return targetHTML;
|
default: return targetHTML;
|
||||||
}
|
}
|
||||||
|
|
@@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetLib.Core.Data{
|
namespace TweetDuck.Data{
|
||||||
public sealed class Result<T>{
|
sealed class Result<T>{
|
||||||
public bool HasValue => exception == null;
|
public bool HasValue => exception == null;
|
||||||
|
|
||||||
public T Value => HasValue ? value : throw new InvalidOperationException("Requested value from a failed result.");
|
public T Value => HasValue ? value : throw new InvalidOperationException("Requested value from a failed result.");
|
||||||
public Exception Exception => exception ?? throw new InvalidOperationException("Requested exception from a successful result.");
|
public Exception Exception => exception ?? throw new InvalidOperationException("Requested exception from a successful result.");
|
||||||
|
|
||||||
private readonly T value;
|
private readonly T value;
|
||||||
private readonly Exception? exception;
|
private readonly Exception exception;
|
||||||
|
|
||||||
public Result(T value){
|
public Result(T value){
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@@ -16,7 +16,7 @@ namespace TweetLib.Core.Data{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Result(Exception exception){
|
public Result(Exception exception){
|
||||||
this.value = default!;
|
this.value = default(T);
|
||||||
this.exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
this.exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,12 +25,12 @@ namespace TweetLib.Core.Data{
|
|||||||
onSuccess(value);
|
onSuccess(value);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
onException(exception!);
|
onException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<R> Select<R>(Func<T, R> map){
|
public Result<R> Select<R>(Func<T, R> map){
|
||||||
return HasValue ? new Result<R>(map(value)) : new Result<R>(exception!);
|
return HasValue ? new Result<R>(map(value)) : new Result<R>(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,11 +4,10 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TweetLib.Core.Serialization.Converters;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Serialization{
|
namespace TweetDuck.Data.Serialization{
|
||||||
public sealed class FileSerializer<T>{
|
sealed class FileSerializer<T>{
|
||||||
private const string NewLineReal = "\r\n";
|
private const string NewLineReal = "\r\n";
|
||||||
private const string NewLineCustom = "\r~\n";
|
private const string NewLineCustom = "\r~\n";
|
||||||
|
|
||||||
@@ -50,6 +49,8 @@ namespace TweetLib.Core.Serialization{
|
|||||||
return build.Append(data.Substring(index)).ToString();
|
return build.Append(data.Substring(index)).ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
|
||||||
|
|
||||||
private readonly Dictionary<string, PropertyInfo> props;
|
private readonly Dictionary<string, PropertyInfo> props;
|
||||||
private readonly Dictionary<Type, ITypeConverter> converters;
|
private readonly Dictionary<Type, ITypeConverter> converters;
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ namespace TweetLib.Core.Serialization{
|
|||||||
public void Write(string file, T obj){
|
public void Write(string file, T obj){
|
||||||
LinkedList<string> errors = new LinkedList<string>();
|
LinkedList<string> errors = new LinkedList<string>();
|
||||||
|
|
||||||
FileUtils.CreateDirectoryForFile(file);
|
WindowsUtils.CreateDirectoryForFile(file);
|
||||||
|
|
||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
||||||
@@ -73,10 +74,10 @@ namespace TweetLib.Core.Serialization{
|
|||||||
object value = prop.Value.GetValue(obj);
|
object value = prop.Value.GetValue(obj);
|
||||||
|
|
||||||
if (!converters.TryGetValue(type, out ITypeConverter serializer)){
|
if (!converters.TryGetValue(type, out ITypeConverter serializer)){
|
||||||
serializer = ClrTypeConverter.Instance;
|
serializer = BasicSerializerObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serializer.TryWriteType(type, value, out string? converted)){
|
if (serializer.TryWriteType(type, value, out string converted)){
|
||||||
if (converted != null){
|
if (converted != null){
|
||||||
writer.Write(prop.Key);
|
writer.Write(prop.Key);
|
||||||
writer.Write(' ');
|
writer.Write(' ');
|
||||||
@@ -141,10 +142,10 @@ namespace TweetLib.Core.Serialization{
|
|||||||
|
|
||||||
if (props.TryGetValue(property, out PropertyInfo info)){
|
if (props.TryGetValue(property, out PropertyInfo info)){
|
||||||
if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)){
|
if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)){
|
||||||
serializer = ClrTypeConverter.Instance;
|
serializer = BasicSerializerObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serializer.TryReadType(info.PropertyType, value, out object? converted)){
|
if (serializer.TryReadType(info.PropertyType, value, out object converted)){
|
||||||
info.SetValue(obj, converted);
|
info.SetValue(obj, converted);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -164,5 +165,53 @@ namespace TweetLib.Core.Serialization{
|
|||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
}catch(DirectoryNotFoundException){}
|
}catch(DirectoryNotFoundException){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class BasicTypeConverter : ITypeConverter{
|
||||||
|
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||||
|
switch(Type.GetTypeCode(type)){
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
converted = value.ToString();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case TypeCode.Int32:
|
||||||
|
converted = ((int)value).ToString(); // cast required for enums
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
converted = value?.ToString();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
converted = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
|
||||||
|
switch(Type.GetTypeCode(type)){
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
if (bool.TryParse(value, out bool b)){
|
||||||
|
converted = b;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else goto default;
|
||||||
|
|
||||||
|
case TypeCode.Int32:
|
||||||
|
if (int.TryParse(value, out int i)){
|
||||||
|
converted = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else goto default;
|
||||||
|
|
||||||
|
case TypeCode.String:
|
||||||
|
converted = value;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
converted = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
8
Data/Serialization/ITypeConverter.cs
Normal file
8
Data/Serialization/ITypeConverter.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Data.Serialization{
|
||||||
|
interface ITypeConverter{
|
||||||
|
bool TryWriteType(Type type, object value, out string converted);
|
||||||
|
bool TryReadType(Type type, string value, out object converted);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TweetLib.Core.Serialization{
|
namespace TweetDuck.Data.Serialization{
|
||||||
public sealed class SerializationSoftException : Exception{
|
sealed class SerializationSoftException : Exception{
|
||||||
public IList<string> Errors { get; }
|
public IList<string> Errors { get; }
|
||||||
|
|
||||||
public SerializationSoftException(IList<string> errors) : base(string.Join(Environment.NewLine, errors)){
|
public SerializationSoftException(IList<string> errors) : base(string.Join(Environment.NewLine, errors)){
|
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetLib.Core.Serialization.Converters{
|
namespace TweetDuck.Data.Serialization{
|
||||||
public sealed class SingleTypeConverter<T> : ITypeConverter{
|
sealed class SingleTypeConverter<T> : ITypeConverter{
|
||||||
public Func<T, string> ConvertToString { get; set; }
|
public Func<T, string> ConvertToString { get; set; }
|
||||||
public Func<string, T> ConvertToObject { get; set; }
|
public Func<string, T> ConvertToObject { get; set; }
|
||||||
|
|
||||||
bool ITypeConverter.TryWriteType(Type type, object value, out string? converted){
|
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||||
try{
|
try{
|
||||||
converted = ConvertToString((T)value);
|
converted = ConvertToString((T)value);
|
||||||
return true;
|
return true;
|
||||||
@@ -15,7 +15,7 @@ namespace TweetLib.Core.Serialization.Converters{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ITypeConverter.TryReadType(Type type, string value, out object? converted){
|
bool ITypeConverter.TryReadType(Type type, string value, out object converted){
|
||||||
try{
|
try{
|
||||||
converted = ConvertToObject(value);
|
converted = ConvertToObject(value);
|
||||||
return true;
|
return true;
|
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace TweetLib.Core.Collections{
|
namespace TweetDuck.Data{
|
||||||
public sealed class TwoKeyDictionary<K1, K2, V>{
|
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;
|
||||||
|
|
||||||
@@ -85,8 +85,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value){
|
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value){
|
||||||
@@ -94,7 +93,7 @@ namespace TweetLib.Core.Collections{
|
|||||||
return innerDict.TryGetValue(innerKey, out value);
|
return innerDict.TryGetValue(innerKey, out value);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
value = default!;
|
value = default(V);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetLib.Core.Serialization.Converters;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Utils;
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Data{
|
namespace TweetDuck.Data{
|
||||||
sealed class WindowState{
|
sealed class WindowState{
|
||||||
|
@@ -3,8 +3,7 @@ using System.Drawing;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Controls{
|
namespace TweetDuck.Plugins.Controls{
|
||||||
sealed partial class PluginControl : UserControl{
|
sealed partial class PluginControl : UserControl{
|
||||||
|
@@ -4,15 +4,15 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins.Enums{
|
namespace TweetDuck.Plugins.Enums{
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum PluginEnvironment{
|
enum PluginEnvironment{
|
||||||
None = 0,
|
None = 0,
|
||||||
Browser = 1,
|
Browser = 1,
|
||||||
Notification = 2
|
Notification = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PluginEnvironmentExtensions{
|
static class PluginEnvironmentExtensions{
|
||||||
public static IEnumerable<PluginEnvironment> Values{
|
public static IEnumerable<PluginEnvironment> Values{
|
||||||
get{
|
get{
|
||||||
yield return PluginEnvironment.Browser;
|
yield return PluginEnvironment.Browser;
|
||||||
@@ -24,7 +24,7 @@ namespace TweetLib.Core.Features.Plugins.Enums{
|
|||||||
return environment == PluginEnvironment.Browser;
|
return environment == PluginEnvironment.Browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? GetPluginScriptFile(this PluginEnvironment environment){
|
public static string GetPluginScriptFile(this PluginEnvironment environment){
|
||||||
switch(environment){
|
switch(environment){
|
||||||
case PluginEnvironment.Browser: return "browser.js";
|
case PluginEnvironment.Browser: return "browser.js";
|
||||||
case PluginEnvironment.Notification: return "notification.js";
|
case PluginEnvironment.Notification: return "notification.js";
|
||||||
@@ -73,7 +73,7 @@ namespace TweetLib.Core.Features.Plugins.Enums{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
value = default;
|
value = default(T);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
5
Plugins/Enums/PluginFolder.cs
Normal file
5
Plugins/Enums/PluginFolder.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace TweetDuck.Plugins.Enums{
|
||||||
|
enum PluginFolder{
|
||||||
|
Root, Data
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +1,9 @@
|
|||||||
namespace TweetLib.Core.Features.Plugins.Enums{
|
namespace TweetDuck.Plugins.Enums{
|
||||||
public enum PluginGroup{
|
enum PluginGroup{
|
||||||
Official, Custom
|
Official, Custom
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PluginGroupExtensions{
|
static class PluginGroupExtensions{
|
||||||
public static string GetIdentifierPrefix(this PluginGroup group){
|
public static string GetIdentifierPrefix(this PluginGroup group){
|
||||||
switch(group){
|
switch(group){
|
||||||
case PluginGroup.Official: return "official/";
|
case PluginGroup.Official: return "official/";
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins.Events{
|
namespace TweetDuck.Plugins.Events{
|
||||||
public sealed class PluginChangedStateEventArgs : EventArgs{
|
sealed class PluginChangedStateEventArgs : EventArgs{
|
||||||
public Plugin Plugin { get; }
|
public Plugin Plugin { get; }
|
||||||
public bool IsEnabled { get; }
|
public bool IsEnabled { get; }
|
||||||
|
|
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins.Events{
|
namespace TweetDuck.Plugins.Events{
|
||||||
public sealed class PluginErrorEventArgs : EventArgs{
|
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; }
|
@@ -1,13 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins.Config{
|
|
||||||
public interface IPluginConfig{
|
|
||||||
event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
|
||||||
|
|
||||||
|
namespace TweetDuck.Plugins{
|
||||||
|
interface IPluginConfig{
|
||||||
IEnumerable<string> DisabledPlugins { get; }
|
IEnumerable<string> DisabledPlugins { get; }
|
||||||
void Reset(IEnumerable<string> newDisabledPlugins);
|
|
||||||
|
event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
||||||
|
|
||||||
void SetEnabled(Plugin plugin, bool enabled);
|
void SetEnabled(Plugin plugin, bool enabled);
|
||||||
bool IsEnabled(Plugin plugin);
|
bool IsEnabled(Plugin plugin);
|
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
public sealed class Plugin{
|
sealed class Plugin{
|
||||||
private static readonly Version AppVersion = new Version(Lib.VersionTag);
|
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||||
|
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
public PluginGroup Group { get; }
|
public PluginGroup Group { get; }
|
||||||
@@ -62,7 +62,7 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
|
|
||||||
public string GetScriptPath(PluginEnvironment environment){
|
public string GetScriptPath(PluginEnvironment environment){
|
||||||
if (Environments.HasFlag(environment)){
|
if (Environments.HasFlag(environment)){
|
||||||
string? file = environment.GetPluginScriptFile();
|
string file = environment.GetPluginScriptFile();
|
||||||
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -124,7 +124,7 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
public sealed class Builder{
|
public sealed class Builder{
|
||||||
private static readonly Version DefaultRequiredVersion = new Version(0, 0, 0, 0);
|
private static readonly Version DefaultRequiredVersion = new Version(0, 0, 0, 0);
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; }
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
public string Author { get; set; } = "(anonymous)";
|
public string Author { get; set; } = "(anonymous)";
|
||||||
public string Version { get; set; } = string.Empty;
|
public string Version { get; set; } = string.Empty;
|
||||||
@@ -144,7 +144,7 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
this.group = group;
|
this.group = group;
|
||||||
this.pathRoot = pathRoot;
|
this.pathRoot = pathRoot;
|
||||||
this.pathData = pathData;
|
this.pathData = pathData;
|
||||||
this.identifier = group.GetIdentifierPrefix() + name;
|
this.identifier = group.GetIdentifierPrefix()+name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEnvironment(PluginEnvironment environment){
|
public void AddEnvironment(PluginEnvironment environment){
|
@@ -1,17 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TweetLib.Core.Collections;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Data;
|
using TweetDuck.Data;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
|
||||||
sealed class PluginBridge{
|
sealed class PluginBridge{
|
||||||
private static string SanitizeCacheKey(string key){
|
private static string SanitizeCacheKey(string key){
|
||||||
return key.Replace('\\', '/').Trim();
|
return key.Replace('\\', '/').Trim();
|
||||||
@@ -84,7 +80,7 @@ namespace TweetDuck.Plugins{
|
|||||||
public void WriteFile(int token, string path, string contents){
|
public void WriteFile(int token, string path, string contents){
|
||||||
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
|
||||||
|
|
||||||
FileUtils.CreateDirectoryForFile(fullPath);
|
WindowsUtils.CreateDirectoryForFile(fullPath);
|
||||||
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
File.WriteAllText(fullPath, contents, Encoding.UTF8);
|
||||||
fileCache[token, SanitizeCacheKey(path)] = contents;
|
fileCache[token, SanitizeCacheKey(path)] = contents;
|
||||||
}
|
}
|
||||||
|
@@ -2,35 +2,40 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
public static class PluginLoader{
|
static class PluginLoader{
|
||||||
private static readonly string[] EndTag = { "[END]" };
|
private static readonly string[] EndTag = { "[END]" };
|
||||||
|
|
||||||
public static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group){
|
public static Plugin FromFolder(string path, PluginGroup group){
|
||||||
Plugin.Builder builder = new Plugin.Builder(group, name, pathRoot, pathData);
|
string name = Path.GetFileName(path);
|
||||||
|
|
||||||
foreach(var environment in Directory.EnumerateFiles(pathRoot, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).Select(EnvironmentFromFileName)){
|
if (string.IsNullOrEmpty(name)){
|
||||||
builder.AddEnvironment(environment);
|
throw new ArgumentException("Could not extract directory name from path: "+path);
|
||||||
}
|
}
|
||||||
|
|
||||||
string metaFile = Path.Combine(pathRoot, ".meta");
|
Plugin.Builder builder = new Plugin.Builder(group, name, path, Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name));
|
||||||
|
|
||||||
|
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
||||||
|
builder.AddEnvironment(PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
string metaFile = Path.Combine(path, ".meta");
|
||||||
|
|
||||||
if (!File.Exists(metaFile)){
|
if (!File.Exists(metaFile)){
|
||||||
throw new ArgumentException("Plugin is missing a .meta file");
|
throw new ArgumentException("Plugin is missing a .meta file");
|
||||||
}
|
}
|
||||||
|
|
||||||
string? currentTag = null;
|
string currentTag = null, currentContents = string.Empty;
|
||||||
string currentContents = string.Empty;
|
|
||||||
|
|
||||||
foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
||||||
if (line[0] == '[' && line[line.Length - 1] == ']'){
|
if (line[0] == '[' && line[line.Length-1] == ']'){
|
||||||
if (currentTag != null){
|
if (currentTag != null){
|
||||||
SetProperty(builder, currentTag, currentContents);
|
SetProperty(builder, currentTag, currentContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTag = line.Substring(1, line.Length - 2).ToUpper();
|
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
||||||
currentContents = string.Empty;
|
currentContents = string.Empty;
|
||||||
|
|
||||||
if (line.Equals(EndTag[0])){
|
if (line.Equals(EndTag[0])){
|
||||||
@@ -38,20 +43,16 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentTag != null){
|
else if (currentTag != null){
|
||||||
currentContents = currentContents.Length == 0 ? line : currentContents + Environment.NewLine + line;
|
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
throw new FormatException($"Missing metadata tag before value: {line}");
|
throw new FormatException("Missing metadata tag before value: "+line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.BuildAndSetup();
|
return builder.BuildAndSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PluginEnvironment EnvironmentFromFileName(string file){
|
|
||||||
return PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetProperty(Plugin.Builder builder, string tag, string value){
|
private static void SetProperty(Plugin.Builder builder, string tag, string value){
|
||||||
switch(tag){
|
switch(tag){
|
||||||
case "NAME": builder.Name = value; break;
|
case "NAME": builder.Name = value; break;
|
||||||
@@ -61,8 +62,8 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
case "WEBSITE": builder.Website = value; break;
|
case "WEBSITE": builder.Website = value; break;
|
||||||
case "CONFIGFILE": builder.ConfigFile = value; break;
|
case "CONFIGFILE": builder.ConfigFile = value; break;
|
||||||
case "CONFIGDEFAULT": builder.ConfigDefault = value; break;
|
case "CONFIGDEFAULT": builder.ConfigDefault = value; break;
|
||||||
case "REQUIRES": builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException($"Invalid required minimum version: {value}"); break;
|
case "REQUIRES": builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException("Invalid required minimum version: "+value); break;
|
||||||
default: throw new FormatException($"Invalid metadata tag: {tag}");
|
default: throw new FormatException("Invalid metadata tag: "+tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -6,12 +6,10 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetDuck.Resources;
|
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;
|
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class PluginManager{
|
sealed class PluginManager{
|
||||||
@@ -128,19 +126,12 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
||||||
string name = Path.GetFileName(fullDir);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(name)){
|
|
||||||
loadErrors.Add($"{group.GetIdentifierPrefix()}(?): Could not extract directory name from path: {fullDir}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plugin plugin;
|
Plugin plugin;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
plugin = PluginLoader.FromFolder(name, fullDir, Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name), group);
|
plugin = PluginLoader.FromFolder(fullDir, group);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
loadErrors.Add($"{group.GetIdentifierPrefix()}{name}: {e.Message}");
|
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using TweetLib.Core.Features.Plugins.Config;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
public static class PluginScriptGenerator{
|
static class PluginScriptGenerator{
|
||||||
public static string GenerateConfig(IPluginConfig config){
|
public static string GenerateConfig(IPluginConfig config){
|
||||||
return "window.TD_PLUGINS.disabled = [" + string.Join(",", config.DisabledPlugins.Select(id => '"' + id + '"')) + "]";
|
return "window.TD_PLUGINS.disabled = ["+string.Join(",", config.DisabledPlugins.Select(id => $"\"{id}\""))+"]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
33
Program.cs
33
Program.cs
@@ -2,8 +2,10 @@ using CefSharp;
|
|||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core;
|
using TweetDuck.Core;
|
||||||
@@ -12,17 +14,15 @@ using TweetDuck.Core.Handling.General;
|
|||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core;
|
using TweetDuck.Data;
|
||||||
using TweetLib.Core.Collections;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
static class Program{
|
static class Program{
|
||||||
public const string BrandName = Lib.BrandName;
|
public const string BrandName = "TweetDuck";
|
||||||
public const string VersionTag = Lib.VersionTag;
|
|
||||||
|
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
|
public const string VersionTag = "1.17.3";
|
||||||
|
|
||||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
||||||
|
|
||||||
@@ -48,18 +48,23 @@ namespace TweetDuck{
|
|||||||
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
||||||
private static bool HasCleanedUp;
|
private static bool HasCleanedUp;
|
||||||
|
|
||||||
|
public static CultureInfo Culture { get; }
|
||||||
public static Reporter Reporter { get; }
|
public static Reporter Reporter { get; }
|
||||||
public static ConfigManager Config { get; }
|
public static ConfigManager Config { get; }
|
||||||
|
|
||||||
static Program(){
|
static Program(){
|
||||||
|
Culture = CultureInfo.CurrentCulture;
|
||||||
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
|
||||||
|
#endif
|
||||||
|
|
||||||
Reporter = new Reporter(ErrorLogFilePath);
|
Reporter = new Reporter(ErrorLogFilePath);
|
||||||
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
|
||||||
|
|
||||||
Config = new ConfigManager();
|
Config = new ConfigManager();
|
||||||
|
|
||||||
Lib.Initialize(new App.Builder{
|
|
||||||
ErrorHandler = Reporter
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
@@ -70,7 +75,7 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||||
|
|
||||||
if (!FileUtils.CheckFolderWritePermission(StoragePath)){
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||||
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,7 +131,7 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
|
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze, null));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: "+e.Message, FormMessage.OK);
|
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: "+e.Message, FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
@@ -163,7 +168,7 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
// ProgramPath has a trailing backslash
|
// ProgramPath has a trailing backslash
|
||||||
string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||||
bool runElevated = !IsPortable || !FileUtils.CheckFolderWritePermission(ProgramPath);
|
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||||
|
|
||||||
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
@@ -175,7 +180,7 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDataStoragePath(){
|
private static string GetDataStoragePath(){
|
||||||
string custom = Arguments.GetValue(Arguments.ArgDataFolder);
|
string custom = Arguments.GetValue(Arguments.ArgDataFolder, null);
|
||||||
|
|
||||||
if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){
|
if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){
|
||||||
if (Path.GetInvalidPathChars().Any(custom.Contains)){
|
if (Path.GetInvalidPathChars().Any(custom.Contains)){
|
||||||
|
2
Properties/Resources.Designer.cs
generated
2
Properties/Resources.Designer.cs
generated
@@ -19,7 +19,7 @@ namespace TweetDuck.Properties {
|
|||||||
// class via a tool like ResGen or Visual Studio.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// with the /str option, or rebuild your VS project.
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
internal class Resources {
|
internal class Resources {
|
||||||
|
17
README.md
17
README.md
@@ -1,23 +1,25 @@
|
|||||||
# Support
|
# Support
|
||||||
|
|
||||||
[Follow TweetDuck on Twitter](https://twitter.com/TryMyAwesomeApp) | [Support via Ko-fi](https://ko-fi.com/chylex) | [Support via Patreon](https://www.patreon.com/chylex)
|
[Follow TweetDuck on Twitter](https://twitter.com/TryMyAwesomeApp) | [Support via PayPal](https://paypal.me/chylex) | [Support via Patreon](https://www.patreon.com/chylex)
|
||||||
|
|
||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
The program can be built using Visual Studio 2019. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
||||||
* **.NET desktop development**
|
* **.NET desktop development**
|
||||||
* .NET Framework 4.7.2 SDK
|
* .NET Framework 4 – 4.6 development tools
|
||||||
* F# desktop language support
|
* F# desktop language support
|
||||||
* **Desktop development with C++**
|
* **Desktop development with C++**
|
||||||
* MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.20)
|
* VC++ 2017 latest v141 tools
|
||||||
|
|
||||||
After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
|
After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
|
||||||
```
|
```
|
||||||
PM> Install-Package CefSharp.WinForms -Version 67.0.0
|
PM> Install-Package CefSharp.WinForms -Version 67.0.0-pre01
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
|
The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
|
||||||
@@ -42,6 +44,11 @@ If you decide to publicly release a custom version, please make it clear that it
|
|||||||
- Some files are checked for invalid characters:
|
- Some files are checked for invalid characters:
|
||||||
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
||||||
|
|
||||||
|
#### Error: The "EmbedAllSources" parameter is not supported by the "Csc" task
|
||||||
|
1. Open `C:\Program Files (x86)\Visual Studio\2017\<edition>\MSBuild\15.0\Bin\Roslyn\Microsoft.CSharp.Core.targets` in a text editor
|
||||||
|
2. Remove line that says `EmbedAllSources="$(EmbedAllSources)"`
|
||||||
|
3. Hope the next Visual Studio update fixes it...
|
||||||
|
|
||||||
### Installers
|
### Installers
|
||||||
|
|
||||||
TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
||||||
|
14
Reporter.cs
14
Reporter.cs
@@ -6,11 +6,9 @@ using System.Text;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetLib.Core;
|
|
||||||
using TweetLib.Core.Application;
|
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
sealed class Reporter : IAppErrorHandler{
|
sealed class Reporter{
|
||||||
private readonly string logFile;
|
private readonly string logFile;
|
||||||
|
|
||||||
public Reporter(string logFile){
|
public Reporter(string logFile){
|
||||||
@@ -30,12 +28,8 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool LogImportant(string data){
|
public bool LogImportant(string data){
|
||||||
return ((IAppErrorHandler)this).Log(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IAppErrorHandler.Log(string text){
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Debug.WriteLine(text);
|
Debug.WriteLine(data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
@@ -44,8 +38,8 @@ namespace TweetDuck{
|
|||||||
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
|
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
build.Append("[").Append(DateTime.Now.ToString("G", Lib.Culture)).Append("]\r\n");
|
build.Append("[").Append(DateTime.Now.ToString("G", Program.Culture)).Append("]\r\n");
|
||||||
build.Append(text).Append("\r\n\r\n");
|
build.Append(data).Append("\r\n\r\n");
|
||||||
|
|
||||||
try{
|
try{
|
||||||
File.AppendAllText(logFile, build.ToString(), Encoding.UTF8);
|
File.AppendAllText(logFile, build.ToString(), Encoding.UTF8);
|
||||||
|
@@ -87,9 +87,9 @@ enabled(){
|
|||||||
// update UI
|
// update UI
|
||||||
|
|
||||||
this.btnClearAllHTML = `
|
this.btnClearAllHTML = `
|
||||||
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--16 padding-v--2" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
|
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
|
||||||
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
|
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
|
||||||
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--14 app-nav-link-text">Clear columns</div>
|
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--16 app-nav-link-text">Clear columns</div>
|
||||||
</a>`;
|
</a>`;
|
||||||
|
|
||||||
this.btnClearOneHTML = `
|
this.btnClearOneHTML = `
|
||||||
@@ -123,7 +123,6 @@ enabled(){
|
|||||||
this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
|
this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
|
||||||
this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
|
this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
|
||||||
this.css.insert(".column-navigator-overflow { bottom: 224px !important; }");
|
this.css.insert(".column-navigator-overflow { bottom: 224px !important; }");
|
||||||
this.css.insert(".app-navigator .clear-columns-btn-all-parent { font-weight: 700; }");
|
|
||||||
|
|
||||||
this.css.insert(".column-header-links { min-width: 51px !important; }");
|
this.css.insert(".column-header-links { min-width: 51px !important; }");
|
||||||
this.css.insert(".column[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
|
this.css.insert(".column[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
|
||||||
|
@@ -8,7 +8,6 @@ enabled(){
|
|||||||
_theme: "light",
|
_theme: "light",
|
||||||
themeOverride: false,
|
themeOverride: false,
|
||||||
columnWidth: "310px",
|
columnWidth: "310px",
|
||||||
composerWidth: "default",
|
|
||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
hideTweetActions: true,
|
hideTweetActions: true,
|
||||||
moveTweetActionsToRight: true,
|
moveTweetActionsToRight: true,
|
||||||
@@ -381,15 +380,8 @@ enabled(){
|
|||||||
this.css.insert("#general_settings .cf { display: none !important }");
|
this.css.insert("#general_settings .cf { display: none !important }");
|
||||||
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
|
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
|
||||||
|
|
||||||
this.css.insert(`html[data-td-font] { font-size: ${this.config.fontSize} !important }`);
|
this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
|
||||||
this.css.insert(`.avatar { border-radius: ${this.config.avatarRadius}% !important }`);
|
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
||||||
|
|
||||||
if (this.config.composerWidth !== "default"){
|
|
||||||
const width = this.config.composerWidth;
|
|
||||||
this.css.insert(`.js-app-content.is-open { margin-right: ${width} !important; transform: translateX(${width}) !important }`);
|
|
||||||
this.css.insert(`#tduck .js-app-content.tduck-is-opening { margin-right: 0 !important }`);
|
|
||||||
this.css.insert(`.js-drawer { width: ${width} !important; left: -${width} !important }`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentTheme = TD.settings.getTheme();
|
let currentTheme = TD.settings.getTheme();
|
||||||
|
|
||||||
@@ -646,13 +638,6 @@ ${notificationScrollbarColor ? `
|
|||||||
$(".js-dropdown.pos-r").toggleClass("pos-r pos-l");
|
$(".js-dropdown.pos-r").toggleClass("pos-r pos-l");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.uiDrawerActiveEvent = (e, data) => {
|
|
||||||
return if data.activeDrawer === null || this.config.composerWidth === "default";
|
|
||||||
|
|
||||||
const ele = $(".js-app-content").addClass("tduck-is-opening");
|
|
||||||
setTimeout(() => ele.removeClass("tduck-is-opening"), 250);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
@@ -662,7 +647,6 @@ ready(){
|
|||||||
|
|
||||||
// layout events
|
// layout events
|
||||||
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||||
$(document).on("uiDrawerActive", this.uiDrawerActiveEvent);
|
|
||||||
|
|
||||||
// modal
|
// modal
|
||||||
$("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent);
|
$("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent);
|
||||||
@@ -725,12 +709,10 @@ disabled(){
|
|||||||
window.clearTimeout(this.optimizationTimer);
|
window.clearTimeout(this.optimizationTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||||
$(window).off("focus", this.onWindowFocusEvent);
|
$(window).off("focus", this.onWindowFocusEvent);
|
||||||
$(window).off("blur", this.onWindowBlurEvent);
|
$(window).off("blur", this.onWindowBlurEvent);
|
||||||
|
|
||||||
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
|
||||||
$(document).off("uiDrawerActive", this.uiDrawerActiveEvent);
|
|
||||||
|
|
||||||
TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo;
|
TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo;
|
||||||
TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab;
|
TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab;
|
||||||
|
|
||||||
|
@@ -55,20 +55,6 @@
|
|||||||
<option disabled></option>
|
<option disabled></option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- COMPOSER SIZE -->
|
|
||||||
|
|
||||||
<label class="txt-uppercase touch-larger-label">
|
|
||||||
<b>New Tweet panel</b>
|
|
||||||
</label>
|
|
||||||
<select data-td-key="composerWidth">
|
|
||||||
<option value="default">Default</option>
|
|
||||||
<option value="270px">Narrow (270px)</option>
|
|
||||||
<option value="310px">Medium (310px)</option>
|
|
||||||
<option value="350px">Wide (350px)</option>
|
|
||||||
<option value="custom-px">Custom</option>
|
|
||||||
<option value="change-custom-px">Change custom value...</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- FONT SIZE -->
|
<!-- FONT SIZE -->
|
||||||
|
|
||||||
<label class="txt-uppercase touch-larger-label">
|
<label class="txt-uppercase touch-larger-label">
|
||||||
@@ -83,6 +69,10 @@
|
|||||||
<option value="custom-px">Custom</option>
|
<option value="custom-px">Custom</option>
|
||||||
<option value="change-custom-px">Change custom value...</option>
|
<option value="change-custom-px">Change custom value...</option>
|
||||||
</select>
|
</select>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input data-td-key="forceArialFont" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
|
Use Arial as default font
|
||||||
|
</label>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input data-td-key="increaseQuoteTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="increaseQuoteTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Increase quoted tweet font size
|
Increase quoted tweet font size
|
||||||
@@ -126,10 +116,6 @@
|
|||||||
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Theme color tweaks
|
Theme color tweaks
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
|
||||||
<input data-td-key="forceArialFont" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
|
||||||
Use Arial as default font
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!-- ADVANCED -->
|
<!-- ADVANCED -->
|
||||||
|
|
||||||
@@ -186,7 +172,7 @@
|
|||||||
|
|
||||||
#edit-design-panel {
|
#edit-design-panel {
|
||||||
width: 693px;
|
width: 693px;
|
||||||
height: 424px;
|
height: 380px;
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
box-shadow: 0 0 10px rgba(17, 17, 17, 0.5);
|
box-shadow: 0 0 10px rgba(17, 17, 17, 0.5);
|
||||||
}
|
}
|
||||||
@@ -231,7 +217,7 @@
|
|||||||
|
|
||||||
#edit-design-panel-content label.radio {
|
#edit-design-panel-content label.radio {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 16px 0 4px;
|
margin: 0 16px 5px 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -804,8 +804,6 @@ html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(85%)brightnes
|
|||||||
html.dark .tweet>.color-twitter-blue{color:#8bd!important}
|
html.dark .tweet>.color-twitter-blue{color:#8bd!important}
|
||||||
html.dark .hw-card-container>div{border-color:#292F33;background:transparent}
|
html.dark .hw-card-container>div{border-color:#292F33;background:transparent}
|
||||||
html.dark .hw-card-container>div>div{border-color:#292F33}
|
html.dark .hw-card-container>div>div{border-color:#292F33}
|
||||||
html.dark .modal-content,html.dark .lst-group,html.dark #actions-modal{color:#292F33}
|
html.dark .modal-content,html.dark .lst-group{color:#292F33}
|
||||||
html.dark #actions-modal .account-link{color:#38444d}
|
|
||||||
html.dark .mdl-accent{background-color:transparent}
|
|
||||||
html.dark .lst-launcher a span{color:#657786!important}
|
html.dark .lst-launcher a span{color:#657786!important}
|
||||||
html.dark .social-proof-names a{color:#3b94d9}
|
html.dark .social-proof-names a{color:#3b94d9}
|
||||||
|
@@ -56,13 +56,11 @@ enabled(){
|
|||||||
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
|
window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
|
||||||
|
|
||||||
this.getDrawerInput = () => {
|
let maybeDockedComposePanel = $(".js-docked-compose");
|
||||||
return $(".js-compose-text", me.composeDrawer);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getDrawerScroller = () => {
|
if (maybeDockedComposePanel.length){
|
||||||
return $(".js-compose-scroller > .scroll-v", me.composeDrawer);
|
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
||||||
};
|
}
|
||||||
|
|
||||||
// keyboard generation
|
// keyboard generation
|
||||||
|
|
||||||
@@ -81,12 +79,12 @@ enabled(){
|
|||||||
|
|
||||||
this.currentKeywords = [];
|
this.currentKeywords = [];
|
||||||
|
|
||||||
this.getDrawerScroller().trigger("scroll");
|
this.composePanelScroller.trigger("scroll");
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||||
|
|
||||||
if (refocus){
|
if (refocus){
|
||||||
this.getDrawerInput().focus();
|
this.composeInput.focus();
|
||||||
|
|
||||||
if (lastEmojiKeyword && lastEmojiPosition === 0){
|
if (lastEmojiKeyword && lastEmojiPosition === 0){
|
||||||
document.execCommand("insertText", false, lastEmojiKeyword);
|
document.execCommand("insertText", false, lastEmojiKeyword);
|
||||||
@@ -231,16 +229,16 @@ enabled(){
|
|||||||
this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px";
|
this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px";
|
||||||
$(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner);
|
$(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner);
|
||||||
|
|
||||||
this.getDrawerScroller().trigger("scroll");
|
this.composePanelScroller.trigger("scroll");
|
||||||
};
|
};
|
||||||
|
|
||||||
const getKeyboardTop = () => {
|
const getKeyboardTop = () => {
|
||||||
let button = $(".emoji-keyboard-popup-btn");
|
let button = $(".emoji-keyboard-popup-btn");
|
||||||
return button.offset().top + button.outerHeight() + me.getDrawerScroller().scrollTop() + 8;
|
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertEmoji = (src, alt) => {
|
const insertEmoji = (src, alt) => {
|
||||||
let input = this.getDrawerInput();
|
let input = this.composeInput;
|
||||||
|
|
||||||
let val = input.val();
|
let val = input.val();
|
||||||
let posStart = input[0].selectionStart;
|
let posStart = input[0].selectionStart;
|
||||||
@@ -380,11 +378,6 @@ enabled(){
|
|||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.composerActiveEvent = function(e){
|
|
||||||
$(".emoji-keyboard-popup-btn", me.composeDrawer).on("click", me.emojiKeyboardButtonClickEvent);
|
|
||||||
$(".js-docked-compose .js-compose-scroller > .scroll-v", me.composeDrawer).on("scroll", me.composerScrollEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.documentClickEvent = function(e){
|
this.documentClickEvent = function(e){
|
||||||
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
|
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
@@ -403,26 +396,20 @@ enabled(){
|
|||||||
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// re-enabling
|
|
||||||
|
|
||||||
let maybeDockedComposePanel = $(".js-docked-compose");
|
|
||||||
|
|
||||||
if (maybeDockedComposePanel.length){
|
|
||||||
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
|
||||||
this.composerActiveEvent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
this.composeDrawer = $(".js-drawer[data-drawer='compose']");
|
this.composeDrawer = $("[data-drawer='compose']");
|
||||||
|
this.composeInput = $(".js-compose-text", ".js-docked-compose");
|
||||||
this.composeSelector = ".js-compose-text,.js-reply-tweetbox";
|
this.composeSelector = ".js-compose-text,.js-reply-tweetbox";
|
||||||
|
|
||||||
|
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
|
||||||
|
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
||||||
|
|
||||||
|
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
||||||
$(document).on("click", this.documentClickEvent);
|
$(document).on("click", this.documentClickEvent);
|
||||||
$(document).on("keydown", this.documentKeyEvent);
|
$(document).on("keydown", this.documentKeyEvent);
|
||||||
$(document).on("tduckOldComposerActive", this.composerActiveEvent);
|
|
||||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
|
||||||
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
$(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
$(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
||||||
@@ -542,13 +529,14 @@ disabled(){
|
|||||||
$(this.currentSpanner).remove();
|
$(this.currentSpanner).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
||||||
|
|
||||||
|
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
||||||
$(".emoji-keyboard-popup-btn").remove();
|
$(".emoji-keyboard-popup-btn").remove();
|
||||||
|
|
||||||
$(document).off("click", this.documentClickEvent);
|
$(document).off("click", this.documentClickEvent);
|
||||||
$(document).off("keydown", this.documentKeyEvent);
|
$(document).off("keydown", this.documentKeyEvent);
|
||||||
$(document).off("tduckOldComposerActive", this.composerActiveEvent);
|
|
||||||
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
|
||||||
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
|
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
$(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
$(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
||||||
|
@@ -376,25 +376,22 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.drawerToggleEvent = function(e, data){
|
this.drawerToggleEvent = function(e, data){
|
||||||
if (typeof data === "undefined" || data.activeDrawer !== "compose"){
|
if (data.activeDrawer === null){
|
||||||
hideTemplateModal();
|
hideTemplateModal();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
$(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
|
$(".manage-templates-btn").on("click", this.manageTemplatesButtonClickEvent);
|
||||||
$(document).on("uiDrawerActive", this.drawerToggleEvent);
|
$(document).on("uiDrawerActive", this.drawerToggleEvent);
|
||||||
$(document).on("click", ".js-new-composer-opt-in", this.drawerToggleEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled(){
|
disabled(){
|
||||||
$(".manage-templates-btn").remove();
|
$(".manage-templates-btn").remove();
|
||||||
$("#templates-modal-wrap").remove();
|
$("#templates-modal-wrap").remove();
|
||||||
|
|
||||||
$(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
|
|
||||||
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
||||||
$(document).off("click", ".js-new-composer-opt-in", this.drawerToggleEvent);
|
|
||||||
|
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
||||||
}
|
}
|
||||||
|
@@ -477,19 +477,6 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
|
||||||
// Block: Hook into composer event.
|
|
||||||
//
|
|
||||||
execSafe(function hookComposerEvents(){
|
|
||||||
$(document).on("uiDrawerActive uiRwebComposerOptOut", function(e, data){
|
|
||||||
return if e.type === "uiDrawerActive" && data.activeDrawer !== "compose";
|
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
$(document).trigger("tduckOldComposerActive");
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Add TweetDuck buttons to the settings menu.
|
// Block: Add TweetDuck buttons to the settings menu.
|
||||||
//
|
//
|
||||||
@@ -573,7 +560,7 @@
|
|||||||
execSafe(function setupShortenerBypass(){
|
execSafe(function setupShortenerBypass(){
|
||||||
$(document.body).delegate("a[data-full-url]", "click auxclick", function(e){
|
$(document.body).delegate("a[data-full-url]", "click auxclick", function(e){
|
||||||
if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick
|
if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick
|
||||||
$TD.openBrowser($(this).attr("data-full-url"));
|
$TD.openBrowser($(this).attr("data-full-url")); // TODO detect rel="tweet"?
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -768,28 +755,35 @@
|
|||||||
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||||
};
|
};
|
||||||
|
|
||||||
app.delegate("section.js-column", {
|
const updateContextInfo = function(chirp){
|
||||||
contextmenu: function(){
|
let quote = chirp.quotedTweet;
|
||||||
let hovered = getHoveredTweet();
|
|
||||||
return if !hovered;
|
|
||||||
|
|
||||||
let tweet = hovered.obj;
|
if (chirp.chirpType === TD.services.ChirpBase.TWEET){
|
||||||
let quote = tweet.quotedTweet;
|
let tweetUrl = chirp.getChirpURL();
|
||||||
|
|
||||||
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
|
||||||
let tweetUrl = tweet.getChirpURL();
|
|
||||||
let quoteUrl = quote && quote.getChirpURL();
|
let quoteUrl = quote && quote.getChirpURL();
|
||||||
|
|
||||||
let chirpAuthors = quote ? [ tweet.getMainUser().screenName, quote.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
let chirpAuthors = quote ? [ chirp.getMainUser().screenName, quote.getMainUser().screenName ].join(";") : chirp.getMainUser().screenName;
|
||||||
let chirpImages = tweet.hasImage() ? processMedia(tweet) : quote && quote.hasImage() ? processMedia(quote) : "";
|
let chirpImages = chirp.hasImage() ? processMedia(chirp) : quote && quote.hasImage() ? processMedia(quote) : "";
|
||||||
|
|
||||||
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
||||||
}
|
}
|
||||||
else if (tweet instanceof TD.services.TwitterActionFollow){
|
else if (chirp instanceof TD.services.TwitterActionFollow){
|
||||||
$TD.setRightClickedLink("link", tweet.following.getProfileURL());
|
$TD.setRightClickedLink("link", chirp.following.getProfileURL());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
app.delegate("section.js-column", {
|
||||||
|
contextmenu: function(){
|
||||||
|
let hovered = getHoveredTweet();
|
||||||
|
hovered && updateContextInfo(hovered.obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (ensurePropertyExists(TD, "services", "TwitterStatus", "prototype", "renderInMediaGallery")){
|
||||||
|
TD.services.TwitterStatus.prototype.renderInMediaGallery = appendToFunction(TD.services.TwitterStatus.prototype.renderInMediaGallery, function(){
|
||||||
|
updateContextInfo(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -823,7 +817,7 @@
|
|||||||
html.addClass($(document.documentElement).attr("class"));
|
html.addClass($(document.documentElement).attr("class"));
|
||||||
html.addClass($(document.body).attr("class"));
|
html.addClass($(document.body).attr("class"));
|
||||||
|
|
||||||
html.css("background-color", getClassStyleProperty("stream-item", "background-color"));
|
html.css("background-color", getClassStyleProperty("column", "background-color"));
|
||||||
html.css("border", "none");
|
html.css("border", "none");
|
||||||
|
|
||||||
for(let selector of [ ".js-quote-detail", ".js-media-preview-container", ".js-media" ]){
|
for(let selector of [ ".js-quote-detail", ".js-media-preview-container", ".js-media" ]){
|
||||||
@@ -1052,16 +1046,14 @@
|
|||||||
// Block: Refocus the textbox after switching accounts.
|
// Block: Refocus the textbox after switching accounts.
|
||||||
//
|
//
|
||||||
onAppReady.push(function setupAccountSwitchRefocus(){
|
onAppReady.push(function setupAccountSwitchRefocus(){
|
||||||
|
const composeInput = $$(".js-compose-text", ".js-docked-compose");
|
||||||
|
|
||||||
const refocusInput = function(){
|
const refocusInput = function(){
|
||||||
$$(".js-compose-text", ".js-docked-compose").focus();
|
composeInput.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const accountItemClickEvent = function(e){
|
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", function(e){
|
||||||
setTimeout(refocusInput, 0);
|
setTimeout(refocusInput, 0);
|
||||||
};
|
|
||||||
|
|
||||||
$(document).on("tduckOldComposerActive", function(e){
|
|
||||||
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", accountItemClickEvent);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1317,9 +1309,14 @@
|
|||||||
//
|
//
|
||||||
// Block: Add a pin icon to make tweet compose drawer stay open.
|
// Block: Add a pin icon to make tweet compose drawer stay open.
|
||||||
//
|
//
|
||||||
execSafe(function setupStayOpenPin(){
|
onAppReady.push(function setupStayOpenPin(){
|
||||||
$(document).on("tduckOldComposerActive", function(e){
|
let ele = $(`
|
||||||
let ele = $(`#import "markup/pin.html"`).appendTo(".js-docked-compose .js-compose-header");
|
<svg id="td-compose-drawer-pin" viewBox="0 0 24 24" class="icon js-show-tip" data-original-title="Stay open" data-tooltip-position="left">
|
||||||
|
<path d="M9.884,16.959l3.272,0.001l-0.82,4.568l-1.635,0l-0.817,-4.569Z"/>
|
||||||
|
<rect x="8.694" y="7.208" width="5.652" height="7.445"/>
|
||||||
|
<path d="M16.877,17.448c0,-1.908 -1.549,-3.456 -3.456,-3.456l-3.802,0c-1.907,0 -3.456,1.548 -3.456,3.456l10.714,0Z"/>
|
||||||
|
<path d="M6.572,5.676l2.182,2.183l5.532,0l2.182,-2.183l0,-1.455l-9.896,0l0,1.455Z"/>
|
||||||
|
</svg>`).appendTo(".js-docked-compose .js-compose-header");
|
||||||
|
|
||||||
ele.click(function(){
|
ele.click(function(){
|
||||||
if (TD.settings.getComposeStayOpen()){
|
if (TD.settings.getComposeStayOpen()){
|
||||||
@@ -1336,7 +1333,6 @@
|
|||||||
ele.css("transform", "rotate(90deg)");
|
ele.css("transform", "rotate(90deg)");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Make temporary search column appear as the first one and clear the input box.
|
// Block: Make temporary search column appear as the first one and clear the input box.
|
||||||
@@ -1516,8 +1512,6 @@
|
|||||||
//
|
//
|
||||||
if (ensurePropertyExists(TD, "components", "ConversationDetailView", "prototype", "showChirp")){
|
if (ensurePropertyExists(TD, "components", "ConversationDetailView", "prototype", "showChirp")){
|
||||||
TD.components.ConversationDetailView.prototype.showChirp = appendToFunction(TD.components.ConversationDetailView.prototype.showChirp, function(){
|
TD.components.ConversationDetailView.prototype.showChirp = appendToFunction(TD.components.ConversationDetailView.prototype.showChirp, function(){
|
||||||
return if !$TDX.focusDmInput;
|
|
||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
$(".js-reply-tweetbox").first().focus();
|
$(".js-reply-tweetbox").first().focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
<svg id="td-compose-drawer-pin" viewBox="0 0 24 24" class="icon js-show-tip" data-original-title="Stay open" data-tooltip-position="left">
|
|
||||||
<path d="M9.884,16.959l3.272,0.001l-0.82,4.568l-1.635,0l-0.817,-4.569Z"/>
|
|
||||||
<rect x="8.694" y="7.208" width="5.652" height="7.445"/>
|
|
||||||
<path d="M16.877,17.448c0,-1.908 -1.549,-3.456 -3.456,-3.456l-3.802,0c-1.907,0 -3.456,1.548 -3.456,3.456l10.714,0Z"/>
|
|
||||||
<path d="M6.572,5.676l2.182,2.183l5.532,0l2.182,-2.183l0,-1.455l-9.896,0l0,1.455Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 488 B |
@@ -249,14 +249,10 @@ a[data-full-url] {
|
|||||||
transition: transform 0.1s ease;
|
transition: transform 0.1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.js-docked-compose .compose-remember-state {
|
.js-docked-compose footer {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.js-new-composer-opt-in {
|
|
||||||
border-bottom: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose-content {
|
.compose-content {
|
||||||
bottom: 0 !important;
|
bottom: 0 !important;
|
||||||
}
|
}
|
||||||
@@ -373,25 +369,14 @@ html[data-td-font='smallest'] .badge-verified:before {
|
|||||||
|
|
||||||
html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
||||||
/* fix cut off badge in detail view */
|
/* fix cut off badge in detail view */
|
||||||
width: 14px !important;
|
width: 13px !important;
|
||||||
height: 14px !important;
|
height: 14px !important;
|
||||||
background-position: -223px -97px !important;
|
background-position: -223px -97px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
|
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
|
||||||
/* fix cut off badge in follow chirps and detail view */
|
/* fix cut off badge in follow chirps */
|
||||||
margin-top: -1px !important;
|
margin-top: -7px !important;
|
||||||
min-width: 14px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html[data-td-font='smallest'] .tweet-detail-wrapper .fullname-badged:before {
|
|
||||||
/* fix misaligned badge in detail view */
|
|
||||||
margin-top: -2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html[data-td-font='small'] .tweet-detail-wrapper .fullname-badged:before {
|
|
||||||
/* fix misaligned badge in detail view */
|
|
||||||
margin-top: 0px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboard-shortcut-list {
|
.keyboard-shortcut-list {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
|
<Import Project="packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
|
||||||
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" />
|
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" />
|
||||||
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" />
|
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" />
|
||||||
<Import Project="packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" />
|
<Import Project="packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" />
|
||||||
@@ -14,8 +14,7 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>TweetDuck</RootNamespace>
|
<RootNamespace>TweetDuck</RootNamespace>
|
||||||
<AssemblyName>TweetDuck</AssemblyName>
|
<AssemblyName>TweetDuck</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
<ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon>
|
||||||
@@ -34,6 +33,7 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
@@ -54,7 +55,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Configuration\Arguments.cs" />
|
<Compile Include="Configuration\Arguments.cs" />
|
||||||
|
<Compile Include="Configuration\Instance\FileConfigInstance.cs" />
|
||||||
<Compile Include="Configuration\ConfigManager.cs" />
|
<Compile Include="Configuration\ConfigManager.cs" />
|
||||||
|
<Compile Include="Configuration\Instance\IConfigInstance.cs" />
|
||||||
|
<Compile Include="Configuration\Instance\PluginConfigInstance.cs" />
|
||||||
<Compile Include="Configuration\LockManager.cs" />
|
<Compile Include="Configuration\LockManager.cs" />
|
||||||
<Compile Include="Configuration\SystemConfig.cs" />
|
<Compile Include="Configuration\SystemConfig.cs" />
|
||||||
<Compile Include="Configuration\UserConfig.cs" />
|
<Compile Include="Configuration\UserConfig.cs" />
|
||||||
@@ -194,7 +198,10 @@
|
|||||||
<DependentUpon>TabSettingsFeedback.cs</DependentUpon>
|
<DependentUpon>TabSettingsFeedback.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\TweetDeckBrowser.cs" />
|
<Compile Include="Core\TweetDeckBrowser.cs" />
|
||||||
|
<Compile Include="Core\Utils\LocaleUtils.cs" />
|
||||||
|
<Compile Include="Core\Utils\StringUtils.cs" />
|
||||||
<Compile Include="Core\Utils\TwitterUtils.cs" />
|
<Compile Include="Core\Utils\TwitterUtils.cs" />
|
||||||
|
<Compile Include="Data\CombinedFileStream.cs" />
|
||||||
<Compile Include="Core\Management\ProfileManager.cs" />
|
<Compile Include="Core\Management\ProfileManager.cs" />
|
||||||
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
|
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
@@ -224,11 +231,19 @@
|
|||||||
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Notification\Screenshot\ScreenshotBridge.cs" />
|
<Compile Include="Core\Notification\Screenshot\ScreenshotBridge.cs" />
|
||||||
|
<Compile Include="Data\CommandLineArgs.cs" />
|
||||||
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
|
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
|
||||||
<Compile Include="Data\ResourceLink.cs" />
|
<Compile Include="Data\ResourceLink.cs" />
|
||||||
|
<Compile Include="Data\Result.cs" />
|
||||||
|
<Compile Include="Data\Serialization\FileSerializer.cs" />
|
||||||
|
<Compile Include="Data\InjectedHTML.cs" />
|
||||||
|
<Compile Include="Data\Serialization\ITypeConverter.cs" />
|
||||||
|
<Compile Include="Data\Serialization\SerializationSoftException.cs" />
|
||||||
|
<Compile Include="Data\Serialization\SingleTypeConverter.cs" />
|
||||||
|
<Compile Include="Data\TwoKeyDictionary.cs" />
|
||||||
<Compile Include="Data\WindowState.cs" />
|
<Compile Include="Data\WindowState.cs" />
|
||||||
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
||||||
<Compile Include="Core\Bridge\TweetDeckBridge.cs" />
|
<Compile Include="Core\Bridge\TweetDeckBridge.cs" />
|
||||||
@@ -247,15 +262,25 @@
|
|||||||
<Compile Include="Plugins\Controls\PluginListFlowLayout.cs">
|
<Compile Include="Plugins\Controls\PluginListFlowLayout.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Plugins\Enums\PluginFolder.cs" />
|
||||||
|
<Compile Include="Plugins\IPluginConfig.cs" />
|
||||||
|
<Compile Include="Plugins\Plugin.cs" />
|
||||||
|
<Compile Include="Plugins\Events\PluginChangedStateEventArgs.cs" />
|
||||||
<Compile Include="Plugins\PluginBridge.cs" />
|
<Compile Include="Plugins\PluginBridge.cs" />
|
||||||
<Compile Include="Configuration\PluginConfig.cs" />
|
<Compile Include="Configuration\PluginConfig.cs" />
|
||||||
|
<Compile Include="Plugins\Enums\PluginEnvironment.cs" />
|
||||||
|
<Compile Include="Plugins\Enums\PluginGroup.cs" />
|
||||||
|
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
|
||||||
|
<Compile Include="Plugins\PluginLoader.cs" />
|
||||||
<Compile Include="Plugins\PluginManager.cs" />
|
<Compile Include="Plugins\PluginManager.cs" />
|
||||||
|
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Reporter.cs" />
|
<Compile Include="Reporter.cs" />
|
||||||
|
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
|
||||||
<Compile Include="Updates\FormUpdateDownload.cs">
|
<Compile Include="Updates\FormUpdateDownload.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -272,6 +297,9 @@
|
|||||||
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
||||||
<Compile Include="Core\Utils\NativeMethods.cs" />
|
<Compile Include="Core\Utils\NativeMethods.cs" />
|
||||||
<Compile Include="Updates\UpdateCheckClient.cs" />
|
<Compile Include="Updates\UpdateCheckClient.cs" />
|
||||||
|
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
||||||
|
<Compile Include="Updates\UpdateHandler.cs" />
|
||||||
|
<Compile Include="Updates\UpdateInfo.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Resources\ScriptLoader.cs" />
|
<Compile Include="Resources\ScriptLoader.cs" />
|
||||||
@@ -352,10 +380,6 @@
|
|||||||
<None Include="Resources\Scripts\update.js" />
|
<None Include="Resources\Scripts\update.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="lib\TweetLib.Core\TweetLib.Core.csproj">
|
|
||||||
<Project>{93ba3cb4-a812-4949-b07d-8d393fb38937}</Project>
|
|
||||||
<Name>TweetLib.Core</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
|
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
|
||||||
<Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project>
|
<Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project>
|
||||||
<Name>TweetDuck.Browser</Name>
|
<Name>TweetDuck.Browser</Name>
|
||||||
@@ -407,7 +431,7 @@ IF EXIST "$(ProjectDir)bld\post_build.exe" (
|
|||||||
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets'))" />
|
||||||
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props'))" />
|
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" />
|
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" />
|
||||||
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" />
|
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" />
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 16.0.28729.10
|
VisualStudioVersion = 15.0.27130.2027
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -14,8 +14,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetTest.System", "lib\Twe
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TweetTest.Unit", "lib\TweetTest.Unit\TweetTest.Unit.fsproj", "{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}"
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TweetTest.Unit", "lib\TweetTest.Unit\TweetTest.Unit.fsproj", "{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x86 = Debug|x86
|
Debug|x86 = Debug|x86
|
||||||
@@ -44,10 +42,6 @@ Global
|
|||||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.ActiveCfg = Debug|x86
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.Build.0 = Debug|x86
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.Build.0 = Debug|x86
|
||||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.ActiveCfg = Debug|x86
|
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.ActiveCfg = Debug|x86
|
||||||
{93BA3CB4-A812-4949-B07D-8D393FB38937}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{93BA3CB4-A812-4949-B07D-8D393FB38937}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{93BA3CB4-A812-4949-B07D-8D393FB38937}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{93BA3CB4-A812-4949-B07D-8D393FB38937}.Release|x86.Build.0 = Release|x86
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetLib.Core.Features.Updates;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed partial class FormUpdateDownload : Form{
|
sealed partial class FormUpdateDownload : Form{
|
||||||
|
@@ -6,12 +6,10 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web.Script.Serialization;
|
using System.Web.Script.Serialization;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Core.Features.Updates;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
|
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateCheckClient : IUpdateCheckClient{
|
sealed class UpdateCheckClient{
|
||||||
private const string ApiLatestRelease = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
private const string ApiLatestRelease = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
||||||
private const string UpdaterAssetName = "TweetDuck.Update.exe";
|
private const string UpdaterAssetName = "TweetDuck.Update.exe";
|
||||||
|
|
||||||
@@ -21,12 +19,10 @@ namespace TweetDuck.Updates{
|
|||||||
this.installerFolder = installerFolder;
|
this.installerFolder = installerFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IUpdateCheckClient.CanCheck => Program.Config.User.EnableUpdateCheck;
|
public Task<UpdateInfo> Check(){
|
||||||
|
|
||||||
Task<UpdateInfo> IUpdateCheckClient.Check(){
|
|
||||||
TaskCompletionSource<UpdateInfo> result = new TaskCompletionSource<UpdateInfo>();
|
TaskCompletionSource<UpdateInfo> result = new TaskCompletionSource<UpdateInfo>();
|
||||||
|
|
||||||
WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentVanilla);
|
WebClient client = BrowserUtils.CreateWebClient();
|
||||||
client.Headers[HttpRequestHeader.Accept] = "application/vnd.github.v3+json";
|
client.Headers[HttpRequestHeader.Accept] = "application/vnd.github.v3+json";
|
||||||
|
|
||||||
client.DownloadStringTaskAsync(ApiLatestRelease).ContinueWith(task => {
|
client.DownloadStringTaskAsync(ApiLatestRelease).ContinueWith(task => {
|
||||||
@@ -69,9 +65,10 @@ namespace TweetDuck.Updates{
|
|||||||
private static Exception ExpandWebException(Exception e){
|
private static Exception ExpandWebException(Exception e){
|
||||||
if (e is WebException we && we.Response is HttpWebResponse response){
|
if (e is WebException we && we.Response is HttpWebResponse response){
|
||||||
try{
|
try{
|
||||||
using var stream = response.GetResponseStream();
|
using(Stream stream = response.GetResponseStream())
|
||||||
using var reader = new StreamReader(stream, Encoding.GetEncoding(response.CharacterSet ?? "utf-8"));
|
using(StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(response.CharacterSet ?? "utf-8"))){
|
||||||
return new Reporter.ExpandedLogException(e, reader.ReadToEnd());
|
return new Reporter.ExpandedLogException(e, reader.ReadToEnd());
|
||||||
|
}
|
||||||
}catch{
|
}catch{
|
||||||
// whatever
|
// whatever
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetLib.Core.Data;
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetDuck.Updates{
|
||||||
public sealed class UpdateCheckEventArgs : EventArgs{
|
sealed class UpdateCheckEventArgs : EventArgs{
|
||||||
public int EventId { get; }
|
public int EventId { get; }
|
||||||
public Result<UpdateInfo> Result { get; }
|
public Result<UpdateInfo> Result { get; }
|
||||||
|
|
@@ -1,4 +1,4 @@
|
|||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetDuck.Updates{
|
||||||
public enum UpdateDownloadStatus{
|
public enum UpdateDownloadStatus{
|
||||||
None = 0,
|
None = 0,
|
||||||
InProgress,
|
InProgress,
|
@@ -1,38 +1,34 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Timers;
|
using TweetDuck.Data;
|
||||||
using TweetLib.Core.Data;
|
using Timer = System.Windows.Forms.Timer;
|
||||||
using Timer = System.Timers.Timer;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetDuck.Updates{
|
||||||
public sealed class UpdateHandler : IDisposable{
|
sealed class UpdateHandler : IDisposable{
|
||||||
public const int CheckCodeUpdatesDisabled = -1;
|
public const int CheckCodeUpdatesDisabled = -1;
|
||||||
|
|
||||||
private readonly IUpdateCheckClient client;
|
private readonly UpdateCheckClient client;
|
||||||
private readonly TaskScheduler scheduler;
|
private readonly TaskScheduler scheduler;
|
||||||
private readonly Timer timer;
|
private readonly Timer timer;
|
||||||
|
|
||||||
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
||||||
private ushort lastEventId;
|
private ushort lastEventId;
|
||||||
|
|
||||||
public UpdateHandler(IUpdateCheckClient client, TaskScheduler scheduler){
|
public UpdateHandler(string installerFolder){
|
||||||
this.client = client;
|
this.client = new UpdateCheckClient(installerFolder);
|
||||||
this.scheduler = scheduler;
|
this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||||
|
|
||||||
this.timer = new Timer{
|
this.timer = new Timer();
|
||||||
AutoReset = false,
|
this.timer.Tick += timer_Tick;
|
||||||
Enabled = false
|
|
||||||
};
|
|
||||||
|
|
||||||
this.timer.Elapsed += timer_Elapsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose(){
|
public void Dispose(){
|
||||||
timer.Dispose();
|
timer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void timer_Elapsed(object sender, ElapsedEventArgs e){
|
private void timer_Tick(object sender, EventArgs e){
|
||||||
|
timer.Stop();
|
||||||
Check(false);
|
Check(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,9 +39,9 @@ namespace TweetLib.Core.Features.Updates{
|
|||||||
|
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|
||||||
if (client.CanCheck){
|
if (Program.Config.User.EnableUpdateCheck){
|
||||||
DateTime now = DateTime.Now;
|
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){
|
if (nextHour.TotalMinutes < 15){
|
||||||
nextHour = nextHour.Add(TimeSpan.FromHours(1));
|
nextHour = nextHour.Add(TimeSpan.FromHours(1));
|
||||||
@@ -57,7 +53,7 @@ namespace TweetLib.Core.Features.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int Check(bool force){
|
public int Check(bool force){
|
||||||
if (client.CanCheck || force){
|
if (Program.Config.User.EnableUpdateCheck || force){
|
||||||
int nextEventId = unchecked(++lastEventId);
|
int nextEventId = unchecked(++lastEventId);
|
||||||
Task<UpdateInfo> checkTask = client.Check();
|
Task<UpdateInfo> checkTask = client.Check();
|
||||||
|
|
@@ -1,20 +1,20 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using TweetLib.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetDuck.Updates{
|
||||||
public sealed class UpdateInfo{
|
sealed class UpdateInfo{
|
||||||
public string VersionTag { get; }
|
public string VersionTag { get; }
|
||||||
public string ReleaseNotes { get; }
|
public string ReleaseNotes { get; }
|
||||||
public string InstallerPath { get; }
|
public string InstallerPath { get; }
|
||||||
|
|
||||||
public UpdateDownloadStatus DownloadStatus { get; private set; }
|
public UpdateDownloadStatus DownloadStatus { get; private set; }
|
||||||
public Exception? DownloadError { get; private set; }
|
public Exception DownloadError { get; private set; }
|
||||||
|
|
||||||
private readonly string downloadUrl;
|
private readonly string downloadUrl;
|
||||||
private readonly string installerFolder;
|
private readonly string installerFolder;
|
||||||
private WebClient? currentDownload;
|
private WebClient currentDownload;
|
||||||
|
|
||||||
public UpdateInfo(string versionTag, string releaseNotes, string downloadUrl, string installerFolder){
|
public UpdateInfo(string versionTag, string releaseNotes, string downloadUrl, string installerFolder){
|
||||||
this.downloadUrl = downloadUrl;
|
this.downloadUrl = downloadUrl;
|
||||||
@@ -22,11 +22,11 @@ namespace TweetLib.Core.Features.Updates{
|
|||||||
|
|
||||||
this.VersionTag = versionTag;
|
this.VersionTag = versionTag;
|
||||||
this.ReleaseNotes = releaseNotes;
|
this.ReleaseNotes = releaseNotes;
|
||||||
this.InstallerPath = Path.Combine(installerFolder, $"{Lib.BrandName}.{versionTag}.exe");
|
this.InstallerPath = Path.Combine(installerFolder, $"TweetDuck.{versionTag}.exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginSilentDownload(){
|
public void BeginSilentDownload(){
|
||||||
if (FileUtils.FileExistsAndNotEmpty(InstallerPath)){
|
if (WindowsUtils.FileExistsAndNotEmpty(InstallerPath)){
|
||||||
DownloadStatus = UpdateDownloadStatus.Done;
|
DownloadStatus = UpdateDownloadStatus.Done;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -48,9 +48,7 @@ namespace TweetLib.Core.Features.Updates{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebClient client = WebUtils.NewClient($"{Lib.BrandName} {Lib.VersionTag}");
|
currentDownload = BrowserUtils.DownloadFileAsync(downloadUrl, InstallerPath, null, () => {
|
||||||
|
|
||||||
client.DownloadFileCompleted += WebUtils.FileDownloadCallback(InstallerPath, () => {
|
|
||||||
DownloadStatus = UpdateDownloadStatus.Done;
|
DownloadStatus = UpdateDownloadStatus.Done;
|
||||||
currentDownload = null;
|
currentDownload = null;
|
||||||
}, e => {
|
}, e => {
|
||||||
@@ -58,8 +56,6 @@ namespace TweetLib.Core.Features.Updates{
|
|||||||
DownloadStatus = UpdateDownloadStatus.Failed;
|
DownloadStatus = UpdateDownloadStatus.Failed;
|
||||||
currentDownload = null;
|
currentDownload = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
client.DownloadFileAsync(new Uri(downloadUrl), InstallerPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@@ -76,14 +76,14 @@ function TDGetNetFrameworkVersion: Cardinal; forward;
|
|||||||
function TDIsVCMissing: Boolean; forward;
|
function TDIsVCMissing: Boolean; forward;
|
||||||
procedure TDInstallVCRedist; forward;
|
procedure TDInstallVCRedist; forward;
|
||||||
|
|
||||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. }
|
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
||||||
function InitializeSetup: Boolean;
|
function InitializeSetup: Boolean;
|
||||||
begin
|
begin
|
||||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||||
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
|
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
|
||||||
VisitedTasksPage := False
|
VisitedTasksPage := False
|
||||||
|
|
||||||
if (TDGetNetFrameworkVersion() < 461808) and (MsgBox('{#MyAppName} requires .NET Framework 4.7.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||||
begin
|
begin
|
||||||
Result := False
|
Result := False
|
||||||
Exit
|
Exit
|
||||||
|
@@ -64,14 +64,14 @@ function TDGetNetFrameworkVersion: Cardinal; forward;
|
|||||||
function TDIsVCMissing: Boolean; forward;
|
function TDIsVCMissing: Boolean; forward;
|
||||||
procedure TDInstallVCRedist; forward;
|
procedure TDInstallVCRedist; forward;
|
||||||
|
|
||||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. }
|
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
||||||
function InitializeSetup: Boolean;
|
function InitializeSetup: Boolean;
|
||||||
begin
|
begin
|
||||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||||
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
|
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
|
||||||
VisitedTasksPage := False
|
VisitedTasksPage := False
|
||||||
|
|
||||||
if (TDGetNetFrameworkVersion() < 461808) and (MsgBox('{#MyAppName} requires .NET Framework 4.7.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||||
begin
|
begin
|
||||||
Result := False
|
Result := False
|
||||||
Exit
|
Exit
|
||||||
|
@@ -81,7 +81,7 @@ procedure TDExecuteFullDownload; forward;
|
|||||||
var IsPortable: Boolean;
|
var IsPortable: Boolean;
|
||||||
var UpdatePath: String;
|
var UpdatePath: String;
|
||||||
|
|
||||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.7.2. Prepare full download package if required. }
|
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. Prepare full download package if required. }
|
||||||
function InitializeSetup: Boolean;
|
function InitializeSetup: Boolean;
|
||||||
begin
|
begin
|
||||||
IsPortable := ExpandConstant('{param:PORTABLE}') = '1'
|
IsPortable := ExpandConstant('{param:PORTABLE}') = '1'
|
||||||
@@ -99,7 +99,7 @@ begin
|
|||||||
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'))
|
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'))
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if (TDGetNetFrameworkVersion() < 461808) and (MsgBox('{#MyAppName} requires .NET Framework 4.7.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||||
begin
|
begin
|
||||||
Result := False
|
Result := False
|
||||||
Exit
|
Exit
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
|
<Import Project="..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
<RootNamespace>TweetLib.Communication</RootNamespace>
|
<RootNamespace>TweetLib.Communication</RootNamespace>
|
||||||
<AssemblyName>TweetLib.Communication</AssemblyName>
|
<AssemblyName>TweetLib.Communication</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
@@ -51,6 +51,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props'))" />
|
<Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Microsoft.Net.Compilers" version="3.0.0" targetFramework="net472" developmentDependency="true" />
|
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
|
||||||
</packages>
|
</packages>
|
@@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using TweetLib.Core.Application;
|
|
||||||
|
|
||||||
namespace TweetLib.Core{
|
|
||||||
public sealed class App{
|
|
||||||
public static IAppErrorHandler ErrorHandler { get; private set; }
|
|
||||||
|
|
||||||
// Builder
|
|
||||||
|
|
||||||
public sealed class Builder{
|
|
||||||
public IAppErrorHandler? ErrorHandler { get; set; }
|
|
||||||
|
|
||||||
// Validation
|
|
||||||
|
|
||||||
internal void Initialize(){
|
|
||||||
App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private T Validate<T>(T obj, string name){
|
|
||||||
if (obj == null){
|
|
||||||
throw new InvalidOperationException("Missing property " + name + " on the provided App.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Application{
|
|
||||||
public interface IAppErrorHandler{
|
|
||||||
bool Log(string text);
|
|
||||||
void HandleException(string caption, string message, bool canIgnore, Exception e);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Configuration{
|
|
||||||
public abstract class BaseConfig{
|
|
||||||
private readonly IConfigManager configManager;
|
|
||||||
|
|
||||||
protected BaseConfig(IConfigManager configManager){
|
|
||||||
this.configManager = configManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Management
|
|
||||||
|
|
||||||
public void Save(){
|
|
||||||
configManager.GetInstanceInfo(this).Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reload(){
|
|
||||||
configManager.GetInstanceInfo(this).Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset(){
|
|
||||||
configManager.GetInstanceInfo(this).Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construction methods
|
|
||||||
|
|
||||||
public T ConstructWithDefaults<T>() where T : BaseConfig{
|
|
||||||
return (T)ConstructWithDefaults(configManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract BaseConfig ConstructWithDefaults(IConfigManager configManager);
|
|
||||||
|
|
||||||
// Utility methods
|
|
||||||
|
|
||||||
protected void UpdatePropertyWithEvent<T>(ref T field, T value, EventHandler eventHandler){
|
|
||||||
if (!EqualityComparer<T>.Default.Equals(field, value)){
|
|
||||||
field = value;
|
|
||||||
eventHandler?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void UpdatePropertyWithRestartRequest<T>(ref T field, T value){
|
|
||||||
if (!EqualityComparer<T>.Default.Equals(field, value)){
|
|
||||||
field = value;
|
|
||||||
configManager.TriggerProgramRestartRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,6 +0,0 @@
|
|||||||
namespace TweetLib.Core.Features.Configuration{
|
|
||||||
public interface IConfigManager{
|
|
||||||
void TriggerProgramRestartRequested();
|
|
||||||
IConfigInstance<BaseConfig> GetInstanceInfo(BaseConfig instance);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,72 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +0,0 @@
|
|||||||
namespace TweetLib.Core.Features.Plugins.Enums{
|
|
||||||
public enum PluginFolder{
|
|
||||||
Root, Data
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
|
||||||
public interface IUpdateCheckClient{
|
|
||||||
bool CanCheck { get; }
|
|
||||||
Task<UpdateInfo> Check();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,24 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace TweetLib.Core{
|
|
||||||
public static class Lib{
|
|
||||||
public const string BrandName = "TweetDuck";
|
|
||||||
public const string VersionTag = "1.18";
|
|
||||||
|
|
||||||
public static CultureInfo Culture { get; private set; }
|
|
||||||
|
|
||||||
public static void Initialize(App.Builder app){
|
|
||||||
Culture = CultureInfo.CurrentCulture;
|
|
||||||
|
|
||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
|
||||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
|
|
||||||
#endif
|
|
||||||
|
|
||||||
app.Initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Serialization.Converters{
|
|
||||||
internal sealed class ClrTypeConverter : ITypeConverter{
|
|
||||||
public static ITypeConverter Instance { get; } = new ClrTypeConverter();
|
|
||||||
|
|
||||||
private ClrTypeConverter(){}
|
|
||||||
|
|
||||||
bool ITypeConverter.TryWriteType(Type type, object value, out string? converted){
|
|
||||||
switch(Type.GetTypeCode(type)){
|
|
||||||
case TypeCode.Boolean:
|
|
||||||
converted = value.ToString();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case TypeCode.Int32:
|
|
||||||
converted = ((int)value).ToString(); // cast required for enums
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case TypeCode.String:
|
|
||||||
converted = value?.ToString();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
converted = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ITypeConverter.TryReadType(Type type, string value, out object? converted){
|
|
||||||
switch(Type.GetTypeCode(type)){
|
|
||||||
case TypeCode.Boolean:
|
|
||||||
if (bool.TryParse(value, out bool b)){
|
|
||||||
converted = b;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else goto default;
|
|
||||||
|
|
||||||
case TypeCode.Int32:
|
|
||||||
if (int.TryParse(value, out int i)){
|
|
||||||
converted = i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else goto default;
|
|
||||||
|
|
||||||
case TypeCode.String:
|
|
||||||
converted = value;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
converted = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Serialization{
|
|
||||||
public interface ITypeConverter{
|
|
||||||
bool TryWriteType(Type type, object value, out string? converted);
|
|
||||||
bool TryReadType(Type type, string value, out object? converted);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
|
||||||
<Platforms>x86</Platforms>
|
|
||||||
<LangVersion>8.0</LangVersion>
|
|
||||||
<NullableContextOptions>enable</NullableContextOptions>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@@ -1,39 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Utils{
|
|
||||||
public static class FileUtils{
|
|
||||||
public static void CreateDirectoryForFile(string file){
|
|
||||||
string dir = Path.GetDirectoryName(file);
|
|
||||||
|
|
||||||
if (dir == null){
|
|
||||||
throw new ArgumentException("Invalid file path: "+file);
|
|
||||||
}
|
|
||||||
else if (dir.Length > 0){
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CheckFolderWritePermission(string path){
|
|
||||||
string testFile = Path.Combine(path, ".test");
|
|
||||||
|
|
||||||
try{
|
|
||||||
Directory.CreateDirectory(path);
|
|
||||||
|
|
||||||
using(File.Create(testFile)){}
|
|
||||||
File.Delete(testFile);
|
|
||||||
return true;
|
|
||||||
}catch{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool FileExistsAndNotEmpty(string path){
|
|
||||||
try{
|
|
||||||
return new FileInfo(path).Length > 0;
|
|
||||||
}catch{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Utils{
|
|
||||||
public static class UrlUtils{
|
|
||||||
private const string TwitterTrackingUrl = "t.co";
|
|
||||||
|
|
||||||
public enum CheckResult{
|
|
||||||
Invalid, Tracking, Fine
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CheckResult Check(string url){
|
|
||||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
|
||||||
string scheme = uri.Scheme;
|
|
||||||
|
|
||||||
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
|
|
||||||
return uri.Host == TwitterTrackingUrl ? CheckResult.Tracking : CheckResult.Fine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CheckResult.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string? GetFileNameFromUrl(string url){
|
|
||||||
string file = Path.GetFileName(new Uri(url).AbsolutePath);
|
|
||||||
return string.IsNullOrEmpty(file) ? null : file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Utils{
|
|
||||||
public static class WebUtils{
|
|
||||||
private static bool HasMicrosoftBeenBroughtTo2008Yet;
|
|
||||||
|
|
||||||
private static void EnsureTLS12(){
|
|
||||||
if (!HasMicrosoftBeenBroughtTo2008Yet){
|
|
||||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
|
||||||
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
|
|
||||||
HasMicrosoftBeenBroughtTo2008Yet = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static WebClient NewClient(string userAgent){
|
|
||||||
EnsureTLS12();
|
|
||||||
|
|
||||||
WebClient client = new WebClient{ Proxy = null };
|
|
||||||
client.Headers[HttpRequestHeader.UserAgent] = userAgent;
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncCompletedEventHandler FileDownloadCallback(string file, Action? onSuccess, Action<Exception>? onFailure){
|
|
||||||
return (sender, args) => {
|
|
||||||
if (args.Cancelled){
|
|
||||||
try{
|
|
||||||
File.Delete(file);
|
|
||||||
}catch{
|
|
||||||
// didn't want it deleted anyways
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (args.Error != null){
|
|
||||||
onFailure?.Invoke(args.Error);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
onSuccess?.Invoke();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user