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

Compare commits

..

22 Commits
1.8.4 ... 1.8.5

Author SHA1 Message Date
a391d8ee83 Fix image pasting allowing more than 1 image in DMs 2017-08-05 21:52:38 +02:00
48c38f6e1d Include tweet author and quality in image download filename 2017-08-05 21:32:07 +02:00
37c5fba162 Change text color of sound notification file option for invalid paths 2017-08-05 19:50:30 +02:00
23e99b1d44 Update GC memory threshold defaults, also GC reload is enabled by default 2017-08-05 19:42:10 +02:00
8432240a47 Update HW acceleration & GC reload tooltips to note they won't be exported 2017-08-05 19:37:14 +02:00
a4bab743d6 Remove notification warning in GC reload option tooltip 2017-08-05 19:34:20 +02:00
60766789ab Move GC reload options to SystemConfig 2017-08-05 19:27:20 +02:00
ca014f881c Rewrite unknown property handling in FileSerializer 2017-08-05 19:23:42 +02:00
886eabe26c Show notifications that were missed during a browser reload 2017-08-05 18:43:57 +02:00
65b7167b5f Rewrite browser reload to save column notification state in session data 2017-08-05 18:36:17 +02:00
abbdde851e Make quoted tweets and RT account selectors square, fix RT account selector heading 2017-08-05 18:30:42 +02:00
54ac54aba6 Add session data that persists across browser reloads 2017-08-05 18:08:22 +02:00
184340f400 Increase delay for clearing recent notifications to prevent duplicates 2017-08-05 17:06:02 +02:00
93dd6813e8 Fix old icon alignment in 'Add column' dialog 2017-08-05 14:44:49 +02:00
b689b08711 Make follow notification button less visible when not hovered 2017-08-05 12:56:18 +02:00
1479a097d6 Fix alignment of old icons on buttons after TweetDeck update 2017-08-05 02:39:23 +02:00
e4967ea46d Add paragraphs and level 1-2 headings to update notification markdown renderer 2017-08-01 17:33:47 +02:00
3f28f18fb4 Release 1.8.4.1 2017-08-01 17:11:34 +02:00
1b90e0f65e Slightly increase default notification height 2017-08-01 17:05:31 +02:00
756ed649e6 Change default avatar shape to square, rename 'Default' to 'Legacy' 2017-08-01 17:03:29 +02:00
fbc423e2a7 Fix like/retweet notifications having invisible space with notification media previews disabled 2017-08-01 16:59:25 +02:00
f04cdb6a13 Fix PropertyBridge not updating properly 2017-08-01 16:58:46 +02:00
18 changed files with 236 additions and 89 deletions

View File

