1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 10:32:10 +02:00

Compare commits

..

8 Commits

48 changed files with 962 additions and 481 deletions

View File

@@ -1,7 +1,9 @@
using System;
using System.IO;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Browser.Handling;
using TweetDuck.Management;
using TweetDuck.Utils;
using TweetLib.Browser.Base;
using TweetLib.Browser.Events;
@@ -11,12 +13,11 @@ using IContextMenuHandler = TweetLib.Browser.Interfaces.IContextMenuHandler;
using IResourceRequestHandler = TweetLib.Browser.Interfaces.IResourceRequestHandler;
namespace TweetDuck.Browser.Adapters {
internal abstract class CefBrowserComponent : IBrowserComponent {
abstract class CefBrowserComponent : IBrowserComponent {
public bool Ready { get; private set; }
public string Url => browser.Address;
public IFileDownloader FileDownloader => TwitterFileDownloader.Instance;
public string CacheFolder => BrowserCache.CacheFolder;
public event EventHandler<BrowserLoadedEventArgs> BrowserLoaded;
public event EventHandler<PageLoadEventArgs> PageLoadStart;
@@ -96,13 +97,29 @@ namespace TweetDuck.Browser.Adapters {
browser.JavascriptObjectRepository.Register(name, bridge, isAsync: true, BindingOptions.DefaultBinder);
}
public void RunFunction(string name, params object[] args) {
browser.BrowserCore.ExecuteScriptAsync(name, args);
}
public void RunScript(string identifier, string script) {
using IFrame frame = browser.GetMainFrame();
frame.ExecuteJavaScriptAsync(script, identifier, 1);
}
public void DownloadFile(string url, string path, Action onSuccess, Action<Exception> onError) {
Cef.UIThreadTaskFactory.StartNew(() => {
try {
using IFrame frame = browser.GetMainFrame();
var request = frame.CreateRequest(false);
request.Method = "GET";
request.Url = url;
request.Flags = UrlRequestFlags.AllowStoredCredentials;
request.SetReferrer(Url, ReferrerPolicy.Default);
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
var client = new DownloadRequestClient(fileStream, onSuccess, onError);
frame.CreateUrlRequest(request, client);
} catch (Exception e) {
onError?.Invoke(e);
}
});
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using CefSharp;
namespace TweetDuck.Browser.Adapters {
internal sealed class CefContextMenuActionRegistry {
sealed class CefContextMenuActionRegistry {
private readonly Dictionary<CefMenuCommand, Action> actions = new Dictionary<CefMenuCommand, Action>();
public CefMenuCommand AddAction(Action action) {

View File

@@ -6,7 +6,7 @@ using TweetLib.Core.Features.TweetDeck;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Browser.Adapters {
internal sealed class CefContextMenuModel : IContextMenuBuilder {
sealed class CefContextMenuModel : IContextMenuBuilder {
private readonly IMenuModel model;
private readonly CefContextMenuActionRegistry actionRegistry;

View File

@@ -4,7 +4,7 @@ using System.Text;
using CefSharp;
namespace TweetDuck.Browser.Adapters {
internal sealed class CefResourceHandlerRegistry {
sealed class CefResourceHandlerRegistry {
private readonly ConcurrentDictionary<string, Func<IResourceHandler>> resourceHandlers = new ConcurrentDictionary<string, Func<IResourceHandler>>(StringComparer.OrdinalIgnoreCase);
public bool HasHandler(string url) {

View File

@@ -4,7 +4,7 @@ using CefSharp.WinForms;
using TweetLib.Browser.Interfaces;
namespace TweetDuck.Browser.Adapters {
internal sealed class CefSchemeHandlerFactory : ISchemeHandlerFactory {
sealed class CefSchemeHandlerFactory : ISchemeHandlerFactory {
public static void Register(CefSettings settings, ICustomSchemeHandler handler) {
settings.RegisterScheme(new CefCustomScheme {
SchemeName = handler.Protocol,

View File

@@ -6,7 +6,7 @@ using TweetLib.Browser.Interfaces;
using TweetLib.Browser.Request;
namespace TweetDuck.Browser.Adapters {
internal sealed class CefSchemeResourceVisitor : ISchemeResourceVisitor<IResourceHandler> {
sealed class CefSchemeResourceVisitor : ISchemeResourceVisitor<IResourceHandler> {
public static CefSchemeResourceVisitor Instance { get; } = new CefSchemeResourceVisitor();
private static readonly SchemeResource.Status FileIsEmpty = new SchemeResource.Status(HttpStatusCode.NoContent, "File is empty.");

View File

@@ -0,0 +1,62 @@
using System;
using System.IO;
using CefSharp;
namespace TweetDuck.Browser.Handling {
sealed class DownloadRequestClient : UrlRequestClient {
private readonly FileStream fileStream;
private readonly Action onSuccess;
private readonly Action<Exception> onError;
private bool hasFailed;
public DownloadRequestClient(FileStream fileStream, Action onSuccess, Action<Exception> onError) {
this.fileStream = fileStream;
this.onSuccess = onSuccess;
this.onError = onError;
}
protected override bool GetAuthCredentials(bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) {
onError?.Invoke(new Exception("This URL requires authentication."));
fileStream.Dispose();
hasFailed = true;
return false;
}
protected override void OnDownloadData(IUrlRequest request, Stream data) {
if (hasFailed) {
return;
}
try {
data.CopyTo(fileStream);
} catch (Exception e) {
fileStream.Dispose();
onError?.Invoke(e);
hasFailed = true;
}
}
protected override void OnRequestComplete(IUrlRequest request) {
if (hasFailed) {
return;
}
bool isEmpty = fileStream.Position == 0;
fileStream.Dispose();
var status = request.RequestStatus;
if (status == UrlRequestStatus.Failed) {
onError?.Invoke(new Exception("Unknown error."));
}
else if (status == UrlRequestStatus.Success) {
if (isEmpty) {
onError?.Invoke(new Exception("File is empty."));
return;
}
onSuccess?.Invoke();
}
}
}
}

View File

@@ -12,8 +12,6 @@ namespace TweetDuck.Configuration {
// internal args
public const string ArgRestart = "-restart";
public const string ArgImportCookies = "-importcookies";
public const string ArgDeleteCookies = "-deletecookies";
public const string ArgUpdated = "-updated";
// class data and methods
@@ -30,8 +28,6 @@ namespace TweetDuck.Configuration {
public static CommandLineArgs GetCurrentClean() {
CommandLineArgs args = Current.Clone();
args.RemoveFlag(ArgRestart);
args.RemoveFlag(ArgImportCookies);
args.RemoveFlag(ArgDeleteCookies);
args.RemoveFlag(ArgUpdated);
return args;
}

View File

@@ -26,6 +26,7 @@ namespace TweetDuck.Configuration {
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
public bool BestImageQuality { get; set; } = true;
public bool EnableAnimatedImages { get; set; } = true;
public bool HideTweetsByNftUsers { get; set; } = false;
private bool _enableSmoothScrolling = true;
private string _customCefArgs = null;

View File

@@ -40,7 +40,7 @@ namespace TweetDuck.Dialogs {
PrepareLoad();
AddButton("General", () => new TabSettingsGeneral(tweetDeckFunctions.ReloadColumns, updates));
AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadToTweetDeck, tweetDeckFunctions.ReloadColumns, updates));
AddButton("Notifications", () => new TabSettingsNotifications(this.browser.CreateExampleNotification()));
AddButton("Sounds", () => new TabSettingsSounds(() => tweetDeckFunctions.PlaySoundNotification(true)));
AddButton("Tray", () => new TabSettingsTray());

View File

@@ -2,12 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Management;
using TweetLib.Core;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Systems.Configuration;
using TweetLib.Utils.Static;
namespace TweetDuck.Dialogs.Settings {
sealed partial class DialogSettingsManage : Form {
@@ -29,10 +27,6 @@ namespace TweetDuck.Dialogs.Settings {
}
}
private bool SelectedItemsForceRestart {
get => _selectedItems.HasFlag(ProfileManager.Items.Session);
}
public bool IsRestarting { get; private set; }
public bool ShouldReloadBrowser { get; private set; }
@@ -146,6 +140,10 @@ namespace TweetDuck.Dialogs.Settings {
App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested;
if (SelectedItems.HasFlag(ProfileManager.Items.Session)) {
ProfileManager.DeleteAuthCookie();
}
if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)) {
Program.Config.Plugins.Reset();
@@ -156,13 +154,8 @@ namespace TweetDuck.Dialogs.Settings {
}
}
if (SelectedItemsForceRestart) {
RestartProgram(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[] { Arguments.ArgDeleteCookies } : StringUtils.EmptyArray);
}
else if (requestedRestartFromConfig) {
if (FormMessage.Information("Profile Reset", "The application must restart for some of the restored options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) {
RestartProgram();
}
if (requestedRestartFromConfig && FormMessage.Information("Profile Reset", "The application must restart for some of the restored options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) {
RestartProgram();
}
ShouldReloadBrowser = true;
@@ -180,13 +173,8 @@ namespace TweetDuck.Dialogs.Settings {
App.ConfigManager.SaveAll();
App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested;
if (SelectedItemsForceRestart) {
RestartProgram(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[] { Arguments.ArgImportCookies } : StringUtils.EmptyArray);
}
else if (requestedRestartFromConfig) {
if (FormMessage.Information("Profile Import", "The application must restart for some of the imported options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) {
RestartProgram();
}
if (requestedRestartFromConfig && FormMessage.Information("Profile Import", "The application must restart for some of the imported options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) {
RestartProgram();
}
}
@@ -235,16 +223,16 @@ namespace TweetDuck.Dialogs.Settings {
btnContinue.Enabled = _selectedItems != ProfileManager.Items.None;
if (currentState == State.Import) {
btnContinue.Text = SelectedItemsForceRestart ? "Import && Restart" : "Import Profile";
btnContinue.Text = "Import Profile";
}
else if (currentState == State.Reset) {
btnContinue.Text = SelectedItemsForceRestart ? "Restore && Restart" : "Restore Defaults";
btnContinue.Text = "Restore Defaults";
}
}
private void RestartProgram(params string[] extraArgs) {
private void RestartProgram() {
IsRestarting = true;
Program.Restart(extraArgs);
Program.Restart();
}
}
}

View File

@@ -40,27 +40,34 @@
this.panelClearCacheAuto = new System.Windows.Forms.Panel();
this.panelConfiguration = new System.Windows.Forms.Panel();
this.labelConfiguration = new System.Windows.Forms.Label();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
this.labelBrowserSettings = new System.Windows.Forms.Label();
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
this.checkAutomaticallyDetectColorProfile = new System.Windows.Forms.CheckBox();
this.checkTouchAdjustment = new System.Windows.Forms.CheckBox();
this.labelProxy = new System.Windows.Forms.Label();
this.checkUseSystemProxyForAllConnections = new System.Windows.Forms.CheckBox();
this.labelDevTools = new System.Windows.Forms.Label();
this.checkDevToolsInContextMenu = new System.Windows.Forms.CheckBox();
this.checkDevToolsWindowOnTop = new System.Windows.Forms.CheckBox();
this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel();
this.panelSeparator = new System.Windows.Forms.Panel();
((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit();
this.panelAppButtons.SuspendLayout();
this.panelClearCacheAuto.SuspendLayout();
this.panelConfiguration.SuspendLayout();
this.flowPanel.SuspendLayout();
this.flowPanelLeft.SuspendLayout();
this.flowPanelRight.SuspendLayout();
this.SuspendLayout();
//
// btnClearCache
//
this.btnClearCache.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnClearCache.Location = new System.Drawing.Point(5, 135);
this.btnClearCache.Location = new System.Drawing.Point(5, 145);
this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnClearCache.Name = "btnClearCache";
this.btnClearCache.Size = new System.Drawing.Size(143, 25);
this.btnClearCache.TabIndex = 3;
this.btnClearCache.TabIndex = 5;
this.btnClearCache.Text = "Clear Cache (...)";
this.btnClearCache.UseVisualStyleBackColor = true;
//
@@ -147,7 +154,7 @@
0,
0});
this.numClearCacheThreshold.Name = "numClearCacheThreshold";
this.numClearCacheThreshold.Size = new System.Drawing.Size(89, 23);
this.numClearCacheThreshold.Size = new System.Drawing.Size(80, 23);
this.numClearCacheThreshold.TabIndex = 1;
this.numClearCacheThreshold.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.numClearCacheThreshold.TextSuffix = " MB";
@@ -196,72 +203,119 @@
//
this.labelCache.AutoSize = true;
this.labelCache.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelCache.Location = new System.Drawing.Point(0, 112);
this.labelCache.Location = new System.Drawing.Point(0, 122);
this.labelCache.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelCache.Name = "labelCache";
this.labelCache.Size = new System.Drawing.Size(123, 19);
this.labelCache.TabIndex = 2;
this.labelCache.TabIndex = 4;
this.labelCache.Text = "BROWSER CACHE";
//
// panelClearCacheAuto
//
this.panelClearCacheAuto.Controls.Add(this.checkClearCacheAuto);
this.panelClearCacheAuto.Controls.Add(this.numClearCacheThreshold);
this.panelClearCacheAuto.Location = new System.Drawing.Point(0, 163);
this.panelClearCacheAuto.Location = new System.Drawing.Point(0, 173);
this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0);
this.panelClearCacheAuto.Name = "panelClearCacheAuto";
this.panelClearCacheAuto.Size = new System.Drawing.Size(300, 28);
this.panelClearCacheAuto.TabIndex = 4;
this.panelClearCacheAuto.TabIndex = 6;
//
// panelConfiguration
//
this.panelConfiguration.Controls.Add(this.btnEditCSS);
this.panelConfiguration.Controls.Add(this.btnEditCefArgs);
this.panelConfiguration.Location = new System.Drawing.Point(0, 241);
this.panelConfiguration.Location = new System.Drawing.Point(0, 132);
this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0);
this.panelConfiguration.Name = "panelConfiguration";
this.panelConfiguration.Size = new System.Drawing.Size(300, 31);
this.panelConfiguration.TabIndex = 6;
this.panelConfiguration.TabIndex = 3;
//
// labelConfiguration
//
this.labelConfiguration.AutoSize = true;
this.labelConfiguration.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelConfiguration.Location = new System.Drawing.Point(0, 221);
this.labelConfiguration.Location = new System.Drawing.Point(0, 112);
this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelConfiguration.Name = "labelConfiguration";
this.labelConfiguration.Size = new System.Drawing.Size(123, 19);
this.labelConfiguration.TabIndex = 5;
this.labelConfiguration.TabIndex = 2;
this.labelConfiguration.Text = "CONFIGURATION";
//
// flowPanel
// flowPanelLeft
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
this.flowPanelLeft.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.flowPanel.Controls.Add(this.labelApp);
this.flowPanel.Controls.Add(this.panelAppButtons);
this.flowPanel.Controls.Add(this.labelCache);
this.flowPanel.Controls.Add(this.btnClearCache);
this.flowPanel.Controls.Add(this.panelClearCacheAuto);
this.flowPanel.Controls.Add(this.labelConfiguration);
this.flowPanel.Controls.Add(this.panelConfiguration);
this.flowPanel.Controls.Add(this.labelProxy);
this.flowPanel.Controls.Add(this.checkUseSystemProxyForAllConnections);
this.flowPanel.Controls.Add(this.labelDevTools);
this.flowPanel.Controls.Add(this.checkDevToolsInContextMenu);
this.flowPanel.Controls.Add(this.checkDevToolsWindowOnTop);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(300, 462);
this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false;
this.flowPanelLeft.Controls.Add(this.labelBrowserSettings);
this.flowPanelLeft.Controls.Add(this.checkHardwareAcceleration);
this.flowPanelLeft.Controls.Add(this.checkAutomaticallyDetectColorProfile);
this.flowPanelLeft.Controls.Add(this.checkTouchAdjustment);
this.flowPanelLeft.Controls.Add(this.labelCache);
this.flowPanelLeft.Controls.Add(this.btnClearCache);
this.flowPanelLeft.Controls.Add(this.panelClearCacheAuto);
this.flowPanelLeft.Controls.Add(this.labelProxy);
this.flowPanelLeft.Controls.Add(this.checkUseSystemProxyForAllConnections);
this.flowPanelLeft.Controls.Add(this.labelDevTools);
this.flowPanelLeft.Controls.Add(this.checkDevToolsInContextMenu);
this.flowPanelLeft.Controls.Add(this.checkDevToolsWindowOnTop);
this.flowPanelLeft.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanelLeft.Location = new System.Drawing.Point(9, 9);
this.flowPanelLeft.Name = "flowPanelLeft";
this.flowPanelLeft.Size = new System.Drawing.Size(300, 462);
this.flowPanelLeft.TabIndex = 0;
this.flowPanelLeft.WrapContents = false;
//
// labelBrowserSettings
//
this.labelBrowserSettings.AutoSize = true;
this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelBrowserSettings.Location = new System.Drawing.Point(0, 0);
this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.labelBrowserSettings.Name = "labelBrowserSettings";
this.labelBrowserSettings.Size = new System.Drawing.Size(143, 19);
this.labelBrowserSettings.TabIndex = 0;
this.labelBrowserSettings.Text = "BROWSER SETTINGS";
//
// checkHardwareAcceleration
//
this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 23);
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19);
this.checkHardwareAcceleration.TabIndex = 1;
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
//
// checkAutomaticallyDetectColorProfile
//
this.checkAutomaticallyDetectColorProfile.AutoSize = true;
this.checkAutomaticallyDetectColorProfile.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkAutomaticallyDetectColorProfile.Location = new System.Drawing.Point(6, 47);
this.checkAutomaticallyDetectColorProfile.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkAutomaticallyDetectColorProfile.Name = "checkAutomaticallyDetectColorProfile";
this.checkAutomaticallyDetectColorProfile.Size = new System.Drawing.Size(206, 19);
this.checkAutomaticallyDetectColorProfile.TabIndex = 2;
this.checkAutomaticallyDetectColorProfile.Text = "Automatically Detect Color Profile";
this.checkAutomaticallyDetectColorProfile.UseVisualStyleBackColor = true;
//
// checkTouchAdjustment
//
this.checkTouchAdjustment.AutoSize = true;
this.checkTouchAdjustment.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkTouchAdjustment.Location = new System.Drawing.Point(6, 71);
this.checkTouchAdjustment.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkTouchAdjustment.Name = "checkTouchAdjustment";
this.checkTouchAdjustment.Size = new System.Drawing.Size(163, 19);
this.checkTouchAdjustment.TabIndex = 3;
this.checkTouchAdjustment.Text = "Touch Screen Adjustment";
this.checkTouchAdjustment.UseVisualStyleBackColor = true;
//
// labelProxy
//
this.labelProxy.AutoSize = true;
this.labelProxy.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelProxy.Location = new System.Drawing.Point(0, 302);
this.labelProxy.Location = new System.Drawing.Point(0, 231);
this.labelProxy.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelProxy.Name = "labelProxy";
this.labelProxy.Size = new System.Drawing.Size(54, 19);
@@ -272,7 +326,7 @@
//
this.checkUseSystemProxyForAllConnections.AutoSize = true;
this.checkUseSystemProxyForAllConnections.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkUseSystemProxyForAllConnections.Location = new System.Drawing.Point(6, 328);
this.checkUseSystemProxyForAllConnections.Location = new System.Drawing.Point(6, 257);
this.checkUseSystemProxyForAllConnections.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkUseSystemProxyForAllConnections.Name = "checkUseSystemProxyForAllConnections";
this.checkUseSystemProxyForAllConnections.Size = new System.Drawing.Size(223, 19);
@@ -284,7 +338,7 @@
//
this.labelDevTools.AutoSize = true;
this.labelDevTools.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelDevTools.Location = new System.Drawing.Point(0, 379);
this.labelDevTools.Location = new System.Drawing.Point(0, 308);
this.labelDevTools.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelDevTools.Name = "labelDevTools";
this.labelDevTools.Size = new System.Drawing.Size(156, 19);
@@ -295,7 +349,7 @@
//
this.checkDevToolsInContextMenu.AutoSize = true;
this.checkDevToolsInContextMenu.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkDevToolsInContextMenu.Location = new System.Drawing.Point(6, 405);
this.checkDevToolsInContextMenu.Location = new System.Drawing.Point(6, 334);
this.checkDevToolsInContextMenu.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkDevToolsInContextMenu.Name = "checkDevToolsInContextMenu";
this.checkDevToolsInContextMenu.Size = new System.Drawing.Size(201, 19);
@@ -307,7 +361,7 @@
//
this.checkDevToolsWindowOnTop.AutoSize = true;
this.checkDevToolsWindowOnTop.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkDevToolsWindowOnTop.Location = new System.Drawing.Point(6, 429);
this.checkDevToolsWindowOnTop.Location = new System.Drawing.Point(6, 358);
this.checkDevToolsWindowOnTop.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkDevToolsWindowOnTop.Name = "checkDevToolsWindowOnTop";
this.checkDevToolsWindowOnTop.Size = new System.Drawing.Size(168, 19);
@@ -315,11 +369,39 @@
this.checkDevToolsWindowOnTop.Text = "Dev Tools Window On Top";
this.checkDevToolsWindowOnTop.UseVisualStyleBackColor = true;
//
// flowPanelRight
//
this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.flowPanelRight.Controls.Add(this.labelApp);
this.flowPanelRight.Controls.Add(this.panelAppButtons);
this.flowPanelRight.Controls.Add(this.labelConfiguration);
this.flowPanelRight.Controls.Add(this.panelConfiguration);
this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanelRight.Location = new System.Drawing.Point(322, 9);
this.flowPanelRight.Name = "flowPanelRight";
this.flowPanelRight.Size = new System.Drawing.Size(300, 462);
this.flowPanelRight.TabIndex = 1;
this.flowPanelRight.WrapContents = false;
//
// panelSeparator
//
this.panelSeparator.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.panelSeparator.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(200)))), ((int)(((byte)(200)))), ((int)(((byte)(200)))));
this.panelSeparator.Location = new System.Drawing.Point(312, 0);
this.panelSeparator.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.panelSeparator.Name = "panelSeparator";
this.panelSeparator.Size = new System.Drawing.Size(1, 480);
this.panelSeparator.TabIndex = 3;
//
// TabSettingsAdvanced
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.flowPanel);
this.Controls.Add(this.panelSeparator);
this.Controls.Add(this.flowPanelRight);
this.Controls.Add(this.flowPanelLeft);
this.Name = "TabSettingsAdvanced";
this.Size = new System.Drawing.Size(631, 480);
((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).EndInit();
@@ -327,8 +409,10 @@
this.panelClearCacheAuto.ResumeLayout(false);
this.panelClearCacheAuto.PerformLayout();
this.panelConfiguration.ResumeLayout(false);
this.flowPanel.ResumeLayout(false);
this.flowPanel.PerformLayout();
this.flowPanelLeft.ResumeLayout(false);
this.flowPanelLeft.PerformLayout();
this.flowPanelRight.ResumeLayout(false);
this.flowPanelRight.PerformLayout();
this.ResumeLayout(false);
}
@@ -351,11 +435,17 @@
private System.Windows.Forms.Label labelConfiguration;
private Controls.NumericUpDownEx numClearCacheThreshold;
private System.Windows.Forms.CheckBox checkClearCacheAuto;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.FlowLayoutPanel flowPanelLeft;
private System.Windows.Forms.Label labelDevTools;
private System.Windows.Forms.CheckBox checkDevToolsInContextMenu;
private System.Windows.Forms.CheckBox checkDevToolsWindowOnTop;
private System.Windows.Forms.Label labelProxy;
private System.Windows.Forms.CheckBox checkUseSystemProxyForAllConnections;
private System.Windows.Forms.FlowLayoutPanel flowPanelRight;
private System.Windows.Forms.Label labelBrowserSettings;
private System.Windows.Forms.CheckBox checkTouchAdjustment;
private System.Windows.Forms.CheckBox checkAutomaticallyDetectColorProfile;
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
private System.Windows.Forms.Panel panelSeparator;
}
}

View File

@@ -25,6 +25,16 @@ namespace TweetDuck.Dialogs.Settings {
toolTip.SetToolTip(btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
toolTip.SetToolTip(btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments.");
// browser settings
toolTip.SetToolTip(checkTouchAdjustment, "Toggles Chromium touch screen adjustment.\r\nDisabled by default, because it is very imprecise with TweetDeck.");
toolTip.SetToolTip(checkAutomaticallyDetectColorProfile, "Automatically detects the color profile of your system.\r\nUses the sRGB profile if disabled.");
toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance.\r\nDisable if you experience visual glitches, or to save a small amount of RAM.");
checkTouchAdjustment.Checked = SysConfig.EnableTouchAdjustment;
checkAutomaticallyDetectColorProfile.Checked = SysConfig.EnableColorProfileDetection;
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
// browser cache
toolTip.SetToolTip(btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
@@ -65,6 +75,10 @@ namespace TweetDuck.Dialogs.Settings {
btnRestart.Click += btnRestart_Click;
btnRestartArgs.Click += btnRestartArgs_Click;
checkTouchAdjustment.CheckedChanged += checkTouchAdjustment_CheckedChanged;
checkAutomaticallyDetectColorProfile.CheckedChanged += checkAutomaticallyDetectColorProfile_CheckedChanged;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
btnClearCache.Click += btnClearCache_Click;
checkClearCacheAuto.CheckedChanged += checkClearCacheAuto_CheckedChanged;
@@ -106,6 +120,22 @@ namespace TweetDuck.Dialogs.Settings {
#endregion
#region Browser Settings
private void checkTouchAdjustment_CheckedChanged(object sender, EventArgs e) {
SysConfig.EnableTouchAdjustment = checkTouchAdjustment.Checked;
}
private void checkAutomaticallyDetectColorProfile_CheckedChanged(object sender, EventArgs e) {
SysConfig.EnableColorProfileDetection = checkAutomaticallyDetectColorProfile.Checked;
}
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e) {
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
}
#endregion
#region Browser Cache
private void btnClearCache_Click(object sender, EventArgs e) {

View File

@@ -34,18 +34,16 @@
this.trackBarZoom = new System.Windows.Forms.TrackBar();
this.labelZoom = new System.Windows.Forms.Label();
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.labelUI = new System.Windows.Forms.Label();
this.panelZoom = new System.Windows.Forms.Panel();
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
this.labelUpdates = new System.Windows.Forms.Label();
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
this.labelUI = new System.Windows.Forms.Label();
this.checkFocusDmInput = new System.Windows.Forms.CheckBox();
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
this.labelBrowserSettings = new System.Windows.Forms.Label();
this.checkSmoothScrolling = new System.Windows.Forms.CheckBox();
this.checkTouchAdjustment = new System.Windows.Forms.CheckBox();
this.checkAutomaticallyDetectColorProfile = new System.Windows.Forms.CheckBox();
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
this.labelTweetDeckSettings = new System.Windows.Forms.Label();
this.checkHideTweetsByNftUsers = new System.Windows.Forms.CheckBox();
this.labelBrowserPath = new System.Windows.Forms.Label();
this.comboBoxCustomBrowser = new System.Windows.Forms.ComboBox();
this.labelSearchEngine = new System.Windows.Forms.Label();
@@ -91,11 +89,11 @@
//
this.checkUpdateNotifications.AutoSize = true;
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, 381);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
this.checkUpdateNotifications.TabIndex = 15;
this.checkUpdateNotifications.TabIndex = 13;
this.checkUpdateNotifications.Text = "Check Updates Automatically";
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
//
@@ -103,12 +101,12 @@
//
this.btnCheckUpdates.AutoSize = true;
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, 405);
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnCheckUpdates.Name = "btnCheckUpdates";
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
this.btnCheckUpdates.TabIndex = 16;
this.btnCheckUpdates.TabIndex = 14;
this.btnCheckUpdates.Text = "Check Updates Now";
this.btnCheckUpdates.UseVisualStyleBackColor = true;
//
@@ -128,12 +126,12 @@
//
this.checkBestImageQuality.AutoSize = true;
this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 122);
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 259);
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkBestImageQuality.Name = "checkBestImageQuality";
this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19);
this.checkBestImageQuality.TabIndex = 5;
this.checkBestImageQuality.Text = "Best Image Quality";
this.checkBestImageQuality.Size = new System.Drawing.Size(182, 19);
this.checkBestImageQuality.TabIndex = 9;
this.checkBestImageQuality.Text = "Download Best Image Quality";
this.checkBestImageQuality.UseVisualStyleBackColor = true;
//
// checkOpenSearchInFirstColumn
@@ -167,11 +165,11 @@
//
this.labelZoom.AutoSize = true;
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelZoom.Location = new System.Drawing.Point(3, 323);
this.labelZoom.Location = new System.Drawing.Point(3, 155);
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelZoom.Name = "labelZoom";
this.labelZoom.Size = new System.Drawing.Size(39, 15);
this.labelZoom.TabIndex = 12;
this.labelZoom.TabIndex = 6;
this.labelZoom.Text = "Zoom";
//
// zoomUpdateTimer
@@ -179,36 +177,25 @@
this.zoomUpdateTimer.Interval = 250;
this.zoomUpdateTimer.Tick += new System.EventHandler(this.zoomUpdateTimer_Tick);
//
// labelUI
//
this.labelUI.AutoSize = true;
this.labelUI.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelUI.Location = new System.Drawing.Point(0, 0);
this.labelUI.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.labelUI.Name = "labelUI";
this.labelUI.Size = new System.Drawing.Size(118, 19);
this.labelUI.TabIndex = 0;
this.labelUI.Text = "USER INTERFACE";
//
// panelZoom
//
this.panelZoom.Controls.Add(this.trackBarZoom);
this.panelZoom.Controls.Add(this.labelZoomValue);
this.panelZoom.Location = new System.Drawing.Point(0, 339);
this.panelZoom.Location = new System.Drawing.Point(0, 171);
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
this.panelZoom.Name = "panelZoom";
this.panelZoom.Size = new System.Drawing.Size(300, 35);
this.panelZoom.TabIndex = 13;
this.panelZoom.TabIndex = 7;
//
// checkAnimatedAvatars
//
this.checkAnimatedAvatars.AutoSize = true;
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, 307);
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19);
this.checkAnimatedAvatars.TabIndex = 6;
this.checkAnimatedAvatars.TabIndex = 11;
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
//
@@ -216,11 +203,11 @@
//
this.labelUpdates.AutoSize = true;
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.Margin = new System.Windows.Forms.Padding(0, 7, 0, 1);
this.labelUpdates.Location = new System.Drawing.Point(0, 355);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1);
this.labelUpdates.Name = "labelUpdates";
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
this.labelUpdates.TabIndex = 14;
this.labelUpdates.TabIndex = 12;
this.labelUpdates.Text = "UPDATES";
//
// flowPanelLeft
@@ -232,15 +219,13 @@
this.flowPanelLeft.Controls.Add(this.checkFocusDmInput);
this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn);
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
this.flowPanelLeft.Controls.Add(this.checkAnimatedAvatars);
this.flowPanelLeft.Controls.Add(this.labelBrowserSettings);
this.flowPanelLeft.Controls.Add(this.checkSmoothScrolling);
this.flowPanelLeft.Controls.Add(this.checkTouchAdjustment);
this.flowPanelLeft.Controls.Add(this.checkAutomaticallyDetectColorProfile);
this.flowPanelLeft.Controls.Add(this.checkHardwareAcceleration);
this.flowPanelLeft.Controls.Add(this.labelZoom);
this.flowPanelLeft.Controls.Add(this.panelZoom);
this.flowPanelLeft.Controls.Add(this.labelTweetDeckSettings);
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
this.flowPanelLeft.Controls.Add(this.checkHideTweetsByNftUsers);
this.flowPanelLeft.Controls.Add(this.checkAnimatedAvatars);
this.flowPanelLeft.Controls.Add(this.labelUpdates);
this.flowPanelLeft.Controls.Add(this.checkUpdateNotifications);
this.flowPanelLeft.Controls.Add(this.btnCheckUpdates);
@@ -251,6 +236,17 @@
this.flowPanelLeft.TabIndex = 0;
this.flowPanelLeft.WrapContents = false;
//
// labelUI
//
this.labelUI.AutoSize = true;
this.labelUI.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelUI.Location = new System.Drawing.Point(0, 0);
this.labelUI.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.labelUI.Name = "labelUI";
this.labelUI.Size = new System.Drawing.Size(118, 19);
this.labelUI.TabIndex = 0;
this.labelUI.Text = "USER INTERFACE";
//
// checkFocusDmInput
//
this.checkFocusDmInput.AutoSize = true;
@@ -275,64 +271,40 @@
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
//
// labelBrowserSettings
//
this.labelBrowserSettings.AutoSize = true;
this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelBrowserSettings.Location = new System.Drawing.Point(0, 192);
this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
this.labelBrowserSettings.Name = "labelBrowserSettings";
this.labelBrowserSettings.Size = new System.Drawing.Size(143, 19);
this.labelBrowserSettings.TabIndex = 7;
this.labelBrowserSettings.Text = "BROWSER SETTINGS";
//
// checkSmoothScrolling
//
this.checkSmoothScrolling.AutoSize = true;
this.checkSmoothScrolling.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 218);
this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 122);
this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkSmoothScrolling.Name = "checkSmoothScrolling";
this.checkSmoothScrolling.Size = new System.Drawing.Size(117, 19);
this.checkSmoothScrolling.TabIndex = 8;
this.checkSmoothScrolling.TabIndex = 5;
this.checkSmoothScrolling.Text = "Smooth Scrolling";
this.checkSmoothScrolling.UseVisualStyleBackColor = true;
//
// checkTouchAdjustment
// labelTweetDeckSettings
//
this.checkTouchAdjustment.AutoSize = true;
this.checkTouchAdjustment.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkTouchAdjustment.Location = new System.Drawing.Point(6, 242);
this.checkTouchAdjustment.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkTouchAdjustment.Name = "checkTouchAdjustment";
this.checkTouchAdjustment.Size = new System.Drawing.Size(163, 19);
this.checkTouchAdjustment.TabIndex = 9;
this.checkTouchAdjustment.Text = "Touch Screen Adjustment";
this.checkTouchAdjustment.UseVisualStyleBackColor = true;
this.labelTweetDeckSettings.AutoSize = true;
this.labelTweetDeckSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelTweetDeckSettings.Location = new System.Drawing.Point(0, 233);
this.labelTweetDeckSettings.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1);
this.labelTweetDeckSettings.Name = "labelTweetDeckSettings";
this.labelTweetDeckSettings.Size = new System.Drawing.Size(67, 19);
this.labelTweetDeckSettings.TabIndex = 8;
this.labelTweetDeckSettings.Text = "TWITTER";
//
// checkAutomaticallyDetectColorProfile
// checkHideTweetsByNftUsers
//
this.checkAutomaticallyDetectColorProfile.AutoSize = true;
this.checkAutomaticallyDetectColorProfile.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkAutomaticallyDetectColorProfile.Location = new System.Drawing.Point(6, 266);
this.checkAutomaticallyDetectColorProfile.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkAutomaticallyDetectColorProfile.Name = "checkAutomaticallyDetectColorProfile";
this.checkAutomaticallyDetectColorProfile.Size = new System.Drawing.Size(206, 19);
this.checkAutomaticallyDetectColorProfile.TabIndex = 10;
this.checkAutomaticallyDetectColorProfile.Text = "Automatically Detect Color Profile";
this.checkAutomaticallyDetectColorProfile.UseVisualStyleBackColor = true;
//
// checkHardwareAcceleration
//
this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 290);
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19);
this.checkHardwareAcceleration.TabIndex = 11;
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
this.checkHideTweetsByNftUsers.AutoSize = true;
this.checkHideTweetsByNftUsers.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkHideTweetsByNftUsers.Location = new System.Drawing.Point(6, 283);
this.checkHideTweetsByNftUsers.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkHideTweetsByNftUsers.Name = "checkHideTweetsByNftUsers";
this.checkHideTweetsByNftUsers.Size = new System.Drawing.Size(231, 19);
this.checkHideTweetsByNftUsers.TabIndex = 10;
this.checkHideTweetsByNftUsers.Text = "Hide Tweets by Users with NFT Avatars";
this.checkHideTweetsByNftUsers.UseVisualStyleBackColor = true;
//
// labelBrowserPath
//
@@ -401,7 +373,7 @@
this.flowPanelRight.Location = new System.Drawing.Point(322, 9);
this.flowPanelRight.Name = "flowPanelRight";
this.flowPanelRight.Size = new System.Drawing.Size(300, 462);
this.flowPanelRight.TabIndex = 1;
this.flowPanelRight.TabIndex = 2;
this.flowPanelRight.WrapContents = false;
//
// labelLocales
@@ -583,7 +555,7 @@
this.panelSeparator.Margin = new System.Windows.Forms.Padding(0, 0, 6, 0);
this.panelSeparator.Name = "panelSeparator";
this.panelSeparator.Size = new System.Drawing.Size(1, 480);
this.panelSeparator.TabIndex = 2;
this.panelSeparator.TabIndex = 1;
//
// TabSettingsGeneral
//
@@ -618,7 +590,6 @@
private System.Windows.Forms.Label labelZoomValue;
private System.Windows.Forms.TrackBar trackBarZoom;
private System.Windows.Forms.Timer zoomUpdateTimer;
private System.Windows.Forms.Label labelUI;
private System.Windows.Forms.Panel panelZoom;
private System.Windows.Forms.Label labelUpdates;
private System.Windows.Forms.CheckBox checkBestImageQuality;
@@ -628,12 +599,9 @@
private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen;
private System.Windows.Forms.Label labelBrowserPath;
private System.Windows.Forms.ComboBox comboBoxCustomBrowser;
private System.Windows.Forms.Label labelBrowserSettings;
private System.Windows.Forms.CheckBox checkSmoothScrolling;
private System.Windows.Forms.Label labelSearchEngine;
private System.Windows.Forms.ComboBox comboBoxSearchEngine;
private System.Windows.Forms.CheckBox checkTouchAdjustment;
private System.Windows.Forms.CheckBox checkAutomaticallyDetectColorProfile;
private System.Windows.Forms.FlowLayoutPanel flowPanelRight;
private System.Windows.Forms.Panel panelSeparator;
private System.Windows.Forms.Label labelLocales;
@@ -642,7 +610,6 @@
private System.Windows.Forms.ComboBox comboBoxSpellCheckLanguage;
private System.Windows.Forms.Label labelTranslationTarget;
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
private System.Windows.Forms.CheckBox checkFocusDmInput;
private System.Windows.Forms.Panel panelCustomBrowser;
private System.Windows.Forms.Button btnCustomBrowserChange;
@@ -653,5 +620,8 @@
private System.Windows.Forms.Label labelExternalApplications;
private System.Windows.Forms.Label labelFirstDayOfWeek;
private System.Windows.Forms.ComboBox comboBoxFirstDayOfWeek;
private System.Windows.Forms.Label labelUI;
private System.Windows.Forms.CheckBox checkHideTweetsByNftUsers;
private System.Windows.Forms.Label labelTweetDeckSettings;
}
}

View File

@@ -13,6 +13,7 @@ using TweetLib.Utils.Globalization;
namespace TweetDuck.Dialogs.Settings {
sealed partial class TabSettingsGeneral : FormSettings.BaseTab {
private readonly Action reloadTweetDeck;
private readonly Action reloadColumns;
private readonly UpdateChecker updates;
@@ -27,9 +28,10 @@ namespace TweetDuck.Dialogs.Settings {
private readonly int searchEngineIndexDefault;
private readonly int searchEngineIndexCustom;
public TabSettingsGeneral(Action reloadColumns, UpdateChecker updates) {
public TabSettingsGeneral(Action reloadTweetDeck, Action reloadColumns, UpdateChecker updates) {
InitializeComponent();
this.reloadTweetDeck = reloadTweetDeck;
this.reloadColumns = reloadColumns;
this.updates = updates;
@@ -43,8 +45,7 @@ namespace TweetDuck.Dialogs.Settings {
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(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(checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs.");
toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling.");
toolTip.SetToolTip(labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
@@ -52,12 +53,21 @@ namespace TweetDuck.Dialogs.Settings {
checkFocusDmInput.Checked = Config.FocusDmInput;
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
checkBestImageQuality.Checked = Config.BestImageQuality;
checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
checkSmoothScrolling.Checked = Config.EnableSmoothScrolling;
trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value + "%";
// twitter
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(checkHideTweetsByNftUsers, "Hides tweets created by users who use Twitter's NFT avatar integration.\r\nThis feature is somewhat experimental, some accounts might not be detected immediately.");
toolTip.SetToolTip(checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs.");
checkBestImageQuality.Checked = Config.BestImageQuality;
checkHideTweetsByNftUsers.Checked = Config.HideTweetsByNftUsers;
checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
// updates
toolTip.SetToolTip(checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again.");
@@ -65,21 +75,12 @@ namespace TweetDuck.Dialogs.Settings {
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
// browser settings
// external applications
toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling.");
toolTip.SetToolTip(checkTouchAdjustment, "Toggles Chromium touch screen adjustment.\r\nDisabled by default, because it is very imprecise with TweetDeck.");
toolTip.SetToolTip(checkAutomaticallyDetectColorProfile, "Automatically detects the color profile of your system.\r\nUses the sRGB profile if disabled.");
toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance.\r\nDisable if you experience visual glitches, or to save a small amount of RAM.");
toolTip.SetToolTip(comboBoxCustomBrowser, "Sets the default browser for opening links.");
toolTip.SetToolTip(comboBoxCustomVideoPlayer, "Sets the default application for playing videos.");
toolTip.SetToolTip(comboBoxSearchEngine, "Sets the default website for opening searches.");
checkSmoothScrolling.Checked = Config.EnableSmoothScrolling;
checkTouchAdjustment.Checked = SysConfig.EnableTouchAdjustment;
checkAutomaticallyDetectColorProfile.Checked = SysConfig.EnableColorProfileDetection;
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
foreach (WindowsUtils.Browser browserInfo in WindowsUtils.FindInstalledBrowsers()) {
comboBoxCustomBrowser.Items.Add(browserInfo);
}
@@ -142,17 +143,16 @@ namespace TweetDuck.Dialogs.Settings {
checkFocusDmInput.CheckedChanged += checkFocusDmInput_CheckedChanged;
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkHideTweetsByNftUsers.CheckedChanged += checkHideTweetsByNftUsers_CheckedChanged;
checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
btnCheckUpdates.Click += btnCheckUpdates_Click;
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
checkTouchAdjustment.CheckedChanged += checkTouchAdjustment_CheckedChanged;
checkAutomaticallyDetectColorProfile.CheckedChanged += checkAutomaticallyDetectColorProfile_CheckedChanged;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
comboBoxCustomBrowser.SelectedIndexChanged += comboBoxCustomBrowser_SelectedIndexChanged;
btnCustomBrowserChange.Click += btnCustomBrowserChange_Click;
comboBoxCustomVideoPlayer.SelectedIndexChanged += comboBoxCustomVideoPlayer_SelectedIndexChanged;
@@ -187,13 +187,8 @@ namespace TweetDuck.Dialogs.Settings {
Config.KeepLikeFollowDialogsOpen = checkKeepLikeFollowDialogsOpen.Checked;
}
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e) {
Config.BestImageQuality = checkBestImageQuality.Checked;
}
private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e) {
Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => reloadColumns());
private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e) {
Config.EnableSmoothScrolling = checkSmoothScrolling.Checked;
}
private void trackBarZoom_ValueChanged(object sender, EventArgs e) {
@@ -211,6 +206,24 @@ namespace TweetDuck.Dialogs.Settings {
#endregion
#region Twitter
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e) {
Config.BestImageQuality = checkBestImageQuality.Checked;
}
private void checkHideTweetsByNftUsers_CheckedChanged(object sender, EventArgs e) {
Config.HideTweetsByNftUsers = checkHideTweetsByNftUsers.Checked;
BeginInvoke(reloadTweetDeck);
}
private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e) {
Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => reloadColumns());
}
#endregion
#region Updates
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e) {
@@ -240,23 +253,7 @@ namespace TweetDuck.Dialogs.Settings {
#endregion
#region Browser Settings
private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e) {
Config.EnableSmoothScrolling = checkSmoothScrolling.Checked;
}
private void checkTouchAdjustment_CheckedChanged(object sender, EventArgs e) {
SysConfig.EnableTouchAdjustment = checkTouchAdjustment.Checked;
}
private void checkAutomaticallyDetectColorProfile_CheckedChanged(object sender, EventArgs e) {
SysConfig.EnableColorProfileDetection = checkAutomaticallyDetectColorProfile.Checked;
}
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e) {
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
}
#region External Applications
private void UpdateBrowserChangeButton() {
btnCustomBrowserChange.Visible = comboBoxCustomBrowser.SelectedIndex == browserListIndexCustom;

View File

@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using CefSharp;
using TweetDuck.Dialogs;
using TweetLib.Core;
using TweetLib.Core.Features.Plugins;
@@ -10,13 +12,10 @@ using TweetLib.Utils.IO;
namespace TweetDuck.Management {
sealed class ProfileManager {
private static readonly string CookiesPath = Path.Combine(App.StoragePath, "Cookies");
private static readonly string LocalPrefsPath = Path.Combine(App.StoragePath, "LocalPrefs.json");
private static readonly string TempCookiesPath = Path.Combine(App.StoragePath, "CookiesTmp");
private static readonly string TempLocalPrefsPath = Path.Combine(App.StoragePath, "LocalPrefsTmp.json");
private const int SessionFileCount = 2;
private const string AuthCookieUrl = "https://twitter.com";
private const string AuthCookieName = "auth_token";
private const string AuthCookieDomain = ".twitter.com";
private const string AuthCookiePath = "/";
[Flags]
public enum Items {
@@ -62,8 +61,14 @@ namespace TweetDuck.Management {
}
if (items.HasFlag(Items.Session)) {
stream.WriteFile("cookies", CookiesPath);
stream.WriteFile("localprefs", LocalPrefsPath);
string authToken = ReadAuthCookie();
if (authToken != null) {
stream.WriteString("cookie.auth", authToken);
}
else {
FormMessage.Warning("Export Profile", "Could not find any login session.", FormMessage.OK);
}
}
stream.Flush();
@@ -98,6 +103,7 @@ namespace TweetDuck.Management {
case "cookies":
case "localprefs":
case "cookie.auth":
items |= Items.Session;
break;
}
@@ -112,7 +118,7 @@ namespace TweetDuck.Management {
public bool Import(Items items) {
try {
var missingPlugins = new HashSet<string>();
var sessionFiles = new HashSet<string>();
bool oldCookies = false;
using (CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))) {
CombinedFileStream.Entry entry;
@@ -154,17 +160,30 @@ namespace TweetDuck.Management {
break;
case "cookies":
case "localprefs":
if (items.HasFlag(Items.Session)) {
entry.WriteToFile(TempCookiesPath);
sessionFiles.Add(entry.KeyName);
oldCookies = true;
}
break;
case "localprefs":
case "cookie.auth":
if (items.HasFlag(Items.Session)) {
entry.WriteToFile(TempLocalPrefsPath);
sessionFiles.Add(entry.KeyName);
using ICookieManager cookies = Cef.GetGlobalCookieManager();
var _ = cookies.SetCookieAsync(AuthCookieUrl, new Cookie {
Name = AuthCookieName,
Domain = AuthCookieDomain,
Path = AuthCookiePath,
Value = Encoding.UTF8.GetString(entry.Contents),
Expires = DateTime.Now.Add(TimeSpan.FromDays(365 * 5)),
HttpOnly = true,
Secure = true
}).ContinueWith(t => {
// ReSharper disable once AccessToDisposedClosure
// ReSharper disable once ConvertToLambdaExpression
return cookies.FlushStoreAsync();
}).Result;
}
break;
@@ -172,10 +191,8 @@ namespace TweetDuck.Management {
}
}
if (items.HasFlag(Items.Session) && sessionFiles.Count != SessionFileCount) {
if (items.HasFlag(Items.Session) && oldCookies) {
FormMessage.Error("Profile Import Error", "Cannot import login session from an older version of TweetDuck.", FormMessage.OK);
File.Delete(TempCookiesPath);
File.Delete(TempLocalPrefsPath);
return false;
}
@@ -190,35 +207,6 @@ namespace TweetDuck.Management {
}
}
public static void ImportCookies() {
if (File.Exists(TempCookiesPath) && File.Exists(TempLocalPrefsPath)) {
try {
if (File.Exists(CookiesPath)) {
File.Delete(CookiesPath);
}
if (File.Exists(LocalPrefsPath)) {
File.Delete(LocalPrefsPath);
}
File.Move(TempCookiesPath, CookiesPath);
File.Move(TempLocalPrefsPath, LocalPrefsPath);
} catch (Exception e) {
App.ErrorHandler.HandleException("Profile Import Error", "Could not import the cookie file to restore login session.", true, e);
}
}
}
public static void DeleteCookies() {
try {
if (File.Exists(CookiesPath)) {
File.Delete(CookiesPath);
}
} catch (Exception e) {
App.ErrorHandler.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e);
}
}
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root) {
if (Directory.Exists(root)) {
int rootLength = root.Length;
@@ -238,5 +226,22 @@ namespace TweetDuck.Management {
this.Relative = fullPath.Substring(rootLength).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); // strip leading separator character
}
}
private static string ReadAuthCookie() {
using var cookieManager = Cef.GetGlobalCookieManager();
foreach (var cookie in cookieManager.VisitUrlCookiesAsync(AuthCookieUrl, true).Result) {
if (cookie.Name == AuthCookieName && cookie.Domain == AuthCookieDomain && cookie.Path == AuthCookiePath && cookie.HttpOnly && cookie.Secure) {
return cookie.Value;
}
}
return null;
}
public static void DeleteAuthCookie() {
using var cookieManager = Cef.GetGlobalCookieManager();
var _ = cookieManager.DeleteCookiesAsync(AuthCookieUrl, "auth_token").Result;
}
}
}

View File

@@ -97,13 +97,6 @@ namespace TweetDuck {
}
public void BeforeLaunch() {
if (Arguments.HasFlag(Arguments.ArgImportCookies)) {
ProfileManager.ImportCookies();
}
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)) {
ProfileManager.DeleteCookies();
}
if (Arguments.HasFlag(Arguments.ArgUpdated)) {
WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, InstallerFolder), 8000);
WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, "Service Worker"), 4000);
@@ -175,10 +168,8 @@ namespace TweetDuck {
}
}
public static void Restart(params string[] extraArgs) {
CommandLineArgs args = Arguments.GetCurrentClean();
CommandLineArgs.ReadStringArray('-', extraArgs, args);
RestartWithArgs(args);
public static void Restart() {
RestartWithArgs(Arguments.GetCurrentClean());
}
public static void RestartWithArgs(CommandLineArgs args) {

View File

@@ -45,6 +45,7 @@ import limit_loaded_dm_count from "./tweetdeck/limit_loaded_dm_count.js";
import make_retweets_lowercase from "./tweetdeck/make_retweets_lowercase.js";
import middle_click_tweet_icon_actions from "./tweetdeck/middle_click_tweet_icon_actions.js";
import move_accounts_above_hashtags_in_search from "./tweetdeck/move_accounts_above_hashtags_in_search.js";
import mute_accounts_with_nft_avatars from "./tweetdeck/mute_accounts_with_nft_avatars.js";
import offline_notification from "./tweetdeck/offline_notification.js";
import open_search_externally from "./tweetdeck/open_search_externally.js";
import open_search_in_first_column from "./tweetdeck/open_search_in_first_column.js";

View File

@@ -37,6 +37,7 @@ if (!("$TDX" in window)) {
* @property {boolean} [expandLinksOnHover]
* @property {number} [firstDayOfWeek]
* @property {boolean} [focusDmInput]
* @property {boolean} [hideTweetsByNftUsers]
* @property {boolean} [keepLikeFollowDialogsOpen]
* @property {boolean} [muteNotifications]
* @property {boolean} [notificationMediaPreviews]

View File

@@ -40,6 +40,7 @@ if (!("TD" in window)) {
* @property {TD_Column_Model} model
* @property {boolean} notificationsDisabled
* @property {function} reloadTweets
* @property {ChirpBase[]} updateArray
* @property {{ columnWidth: number }} visibility
*/
@@ -136,7 +137,7 @@ if (!("TD" in window)) {
* @property {Class} TwitterActionRetweetedInteraction
* @property {Class<TwitterClient>} TwitterClient
* @property {Class<TwitterConversation>} TwitterConversation
* @property {Class} TwitterConversationMessageEvent
* @property {Class<TwitterConversationMessageEvent>} TwitterConversationMessageEvent
* @property {TwitterMedia_Class} TwitterMedia
* @property {Class<TwitterStatus>} TwitterStatus
* @property {Class<TwitterUser>} TwitterUser
@@ -225,13 +226,20 @@ if (!("TD" in window)) {
* @typedef ChirpRenderSettings
* @type {Object}
*
* @property {boolean} withFooter
* @property {boolean} withTweetActions
* @property {boolean} isInConvo
* @property {boolean} isFavorite
* @property {boolean} isRetweeted
* @property {boolean} isPossiblySensitive
* @property {string} mediaPreviewSize
* @property {boolean} [isFavorite}
* @property {boolean} [isInConvo}
* @property {boolean} [isMediaPreviewCompact}
* @property {boolean} [isMediaPreviewInQuoted}
* @property {boolean} [isMediaPreviewLarge}
* @property {boolean} [isMediaPreviewOff}
* @property {boolean} [isMediaPreviewSmall}
* @property {boolean} [isPossiblySensitive}
* @property {boolean} [isRetweeted}
* @property {string} [mediaPreviewSize}
* @property {string} [thumbSizeClass}
* @property {boolean} [withFooter}
* @property {boolean} [withMediaPreview}
* @property {boolean} [withTweetActions}
*/
/**
@@ -265,14 +273,19 @@ if (!("TD" in window)) {
* @typedef TwitterClient
* @type {Object}
*
* @property {string} API_BASE_URL
* @property {function(id: string)} addIdToMuteList
* @property {function(chirp: ChirpBase)} callback
* @property {string} chirpId
* @property {TwitterConversations} conversations
* @property {function(ids: string[], onSuccess: function(users: TwitterUser[]), onError: function)} getUsersByIds
* @property {function(url: string, data: object, method: "GET"|"POST", responseProcessor: function, onSuccess: function, onError: function)} makeTwitterCall
* @property {function(json: string[]): TwitterUser[]} processUsers
*/
/**
* @typedef TwitterConversation
* @extends ChirpBase
* @type {Object}
*
* @property {function} markAsRead
@@ -352,6 +365,7 @@ if (!("TD" in window)) {
* @typedef TwitterUserJSON
* @type {Object}
*
* @property {boolean} [ext_has_nft_avatar]
* @property {string} id
* @property {string} id_str
* @property {string} name

View File

@@ -34,4 +34,4 @@
onNextFrame();
});
})(/** @type TD_Screenshot_Bridge */ $TD_NotificationScreenshot);
})(/** @type {TD_Screenshot_Bridge} */ $TD_NotificationScreenshot);

View File

@@ -49,7 +49,7 @@ export function loadConfigurationFile(pluginObject, fileNameUser, fileNameDefaul
const token = pluginObject.$token;
$TDP.checkFileExists(token, fileNameUser).then(exists => {
/** @type string|null */
/** @type {string|null} */
const fileName = exists ? fileNameUser : fileNameDefault;
if (fileName === null) {

View File

@@ -19,10 +19,16 @@ export default function() {
ensurePropertyExists(TD, "controller", "columnManager", "_columnOrder");
ensurePropertyExists(TD, "controller", "columnManager", "move");
$(document).on("uiSearchNoTemporaryColumn", function(e, /** @type SearchEventData */ data) {
/**
* @param e
* @param {SearchEventData} data
*/
const onSearch = function(e, data) {
if (data.query && data.searchScope !== "users" && !data.columnKey && !("tduckResetInput" in data)) {
$(".js-app-search-input").val("");
$(".js-perform-search").blur();
}
});
};
$(document).on("uiSearchNoTemporaryColumn", onSearch);
};

View File

@@ -23,7 +23,7 @@ import { ensurePropertyExists } from "../api/utils.js";
export default function() {
ensurePropertyExists($, "tools", "dateinput", "conf", "firstDay");
/** @type DateInput */
/** @type {DateInput} */
const dateinput = $["tools"]["dateinput"];
onAppReady(function setupDatePickerFirstDayCallback() {

View File

@@ -16,7 +16,13 @@ export default function() {
ensurePropertyExists(TD, "controller", "clients", "getClient");
ensurePropertyExists(TD, "services", "Conversations", "prototype", "getConversation");
$(document).on("dataDmSent", function(e, /** @type DmSentEventData */ data) {
/**
* @param e
* @param {DmSentEventData} data
*/
const onDataDmSent = function(e, data) {
TD.controller.clients.getClient(data.request.accountKey)?.conversations.getConversation(data.request.conversationId)?.markAsRead();
});
};
$(document).on("dataDmSent", onDataDmSent);
};

View File

@@ -0,0 +1,210 @@
import { TD } from "../../api/td.js";
import { checkPropertyExists, noop } from "../../api/utils.js";
function isSupported() {
return checkPropertyExists(TD, "controller", "clients", "getPreferredClient") &&
checkPropertyExists(TD, "services", "TwitterClient", "prototype", "API_BASE_URL") &&
checkPropertyExists(TD, "services", "TwitterClient", "prototype", "makeTwitterCall") &&
checkPropertyExists(TD, "services", "TwitterClient", "prototype", "processUsers") &&
checkPropertyExists(TD, "services", "TwitterUser", "prototype");
}
/**
* @type {function(id: string)[]}
*/
const nftUserListeners = [];
/**
* @type {Map<string, boolean>}
*/
const knownStatus = new Map();
/**
* @type {Map<string, function(result: boolean)[]>}
*/
const deferredCallbacks = new Map();
/**
* @type {Set<string>}
*/
const usersInQueue = new Set();
/**
* @type {Set<string>}
*/
const usersPending = new Set();
let checkTimer = -1;
function requestQueuedUserInfo() {
if (usersInQueue.size === 0) {
return;
}
const ids = [];
for (const id of usersInQueue) {
if (ids.length === 100) {
break;
}
ids.push(id);
usersInQueue.delete(id);
}
// noinspection JSUnusedGlobalSymbols
const data = {
user_id: ids.join(",")
};
/**
* @param {TwitterUserJSON[]} users
*/
const processUserData = function(users) {
for (const user of users) {
setUserNftStatus(user.id_str, user.ext_has_nft_avatar === true);
}
};
const client = TD.controller.clients.getPreferredClient();
client.makeTwitterCall(client.API_BASE_URL + "users/lookup.json?include_ext_has_nft_avatar=1", data, "POST", processUserData, noop, function() {
// In case of API error, assume the users are not associated with NFTs so that callbacks can fire.
for (const id of ids) {
setUserNftStatus(id, false);
}
});
if (usersInQueue.size === 0) {
checkTimer = -1;
}
else {
checkTimer = window.setTimeout(requestQueuedUserInfo, 400);
}
}
/**
* Calls the provided callback function with the result of whether a user id is associated with NFTs.
* If the user id is null, it will be presumed as not associated with NFTs.
* @param {string|null} id
* @param {function(nft: boolean)} [callback]
*/
function checkUserNftStatusCallback(id, callback) {
if (id === null) {
callback && callback(false);
return;
}
const status = knownStatus.get(id);
if (status !== undefined) {
callback && callback(status);
return;
}
if (callback) {
let callbackList = deferredCallbacks.get(id);
if (callbackList === undefined) {
deferredCallbacks.set(id, callbackList = []);
}
callbackList.push(callback);
}
if (usersPending.has(id)) {
return;
}
usersInQueue.add(id);
usersPending.add(id);
window.clearTimeout(checkTimer);
checkTimer = window.setTimeout(requestQueuedUserInfo, 400);
}
/**
* Checks whether a user id is associated with NFTs, but only using already known results.
* If the user id is null or has not been checked yet, it will be presumed as not associated with NFTs.
* @param {string|null} id
* @return {boolean}
*/
export function checkUserNftStatusImmediately(id) {
return id !== null && knownStatus.get(id) === true;
}
/**
* Adds a listener that gets called when a user is added to the list of users associated with NFTs.
* If some users were already known to be associated with NFTs before registering the listener, the listener will be called for every user.
* @param {function(id: string)} listener
*/
export function addNftUserListener(listener) {
nftUserListeners.push(listener);
for (const entry of knownStatus.entries()) {
if (entry[1]) {
listener(entry[0]);
}
}
}
/**
* Sets whether a user id is associated with NFTs.
* @param {string} id
* @param {boolean} status
*/
export function setUserNftStatus(id, status) {
usersInQueue.delete(id);
usersPending.delete(id);
if (knownStatus.get(id) !== status) {
knownStatus.set(id, status);
if (status) {
for (const listener of nftUserListeners) {
try {
listener(id);
} catch (e) {
console.error("Error in NFT user listener: " + e);
}
}
}
}
if (deferredCallbacks.has(id)) {
for (const callback of deferredCallbacks.get(id)) {
callback(status);
}
deferredCallbacks.delete(id);
}
}
/**
* Calls the provided callback function with the result of whether a user id is associated with NFTs.
* @param {string} id
* @param {function(nft: boolean)} [callback]
*/
export const checkUserNftStatus = isSupported() ? checkUserNftStatusCallback : function(id, callback) {
callback && callback(false);
};
/**
* Utility function that returns the user id from a tweet.
* @param {ChirpBase} tweet
* @returns {string|null}
*/
export function getTweetUserId(tweet) {
const user = tweet.user;
return typeof user === "object" && typeof user.id === "string" ? user.id : null;
}
/**
* Clears known status of users who are not associated with NFTs, in case they became associated with NFTs in the meantime.
*/
window.setInterval(function() {
for (const entry of knownStatus.entries()) {
if (!entry[1]) {
knownStatus.delete(entry[0]);
}
}
}, 1000 * 60 * 60);

View File

@@ -0,0 +1,54 @@
import { $TDX } from "../api/bridge.js";
import { replaceFunction } from "../api/patch.js";
import { TD } from "../api/td.js";
import { ensurePropertyExists } from "../api/utils.js";
import { addNftUserListener, checkUserNftStatus, checkUserNftStatusImmediately, getTweetUserId, setUserNftStatus } from "./globals/user_nft_status.js";
export default function() {
if (!$TDX.hideTweetsByNftUsers) {
return;
}
ensurePropertyExists(TD, "controller", "clients", "getPreferredClient");
ensurePropertyExists(TD, "services", "TwitterClient", "prototype", "addIdToMuteList");
ensurePropertyExists(TD, "services", "TwitterUser", "prototype");
ensurePropertyExists(TD, "vo", "Column", "prototype", "addItemsToIndex");
addNftUserListener(function(id) {
TD.controller.clients.getPreferredClient().addIdToMuteList(id);
});
replaceFunction(TD.services.TwitterUser.prototype, "fromJSONObject", function(func, args) {
/** @type {TwitterUser} */
const user = func.apply(this, args);
if (args.length > 0 && typeof args[0] === "object") {
const id = user.id;
const json = args[0];
if ("ext_has_nft_avatar" in json) {
setUserNftStatus(id, json.ext_has_nft_avatar === true);
}
else {
checkUserNftStatus(id);
}
}
return user;
});
replaceFunction(TD.vo.Column.prototype, "mergeAndRenderChirps", function(func, args) {
/** @type ChirpBase[] */
const tweets = args[0];
if (Array.isArray(tweets)) {
for (let i = tweets.length - 1; i >= 0; i--) {
if (checkUserNftStatusImmediately(getTweetUserId(tweets[i]))) {
tweets.splice(i, 1);
}
}
}
return func.apply(this, args);
});
};

View File

@@ -20,7 +20,11 @@ export default function() {
ensurePropertyExists(TD, "controller", "columnManager", "_columnOrder");
ensurePropertyExists(TD, "controller", "columnManager", "move");
$(document).on("uiSearchNoTemporaryColumn", function(e, /** @type SearchEventData */ data) {
/**
* @param e
* @param {SearchEventData} data
*/
const onSearch = function(e, data) {
if (data.query && data.searchScope !== "users" && !data.columnKey && $TDX.openSearchInFirstColumn) {
const order = TD.controller.columnManager._columnOrder;
@@ -32,5 +36,7 @@ export default function() {
TD.controller.columnManager.move(columnKey, "left");
}
}
});
};
$(document).on("uiSearchNoTemporaryColumn", onSearch);
};

View File

@@ -4,7 +4,11 @@ import { $ } from "../api/jquery.js";
* Creates a `tduckOldComposerActive` event on the `document` object, which triggers when the composer is activated.
*/
export default function() {
$(document).on("uiDrawerActive uiRwebComposerOptOut", function(e, /** @type {{ activeDrawer: string }} */ data) {
/**
* @param e
* @param {{ activeDrawer: string }} data
*/
const onDrawerEvent = function(e, data) {
if (e.type === "uiDrawerActive" && data.activeDrawer !== "compose") {
return;
}
@@ -12,5 +16,7 @@ export default function() {
setTimeout(function() {
$(document).trigger("tduckOldComposerActive");
}, 0);
});
};
$(document).on("uiDrawerActive uiRwebComposerOptOut", onDrawerEvent);
};

View File

@@ -4,6 +4,7 @@ import { replaceFunction } from "../api/patch.js";
import { TD } from "../api/td.js";
import { checkPropertyExists, ensurePropertyExists } from "../api/utils.js";
import { getColumnName } from "./globals/get_column_name.js";
import { checkUserNftStatus, getTweetUserId } from "./globals/user_nft_status.js";
/**
* Event callback for a new tweet.
@@ -67,20 +68,7 @@ const onNewTweet = (function() {
* @param {TD_Column} column
* @param {ChirpBase} tweet
*/
return function(column, tweet) {
if (tweet instanceof TD.services.TwitterConversation || tweet instanceof TD.services.TwitterConversationMessageEvent) {
if (checkTweetCache(recentMessages, tweet.id)) {
return;
}
}
else {
if (checkTweetCache(recentTweets, tweet.id)) {
return;
}
}
startRecentTweetTimer();
const showTweetNotification = function(column, tweet) {
if (column.model.getHasNotification()) {
const sensitive = isSensitive(tweet);
const previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
@@ -184,6 +172,36 @@ const onNewTweet = (function() {
$TD.onTweetSound();
}
};
/**
* @param {TD_Column} column
* @param {ChirpBase} tweet
*/
return function(column, tweet) {
if (tweet instanceof TD.services.TwitterConversation || tweet instanceof TD.services.TwitterConversationMessageEvent) {
if (checkTweetCache(recentMessages, tweet.id)) {
return;
}
}
else {
if (checkTweetCache(recentTweets, tweet.id)) {
return;
}
}
startRecentTweetTimer();
if (!$TDX.hideTweetsByNftUsers) {
showTweetNotification(column, tweet);
}
else {
checkUserNftStatus(getTweetUserId(tweet), function(nft) {
if (!nft) {
showTweetNotification(column, tweet);
}
});
}
};
})();
/**

View File

@@ -12,7 +12,7 @@ export default function() {
if (checkPropertyExists(TD, "services", "TwitterUser", "prototype")) {
replaceFunction(TD.services.TwitterUser.prototype, "fromJSONObject", function(func, args) {
/** @type TwitterUser */
/** @type {TwitterUser} */
const user = func.apply(this, args);
if (user.id === accountId) {

View File

@@ -106,6 +106,7 @@
<Compile Include="Browser\Handling\ContextMenuNotification.cs" />
<Compile Include="Browser\Handling\CustomKeyboardHandler.cs" />
<Compile Include="Browser\Handling\CustomLifeSpanHandler.cs" />
<Compile Include="Browser\Handling\DownloadRequestClient.cs" />
<Compile Include="Browser\Handling\DragHandlerBrowser.cs" />
<Compile Include="Browser\Handling\FileDialogHandler.cs" />
<Compile Include="Browser\Handling\JavaScriptDialogHandler.cs" />
@@ -138,7 +139,6 @@
<Compile Include="Updates\UpdateInstaller.cs" />
<Compile Include="Utils\BrowserUtils.cs" />
<Compile Include="Utils\NativeMethods.cs" />
<Compile Include="Utils\TwitterFileDownloader.cs" />
<Compile Include="Utils\WindowsUtils.cs" />
<Compile Include="Version.cs" />
</ItemGroup>
@@ -402,6 +402,7 @@
<None Include="Resources\Content\tweetdeck\globals\reload_columns.js" />
<None Include="Resources\Content\tweetdeck\globals\retrieve_tweet.js" />
<None Include="Resources\Content\tweetdeck\globals\show_tweet_detail.js" />
<None Include="Resources\Content\tweetdeck\globals\user_nft_status.js" />
<None Include="Resources\Content\tweetdeck\handle_extra_mouse_buttons.js" />
<None Include="Resources\Content\tweetdeck\hook_theme_settings.js" />
<None Include="Resources\Content\tweetdeck\inject_css.js" />
@@ -410,6 +411,7 @@
<None Include="Resources\Content\tweetdeck\make_retweets_lowercase.js" />
<None Include="Resources\Content\tweetdeck\middle_click_tweet_icon_actions.js" />
<None Include="Resources\Content\tweetdeck\move_accounts_above_hashtags_in_search.js" />
<None Include="Resources\Content\tweetdeck\mute_accounts_with_nft_avatars.js" />
<None Include="Resources\Content\tweetdeck\offline_notification.js" />
<None Include="Resources\Content\tweetdeck\open_search_externally.js" />
<None Include="Resources\Content\tweetdeck\open_search_in_first_column.js" />

View File

@@ -1,41 +0,0 @@
using System;
using System.Net;
using System.Threading.Tasks;
using CefSharp;
using TweetDuck.Management;
using TweetLib.Browser.Interfaces;
using TweetLib.Utils.Static;
using Cookie = CefSharp.Cookie;
namespace TweetDuck.Utils {
sealed class TwitterFileDownloader : IFileDownloader {
public static TwitterFileDownloader Instance { get; } = new TwitterFileDownloader();
private TwitterFileDownloader() {}
public string CacheFolder => BrowserCache.CacheFolder;
public void DownloadFile(string url, string path, Action onSuccess, Action<Exception> onFailure) {
const string authCookieName = "auth_token";
using ICookieManager cookies = Cef.GetGlobalCookieManager();
cookies.VisitUrlCookiesAsync(url, true).ContinueWith(task => {
string cookieStr = null;
if (task.Status == TaskStatus.RanToCompletion) {
Cookie found = task.Result?.Find(cookie => cookie.Name == authCookieName); // the list may be null
if (found != null) {
cookieStr = $"{found.Name}={found.Value}";
}
}
WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentChrome);
client.Headers[HttpRequestHeader.Cookie] = cookieStr;
client.DownloadFileCompleted += WebUtils.FileDownloadCallback(path, onSuccess, onFailure);
client.DownloadFileAsync(new Uri(url), path);
});
}
}
}

View File

@@ -6,6 +6,6 @@ using TweetDuck;
namespace TweetDuck {
internal static class Version {
public const string Tag = "1.21.1";
public const string Tag = "1.21.2";
}
}

View File

@@ -5,8 +5,7 @@ using TweetLib.Browser.Events;
namespace TweetLib.Browser.Interfaces {
public interface IBrowserComponent : IScriptExecutor {
string Url { get; }
IFileDownloader FileDownloader { get; }
string CacheFolder { get; }
event EventHandler<BrowserLoadedEventArgs> BrowserLoaded;
event EventHandler<PageLoadEventArgs> PageLoadStart;
@@ -14,5 +13,6 @@ namespace TweetLib.Browser.Interfaces {
void Setup(BrowserSetup setup);
void AttachBridgeObject(string name, object bridge);
void DownloadFile(string url, string path, Action? onSuccess, Action<Exception>? onError);
}
}

View File

@@ -1,8 +0,0 @@
using System;
namespace TweetLib.Browser.Interfaces {
public interface IFileDownloader {
string CacheFolder { get; }
void DownloadFile(string url, string path, Action? onSuccess, Action<Exception>? onError);
}
}

View File

@@ -1,6 +1,58 @@
namespace TweetLib.Browser.Interfaces {
using System;
using System.Globalization;
using System.Text;
namespace TweetLib.Browser.Interfaces {
public interface IScriptExecutor {
void RunFunction(string name, params object[] args);
void RunScript(string identifier, string script);
}
public static class ScriptExecutorExtensions {
public static void RunFunction(this IScriptExecutor executor, string name, params object?[] args) {
executor.RunScript("about:blank", GenerateJavaScriptFunctionCall(name, args));
}
private static string GenerateJavaScriptFunctionCall(string name, object?[] args) {
var builder = new StringBuilder();
builder.Append(name);
builder.Append('(');
for (var index = 0; index < args.Length; index++) {
var obj = args[index];
switch (obj) {
case null:
builder.Append("null");
break;
case bool b:
builder.Append(b.ToString().ToLowerInvariant());
break;
case sbyte or byte or short or ushort or int or uint or long or ulong or float or double or decimal:
builder.Append(Convert.ToString(obj, CultureInfo.InvariantCulture));
break;
default:
var str = obj.ToString() ?? string.Empty;
var escaped = str.Replace("\\", "\\\\")
.Replace("'", "\\'")
.Replace("\t", "\\t")
.Replace("\r", "\\r")
.Replace("\n", "\\n");
builder.Append('\'');
builder.Append(escaped);
builder.Append('\'');
break;
}
if (index < args.Length - 1) {
builder.Append(',');
}
}
return builder.Append(");").ToString();
}
}
}

View File

@@ -9,6 +9,7 @@ namespace TweetLib.Core.Application {
bool ExpandLinksOnHover { get; }
bool FirstRun { get; }
bool FocusDmInput { get; }
bool HideTweetsByNftUsers { get; }
bool IsCustomSoundNotificationSet { get; }
bool KeepLikeFollowDialogsOpen { get; }
bool MuteNotifications { get; }

View File

@@ -5,13 +5,13 @@ using TweetLib.Core.Features.Twitter;
using TweetLib.Utils.Static;
namespace TweetLib.Core.Features {
internal class BaseContextMenu : IContextMenuHandler {
class BaseContextMenu : IContextMenuHandler {
private readonly IBrowserComponent browser;
private readonly FileDownloadManager fileDownloadManager;
public BaseContextMenu(IBrowserComponent browser) {
this.browser = browser;
this.fileDownloadManager = new FileDownloadManager(browser.FileDownloader);
this.fileDownloadManager = new FileDownloadManager(browser);
}
public virtual void Show(IContextMenuBuilder menu, Context context) {

View File

@@ -14,15 +14,15 @@ namespace TweetLib.Core.Features {
public bool SupportsCopyingImage => App.SystemHandler.CopyImageFromFile != null;
public bool SupportsFileSaving => App.FileDialogs != null;
private readonly IFileDownloader fileDownloader;
private readonly IBrowserComponent browserComponent;
internal FileDownloadManager(IFileDownloader fileDownloader) {
this.fileDownloader = fileDownloader;
internal FileDownloadManager(IBrowserComponent browserComponent) {
this.browserComponent = browserComponent;
}
private void DownloadTempImage(string url, Action<string> process) {
string? staticFileName = TwitterUrls.GetImageFileName(url);
string file = Path.Combine(fileDownloader.CacheFolder, staticFileName ?? Path.GetRandomFileName());
string file = Path.Combine(browserComponent.CacheFolder, staticFileName ?? Path.GetRandomFileName());
if (staticFileName != null && FileUtils.FileExistsAndNotEmpty(file)) {
process(file);
@@ -36,7 +36,7 @@ namespace TweetLib.Core.Features {
App.MessageDialogs.Error("Image Download", "An error occurred while downloading the image: " + ex.Message);
}
fileDownloader.DownloadFile(url, file, OnSuccess, OnFailure);
browserComponent.DownloadFile(url, file, OnSuccess, OnFailure);
}
}
@@ -94,14 +94,14 @@ namespace TweetLib.Core.Features {
}
if (oneImage) {
fileDownloader.DownloadFile(firstImageLink, path, null, OnFailure);
browserComponent.DownloadFile(firstImageLink, path, null, OnFailure);
}
else {
string pathBase = Path.ChangeExtension(path, null);
string pathExt = Path.GetExtension(path);
for (int index = 0; index < urls.Length; index++) {
fileDownloader.DownloadFile(urls[index], $"{pathBase} {index + 1}{pathExt}", null, OnFailure);
browserComponent.DownloadFile(urls[index], $"{pathBase} {index + 1}{pathExt}", null, OnFailure);
}
}
});
@@ -129,7 +129,7 @@ namespace TweetLib.Core.Features {
App.MessageDialogs.Error("Video Download", "An error occurred while downloading the video: " + ex.Message);
}
fileDownloader.DownloadFile(url, path, null, OnError);
browserComponent.DownloadFile(url, path, null, OnError);
});
}
}

View File

@@ -5,7 +5,7 @@ using System.Text;
using TweetLib.Core.Systems.Configuration;
namespace TweetLib.Core.Features.Plugins.Config {
internal sealed class PluginConfigInstance : IConfigInstance {
sealed class PluginConfigInstance : IConfigInstance {
public PluginConfig Instance { get; }
private readonly string filename;

View File

@@ -11,7 +11,7 @@ using TweetLib.Utils.Static;
namespace TweetLib.Core.Features.Plugins {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class PluginBridge {
sealed class PluginBridge {
private readonly Dictionary<int, Plugin> tokens = new ();
private readonly Random rand = new ();

View File

@@ -13,7 +13,7 @@ namespace TweetLib.Core.Features {
static string Bool(bool value) => value ? "true;" : "false;";
static string Str(string value) => $"\"{value}\";";
StringBuilder build = new StringBuilder(384).Append("(function(x){");
StringBuilder build = new StringBuilder(414).Append("(function(x){");
build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover));
@@ -24,6 +24,7 @@ namespace TweetLib.Core.Features {
build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications));
build.Append("x.notificationMediaPreviews=").Append(Bool(config.NotificationMediaPreviews));
build.Append("x.translationTarget=").Append(Str(config.TranslationTarget));
build.Append("x.hideTweetsByNftUsers=").Append(Bool(config.HideTweetsByNftUsers));
build.Append("x.firstDayOfWeek=").Append(config.CalendarFirstDay == -1 ? JQuery.GetDatePickerDayOfWeek(Lib.Culture.DateTimeFormat.FirstDayOfWeek) : config.CalendarFirstDay);
}

View File

@@ -20,7 +20,7 @@ namespace TweetLib.Core.Features.TweetDeck {
private const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important;margin:0}';h.appendChild(e);},1)";
public TweetDeckFunctions Functions { get; }
public FileDownloadManager FileDownloadManager => new (browserComponent.FileDownloader);
public FileDownloadManager FileDownloadManager => new (browserComponent);
private readonly ISoundNotificationHandler soundNotificationHandler;
private readonly PluginManager pluginManager;
@@ -249,6 +249,9 @@ namespace TweetLib.Core.Features.TweetDeck {
case ResourceType.Xhr when url.Contains(UrlVersionCheck):
return RequestHandleResult.Cancel.Instance;
case ResourceType.Xhr when url.Contains("://api.twitter.com/") && url.Contains("include_entities=1") && !url.Contains("&include_ext_has_nft_avatar=1"):
return new RequestHandleResult.Redirect(url.Replace("include_entities=1", "include_entities=1&include_ext_has_nft_avatar=1"));
default:
return base.Handle(url, resourceType);
}

View File

@@ -3,7 +3,7 @@ using System.IO;
using TweetLib.Utils.Serialization;
namespace TweetLib.Core.Systems.Configuration {
internal sealed class FileConfigInstance<T> : IConfigInstance where T : IConfigObject<T> {
sealed class FileConfigInstance<T> : IConfigInstance where T : IConfigObject<T> {
public T Instance { get; }
private readonly SimpleObjectSerializer<T> serializer;

View File

@@ -28,21 +28,23 @@ namespace TweetLib.Utils.IO {
}
public void WriteFile(string[] identifier, string path) {
using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
WriteStreamImpl(JoinIdentifier(identifier), fileStream);
}
public void WriteFile(string identifier, string path) {
using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
WriteStreamImpl(ValidateIdentifier(identifier), fileStream);
}
public void WriteStream(string[] identifier, Stream sourceStream) {
WriteStreamImpl(JoinIdentifier(identifier), sourceStream);
public void WriteString(string[] identifier, string contents) {
using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
WriteStreamImpl(JoinIdentifier(identifier), memoryStream);
}
public void WriteStream(string identifier, Stream sourceStream) {
WriteStreamImpl(ValidateIdentifier(identifier), sourceStream);
public void WriteString(string identifier, string contents) {
using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
WriteStreamImpl(ValidateIdentifier(identifier), memoryStream);
}
private void WriteStreamImpl(string identifier, Stream sourceStream) {

View File

@@ -1,7 +1,7 @@
using System;
namespace TweetLib.Utils.Serialization.Converters {
internal sealed class ClrTypeConverter : ITypeConverter {
sealed class ClrTypeConverter : ITypeConverter {
public static ITypeConverter Instance { get; } = new ClrTypeConverter();
private ClrTypeConverter() {}

View File

@@ -26,12 +26,12 @@ type internal TestData =
static member singleFile =
TestData.setup (fun f ->
f.WriteStream("File 1", new MemoryStream(Encoding.UTF8.GetBytes("test file\n123")))
f.WriteString("File 1", "test file\n123")
)
static member singleFileWithMultiIdentifier =
TestData.setup (fun f ->
f.WriteStream([| "File 1"; "A"; "B" |], new MemoryStream(Encoding.UTF8.GetBytes("test file\n123")))
f.WriteString([| "File 1"; "A"; "B" |], "test file\n123")
)
static member singleFileStreams =
@@ -40,9 +40,9 @@ type internal TestData =
static member threeFiles =
TestData.setup (fun f ->
f.WriteStream("File 1", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 1")))
f.WriteStream("File 2", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 2")))
f.WriteStream("File 3", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 3")))
f.WriteString("File 1", "Contents of\nFile 1")
f.WriteString("File 2", "Contents of\nFile 2")
f.WriteString("File 3", "Contents of\nFile 3")
)
@@ -52,21 +52,21 @@ module Validation =
let ``an identifier containing '|' throws`` () =
TestData.setup (fun f ->
Assert.Throws<ArgumentException>(fun () ->
f.WriteStream("File|1", new MemoryStream(Array.empty))
f.WriteString("File|1", "")
) |> ignore
)
[<Fact>]
let ``an identifier 255 bytes long does not throw`` () =
TestData.setup (fun f ->
f.WriteStream(String.replicate 255 "a", new MemoryStream(Array.empty))
f.WriteString(String.replicate 255 "a", "")
)
[<Fact>]
let ``an identifier 256 bytes long throws`` () =
TestData.setup (fun f ->
Assert.Throws<ArgumentOutOfRangeException>(fun () ->
f.WriteStream(String.replicate 256 "a", new MemoryStream(Array.empty))
f.WriteString(String.replicate 256 "a", "")
) |> ignore
)