@@ -6,7 +6,7 @@ using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class SystemConfig{ sealed class SystemConfig{
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>{ private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>{
OnReadUnknownProperty = (obj, property, value) => false // HandleUnknownProperties = (obj, data) => {}
}; };
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) && public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
@@ -16,6 +16,9 @@ namespace TweetDuck.Configuration{
private bool _hardwareAcceleration = true; private bool _hardwareAcceleration = true;
public bool EnableBrowserGCReload { get; set; } = true;
public int BrowserMemoryThreshold { get; set; } = 400;
// SPECIAL PROPERTIES // SPECIAL PROPERTIES
public bool HardwareAcceleration{ public bool HardwareAcceleration{

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using TweetDuck.Core; using TweetDuck.Core;
@@ -10,9 +11,26 @@ using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class UserConfig{ sealed class UserConfig{
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
OnReadUnknownProperty = (obj, property, value) => false
}; private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
if (data.TryGetValue("EnableBrowserGCReload", out string propGCReload) && data.TryGetValue("BrowserMemoryThreshold", out string propMemThreshold)){
if (bool.TryParse(propGCReload, out bool isGCReloadEnabled) && isGCReloadEnabled && int.TryParse(propMemThreshold, out int memThreshold)){
// SystemConfig initialization was moved before UserConfig to allow for this
// TODO remove the migration soon
Program.SystemConfig.EnableBrowserGCReload = true;
Program.SystemConfig.BrowserMemoryThreshold = memThreshold;
Program.SystemConfig.Save();
}
}
data.Remove("EnableBrowserGCReload");
data.Remove("BrowserMemoryThreshold");
if (data.Count == 0){
obj.Save();
}
}
static UserConfig(){ static UserConfig(){
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter); Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
@@ -76,9 +94,6 @@ namespace TweetDuck.Configuration{
public string CustomCefArgs { get; set; } = null; public string CustomCefArgs { get; set; } = null;
public string CustomBrowserCSS { get; set; } = null; public string CustomBrowserCSS { get; set; } = null;
public string CustomNotificationCSS { get; set; } = null; public string CustomNotificationCSS { get; set; } = null;
public bool EnableBrowserGCReload { get; set; } = false;
public int BrowserMemoryThreshold { get; set; } = 350;
// SPECIAL PROPERTIES // SPECIAL PROPERTIES

View File

@@ -8,25 +8,25 @@ namespace TweetDuck.Core.Bridge{
public static string GenerateScript(Environment environment){ public static string GenerateScript(Environment environment){
string Bool(bool value){ string Bool(bool value){
return value ? "true," : "false,"; return value ? "true;" : "false;";
} }
StringBuilder build = new StringBuilder().Append("window.$TDX={"); StringBuilder build = new StringBuilder().Append("(function(x){");
build.Append("expandLinksOnHover:").Append(Bool(Program.UserConfig.ExpandLinksOnHover)); build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
if (environment == Environment.Browser){ if (environment == Environment.Browser){
build.Append("switchAccountSelectors:").Append(Bool(Program.UserConfig.SwitchAccountSelectors)); build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
build.Append("muteNotifications:").Append(Bool(Program.UserConfig.MuteNotifications)); build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
build.Append("hasCustomNotificationSound:").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0)); build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
build.Append("notificationMediaPreviews:").Append(Bool(Program.UserConfig.NotificationMediaPreviews)); build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
} }
if (environment == Environment.Notification){ if (environment == Environment.Notification){
build.Append("skipOnLinkClick:").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick)); build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
} }
return build.Append("}").ToString(); return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
} }
} }
} }

View File

@@ -1,8 +1,12 @@
using System.Windows.Forms; using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Resources;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
sealed class TweetDeckBridge{ sealed class TweetDeckBridge{
@@ -10,13 +14,28 @@ namespace TweetDuck.Core.Bridge{
public static string LastRightClickedImage = string.Empty; public static string LastRightClickedImage = string.Empty;
public static string LastHighlightedTweet = string.Empty; public static string LastHighlightedTweet = string.Empty;
public static string LastHighlightedQuotedTweet = string.Empty; public static string LastHighlightedQuotedTweet = string.Empty;
public static string LastHighlightedTweetAuthor = string.Empty;
public static string[] LastHighlightedTweetImages = StringUtils.EmptyArray; public static string[] LastHighlightedTweetImages = StringUtils.EmptyArray;
public static Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
public static void ResetStaticProperties(){ public static void ResetStaticProperties(){
LastRightClickedLink = LastRightClickedImage = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty; LastRightClickedLink = LastRightClickedImage = LastHighlightedTweet = LastHighlightedQuotedTweet = LastHighlightedTweetAuthor = string.Empty;
LastHighlightedTweetImages = StringUtils.EmptyArray; LastHighlightedTweetImages = StringUtils.EmptyArray;
} }
public static void RestoreSessionData(IFrame frame){
if (SessionData.Count > 0){
StringBuilder build = new StringBuilder().Append("window.TD_SESSION={");
foreach(KeyValuePair<string, string> kvp in SessionData){
build.Append(kvp.Key).Append(":'").Append(kvp.Value.Replace("'", "\\'")).Append("',");
}
ScriptLoader.ExecuteScript(frame, build.Append("}").ToString(), "gen:session");
SessionData.Clear();
}
}
private readonly FormBrowser form; private readonly FormBrowser form;
private readonly FormNotificationMain notification; private readonly FormNotificationMain notification;
@@ -45,10 +64,11 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(() => LastRightClickedImage = link); form.InvokeAsyncSafe(() => LastRightClickedImage = link);
} }
public void SetLastHighlightedTweet(string link, string quotedLink, string imageList){ public void SetLastHighlightedTweet(string link, string quotedLink, string author, string imageList){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
LastHighlightedTweet = link; LastHighlightedTweet = link;
LastHighlightedQuotedTweet = quotedLink; LastHighlightedQuotedTweet = quotedLink;
LastHighlightedTweetAuthor = author;
LastHighlightedTweetImages = imageList.Split(';'); LastHighlightedTweetImages = imageList.Split(';');
}); });
} }
@@ -80,6 +100,12 @@ namespace TweetDuck.Core.Bridge{
} }
} }
public void SetSessionData(string key, string value){
form.InvokeSafe(() => { // do not use InvokeAsyncSafe, return only after invocation
SessionData.Add(key, value);
});
}
public void LoadNextNotification(){ public void LoadNextNotification(){
notification.InvokeAsyncSafe(notification.FinishCurrentNotification); notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
} }

View File

@@ -204,6 +204,7 @@ namespace TweetDuck.Core{
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix); e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties(PropertyBridge.Environment.Browser); UpdateProperties(PropertyBridge.Environment.Browser);
TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js"); ScriptLoader.ExecuteFile(e.Frame, "code.js");
ReinjectCustomCSS(Config.CustomBrowserCSS); ReinjectCustomCSS(Config.CustomBrowserCSS);
@@ -215,8 +216,8 @@ namespace TweetDuck.Core{
TweetDeckBridge.ResetStaticProperties(); TweetDeckBridge.ResetStaticProperties();
if (Config.EnableBrowserGCReload){ if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, e.Browser, Config.BrowserMemoryThreshold); memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
} }
} }
} }
@@ -327,7 +328,7 @@ namespace TweetDuck.Core{
} }
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){ private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
browser.GetBrowser().Reload(); ReloadToTweetDeck();
} }
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){ private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
@@ -428,7 +429,7 @@ namespace TweetDuck.Core{
} }
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"gc&&gc();window.location.href='{TwitterUtils.TweetDeckURL}'"); browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
} }
// callback handlers // callback handlers
@@ -457,8 +458,8 @@ namespace TweetDuck.Core{
trayIcon.HasNotifications = false; trayIcon.HasNotifications = false;
} }
if (Config.EnableBrowserGCReload){ if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, browser.GetBrowser(), Config.BrowserMemoryThreshold); memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
} }
else{ else{
memoryUsageTracker.Stop(); memoryUsageTracker.Stop();

View File

@@ -32,6 +32,7 @@ namespace TweetDuck.Core.Handling{
private readonly Form form; private readonly Form form;
private string lastHighlightedTweetAuthor;
private string[] lastHighlightedTweetImageList; private string[] lastHighlightedTweetImageList;
protected ContextMenuBase(Form form){ protected ContextMenuBase(Form form){
@@ -40,9 +41,11 @@ namespace TweetDuck.Core.Handling{
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
bool hasTweetImage = !string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage); bool hasTweetImage = !string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage);
lastHighlightedTweetAuthor = TweetDeckBridge.LastHighlightedTweetAuthor;
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages; lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages;
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweetAuthor = string.Empty;
lastHighlightedTweetImageList = StringUtils.EmptyArray; lastHighlightedTweetImageList = StringUtils.EmptyArray;
} }
@@ -88,11 +91,11 @@ namespace TweetDuck.Core.Handling{
break; break;
case MenuSaveImage: case MenuSaveImage:
TwitterUtils.DownloadImage(GetImage(parameters), ImageQuality); TwitterUtils.DownloadImage(GetImage(parameters), lastHighlightedTweetAuthor, ImageQuality);
break; break;
case MenuSaveAllImages: case MenuSaveAllImages:
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, ImageQuality); TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthor, ImageQuality);
break; break;
case MenuCopyImageUrl: case MenuCopyImageUrl:

View File

@@ -67,7 +67,7 @@ namespace TweetDuck.Core.Notification{
get{ get{
switch(Program.UserConfig.NotificationSize){ switch(Program.UserConfig.NotificationSize){
default: default:
return BrowserUtils.Scale(118, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel)); return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*TweetNotification.FontSizeLevel));
case TweetNotification.Size.Custom: case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Height; return Program.UserConfig.CustomNotificationSize.Height;

View File

@@ -68,7 +68,7 @@
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17); this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
this.checkHardwareAcceleration.TabIndex = 0; this.checkHardwareAcceleration.TabIndex = 0;
this.checkHardwareAcceleration.Text = "Hardware Acceleration"; this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses your graphics card to improve performance.\r\nDisable if you experience issues with rendering."); this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile.");
this.checkHardwareAcceleration.UseVisualStyleBackColor = true; this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
// //
// btnEditCefArgs // btnEditCefArgs
@@ -142,26 +142,14 @@
0, 0,
0}); 0});
this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82); this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82);
this.numMemoryThreshold.Maximum = new decimal(new int[] { this.numMemoryThreshold.Maximum = 2000;
3000, this.numMemoryThreshold.Minimum = 200;
0,
0,
0});
this.numMemoryThreshold.Minimum = new decimal(new int[] {
200,
0,
0,
0});
this.numMemoryThreshold.Name = "numMemoryThreshold"; this.numMemoryThreshold.Name = "numMemoryThreshold";
this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20); this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
this.numMemoryThreshold.TabIndex = 4; this.numMemoryThreshold.TabIndex = 4;
this.numMemoryThreshold.TextSuffix = " MB"; this.numMemoryThreshold.TextSuffix = " MB";
this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value."); this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value.");
this.numMemoryThreshold.Value = new decimal(new int[] { this.numMemoryThreshold.Value = 400;
350,
0,
0,
0});
// //
// checkBrowserGCReload // checkBrowserGCReload
// //
@@ -172,7 +160,7 @@
this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17); this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17);
this.checkBrowserGCReload.TabIndex = 3; this.checkBrowserGCReload.TabIndex = 3;
this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold"; this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold";
this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works\r\nif the browser is in a \'default state\', i.e. all modals and drawers are closed,\r\nand all columns are scrolled to top. Some notifications may be lost."); this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works if\r\nthe browser is in a \'default state\', i.e. all modals and drawers are closed, and\r\nall columns are scrolled to top. This option will not be exported in a profile.");
this.checkBrowserGCReload.UseVisualStyleBackColor = true; this.checkBrowserGCReload.UseVisualStyleBackColor = true;
// //
// labelApp // labelApp

View File

@@ -8,6 +8,8 @@ using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
partial class TabSettingsAdvanced : BaseTabSettings{ partial class TabSettingsAdvanced : BaseTabSettings{
private static SystemConfig SysConfig => Program.SystemConfig;
private readonly Action<string> reinjectBrowserCSS; private readonly Action<string> reinjectBrowserCSS;
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){ public TabSettingsAdvanced(Action<string> reinjectBrowserCSS){
@@ -16,16 +18,16 @@ namespace TweetDuck.Core.Other.Settings{
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;
if (SystemConfig.IsHardwareAccelerationSupported){ if (SystemConfig.IsHardwareAccelerationSupported){
checkHardwareAcceleration.Checked = Program.SystemConfig.HardwareAcceleration; checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
} }
else{ else{
checkHardwareAcceleration.Enabled = false; checkHardwareAcceleration.Enabled = false;
checkHardwareAcceleration.Checked = false; checkHardwareAcceleration.Checked = false;
} }
checkBrowserGCReload.Checked = Config.EnableBrowserGCReload; checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload;
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked; numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
numMemoryThreshold.SetValueSafe(Config.BrowserMemoryThreshold); numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => { BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
if (bytes == -1L){ if (bytes == -1L){
@@ -53,6 +55,10 @@ namespace TweetDuck.Core.Other.Settings{
btnRestartArgs.Click += btnRestartArgs_Click; btnRestartArgs.Click += btnRestartArgs_Click;
} }
public override void OnClosing(){
SysConfig.Save();
}
private void btnClearCache_Click(object sender, EventArgs e){ private void btnClearCache_Click(object sender, EventArgs e){
btnClearCache.Enabled = false; btnClearCache.Enabled = false;
BrowserCache.SetClearOnExit(); BrowserCache.SetClearOnExit();
@@ -60,18 +66,17 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){ private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
Program.SystemConfig.HardwareAcceleration = checkHardwareAcceleration.Checked; SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
Program.SystemConfig.Save(); PromptRestart(); // calls OnClosing
PromptRestart();
} }
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){ private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
Config.EnableBrowserGCReload = checkBrowserGCReload.Checked; SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked; numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
} }
private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){ private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
Config.BrowserMemoryThreshold = (int)numMemoryThreshold.Value; SysConfig.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
} }
private void btnEditCefArgs_Click(object sender, EventArgs e){ private void btnEditCefArgs_Click(object sender, EventArgs e){

View File

@@ -34,7 +34,7 @@ namespace TweetDuck.Core.Other.Settings{
private void tbCustomSound_TextChanged(object sender, EventArgs e){ private void tbCustomSound_TextChanged(object sender, EventArgs e){
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text); bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Maroon; tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
btnPlaySound.Enabled = !isEmpty; btnPlaySound.Enabled = !isEmpty;
btnResetSound.Enabled = !isEmpty; btnResetSound.Enabled = !isEmpty;
} }

View File

@@ -2,6 +2,7 @@
using CefSharp; using CefSharp;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
@@ -52,23 +53,32 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static void DownloadImage(string url, ImageQuality quality){ public static void DownloadImage(string url, string username, ImageQuality quality){
DownloadImages(new string[]{ url }, quality); DownloadImages(new string[]{ url }, username, quality);
} }
public static void DownloadImages(string[] urls, ImageQuality quality){ public static void DownloadImages(string[] urls, string username, ImageQuality quality){
if (urls.Length == 0){ if (urls.Length == 0){
return; return;
} }
string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(urls[0])); string firstImageLink = GetImageLink(urls[0], quality);
int qualityIndex = firstImageLink.LastIndexOf(':');
string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(firstImageLink));
string ext = Path.GetExtension(file); // includes dot string ext = Path.GetExtension(file); // includes dot
string[] fileNameParts = {
username,
Path.ChangeExtension(file, null),
qualityIndex == -1 ? string.Empty : firstImageLink.Substring(qualityIndex+1)
};
using(SaveFileDialog dialog = new SaveFileDialog{ using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
OverwritePrompt = urls.Length == 1, OverwritePrompt = urls.Length == 1,
Title = "Save image", Title = "Save image",
FileName = file, FileName = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{ext}",
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){ }){
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
@@ -77,14 +87,14 @@ namespace TweetDuck.Core.Utils{
} }
if (urls.Length == 1){ if (urls.Length == 1){
BrowserUtils.DownloadFileAsync(GetImageLink(urls[0], quality), dialog.FileName, null, OnFailure); BrowserUtils.DownloadFileAsync(firstImageLink, dialog.FileName, null, OnFailure);
} }
else{ else{
string pathBase = Path.ChangeExtension(dialog.FileName, null); string pathBase = Path.ChangeExtension(dialog.FileName, null);
string pathExt = Path.GetExtension(dialog.FileName); string pathExt = Path.GetExtension(dialog.FileName);
for(int index = 0; index < urls.Length; index++){ for(int index = 0; index < urls.Length; index++){
BrowserUtils.DownloadFileAsync(GetImageLink(urls[index], quality), pathBase+(index+1)+pathExt, null, OnFailure); BrowserUtils.DownloadFileAsync(GetImageLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure);
} }
} }
} }

View File

@@ -12,8 +12,8 @@ namespace TweetDuck.Data.Serialization{
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter(); private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
public delegate bool OnReadUnknownPropertyHandler(T obj, string property, string value); public delegate void HandleUnknownPropertiesHandler(T obj, Dictionary<string, string> data);
public OnReadUnknownPropertyHandler OnReadUnknownProperty { get; set; } public HandleUnknownPropertiesHandler HandleUnknownProperties { get; set; }
private readonly Dictionary<string, PropertyInfo> props; private readonly Dictionary<string, PropertyInfo> props;
private readonly Dictionary<Type, ITypeConverter> converters; private readonly Dictionary<Type, ITypeConverter> converters;
@@ -51,6 +51,8 @@ namespace TweetDuck.Data.Serialization{
} }
public void Read(string file, T obj){ public void Read(string file, T obj){
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){ using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
if (reader.Peek() <= 1){ if (reader.Peek() <= 1){
throw new FormatException("Input appears to be a binary file."); throw new FormatException("Input appears to be a binary file.");
@@ -78,11 +80,19 @@ namespace TweetDuck.Data.Serialization{
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})"); throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
} }
} }
else if (!(OnReadUnknownProperty?.Invoke(obj, property, value) ?? false)){ else{
throw new SerializationException($"Invalid file format, unknown property: {property}+"); unknownProperties[property] = value;
} }
} }
} }
if (unknownProperties.Count > 0){
HandleUnknownProperties?.Invoke(obj, unknownProperties);
if (unknownProperties.Count > 0){
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}+");
}
}
} }
private class BasicTypeConverter : ITypeConverter{ private class BasicTypeConverter : ITypeConverter{

View File

@@ -22,8 +22,8 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com"; public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.8.4"; public const string VersionTag = "1.8.4.1";
public const string VersionFull = "1.8.4"; public const string VersionFull = "1.8.4.1";
public static readonly Version Version = new Version(VersionTag); public static readonly Version Version = new Version(VersionTag);
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");
@@ -114,9 +114,9 @@ namespace TweetDuck{
return; return;
} }
} }
ReloadConfig();
SystemConfig = SystemConfig.Load(SystemConfigFilePath); SystemConfig = SystemConfig.Load(SystemConfigFilePath);
ReloadConfig();
if (Arguments.HasFlag(Arguments.ArgImportCookies)){ if (Arguments.HasFlag(Arguments.ArgImportCookies)){
ExportManager.ImportCookies(); ExportManager.ImportCookies();

View File

@@ -14,7 +14,7 @@ enabled(){
revertIcons: true, revertIcons: true,
smallComposeTextSize: false, smallComposeTextSize: false,
optimizeAnimations: true, optimizeAnimations: true,
avatarRadius: 10 avatarRadius: 2
}; };
this.firstTimeLoad = null; this.firstTimeLoad = null;
@@ -459,7 +459,8 @@ enabled(){
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold} .icon-list-filled:before{content:"\\f014";font-family:tweetdeckold}
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold} .icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
.column-type-icon { bottom: 26px !important } .drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
.column-header .column-type-icon { bottom: 26px !important }
.is-options-open .column-type-icon { bottom: 25px !important } .is-options-open .column-type-icon { bottom: 25px !important }
.tweet-footer { margin-top: 6px !important }`; .tweet-footer { margin-top: 6px !important }`;

View File

@@ -122,7 +122,7 @@
</div> </div>
<div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="10"> <div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="10">
<div class="td-avatar-shape" style="border-radius:10%"></div> <div class="td-avatar-shape" style="border-radius:10%"></div>
<label>Default</label> <label>Legacy</label>
</div> </div>
<div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="30"> <div class="td-avatar-shape-item-outer" data-td-key="avatarRadius" data-td-value="30">
<div class="td-avatar-shape" style="border-radius:30%"></div> <div class="td-avatar-shape" style="border-radius:30%"></div>

View File

@@ -108,7 +108,7 @@
recentTweetTimer = window.setTimeout(() => { recentTweetTimer = window.setTimeout(() => {
recentTweetTimer = null; recentTweetTimer = null;
recentTweets.clear(); recentTweets.clear();
}, 10000); }, 20000);
}; };
let checkRecentTweet = id => { let checkRecentTweet = id => {
@@ -165,6 +165,9 @@
} }
} }
} }
else if (tweet instanceof TD.services.TwitterActionOnTweet){
html.find(".js-media").remove();
}
html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to) html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to)
this.outerHTML = this.innerHTML; this.outerHTML = this.innerHTML;
@@ -388,12 +391,12 @@
return !!highlightedColumnObj; return !!highlightedColumnObj;
}; };
var updateHighlightedTweet = function(ele, obj, link, embeddedLink, imageList){ var updateHighlightedTweet = function(ele, obj, link, embeddedLink, author, imageList){
highlightedTweetEle = ele; highlightedTweetEle = ele;
highlightedTweetObj = obj; highlightedTweetObj = obj;
if (lastTweet !== link){ if (lastTweet !== link){
$TD.setLastHighlightedTweet(link, embeddedLink, imageList); $TD.setLastHighlightedTweet(link, embeddedLink, author, imageList);
lastTweet = link; lastTweet = link;
} }
}; };
@@ -423,18 +426,19 @@
if (tweet.chirpType === TD.services.ChirpBase.TWEET){ if (tweet.chirpType === TD.services.ChirpBase.TWEET){
let link = tweet.getChirpURL(); let link = tweet.getChirpURL();
let embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : ""; let embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
let username = tweet.getMainUser().screenName;
let images = tweet.hasImage() ? tweet.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";") : ""; let images = tweet.hasImage() ? tweet.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";") : "";
// TODO maybe handle embedded images too? // TODO maybe handle embedded images too?
updateHighlightedTweet(me, tweet, link || "", embedded || "", images); updateHighlightedTweet(me, tweet, link || "", embedded || "", username, images);
} }
else{ else{
updateHighlightedTweet(me, tweet, "", "", ""); updateHighlightedTweet(me, tweet, "", "", "", "");
} }
} }
} }
else if (e.type === "mouseleave"){ else if (e.type === "mouseleave"){
updateHighlightedTweet(null, null, "", "", ""); updateHighlightedTweet(null, null, "", "", "", "");
} }
}); });
})(); })();
@@ -518,7 +522,12 @@
app.delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(e){ app.delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(e){
for(let item of e.originalEvent.clipboardData.items){ for(let item of e.originalEvent.clipboardData.items){
if (item.type.startsWith("image/")){ if (item.type.startsWith("image/")){
$(this).closest(".rpl").find(".js-reply-popout").click(); // popout direct messages if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
if ($(".js-add-image-button").is(".is-disabled")){ // tweetdeck does not check upload count properly
return;
}
}
uploader.addFilesToUpload([ item.getAsFile() ]); uploader.addFilesToUpload([ item.getAsFile() ]);
$(".js-compose-text", ".js-docked-compose").focus(); $(".js-compose-text", ".js-docked-compose").focus();
@@ -702,10 +711,13 @@
addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in addRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }"); // fix cut off badge icon when zoomed in
addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item { border-radius: 1px !important; }"); // square-ify buttons, inputs, dialogs, menus, and media previews addRule(".btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item { border-radius: 1px !important; }"); // square-ify buttons, inputs, dialogs, menus, media previews
addRule(".dropdown-menu, .list-item-last { border-radius: 0 !important; }"); // square-ify dropdowns addRule(".dropdown-menu, .list-item-last, .quoted-tweet { border-radius: 0 !important; }"); // square-ify dropdowns, quoted tweets, and account selectors
addRule(".prf-header { border-radius: 0; }"); // fix user account header border addRule(".prf-header { border-radius: 0; }"); // fix user account header border
addRule(".accs li, .accs img { border-radius: 0 !important; }"); // square-ify retweet account selector
addRule(".accs-header { padding-left: 0 !important; }"); // fix retweet account selector heading
addRule(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar { border-radius: 0; }"); // square-ify scroll bars addRule(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar { border-radius: 0; }"); // square-ify scroll bars
addRule(".antiscroll-scrollbar-vertical { margin-top: 0; }"); // square-ify scroll bars addRule(".antiscroll-scrollbar-vertical { margin-top: 0; }"); // square-ify scroll bars
addRule(".antiscroll-scrollbar-horizontal { margin-left: 0; }"); // square-ify scroll bars addRule(".antiscroll-scrollbar-horizontal { margin-left: 0; }"); // square-ify scroll bars
@@ -730,6 +742,9 @@
addRule(".activity-header { align-items: center !important; margin-bottom: 4px; }"); // tweak alignment of avatar and text in notifications addRule(".activity-header { align-items: center !important; margin-bottom: 4px; }"); // tweak alignment of avatar and text in notifications
addRule(".activity-header .tweet-timestamp { line-height: unset; }"); // fix timestamp position in notifications addRule(".activity-header .tweet-timestamp { line-height: unset; }"); // fix timestamp position in notifications
addRule("html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { color: #000; border-color: #000; opacity: 0.25; }"); // make follow notification button nicer
addRule("html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu { color: #fff; border-color: #fff; opacity: 0.25; }"); // make follow notification button nicer
addRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }"); // remove weird border in the column container scrollbar addRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }"); // remove weird border in the column container scrollbar
addRule(".app-columns-container { bottom: 0 !important; }"); // move column container scrollbar to bottom to fit updated style addRule(".app-columns-container { bottom: 0 !important; }"); // move column container scrollbar to bottom to fit updated style
@@ -845,8 +860,19 @@
} }
// //
// Block: Memory cleanup check and execution. // Block: Custom reload function with memory cleanup.
// //
window.TDGF_reload = function(){
let session = TD.storage.feedController.getAll()
.filter(feed => !!feed.getTopSortIndex())
.reduce((obj, feed) => (obj[feed.privateState.key] = feed.getTopSortIndex(), obj), {});
$TD.setSessionData("gc", JSON.stringify(session)).then(() => {
window.gc && window.gc();
window.location.reload();
});
};
window.TDGF_tryRunCleanup = function(){ window.TDGF_tryRunCleanup = function(){
// all textareas are empty // all textareas are empty
if ($("textarea").is(function(){ if ($("textarea").is(function(){
@@ -873,11 +899,62 @@
} }
// cleanup // cleanup
window.gc && window.gc(); window.TDGF_reload();
window.location.reload();
return true; return true;
}; };
if (window.TD_SESSION && window.TD_SESSION.gc){
var state;
try{
state = JSON.parse(window.TD_SESSION.gc);
}catch(err){
$TD.crashDebug("Invalid session gc data: "+window.TD_SESSION.gc);
state = {};
}
var showMissedNotifications = function(){
let tweets = [];
let columns = {};
let tmp = new TD.services.ChirpBase;
for(let column of Object.values(TD.controller.columnManager.getAll())){
for(let feed of column.getFeeds()){
if (feed.privateState.key in state){
tmp.sortIndex = state[feed.privateState.key];
for(let tweet of [].concat.apply([], column.updateArray.map(function(chirp){
return chirp.getUnreadChirps(tmp);
}))){
tweets.push(tweet);
columns[tweet.id] = column;
}
}
}
}
tweets.sort(TD.util.chirpReverseColumnSort);
for(let tweet of tweets){
onNewTweet(columns[tweet.id], tweet);
}
};
$(document).one("dataColumnsLoaded", function(){
let columns = Object.values(TD.controller.columnManager.getAll());
let remaining = columns.length;
for(let column of columns){
column.ui.getChirpContainer().one("dataColumnFeedUpdated", () => {
if (--remaining === 0){
setTimeout(showMissedNotifications, 1);
}
});
}
});
}
// //
// Block: Disable TweetDeck metrics. // Block: Disable TweetDeck metrics.
// //
@@ -902,6 +979,8 @@
onAppReady.forEach(func => func()); onAppReady.forEach(func => func());
onAppReady = null; onAppReady = null;
delete window.TD_SESSION;
if (window.TD_PLUGINS){ if (window.TD_PLUGINS){
window.TD_PLUGINS.onReady(); window.TD_PLUGINS.onReady();
} }

View File

@@ -98,7 +98,7 @@
// //
window.TDPF_requestReload = function(){ window.TDPF_requestReload = function(){
if (!isReloading){ if (!isReloading){
window.setTimeout(() => location.reload(), 1); window.setTimeout(window.TDGF_reload, 1);
isReloading = true; isReloading = true;
} }
}; };

View File

@@ -131,11 +131,15 @@
} }
#tweetduck-changelog p { #tweetduck-changelog p {
margin: 0 0 2px 30px; margin: 8px 8px 0 6px;
}
#tweetduck-changelog p.li {
margin: 0 8px 2px 30px;
display: list-item; display: list-item;
} }
#tweetduck-changelog p.l2 { #tweetduck-changelog .l2 {
margin-left: 50px; margin-left: 50px;
} }
@@ -268,13 +272,15 @@
return md.replace(/&/g, "&amp;") return md.replace(/&/g, "&amp;")
.replace(/</g, "&lt;") .replace(/</g, "&lt;")
.replace(/>/g, "&gt;") .replace(/>/g, "&gt;")
.replace(/^##? (.*?)$/gm, "<h2>$1</h2>")
.replace(/^### (.*?)$/gm, "<h3>$1</h3>") .replace(/^### (.*?)$/gm, "<h3>$1</h3>")
.replace(/^- (.*?)$/gm, "<p>$1</p>") .replace(/^- (.*?)$/gm, "<p class='li'>$1</p>")
.replace(/^ - (.*?)$/gm, "<p class='l2'>$1</p>") .replace(/^ - (.*?)$/gm, "<p class='li l2'>$1</p>")
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>") .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
.replace(/\*(.*?)\*/g, "<em>$1</em>") .replace(/\*(.*?)\*/g, "<em>$1</em>")
.replace(/`(.*?)`/g, "<code>$1</code>") .replace(/`(.*?)`/g, "<code>$1</code>")
.replace(/\[(.*?)\]\((.*?)\)/g, "<a href='$2'>$1</a>") .replace(/\[(.*?)\]\((.*?)\)/g, "<a href='$2'>$1</a>")
.replace(/^([a-z0-9].*?)$/gmi, "<p>$1</p>")
.replace(/\n\r?\n/g, "<br>"); .replace(/\n\r?\n/g, "<br>");
}; };