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

Compare commits

..

50 Commits

Author SHA1 Message Date
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
63b58b1cfe Release 1.8.4 2017-08-01 15:07:03 +02:00
77e656d8e4 Tweak JS prompt dialog layout on high DPI 2017-08-01 15:06:09 +02:00
a673957bd0 Tweak JS prompt dialog layout 2017-08-01 14:54:21 +02:00
c99a0c9974 Add Layout & Design plugin button to the TweetDeck settings modal 2017-08-01 13:45:44 +02:00
0fb06d0ff2 Remove reply revert option from edit-design plugin 2017-08-01 12:28:52 +02:00
c51eebfe22 Add new unit tests for TwitterUtils and CombinedFileStream 2017-07-31 22:27:02 +02:00
a51b34b48f Move CommandLineArgsParser code to CommandLineArgs 2017-07-31 22:26:48 +02:00
1b239bada1 Delay screenshots again due to iframes 2017-07-31 21:17:31 +02:00
50ab1a6ac3 Improve login/logout page design 2017-07-31 20:29:07 +02:00
f181f1fadc Refactor PropertyBridge 2017-07-31 19:58:23 +02:00
c686349922 Refactor Program (tweak properties, move locking code) 2017-07-31 18:04:04 +02:00
5f44a1f4ad Fix semicolons in code.js 2017-07-31 14:58:42 +02:00
a968938832 Move square scrollbars from edit-design plugin to code.js 2017-07-31 14:55:31 +02:00
8d67f3dfdc Move code.js notification setup and fix dropdown border radius 2017-07-31 14:42:26 +02:00
973ae8cb5d Move twitter account regex to TwitterUtils 2017-07-31 14:31:32 +02:00
a4747b0d7b Add JS dialog handler to notifications 2017-07-31 14:25:00 +02:00
f07640cc84 Reorganize CEF handlers 2017-07-31 14:24:42 +02:00
c235c55b19 Add option to show media previews in notification 2017-07-31 14:12:24 +02:00
485ef684be Prevent notification keyboard controls from triggering in dev tools 2017-07-31 13:36:44 +02:00
7caca22e57 Remove 'TweetDuck' from JS dialog captions 2017-07-31 01:42:22 +02:00
f1d9e32bf5 Add keyboard controls to notifications
Closes #153
2017-07-31 01:23:57 +02:00
23d5fa3107 Tweak emoji keyboard border radius and character count width 2017-07-30 23:58:35 +02:00
4e7d8aba1c Improve FormMessage to match MessageBox closer and look better on high DPI 2017-07-30 23:50:24 +02:00
98ba871a71 Fix back mouse button ignoring columns inside User modals
Closes #155
2017-07-30 21:38:38 +02:00
3ff23c0264 Remove unnecessary TweetDeck logo CSS rule 2017-07-30 21:29:02 +02:00
e21f89477b Fix ISerializedObject not being removed from unit tests and csproj file 2017-07-30 21:28:26 +02:00
f177f514f5 Fix column type icons jumping when opening column settings 2017-07-30 21:19:03 +02:00
af30f3b348 Square-ify many elements of TweetDeck (buttons, inputs, dialogs, menus, previews) 2017-07-30 21:15:39 +02:00
82df618429 Fix code.js after refactoring CSS insertion 2017-07-30 21:13:45 +02:00
bb3538e270 Refocus tweet textarea after selecting a different account
Closes #156
2017-07-30 20:36:17 +02:00
71925e1126 Refactor parts of code.js (make code shorter, use 'let') 2017-07-30 20:19:59 +02:00
93c1cbd231 Update SystemConfig to use FileSerializer and migrate old files 2017-07-30 19:54:28 +02:00
894b890fe5 Tweak serialization code and remove ISerializedObject 2017-07-30 19:28:03 +02:00
8e9e8f7fad Fix magic number and add a comment 2017-07-30 19:02:30 +02:00
2a0461a76f Add safeguards for accessing TweetDeckBridge.LastHighlightedTweetImages 2017-07-21 12:43:10 +02:00
85f923a6fc Add StringUtils.EmptyArray and use it instead of new string[0] 2017-07-21 12:37:30 +02:00
b35e4d4d01 Add "Save all images as..." context menu option for tweets with multiple images 2017-07-21 12:14:15 +02:00
cb24a859f4 Fix file type description in Save image dialog 2017-07-21 11:16:47 +02:00
b1ef00746f Hide open/copy link context menu items for media previews 2017-07-21 11:07:40 +02:00
aebe82e3a7 Add context menu for image previews that use background-image 2017-07-21 10:46:28 +02:00
7c87856b4d Show waiting cursor while taking a tweet screenshot 2017-07-20 16:29:39 +02:00
d1b1dd539f Add an option to use :orig image links in context menu 2017-07-17 05:39:59 +02:00
55eea88ace Add twitter image link & download methods to TwitterUtils 2017-07-17 05:10:06 +02:00
a70f64e1f6 Move some stuff from BrowserUtils to a new TwitterUtils class 2017-07-17 02:09:20 +02:00
fa0cb120a7 Add a 'Close' button to the modal dialog in the template plugin
Closes #143
2017-07-13 05:57:12 +02:00
e3080d07dc Ensure plugin config exists after first run, fixes profile export crash
Closes #147
2017-07-13 05:21:22 +02:00
53 changed files with 845 additions and 544 deletions

View File

@@ -2,28 +2,26 @@
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class LockManager{ sealed class LockManager{
private const int RetryDelay = 250;
public enum Result{ public enum Result{
Success, HasProcess, Fail Success, HasProcess, Fail
} }
public Process LockingProcess { get; private set; }
private readonly string file; private readonly string file;
private FileStream lockStream; private FileStream lockStream;
private Process lockingProcess;
public LockManager(string file){ public LockManager(string file){
this.file = file; this.file = file;
} }
private void CreateLockFileStream(){ // Lock file
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
WriteIntToStream(lockStream, WindowsUtils.CurrentProcessID);
lockStream.Flush(true);
}
private bool ReleaseLockFileStream(){ private bool ReleaseLockFileStream(){
if (lockStream != null){ if (lockStream != null){
@@ -37,8 +35,10 @@ namespace TweetDuck.Configuration{
} }
private Result TryCreateLockFile(){ private Result TryCreateLockFile(){
if (lockStream != null){ void CreateLockFileStream(){
throw new InvalidOperationException("Lock file already exists."); lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
lockStream.Flush(true);
} }
try{ try{
@@ -60,6 +60,8 @@ namespace TweetDuck.Configuration{
} }
} }
// Lock management
public Result Lock(){ public Result Lock(){
if (lockStream != null){ if (lockStream != null){
return Result.Success; return Result.Success;
@@ -72,7 +74,9 @@ namespace TweetDuck.Configuration{
int pid; int pid;
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
pid = ReadIntFromStream(fileStream); byte[] bytes = new byte[sizeof(int)];
fileStream.Read(bytes, 0, bytes.Length);
pid = BitConverter.ToInt32(bytes, 0);
} }
try{ try{
@@ -80,7 +84,7 @@ namespace TweetDuck.Configuration{
using(Process currentProcess = Process.GetCurrentProcess()){ using(Process currentProcess = Process.GetCurrentProcess()){
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){ if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
LockingProcess = foundProcess; lockingProcess = foundProcess;
} }
else{ else{
foundProcess.Close(); foundProcess.Close();
@@ -91,7 +95,7 @@ namespace TweetDuck.Configuration{
// Process.MainModule can throw exceptions in some cases // Process.MainModule can throw exceptions in some cases
} }
return LockingProcess == null ? Result.Fail : Result.HasProcess; return lockingProcess == null ? Result.Fail : Result.HasProcess;
}catch{ }catch{
return Result.Fail; return Result.Fail;
} }
@@ -100,45 +104,72 @@ namespace TweetDuck.Configuration{
return initialResult; return initialResult;
} }
public bool Unlock(){ public Result LockWait(int timeout){
bool result = true; for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
Result result = Lock();
if (result == Result.HasProcess){
Thread.Sleep(RetryDelay);
}
else{
return result;
}
}
return Lock();
}
public bool Unlock(){
if (ReleaseLockFileStream()){ if (ReleaseLockFileStream()){
try{ try{
File.Delete(file); File.Delete(file);
}catch(Exception e){ }catch(Exception e){
Program.Reporter.Log(e.ToString()); Program.Reporter.Log(e.ToString());
result = false; return false;
} }
} }
return result; return true;
}
// Locking process
public bool RestoreLockingProcess(int failTimeout){
if (lockingProcess != null){
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, Program.WindowRestoreMessage, new UIntPtr((uint)lockingProcess.Id), IntPtr.Zero);
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
return true;
}
}
}
return false;
} }
public bool CloseLockingProcess(int closeTimeout, int killTimeout){ public bool CloseLockingProcess(int closeTimeout, int killTimeout){
if (LockingProcess != null){ if (lockingProcess != null){
try{ try{
if (LockingProcess.CloseMainWindow()){ if (lockingProcess.CloseMainWindow()){
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
} }
if (!LockingProcess.HasExited){ if (!lockingProcess.HasExited){
LockingProcess.Kill(); lockingProcess.Kill();
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
} }
if (LockingProcess.HasExited){ if (lockingProcess.HasExited){
LockingProcess.Dispose(); lockingProcess.Dispose();
LockingProcess = null; lockingProcess = null;
return true; return true;
} }
}catch(Exception ex){ }catch(Exception ex){
if (ex is InvalidOperationException || ex is Win32Exception){ if (ex is InvalidOperationException || ex is Win32Exception){
if (LockingProcess != null){ if (lockingProcess != null){
LockingProcess.Refresh(); bool hasExited = CheckLockingProcessExited();
lockingProcess.Dispose();
bool hasExited = LockingProcess.HasExited;
LockingProcess.Dispose();
return hasExited; return hasExited;
} }
} }
@@ -150,21 +181,8 @@ namespace TweetDuck.Configuration{
} }
private bool CheckLockingProcessExited(){ private bool CheckLockingProcessExited(){
LockingProcess.Refresh(); lockingProcess.Refresh();
return LockingProcess.HasExited; return lockingProcess.HasExited;
}
// Utility functions
private static void WriteIntToStream(Stream stream, int value){
byte[] id = BitConverter.GetBytes(value);
stream.Write(id, 0, id.Length);
}
private static int ReadIntFromStream(Stream stream){
byte[] bytes = new byte[4];
stream.Read(bytes, 0, 4);
return BitConverter.ToInt32(bytes, 0);
} }
} }
} }

View File

@@ -1,43 +1,40 @@
using System; using System;
using System.IO; using System.IO;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
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>{
OnReadUnknownProperty = (obj, property, value) => false
};
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")) &&
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll")); File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));
// CONFIGURATION DATA
private bool _hardwareAcceleration = true;
// SPECIAL PROPERTIES
public bool HardwareAcceleration{ public bool HardwareAcceleration{
get => hardwareAcceleration && IsHardwareAccelerationSupported; get => _hardwareAcceleration && IsHardwareAccelerationSupported;
set => hardwareAcceleration = value; set => _hardwareAcceleration = value;
} }
// END OF CONFIG
private readonly string file; private readonly string file;
private bool hardwareAcceleration;
private SystemConfig(string file){ private SystemConfig(string file){
this.file = file; this.file = file;
HardwareAcceleration = true;
}
private void WriteToStream(Stream stream){
stream.WriteByte((byte)(HardwareAcceleration ? 1 : 0));
}
private void ReadFromStream(Stream stream){
HardwareAcceleration = stream.ReadByte() > 0;
} }
public bool Save(){ public bool Save(){
try{ try{
WindowsUtils.CreateDirectoryForFile(file); WindowsUtils.CreateDirectoryForFile(file);
Serializer.Write(file, this);
using(Stream stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None)){
WriteToStream(stream);
}
return true; return true;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
@@ -49,11 +46,20 @@ namespace TweetDuck.Configuration{
SystemConfig config = new SystemConfig(file); SystemConfig config = new SystemConfig(file);
try{ try{
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){ Serializer.Read(file, config);
config.ReadFromStream(stream); return config;
}
}catch(FileNotFoundException){ }catch(FileNotFoundException){
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
}catch(FormatException){
try{
using(Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)){
config.HardwareAcceleration = stream.ReadByte() > 0;
}
config.Save();
}catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not update the system configuration file.", true, e);
}
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e);
} }

View File

@@ -9,8 +9,10 @@ using TweetDuck.Data;
using TweetDuck.Data.Serialization; using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class UserConfig : ISerializedObject{ sealed class UserConfig{
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>(); private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{
OnReadUnknownProperty = (obj, property, value) => false
};
static UserConfig(){ static UserConfig(){
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter); Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
@@ -39,6 +41,7 @@ namespace TweetDuck.Configuration{
public bool ExpandLinksOnHover { get; set; } = true; public bool ExpandLinksOnHover { get; set; } = true;
public bool SwitchAccountSelectors { get; set; } = true; public bool SwitchAccountSelectors { get; set; } = true;
public bool BestImageQuality { get; set; } = true;
public bool EnableSpellCheck { get; set; } = false; public bool EnableSpellCheck { get; set; } = false;
private int _zoomLevel = 100; private int _zoomLevel = 100;
private bool _muteNotifications; private bool _muteNotifications;
@@ -50,6 +53,7 @@ namespace TweetDuck.Configuration{
public string DismissedUpdate { get; set; } = null; public string DismissedUpdate { get; set; } = null;
public bool DisplayNotificationColumn { get; set; } = false; public bool DisplayNotificationColumn { get; set; } = false;
public bool NotificationMediaPreviews { get; set; } = true;
public bool NotificationSkipOnLinkClick { get; set; } = false; public bool NotificationSkipOnLinkClick { get; set; } = false;
public bool NotificationNonIntrusiveMode { get; set; } = true; public bool NotificationNonIntrusiveMode { get; set; } = true;
public int NotificationIdlePauseSeconds { get; set; } = 0; public int NotificationIdlePauseSeconds { get; set; } = 0;
@@ -81,6 +85,8 @@ namespace TweetDuck.Configuration{
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation; public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty; public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;
public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default;
public string NotificationSoundPath{ public string NotificationSoundPath{
get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath; get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath;
set => _notificationSoundPath = value; set => _notificationSoundPath = value;
@@ -127,16 +133,14 @@ namespace TweetDuck.Configuration{
public event EventHandler ZoomLevelChanged; public event EventHandler ZoomLevelChanged;
public event EventHandler TrayBehaviorChanged; public event EventHandler TrayBehaviorChanged;
// END OF CONFIG
private readonly string file; private readonly string file;
public UserConfig(string file){ // TODO make private after removing UserConfigLegacy public UserConfig(string file){ // TODO make private after removing UserConfigLegacy
this.file = file; this.file = file;
} }
bool ISerializedObject.OnReadUnknownProperty(string property, string value){
return false;
}
public bool Save(){ public bool Save(){
try{ try{
WindowsUtils.CreateDirectoryForFile(file); WindowsUtils.CreateDirectoryForFile(file);

View File

@@ -1,45 +1,32 @@
using System; using System.Text;
using System.Text;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
static class PropertyBridge{ static class PropertyBridge{
[Flags] public enum Environment{
public enum Properties{ Browser, Notification
ExpandLinksOnHover = 1,
MuteNotifications = 2,
HasCustomNotificationSound = 4,
SkipOnLinkClick = 8,
SwitchAccountSelectors = 16,
AllBrowser = ExpandLinksOnHover | SwitchAccountSelectors | MuteNotifications | HasCustomNotificationSound,
AllNotification = ExpandLinksOnHover | SkipOnLinkClick
} }
public static string GenerateScript(Properties properties){ public static string GenerateScript(Environment environment){
StringBuilder build = new StringBuilder(); string Bool(bool value){
build.Append("(function(c){"); return value ? "true;" : "false;";
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
} }
if (properties.HasFlag(Properties.SwitchAccountSelectors)){ StringBuilder build = new StringBuilder().Append("(function(x){");
build.Append("c.switchAccountSelectors=").Append(Program.UserConfig.SwitchAccountSelectors ? "true;" : "false;");
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
if (environment == Environment.Browser){
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
} }
if (properties.HasFlag(Properties.MuteNotifications)){ if (environment == Environment.Notification){
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;"); build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
} }
if (properties.HasFlag(Properties.HasCustomNotificationSound)){ return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
build.Append("c.hasCustomNotificationSound=").Append(Program.UserConfig.NotificationSoundPath.Length > 0 ? "true;" : "false;");
}
if (properties.HasFlag(Properties.SkipOnLinkClick)){
build.Append("c.skipOnLinkClick=").Append(Program.UserConfig.NotificationSkipOnLinkClick ? "true;" : "false;");
}
build.Append("})(window.$TDX=window.$TDX||{})");
return build.ToString();
} }
} }
} }

View File

@@ -7,11 +7,14 @@ using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
sealed class TweetDeckBridge{ sealed class TweetDeckBridge{
public static string LastRightClickedLink = string.Empty; public static string LastRightClickedLink = 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[] LastHighlightedTweetImages = StringUtils.EmptyArray;
public static void ResetStaticProperties(){ public static void ResetStaticProperties(){
LastRightClickedLink = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty; LastRightClickedLink = LastRightClickedImage = LastHighlightedTweet = LastHighlightedQuotedTweet = string.Empty;
LastHighlightedTweetImages = StringUtils.EmptyArray;
} }
private readonly FormBrowser form; private readonly FormBrowser form;
@@ -38,10 +41,15 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(() => LastRightClickedLink = link); form.InvokeAsyncSafe(() => LastRightClickedLink = link);
} }
public void SetLastHighlightedTweet(string link, string quotedLink){ public void SetLastRightClickedImage(string link){
form.InvokeAsyncSafe(() => LastRightClickedImage = link);
}
public void SetLastHighlightedTweet(string link, string quotedLink, string imageList){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
LastHighlightedTweet = link; LastHighlightedTweet = link;
LastHighlightedQuotedTweet = quotedLink; LastHighlightedQuotedTweet = quotedLink;
LastHighlightedTweetImages = imageList.Split(';');
}); });
} }

View File

@@ -38,7 +38,7 @@
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = TweetDuck.Core.Utils.BrowserUtils.BackgroundColor; this.BackColor = TweetDuck.Core.Utils.TwitterUtils.BackgroundColor;
this.ClientSize = new System.Drawing.Size(324, 386); this.ClientSize = new System.Drawing.Size(324, 386);
this.Icon = Properties.Resources.icon; this.Icon = Properties.Resources.icon;
this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation; this.Location = TweetDuck.Core.Controls.ControlExtensions.InvisibleLocation;

View File

@@ -8,6 +8,7 @@ using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Screenshot; using TweetDuck.Core.Notification.Screenshot;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
@@ -25,6 +26,23 @@ namespace TweetDuck.Core{
sealed partial class FormBrowser : Form{ sealed partial class FormBrowser : Form{
private static UserConfig Config => Program.UserConfig; private static UserConfig Config => Program.UserConfig;
public bool IsWaiting{
set{
if (value){
browser.Enabled = false;
Cursor = Cursors.WaitCursor;
}
else{
browser.Enabled = true;
Cursor = Cursors.Default;
if (Focused){ // re-focus browser only if the window or a child is activated
browser.Focus();
}
}
}
}
public string UpdateInstallerPath { get; private set; } public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
@@ -81,7 +99,7 @@ namespace TweetDuck.Core{
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification)); this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge); this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
browser.BrowserSettings.BackgroundColor = (uint)BrowserUtils.BackgroundColor.ToArgb(); browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
browser.Dock = DockStyle.None; browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation; browser.Location = ControlExtensions.InvisibleLocation;
Controls.Add(browser); Controls.Add(browser);
@@ -158,7 +176,7 @@ namespace TweetDuck.Core{
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){ if (!e.IsLoading){
foreach(string word in BrowserUtils.DictionaryWords){ foreach(string word in TwitterUtils.DictionaryWords){
browser.AddWordToDictionary(word); browser.AddWordToDictionary(word);
} }
@@ -175,17 +193,17 @@ namespace TweetDuck.Core{
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel); BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
} }
if (BrowserUtils.IsTwitterWebsite(e.Frame)){ if (TwitterUtils.IsTwitterWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "twitter.js"); ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
} }
} }
} }
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){ if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
e.Frame.ExecuteJavaScriptAsync(BrowserUtils.BackgroundColorFix); e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties(PropertyBridge.Properties.AllBrowser); UpdateProperties(PropertyBridge.Environment.Browser);
ScriptLoader.ExecuteFile(e.Frame, "code.js"); ScriptLoader.ExecuteFile(e.Frame, "code.js");
ReinjectCustomCSS(Config.CustomBrowserCSS); ReinjectCustomCSS(Config.CustomBrowserCSS);
@@ -225,6 +243,10 @@ namespace TweetDuck.Core{
if (!isLoaded)return; if (!isLoaded)return;
trayIcon.HasNotifications = false; trayIcon.HasNotifications = false;
if (!browser.Enabled){ // when taking a screenshot, the window is unfocused and
browser.Enabled = true; // the browser is disabled; if the user clicks back into
} // the window, enable the browser again
} }
private void FormBrowser_LocationChanged(object sender, EventArgs e){ private void FormBrowser_LocationChanged(object sender, EventArgs e){
@@ -282,7 +304,7 @@ namespace TweetDuck.Core{
} }
private void Config_MuteToggled(object sender, EventArgs e){ private void Config_MuteToggled(object sender, EventArgs e){
UpdateProperties(PropertyBridge.Properties.MuteNotifications); UpdateProperties(PropertyBridge.Environment.Browser);
} }
private void Config_ZoomLevelChanged(object sender, EventArgs e){ private void Config_ZoomLevelChanged(object sender, EventArgs e){
@@ -401,12 +423,12 @@ namespace TweetDuck.Core{
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty); browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
} }
public void UpdateProperties(PropertyBridge.Properties properties){ public void UpdateProperties(PropertyBridge.Environment environment){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties)); browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
} }
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"gc&&gc();window.location.href='{BrowserUtils.TweetDeckURL}'"); browser.ExecuteScriptAsync($"gc&&gc();window.location.href='{TwitterUtils.TweetDeckURL}'");
} }
// callback handlers // callback handlers
@@ -442,7 +464,7 @@ namespace TweetDuck.Core{
memoryUsageTracker.Stop(); memoryUsageTracker.Stop();
} }
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.SwitchAccountSelectors | PropertyBridge.Properties.HasCustomNotificationSound); UpdateProperties(PropertyBridge.Environment.Browser);
notification.RequiresResize = true; notification.RequiresResize = true;
form.Dispose(); form.Dispose();

View File

@@ -1,35 +1,53 @@
using CefSharp; using System;
using System;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{ abstract class ContextMenuBase : IContextMenuHandler{
private static readonly Lazy<Regex> RegexTwitterAccount = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/([^/]+)/?$", RegexOptions.Compiled), false);
protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak")); protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
private static string GetLink(IContextMenuParams parameters){
return string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink;
}
private static string GetImage(IContextMenuParams parameters){
return string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage) ? parameters.SourceUrl : TweetDeckBridge.LastRightClickedImage;
}
private const int MenuOpenLinkUrl = 26500; private const int MenuOpenLinkUrl = 26500;
private const int MenuCopyLinkUrl = 26501; private const int MenuCopyLinkUrl = 26501;
private const int MenuCopyUsername = 26502; private const int MenuCopyUsername = 26502;
private const int MenuOpenImage = 26503; private const int MenuOpenImageUrl = 26503;
private const int MenuSaveImage = 26504; private const int MenuCopyImageUrl = 26504;
private const int MenuCopyImageUrl = 26505; private const int MenuSaveImage = 26505;
private const int MenuSaveAllImages = 26506;
private const int MenuOpenDevTools = 26599; private const int MenuOpenDevTools = 26599;
private readonly Form form; private readonly Form form;
private string[] lastHighlightedTweetImageList;
protected ContextMenuBase(Form form){ protected ContextMenuBase(Form form){
this.form = form; this.form = form;
} }
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){
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){ bool hasTweetImage = !string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage);
if (RegexTwitterAccount.Value.IsMatch(parameters.UnfilteredLinkUrl)){ lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages;
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweetImageList = StringUtils.EmptyArray;
}
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage){
if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open account in browser"); model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open account in browser");
model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy account address"); model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy account address");
model.AddItem((CefMenuCommand)MenuCopyUsername, "Copy account username"); model.AddItem((CefMenuCommand)MenuCopyUsername, "Copy account username");
@@ -42,10 +60,15 @@ namespace TweetDuck.Core.Handling{
model.AddSeparator(); model.AddSeparator();
} }
if (parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){ if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
model.AddItem((CefMenuCommand)MenuOpenImage, "Open image in browser"); model.AddItem((CefMenuCommand)MenuOpenImageUrl, "Open image in browser");
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as...");
model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address"); model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address");
model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as...");
if (lastHighlightedTweetImageList.Length > 1){
model.AddItem((CefMenuCommand)MenuSaveAllImages, "Save all images as...");
}
model.AddSeparator(); model.AddSeparator();
} }
} }
@@ -57,42 +80,27 @@ namespace TweetDuck.Core.Handling{
break; break;
case MenuCopyLinkUrl: case MenuCopyLinkUrl:
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink); SetClipboardText(GetLink(parameters));
break; break;
case MenuOpenImage: case MenuOpenImageUrl:
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl); BrowserUtils.OpenExternalBrowser(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality));
break; break;
case MenuSaveImage: case MenuSaveImage:
string fileName = GetImageFileName(parameters.SourceUrl); TwitterUtils.DownloadImage(GetImage(parameters), ImageQuality);
string extension = Path.GetExtension(fileName); break;
string saveTarget;
using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true,
OverwritePrompt = true,
Title = "Save image",
FileName = fileName,
Filter = "Image ("+(string.IsNullOrEmpty(extension) ? "unknown" : extension)+")|*.*"
}){
saveTarget = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
}
if (saveTarget != null){
BrowserUtils.DownloadFileAsync(parameters.SourceUrl, saveTarget, null, ex => {
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
});
}
case MenuSaveAllImages:
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, ImageQuality);
break; break;
case MenuCopyImageUrl: case MenuCopyImageUrl:
SetClipboardText(parameters.SourceUrl); SetClipboardText(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality));
break; break;
case MenuCopyUsername: case MenuCopyUsername:
Match match = RegexTwitterAccount.Value.Match(parameters.UnfilteredLinkUrl); Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl); SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
break; break;
@@ -104,7 +112,10 @@ namespace TweetDuck.Core.Handling{
return false; return false;
} }
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){} public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
TweetDeckBridge.LastRightClickedLink = string.Empty;
TweetDeckBridge.LastRightClickedImage = string.Empty;
}
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){ public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
return false; return false;
@@ -129,17 +140,5 @@ namespace TweetDuck.Core.Handling{
model.AddSeparator(); model.AddSeparator();
} }
} }
private static string GetImageFileName(string url){
// twimg adds a colon after file extension
int dot = url.LastIndexOf('.');
if (dot != -1){
url = StringUtils.ExtractBefore(url, ':', dot);
}
// return file name
return BrowserUtils.GetFileNameFromUrl(url) ?? "unknown";
}
} }
} }

View File

@@ -49,7 +49,7 @@ namespace TweetDuck.Core.Handling{
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet; lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet; lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
if (!BrowserUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweet = string.Empty; lastHighlightedTweet = string.Empty;
lastHighlightedQuotedTweet = string.Empty; lastHighlightedQuotedTweet = string.Empty;
} }

View File

@@ -1,7 +1,7 @@
using CefSharp; using System;
using System; using CefSharp;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling.General{
class BrowserProcessHandler : IBrowserProcessHandler{ class BrowserProcessHandler : IBrowserProcessHandler{
void IBrowserProcessHandler.OnContextInitialized(){ void IBrowserProcessHandler.OnContextInitialized(){
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){ using(IRequestContext ctx = Cef.GetGlobalRequestContext()){

View File

@@ -1,11 +1,12 @@
using CefSharp; using System.Drawing;
using CefSharp.WinForms;
using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling { namespace TweetDuck.Core.Handling.General{
class JavaScriptDialogHandler : IJsDialogHandler{ class JavaScriptDialogHandler : IJsDialogHandler{
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){ bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
((ChromiumWebBrowser)browserControl).InvokeSafe(() => { ((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
@@ -13,23 +14,25 @@ namespace TweetDuck.Core.Handling {
TextBox input = null; TextBox input = null;
if (dialogType == CefJsDialogType.Alert){ if (dialogType == CefJsDialogType.Alert){
form = new FormMessage("TweetDuck Browser Message", messageText, MessageBoxIcon.None); form = new FormMessage("Browser Message", messageText, MessageBoxIcon.None);
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused); form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
} }
else if (dialogType == CefJsDialogType.Confirm){ else if (dialogType == CefJsDialogType.Confirm){
form = new FormMessage("TweetDuck Browser Confirmation", messageText, MessageBoxIcon.None); form = new FormMessage("Browser Confirmation", messageText, MessageBoxIcon.None);
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel); form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel);
form.AddButton(FormMessage.Yes, ControlType.Focused); form.AddButton(FormMessage.Yes, ControlType.Focused);
} }
else if (dialogType == CefJsDialogType.Prompt){ else if (dialogType == CefJsDialogType.Prompt){
form = new FormMessage("TweetDuck Browser Prompt", messageText, MessageBoxIcon.None); form = new FormMessage("Browser Prompt", messageText, MessageBoxIcon.None);
form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel); form.AddButton(FormMessage.Cancel, DialogResult.Cancel, ControlType.Cancel);
form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused); form.AddButton(FormMessage.OK, ControlType.Accept | ControlType.Focused);
float dpiScale = form.GetDPIScale();
input = new TextBox{ input = new TextBox{
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom, Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
Location = new Point(27, form.ActionPanelY-46), Location = new Point(BrowserUtils.Scale(22, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)),
Size = new Size(form.ClientSize.Width-54, 20) Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44, dpiScale), 20)
}; };
form.Controls.Add(input); form.Controls.Add(input);

View File

@@ -1,7 +1,7 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling.General{
class LifeSpanHandler : ILifeSpanHandler{ class LifeSpanHandler : ILifeSpanHandler{
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){ public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
newBrowser = null; newBrowser = null;

View File

@@ -1,8 +1,8 @@
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using CefSharp; using CefSharp;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling.General{
abstract class RequestHandler : IRequestHandler{ abstract class RequestHandlerBase : IRequestHandler{
// Browser // Browser
public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){} public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}

View File

@@ -0,0 +1,38 @@
using CefSharp;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification;
namespace TweetDuck.Core.Handling {
sealed class KeyboardHandlerNotification : IKeyboardHandler{
private readonly FormNotificationBase notification;
public KeyboardHandlerNotification(FormNotificationBase notification){
this.notification = notification;
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
switch((Keys)windowsKeyCode){
case Keys.Enter:
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
return true;
case Keys.Escape:
notification.InvokeAsyncSafe(() => notification.HideNotification(true));
return true;
case Keys.Space:
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
return true;
}
}
return false;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
}
}
}

View File

@@ -1,7 +1,8 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Handling.General;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
class RequestHandlerBrowser : RequestHandler{ class RequestHandlerBrowser : RequestHandlerBase{
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){ public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
browser.Reload(); browser.Reload();
} }

View File

@@ -6,6 +6,7 @@ using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
@@ -97,6 +98,7 @@ namespace TweetDuck.Core.Notification{
this.browser = new ChromiumWebBrowser("about:blank"){ this.browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this, enableContextMenu), MenuHandler = new ContextMenuNotification(this, enableContextMenu),
JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler() LifeSpanHandler = new LifeSpanHandler()
}; };
@@ -111,7 +113,7 @@ namespace TweetDuck.Core.Notification{
this.dpiScale = this.GetDPIScale(); this.dpiScale = this.GetDPIScale();
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory; DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
handlerFactory.RegisterHandler(BrowserUtils.TweetDeckURL, this.resourceHandler); handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
Controls.Add(browser); Controls.Add(browser);
@@ -183,7 +185,7 @@ namespace TweetDuck.Core.Notification{
currentColumn = tweet.Column; currentColumn = tweet.Column;
resourceHandler.SetHTML(GetTweetHTML(tweet)); resourceHandler.SetHTML(GetTweetHTML(tweet));
browser.Load(BrowserUtils.TweetDeckURL); browser.Load(TwitterUtils.TweetDeckURL);
} }
protected virtual void SetNotificationSize(int width, int height){ protected virtual void SetNotificationSize(int width, int height){

View File

@@ -4,6 +4,7 @@ using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Plugins; using TweetDuck.Plugins;
@@ -66,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;
@@ -83,6 +84,8 @@ namespace TweetDuck.Core.Notification{
this.plugins = pluginManager; this.plugins = pluginManager;
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this)); browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge); browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
@@ -164,7 +167,7 @@ namespace TweetDuck.Core.Notification{
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){ if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.AllNotification)); e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier); ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){ if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){

View File

@@ -2,6 +2,7 @@
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -18,9 +19,11 @@ namespace TweetDuck.Core.Notification.Screenshot{
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback)); browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
browser.FrameLoadEnd += (sender, args) => { browser.LoadingStateChanged += (sender, args) => {
if (args.Frame.IsMain && browser.Address != "about:blank"){ if (!args.IsLoading){
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 67)", "gen:screenshot"); using(IFrame frame = args.Browser.MainFrame){
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 129)", "gen:screenshot");
}
} }
}; };
} }

View File

@@ -8,14 +8,14 @@ using TweetDuck.Plugins;
namespace TweetDuck.Core.Notification.Screenshot{ namespace TweetDuck.Core.Notification.Screenshot{
sealed class TweetScreenshotManager : IDisposable{ sealed class TweetScreenshotManager : IDisposable{
private readonly Form owner; private readonly FormBrowser owner;
private readonly PluginManager plugins; private readonly PluginManager plugins;
private readonly Timer timeout; private readonly Timer timeout;
private readonly Timer disposer; private readonly Timer disposer;
private FormNotificationScreenshotable screenshot; private FormNotificationScreenshotable screenshot;
public TweetScreenshotManager(Form owner, PluginManager pluginManager){ public TweetScreenshotManager(FormBrowser owner, PluginManager pluginManager){
this.owner = owner; this.owner = owner;
this.plugins = pluginManager; this.plugins = pluginManager;
@@ -28,8 +28,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
private void timeout_Tick(object sender, EventArgs e){ private void timeout_Tick(object sender, EventArgs e){
timeout.Stop(); timeout.Stop();
screenshot.Location = ControlExtensions.InvisibleLocation; OnFinished();
disposer.Start();
} }
private void disposer_Tick(object sender, EventArgs e){ private void disposer_Tick(object sender, EventArgs e){
@@ -50,6 +49,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, html, 0, string.Empty, string.Empty), width, height); screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, html, 0, string.Empty, string.Empty), width, height);
screenshot.Show(); screenshot.Show();
timeout.Start(); timeout.Start();
#if !(DEBUG && NO_HIDE_SCREENSHOTS)
owner.IsWaiting = true;
#endif
} }
private void Callback(){ private void Callback(){
@@ -61,14 +64,19 @@ namespace TweetDuck.Core.Notification.Screenshot{
screenshot.TakeScreenshot(); screenshot.TakeScreenshot();
#if !(DEBUG && NO_HIDE_SCREENSHOTS) #if !(DEBUG && NO_HIDE_SCREENSHOTS)
screenshot.Location = ControlExtensions.InvisibleLocation; OnFinished();
disposer.Start();
#else #else
screenshot.MoveToVisibleLocation(); screenshot.MoveToVisibleLocation();
screenshot.FormClosed += (sender, args) => disposer.Start(); screenshot.FormClosed += (sender, args) => disposer.Start();
#endif #endif
} }
private void OnFinished(){
screenshot.Location = ControlExtensions.InvisibleLocation;
owner.IsWaiting = false;
disposer.Start();
}
public void Dispose(){ public void Dispose(){
timeout.Dispose(); timeout.Dispose();
disposer.Dispose(); disposer.Dispose();

View File

@@ -9,7 +9,7 @@ namespace TweetDuck.Core.Notification{
private const string DefaultFontSizeClass = "medium"; private const string DefaultFontSizeClass = "medium";
private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>"; private const string DefaultHeadTag = @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}"; private const string CustomCSS = @"body:before{content:none}body{overflow-y:auto}.scroll-styled-v::-webkit-scrollbar{width:7px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}.scroll-styled-v::-webkit-scrollbar-track{border-left:0}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}.media-size-medium{height:calc(100vh - 16px)!important;max-height:240px;border-radius:1px!important}.js-quote-detail .media-size-medium{height:calc(100vh - 28px)!important;}.js-media.margin-vm, .js-media-preview-container.margin-vm{margin-bottom:0!important}";
public static int FontSizeLevel{ public static int FontSizeLevel{
get{ get{

View File

@@ -87,7 +87,7 @@ namespace TweetDuck.Core.Other{
this.prevLabelWidth = labelMessage.Width; this.prevLabelWidth = labelMessage.Width;
this.prevLabelHeight = labelMessage.Height; this.prevLabelHeight = labelMessage.Height;
this.minFormWidth = BrowserUtils.Scale(40, dpiScale); this.minFormWidth = BrowserUtils.Scale(42, dpiScale);
switch(messageIcon){ switch(messageIcon){
case MessageBoxIcon.Information: case MessageBoxIcon.Information:
@@ -108,7 +108,7 @@ namespace TweetDuck.Core.Other{
default: default:
icon = null; icon = null;
labelMessage.Location = new Point(labelMessage.Location.X-38, labelMessage.Location.Y); labelMessage.Location = new Point(BrowserUtils.Scale(19, dpiScale), labelMessage.Location.Y); // 19 instead of 9 due to larger height
break; break;
} }
@@ -176,7 +176,7 @@ namespace TweetDuck.Core.Other{
private void RecalculateButtonLocation(){ private void RecalculateButtonLocation(){
int dist = ButtonDistance; int dist = ButtonDistance;
int start = ClientWidth-dist-BrowserUtils.Scale(1, dpiScale); int start = ClientWidth-dist;
for(int index = 0; index < buttonCount; index++){ for(int index = 0; index < buttonCount; index++){
Control control = panelActions.Controls[index]; Control control = panelActions.Controls[index];
@@ -201,7 +201,7 @@ namespace TweetDuck.Core.Other{
prevLabelHeight -= labelOffset; prevLabelHeight -= labelOffset;
} }
realFormWidth = ClientWidth-(icon == null ? 50 : 0)+labelMessage.Width-prevLabelWidth; realFormWidth = ClientWidth-(icon == null ? BrowserUtils.Scale(50, dpiScale) : 0)+labelMessage.Width-prevLabelWidth;
ClientWidth = Math.Max(realFormWidth, minFormWidth); ClientWidth = Math.Max(realFormWidth, minFormWidth);
Height += labelMessage.Height-prevLabelHeight; Height += labelMessage.Height-prevLabelHeight;
@@ -212,7 +212,7 @@ namespace TweetDuck.Core.Other{
protected override void OnPaint(PaintEventArgs e){ protected override void OnPaint(PaintEventArgs e){
if (icon != null){ if (icon != null){
e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), BrowserUtils.Scale(26, dpiScale)); e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale));
} }
base.OnPaint(e); base.OnPaint(e);

View File

@@ -2,6 +2,7 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsCefArgs : Form{ sealed partial class DialogSettingsCefArgs : Form{
@@ -30,7 +31,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
return; return;
} }
int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count; int count = CommandLineArgs.ReadCefArguments(CefArgs).Count;
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?"; string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){ if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){

View File

@@ -43,6 +43,7 @@
this.panelUpdates = new System.Windows.Forms.Panel(); this.panelUpdates = new System.Windows.Forms.Panel();
this.panelTray = new System.Windows.Forms.Panel(); this.panelTray = new System.Windows.Forms.Panel();
this.labelUpdates = new System.Windows.Forms.Label(); this.labelUpdates = new System.Windows.Forms.Label();
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
this.panelUI.SuspendLayout(); this.panelUI.SuspendLayout();
this.panelUpdates.SuspendLayout(); this.panelUpdates.SuspendLayout();
@@ -87,11 +88,11 @@
// checkSpellCheck // checkSpellCheck
// //
this.checkSpellCheck.AutoSize = true; this.checkSpellCheck.AutoSize = true;
this.checkSpellCheck.Location = new System.Drawing.Point(6, 51); this.checkSpellCheck.Location = new System.Drawing.Point(6, 74);
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSpellCheck.Name = "checkSpellCheck"; this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17); this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
this.checkSpellCheck.TabIndex = 2; this.checkSpellCheck.TabIndex = 3;
this.checkSpellCheck.Text = "Enable Spell Check"; this.checkSpellCheck.Text = "Enable Spell Check";
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly."); this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
this.checkSpellCheck.UseVisualStyleBackColor = true; this.checkSpellCheck.UseVisualStyleBackColor = true;
@@ -122,11 +123,11 @@
// labelZoomValue // labelZoomValue
// //
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
this.labelZoomValue.Location = new System.Drawing.Point(141, 100); this.labelZoomValue.Location = new System.Drawing.Point(141, 123);
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelZoomValue.Name = "labelZoomValue"; this.labelZoomValue.Name = "labelZoomValue";
this.labelZoomValue.Size = new System.Drawing.Size(38, 13); this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
this.labelZoomValue.TabIndex = 5; this.labelZoomValue.TabIndex = 6;
this.labelZoomValue.Text = "100%"; this.labelZoomValue.Text = "100%";
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots."); this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
@@ -158,24 +159,24 @@
this.trackBarZoom.AutoSize = false; this.trackBarZoom.AutoSize = false;
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control; this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
this.trackBarZoom.LargeChange = 25; this.trackBarZoom.LargeChange = 25;
this.trackBarZoom.Location = new System.Drawing.Point(3, 99); this.trackBarZoom.Location = new System.Drawing.Point(3, 122);
this.trackBarZoom.Maximum = 200; this.trackBarZoom.Maximum = 200;
this.trackBarZoom.Minimum = 50; this.trackBarZoom.Minimum = 50;
this.trackBarZoom.Name = "trackBarZoom"; this.trackBarZoom.Name = "trackBarZoom";
this.trackBarZoom.Size = new System.Drawing.Size(148, 30); this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
this.trackBarZoom.SmallChange = 5; this.trackBarZoom.SmallChange = 5;
this.trackBarZoom.TabIndex = 4; this.trackBarZoom.TabIndex = 5;
this.trackBarZoom.TickFrequency = 25; this.trackBarZoom.TickFrequency = 25;
this.trackBarZoom.Value = 100; this.trackBarZoom.Value = 100;
// //
// labelZoom // labelZoom
// //
this.labelZoom.AutoSize = true; this.labelZoom.AutoSize = true;
this.labelZoom.Location = new System.Drawing.Point(3, 83); this.labelZoom.Location = new System.Drawing.Point(3, 106);
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelZoom.Name = "labelZoom"; this.labelZoom.Name = "labelZoom";
this.labelZoom.Size = new System.Drawing.Size(34, 13); this.labelZoom.Size = new System.Drawing.Size(34, 13);
this.labelZoom.TabIndex = 3; this.labelZoom.TabIndex = 4;
this.labelZoom.Text = "Zoom"; this.labelZoom.Text = "Zoom";
// //
// zoomUpdateTimer // zoomUpdateTimer
@@ -198,6 +199,7 @@
// //
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelUI.Controls.Add(this.checkBestImageQuality);
this.panelUI.Controls.Add(this.checkExpandLinks); this.panelUI.Controls.Add(this.checkExpandLinks);
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors); this.panelUI.Controls.Add(this.checkSwitchAccountSelectors);
this.panelUI.Controls.Add(this.checkSpellCheck); this.panelUI.Controls.Add(this.checkSpellCheck);
@@ -206,14 +208,14 @@
this.panelUI.Controls.Add(this.trackBarZoom); this.panelUI.Controls.Add(this.trackBarZoom);
this.panelUI.Location = new System.Drawing.Point(9, 31); this.panelUI.Location = new System.Drawing.Point(9, 31);
this.panelUI.Name = "panelUI"; this.panelUI.Name = "panelUI";
this.panelUI.Size = new System.Drawing.Size(322, 134); this.panelUI.Size = new System.Drawing.Size(322, 157);
this.panelUI.TabIndex = 1; this.panelUI.TabIndex = 1;
// //
// labelTray // labelTray
// //
this.labelTray.AutoSize = true; this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTray.Location = new System.Drawing.Point(5, 189); this.labelTray.Location = new System.Drawing.Point(6, 212);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelTray.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelTray.Name = "labelTray"; this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(96, 20); this.labelTray.Size = new System.Drawing.Size(96, 20);
@@ -226,7 +228,7 @@
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelUpdates.Controls.Add(this.checkUpdateNotifications); this.panelUpdates.Controls.Add(this.checkUpdateNotifications);
this.panelUpdates.Controls.Add(this.btnCheckUpdates); this.panelUpdates.Controls.Add(this.btnCheckUpdates);
this.panelUpdates.Location = new System.Drawing.Point(9, 335); this.panelUpdates.Location = new System.Drawing.Point(9, 358);
this.panelUpdates.Name = "panelUpdates"; this.panelUpdates.Name = "panelUpdates";
this.panelUpdates.Size = new System.Drawing.Size(322, 55); this.panelUpdates.Size = new System.Drawing.Size(322, 55);
this.panelUpdates.TabIndex = 5; this.panelUpdates.TabIndex = 5;
@@ -238,7 +240,7 @@
this.panelTray.Controls.Add(this.checkTrayHighlight); this.panelTray.Controls.Add(this.checkTrayHighlight);
this.panelTray.Controls.Add(this.comboBoxTrayType); this.panelTray.Controls.Add(this.comboBoxTrayType);
this.panelTray.Controls.Add(this.labelTrayIcon); this.panelTray.Controls.Add(this.labelTrayIcon);
this.panelTray.Location = new System.Drawing.Point(9, 212); this.panelTray.Location = new System.Drawing.Point(9, 235);
this.panelTray.Name = "panelTray"; this.panelTray.Name = "panelTray";
this.panelTray.Size = new System.Drawing.Size(322, 76); this.panelTray.Size = new System.Drawing.Size(322, 76);
this.panelTray.TabIndex = 3; this.panelTray.TabIndex = 3;
@@ -247,13 +249,25 @@
// //
this.labelUpdates.AutoSize = true; this.labelUpdates.AutoSize = true;
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelUpdates.Location = new System.Drawing.Point(6, 312); this.labelUpdates.Location = new System.Drawing.Point(6, 335);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelUpdates.Name = "labelUpdates"; this.labelUpdates.Name = "labelUpdates";
this.labelUpdates.Size = new System.Drawing.Size(70, 20); this.labelUpdates.Size = new System.Drawing.Size(70, 20);
this.labelUpdates.TabIndex = 4; this.labelUpdates.TabIndex = 4;
this.labelUpdates.Text = "Updates"; this.labelUpdates.Text = "Updates";
// //
// checkBestImageQuality
//
this.checkBestImageQuality.AutoSize = true;
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 51);
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkBestImageQuality.Name = "checkBestImageQuality";
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
this.checkBestImageQuality.TabIndex = 2;
this.checkBestImageQuality.Text = "Best Image Quality";
this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
this.checkBestImageQuality.UseVisualStyleBackColor = true;
//
// TabSettingsGeneral // TabSettingsGeneral
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -265,7 +279,7 @@
this.Controls.Add(this.panelUI); this.Controls.Add(this.panelUI);
this.Controls.Add(this.labelUI); this.Controls.Add(this.labelUI);
this.Name = "TabSettingsGeneral"; this.Name = "TabSettingsGeneral";
this.Size = new System.Drawing.Size(340, 400); this.Size = new System.Drawing.Size(340, 422);
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
this.panelUI.ResumeLayout(false); this.panelUI.ResumeLayout(false);
this.panelUI.PerformLayout(); this.panelUI.PerformLayout();
@@ -299,5 +313,6 @@
private System.Windows.Forms.Panel panelUpdates; private System.Windows.Forms.Panel panelUpdates;
private System.Windows.Forms.Panel panelTray; private System.Windows.Forms.Panel panelTray;
private System.Windows.Forms.Label labelUpdates; private System.Windows.Forms.Label labelUpdates;
private System.Windows.Forms.CheckBox checkBestImageQuality;
} }
} }

View File

@@ -28,6 +28,7 @@ namespace TweetDuck.Core.Other.Settings{
checkExpandLinks.Checked = Config.ExpandLinksOnHover; checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors; checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
checkBestImageQuality.Checked = Config.BestImageQuality;
checkSpellCheck.Checked = Config.EnableSpellCheck; checkSpellCheck.Checked = Config.EnableSpellCheck;
checkTrayHighlight.Checked = Config.EnableTrayHighlight; checkTrayHighlight.Checked = Config.EnableTrayHighlight;
@@ -37,6 +38,7 @@ namespace TweetDuck.Core.Other.Settings{
public override void OnReady(){ public override void OnReady(){
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged; checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged; checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged; checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
@@ -59,6 +61,10 @@ namespace TweetDuck.Core.Other.Settings{
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked; Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
} }
private void checkBestImageQuality_CheckedChanged(object sender, EventArgs e){
Config.BestImageQuality = checkBestImageQuality.Checked;
}
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){ private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
Config.EnableSpellCheck = checkSpellCheck.Checked; Config.EnableSpellCheck = checkSpellCheck.Checked;
PromptRestart(); PromptRestart();

View File

@@ -63,6 +63,7 @@
this.labelSize = new System.Windows.Forms.Label(); this.labelSize = new System.Windows.Forms.Label();
this.panelMiscellaneous = new System.Windows.Forms.Panel(); this.panelMiscellaneous = new System.Windows.Forms.Panel();
this.durationUpdateTimer = new System.Windows.Forms.Timer(this.components); this.durationUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.checkMediaPreviews = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.tableLayoutDurationButtons.SuspendLayout(); this.tableLayoutDurationButtons.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
@@ -273,11 +274,11 @@
// checkSkipOnLinkClick // checkSkipOnLinkClick
// //
this.checkSkipOnLinkClick.AutoSize = true; this.checkSkipOnLinkClick.AutoSize = true;
this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 28); this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 51);
this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick"; this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick";
this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17); this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17);
this.checkSkipOnLinkClick.TabIndex = 1; this.checkSkipOnLinkClick.TabIndex = 2;
this.checkSkipOnLinkClick.Text = "Skip On Link Click"; this.checkSkipOnLinkClick.Text = "Skip On Link Click";
this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked."); this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true; this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
@@ -297,32 +298,32 @@
// labelIdlePause // labelIdlePause
// //
this.labelIdlePause.AutoSize = true; this.labelIdlePause.AutoSize = true;
this.labelIdlePause.Location = new System.Drawing.Point(3, 83); this.labelIdlePause.Location = new System.Drawing.Point(3, 106);
this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelIdlePause.Name = "labelIdlePause"; this.labelIdlePause.Name = "labelIdlePause";
this.labelIdlePause.Size = new System.Drawing.Size(89, 13); this.labelIdlePause.Size = new System.Drawing.Size(89, 13);
this.labelIdlePause.TabIndex = 3; this.labelIdlePause.TabIndex = 4;
this.labelIdlePause.Text = "Pause When Idle"; this.labelIdlePause.Text = "Pause When Idle";
// //
// comboBoxIdlePause // comboBoxIdlePause
// //
this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxIdlePause.FormattingEnabled = true; this.comboBoxIdlePause.FormattingEnabled = true;
this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 99); this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 122);
this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxIdlePause.Name = "comboBoxIdlePause"; this.comboBoxIdlePause.Name = "comboBoxIdlePause";
this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21); this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21);
this.comboBoxIdlePause.TabIndex = 4; this.comboBoxIdlePause.TabIndex = 5;
this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time."); this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
// //
// checkNonIntrusive // checkNonIntrusive
// //
this.checkNonIntrusive.AutoSize = true; this.checkNonIntrusive.AutoSize = true;
this.checkNonIntrusive.Location = new System.Drawing.Point(6, 51); this.checkNonIntrusive.Location = new System.Drawing.Point(6, 74);
this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkNonIntrusive.Name = "checkNonIntrusive"; this.checkNonIntrusive.Name = "checkNonIntrusive";
this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17); this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17);
this.checkNonIntrusive.TabIndex = 2; this.checkNonIntrusive.TabIndex = 3;
this.checkNonIntrusive.Text = "Non-Intrusive Popups"; this.checkNonIntrusive.Text = "Non-Intrusive Popups";
this.toolTip.SetToolTip(this.checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks."); this.toolTip.SetToolTip(this.checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks.");
this.checkNonIntrusive.UseVisualStyleBackColor = true; this.checkNonIntrusive.UseVisualStyleBackColor = true;
@@ -389,6 +390,7 @@
// //
this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelGeneral.Controls.Add(this.checkMediaPreviews);
this.panelGeneral.Controls.Add(this.checkColumnName); this.panelGeneral.Controls.Add(this.checkColumnName);
this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick); this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick);
this.panelGeneral.Controls.Add(this.checkNonIntrusive); this.panelGeneral.Controls.Add(this.checkNonIntrusive);
@@ -396,7 +398,7 @@
this.panelGeneral.Controls.Add(this.comboBoxIdlePause); this.panelGeneral.Controls.Add(this.comboBoxIdlePause);
this.panelGeneral.Location = new System.Drawing.Point(9, 31); this.panelGeneral.Location = new System.Drawing.Point(9, 31);
this.panelGeneral.Name = "panelGeneral"; this.panelGeneral.Name = "panelGeneral";
this.panelGeneral.Size = new System.Drawing.Size(322, 126); this.panelGeneral.Size = new System.Drawing.Size(322, 149);
this.panelGeneral.TabIndex = 1; this.panelGeneral.TabIndex = 1;
// //
// labelScrollSpeedValue // labelScrollSpeedValue
@@ -437,7 +439,7 @@
// //
this.labelLocation.AutoSize = true; this.labelLocation.AutoSize = true;
this.labelLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelLocation.Location = new System.Drawing.Point(6, 372); this.labelLocation.Location = new System.Drawing.Point(6, 395);
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelLocation.Name = "labelLocation"; this.labelLocation.Name = "labelLocation";
this.labelLocation.Size = new System.Drawing.Size(70, 20); this.labelLocation.Size = new System.Drawing.Size(70, 20);
@@ -458,7 +460,7 @@
this.panelLocation.Controls.Add(this.radioLocBL); this.panelLocation.Controls.Add(this.radioLocBL);
this.panelLocation.Controls.Add(this.radioLocCustom); this.panelLocation.Controls.Add(this.radioLocCustom);
this.panelLocation.Controls.Add(this.radioLocBR); this.panelLocation.Controls.Add(this.radioLocBR);
this.panelLocation.Location = new System.Drawing.Point(9, 395); this.panelLocation.Location = new System.Drawing.Point(9, 418);
this.panelLocation.Name = "panelLocation"; this.panelLocation.Name = "panelLocation";
this.panelLocation.Size = new System.Drawing.Size(322, 165); this.panelLocation.Size = new System.Drawing.Size(322, 165);
this.panelLocation.TabIndex = 5; this.panelLocation.TabIndex = 5;
@@ -473,7 +475,7 @@
this.panelTimer.Controls.Add(this.checkTimerCountDown); this.panelTimer.Controls.Add(this.checkTimerCountDown);
this.panelTimer.Controls.Add(this.labelDurationValue); this.panelTimer.Controls.Add(this.labelDurationValue);
this.panelTimer.Controls.Add(this.trackBarDuration); this.panelTimer.Controls.Add(this.trackBarDuration);
this.panelTimer.Location = new System.Drawing.Point(9, 204); this.panelTimer.Location = new System.Drawing.Point(9, 227);
this.panelTimer.Name = "panelTimer"; this.panelTimer.Name = "panelTimer";
this.panelTimer.Size = new System.Drawing.Size(322, 144); this.panelTimer.Size = new System.Drawing.Size(322, 144);
this.panelTimer.TabIndex = 3; this.panelTimer.TabIndex = 3;
@@ -492,7 +494,7 @@
// //
this.labelTimer.AutoSize = true; this.labelTimer.AutoSize = true;
this.labelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTimer.Location = new System.Drawing.Point(6, 181); this.labelTimer.Location = new System.Drawing.Point(6, 204);
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelTimer.Name = "labelTimer"; this.labelTimer.Name = "labelTimer";
this.labelTimer.Size = new System.Drawing.Size(48, 20); this.labelTimer.Size = new System.Drawing.Size(48, 20);
@@ -503,7 +505,7 @@
// //
this.labelSize.AutoSize = true; this.labelSize.AutoSize = true;
this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelSize.Location = new System.Drawing.Point(6, 584); this.labelSize.Location = new System.Drawing.Point(6, 607);
this.labelSize.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelSize.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0);
this.labelSize.Name = "labelSize"; this.labelSize.Name = "labelSize";
this.labelSize.Size = new System.Drawing.Size(40, 20); this.labelSize.Size = new System.Drawing.Size(40, 20);
@@ -519,7 +521,7 @@
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue); this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue);
this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed); this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed);
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed); this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed);
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 607); this.panelMiscellaneous.Location = new System.Drawing.Point(9, 630);
this.panelMiscellaneous.Name = "panelMiscellaneous"; this.panelMiscellaneous.Name = "panelMiscellaneous";
this.panelMiscellaneous.Size = new System.Drawing.Size(322, 92); this.panelMiscellaneous.Size = new System.Drawing.Size(322, 92);
this.panelMiscellaneous.TabIndex = 7; this.panelMiscellaneous.TabIndex = 7;
@@ -529,6 +531,18 @@
this.durationUpdateTimer.Interval = 200; this.durationUpdateTimer.Interval = 200;
this.durationUpdateTimer.Tick += new System.EventHandler(this.durationUpdateTimer_Tick); this.durationUpdateTimer.Tick += new System.EventHandler(this.durationUpdateTimer_Tick);
// //
// checkMediaPreviews
//
this.checkMediaPreviews.AutoSize = true;
this.checkMediaPreviews.Location = new System.Drawing.Point(6, 28);
this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkMediaPreviews.Name = "checkMediaPreviews";
this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17);
this.checkMediaPreviews.TabIndex = 1;
this.checkMediaPreviews.Text = "Show Media Previews";
this.toolTip.SetToolTip(this.checkMediaPreviews, "Shows image and video thumbnails in the notification window.");
this.checkMediaPreviews.UseVisualStyleBackColor = true;
//
// TabSettingsNotifications // TabSettingsNotifications
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -542,7 +556,7 @@
this.Controls.Add(this.labelGeneral); this.Controls.Add(this.labelGeneral);
this.Controls.Add(this.panelTimer); this.Controls.Add(this.panelTimer);
this.Name = "TabSettingsNotifications"; this.Name = "TabSettingsNotifications";
this.Size = new System.Drawing.Size(340, 708); this.Size = new System.Drawing.Size(340, 731);
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged); this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
this.tableLayoutDurationButtons.ResumeLayout(false); this.tableLayoutDurationButtons.ResumeLayout(false);
@@ -601,5 +615,6 @@
private System.Windows.Forms.Timer durationUpdateTimer; private System.Windows.Forms.Timer durationUpdateTimer;
private System.Windows.Forms.RadioButton radioSizeCustom; private System.Windows.Forms.RadioButton radioSizeCustom;
private System.Windows.Forms.RadioButton radioSizeAuto; private System.Windows.Forms.RadioButton radioSizeAuto;
private System.Windows.Forms.CheckBox checkMediaPreviews;
} }
} }

View File

@@ -63,6 +63,7 @@ namespace TweetDuck.Core.Other.Settings{
checkNotificationTimer.Checked = Config.DisplayNotificationTimer; checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkTimerCountDown.Enabled = checkNotificationTimer.Checked; checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
checkTimerCountDown.Checked = Config.NotificationTimerCountDown; checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick; checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode; checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
@@ -96,6 +97,7 @@ namespace TweetDuck.Core.Other.Settings{
checkColumnName.CheckedChanged += checkColumnName_CheckedChanged; checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged; checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged; checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged;
checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged; checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged; checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
@@ -218,6 +220,10 @@ namespace TweetDuck.Core.Other.Settings{
notification.ShowNotificationForSettings(true); notification.ShowNotificationForSettings(true);
} }
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
Config.NotificationMediaPreviews = checkMediaPreviews.Checked;
}
private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){ private void checkSkipOnLinkClick_CheckedChanged(object sender, EventArgs e){
Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked; Config.NotificationSkipOnLinkClick = checkSkipOnLinkClick.Checked;
} }

View File

@@ -2,7 +2,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
@@ -10,8 +9,6 @@ using TweetDuck.Core.Other;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
public static string HeaderAcceptLanguage{ public static string HeaderAcceptLanguage{
get{ get{
string culture = Program.Culture.Name; string culture = Program.Culture.Name;
@@ -27,13 +24,6 @@ namespace TweetDuck.Core.Utils{
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion; public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
public static readonly string[] DictionaryWords = {
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
};
public static void SetupCefArgs(IDictionary<string, string> args){ public static void SetupCefArgs(IDictionary<string, string> args){
if (!Program.SystemConfig.HardwareAcceleration){ if (!Program.SystemConfig.HardwareAcceleration){
args["disable-gpu"] = "1"; args["disable-gpu"] = "1";
@@ -117,14 +107,6 @@ namespace TweetDuck.Core.Utils{
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2)); browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
} }
public static bool IsTweetDeckWebsite(IFrame frame){
return frame.Url.Contains("//tweetdeck.twitter.com/");
}
public static bool IsTwitterWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/");
}
#if DEBUG #if DEBUG
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){ public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line); Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);

View File

@@ -1,39 +0,0 @@
using System;
using System.Text.RegularExpressions;
using TweetDuck.Data;
namespace TweetDuck.Core.Utils{
static class CommandLineArgsParser{
private static readonly Lazy<Regex> SplitRegex = new Lazy<Regex>(() => new Regex(@"([^=\s]+(?:=(?:\S*""[^""]*?""\S*|\S*))?)", RegexOptions.Compiled), false);
public static CommandLineArgs ReadCefArguments(string argumentString){
CommandLineArgs args = new CommandLineArgs();
if (string.IsNullOrWhiteSpace(argumentString)){
return args;
}
foreach(Match match in SplitRegex.Value.Matches(argumentString)){
string matchValue = match.Value;
int indexEquals = matchValue.IndexOf('=');
string key, value;
if (indexEquals == -1){
key = matchValue.TrimStart('-');
value = "1";
}
else{
key = matchValue.Substring(0, indexEquals).TrimStart('-');
value = matchValue.Substring(indexEquals+1).Trim('"');
}
if (key.Length != 0){
args.SetValue(key, value);
}
}
return args;
}
}
}

View File

@@ -4,6 +4,8 @@ using System.Text.RegularExpressions;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class StringUtils{ static class StringUtils{
public static readonly string[] EmptyArray = new string[0];
public static string ExtractBefore(string str, char search, int startIndex = 0){ public static string ExtractBefore(string str, char search, int startIndex = 0){
int index = str.IndexOf(search, startIndex); int index = str.IndexOf(search, startIndex);
return index == -1 ? str : str.Substring(0, index); return index == -1 ? str : str.Substring(0, index);

View File

@@ -0,0 +1,94 @@
using System;
using CefSharp;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using TweetDuck.Core.Other;
namespace TweetDuck.Core.Utils{
static class TwitterUtils{
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'";
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/([^/]+)/?$", RegexOptions.Compiled), false);
public static Regex RegexAccount => RegexAccountLazy.Value;
public static readonly string[] DictionaryWords = {
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
};
public enum ImageQuality{
Default, Orig
}
public static bool IsTweetDeckWebsite(IFrame frame){
return frame.Url.Contains("//tweetdeck.twitter.com/");
}
public static bool IsTwitterWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/");
}
private static string ExtractImageBaseLink(string url){
int dot = url.LastIndexOf('.');
return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
}
public static string GetImageLink(string url, ImageQuality quality){
if (quality == ImageQuality.Orig){
string result = ExtractImageBaseLink(url);
if (result != url){
result += ":orig";
}
return result;
}
else{
return url;
}
}
public static void DownloadImage(string url, ImageQuality quality){
DownloadImages(new string[]{ url }, quality);
}
public static void DownloadImages(string[] urls, ImageQuality quality){
if (urls.Length == 0){
return;
}
string file = BrowserUtils.GetFileNameFromUrl(ExtractImageBaseLink(urls[0]));
string ext = Path.GetExtension(file); // includes dot
using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true,
OverwritePrompt = urls.Length == 1,
Title = "Save image",
FileName = file,
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){
if (dialog.ShowDialog() == DialogResult.OK){
void OnFailure(Exception ex){
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
}
if (urls.Length == 1){
BrowserUtils.DownloadFileAsync(GetImageLink(urls[0], quality), dialog.FileName, null, OnFailure);
}
else{
string pathBase = Path.ChangeExtension(dialog.FileName, null);
string pathExt = Path.GetExtension(dialog.FileName);
for(int index = 0; index < urls.Length; index++){
BrowserUtils.DownloadFileAsync(GetImageLink(urls[index], quality), pathBase+(index+1)+pathExt, null, OnFailure);
}
}
}
}
}
}
}

View File

@@ -103,7 +103,7 @@ namespace TweetDuck.Data{
public string[] KeyValue{ public string[] KeyValue{
get{ get{
int index = Identifier.IndexOf(KeySeparator); int index = Identifier.IndexOf(KeySeparator);
return index == -1 ? new string[0] : Identifier.Substring(index+1).Split(KeySeparator); return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index+1).Split(KeySeparator);
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
namespace TweetDuck.Data{ namespace TweetDuck.Data{
sealed class CommandLineArgs{ sealed class CommandLineArgs{
@@ -32,6 +33,36 @@ namespace TweetDuck.Data{
} }
} }
public static CommandLineArgs ReadCefArguments(string argumentString){
CommandLineArgs args = new CommandLineArgs();
if (string.IsNullOrWhiteSpace(argumentString)){
return args;
}
foreach(Match match in Regex.Matches(argumentString, @"([^=\s]+(?:=(?:\S*""[^""]*?""\S*|\S*))?)")){
string matchValue = match.Value;
int indexEquals = matchValue.IndexOf('=');
string key, value;
if (indexEquals == -1){
key = matchValue.TrimStart('-');
value = "1";
}
else{
key = matchValue.Substring(0, indexEquals).TrimStart('-');
value = matchValue.Substring(indexEquals+1).Trim('"');
}
if (key.Length != 0){
args.SetValue(key, value);
}
}
return args;
}
private readonly HashSet<string> flags = new HashSet<string>(); private readonly HashSet<string> flags = new HashSet<string>();
private readonly Dictionary<string, string> values = new Dictionary<string, string>(); private readonly Dictionary<string, string> values = new Dictionary<string, string>();

View File

@@ -6,12 +6,15 @@ using System.Reflection;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace TweetDuck.Data.Serialization{ namespace TweetDuck.Data.Serialization{
sealed class FileSerializer<T> where T : ISerializedObject{ sealed class FileSerializer<T>{
private const string NewLineReal = "\r\n"; private const string NewLineReal = "\r\n";
private const string NewLineCustom = "\r~\n"; private const string NewLineCustom = "\r~\n";
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 OnReadUnknownPropertyHandler OnReadUnknownProperty { 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;
@@ -49,7 +52,7 @@ namespace TweetDuck.Data.Serialization{
public void Read(string file, T obj){ public void Read(string file, T obj){
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() == 0){ if (reader.Peek() <= 1){
throw new FormatException("Input appears to be a binary file."); throw new FormatException("Input appears to be a binary file.");
} }
@@ -75,7 +78,7 @@ 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 (!obj.OnReadUnknownProperty(property, value)){ else if (!(OnReadUnknownProperty?.Invoke(obj, property, value) ?? false)){
throw new SerializationException($"Invalid file format, unknown property: {property}+"); throw new SerializationException($"Invalid file format, unknown property: {property}+");
} }
} }

View File

@@ -1,5 +0,0 @@
namespace TweetDuck.Data.Serialization{
interface ISerializedObject{
bool OnReadUnknownProperty(string property, string value);
}
}

View File

@@ -2,11 +2,8 @@
namespace TweetDuck.Data.Serialization{ namespace TweetDuck.Data.Serialization{
sealed class SingleTypeConverter<T> : ITypeConverter{ sealed class SingleTypeConverter<T> : ITypeConverter{
public delegate string FuncConvertToString<U>(U value); public Func<T, string> ConvertToString { get; set; }
public delegate U FuncConvertToObject<U>(string value); public Func<string, T> ConvertToObject { get; set; }
public FuncConvertToString<T> ConvertToString { get; set; }
public FuncConvertToObject<T> ConvertToObject { get; set; }
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){ bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
try{ try{

View File

@@ -42,7 +42,7 @@ namespace TweetDuck.Plugins{
private readonly string pathRoot; private readonly string pathRoot;
private readonly string pathData; private readonly string pathData;
private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(4){ private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(8){
{ "NAME", "" }, { "NAME", "" },
{ "DESCRIPTION", "" }, { "DESCRIPTION", "" },
{ "AUTHOR", "(anonymous)" }, { "AUTHOR", "(anonymous)" },

View File

@@ -40,6 +40,7 @@ namespace TweetDuck.Plugins{
} }
} }
}catch(FileNotFoundException){ }catch(FileNotFoundException){
Save(file);
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e); Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);

View File

@@ -8,7 +8,7 @@ using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core; using TweetDuck.Core;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Settings.Export; using TweetDuck.Core.Other.Settings.Export;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -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.3"; public const string VersionTag = "1.8.4";
public const string VersionFull = "1.8.3.0"; public const string VersionFull = "1.8.4";
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");
@@ -34,13 +34,13 @@ namespace TweetDuck{
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts"); public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins"); public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
public static readonly string UserConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static readonly string SystemConfigFilePath = Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins"); public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates"); private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt"); private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt"); private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
@@ -80,53 +80,36 @@ namespace TweetDuck{
} }
if (Arguments.HasFlag(Arguments.ArgRestart)){ if (Arguments.HasFlag(Arguments.ArgRestart)){
for(int attempt = 0; attempt < 21; attempt++){ LockManager.Result lockResult = LockManager.LockWait(10000);
LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.Success){ while(lockResult != LockManager.Result.Success){
break; if (lockResult == LockManager.Result.Fail){
}
else if (lockResult == LockManager.Result.Fail){
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
return; return;
} }
else if (attempt == 20){ else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
if (FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
attempt /= 2;
continue;
}
return; return;
} }
else Thread.Sleep(500);
lockResult = LockManager.LockWait(5000);
} }
} }
else{ else{
LockManager.Result lockResult = LockManager.Lock(); LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.HasProcess){ if (lockResult == LockManager.Result.HasProcess){
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray if (!LockManager.RestoreLockingProcess(2000) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, new UIntPtr((uint)LockManager.LockingProcess.Id), IntPtr.Zero);
if (WindowsUtils.TrySleepUntil(() => {
LockManager.LockingProcess.Refresh();
return LockManager.LockingProcess.HasExited || (LockManager.LockingProcess.MainWindowHandle != IntPtr.Zero && LockManager.LockingProcess.Responding);
}, 2000, 250)){
return;
}
}
if (FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
if (!LockManager.CloseLockingProcess(10000, 5000)){ if (!LockManager.CloseLockingProcess(10000, 5000)){
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
return; return;
} }
LockManager.Lock(); lockResult = LockManager.Lock();
} }
else return; else return;
} }
else if (lockResult != LockManager.Result.Success){
if (lockResult != LockManager.Result.Success){
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
return; return;
} }
@@ -157,7 +140,7 @@ namespace TweetDuck{
#endif #endif
}; };
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs); CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs); BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
Cef.EnableHighDPISupport(); Cef.EnableHighDPISupport();

View File

@@ -1,9 +1,3 @@
constructor(){
super({
requiresPageReload: true
})
}
enabled(){ enabled(){
// elements & data // elements & data
this.css = null; this.css = null;
@@ -16,13 +10,11 @@ enabled(){
fontSize: "12px", fontSize: "12px",
hideTweetActions: true, hideTweetActions: true,
moveTweetActionsToRight: true, moveTweetActionsToRight: true,
revertReplies: false,
themeColorTweaks: true, themeColorTweaks: true,
roundedScrollBars: false,
revertIcons: true, revertIcons: true,
smallComposeTextSize: false, smallComposeTextSize: false,
optimizeAnimations: true, optimizeAnimations: true,
avatarRadius: 10 avatarRadius: 2
}; };
this.firstTimeLoad = null; this.firstTimeLoad = null;
@@ -60,7 +52,6 @@ enabled(){
this.firstTimeLoad = obj === null; this.firstTimeLoad = obj === null;
this.onStageReady(); this.onStageReady();
this.injectDeciderReplyHook(obj && obj.revertReplies);
}; };
if (this.$$wasLoadedBefore){ if (this.$$wasLoadedBefore){
@@ -129,9 +120,7 @@ enabled(){
let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout &amp; design</a></li>'); let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout &amp; design</a></li>');
itemTD.after(itemEditDesign); itemTD.after(itemEditDesign);
itemEditDesign.on("click", "a", function(){ itemEditDesign.on("click", "a", this.openEditDesignDialog);
new customDesignModal();
});
itemEditDesign.hover(function(){ itemEditDesign.hover(function(){
$(this).addClass("is-selected"); $(this).addClass("is-selected");
@@ -249,18 +238,7 @@ enabled(){
} }
}); });
// decider injections this.openEditDesignDialog = () => new customDesignModal();
this.injectDeciderReplyHook = enable => {
let prevFunc = TD.decider.updateFromBackend;
TD.decider.updateFromBackend = function(data){
data["simplified_replies"] = !enable;
return prevFunc.apply(this, arguments);
};
TD.decider.updateForGuestId();
this.$requiresReload = enable;
};
// animation optimization // animation optimization
this.optimizations = null; this.optimizations = null;
@@ -353,7 +331,7 @@ enabled(){
} }
this.css.insert("#general_settings .cf { display: none !important }"); this.css.insert("#general_settings .cf { display: none !important }");
this.css.insert("#general_settings .divider-bar::after { display: inline-block; padding-top: 10px; line-height: 17px; content: 'Use the new | Edit layout & design | option in the Settings to modify TweetDeck theme, column width, font size, and other features.' }"); this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }"); this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }"); this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
@@ -389,22 +367,6 @@ enabled(){
this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important }"); this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important }");
} }
if (!this.config.roundedScrollBars){
this.css.insert(".scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar { width: 8px }");
this.css.insert(".scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar { height: 8px }");
this.css.insert(".scroll-styled-v::-webkit-scrollbar-thumb { border-radius: 0 }");
this.css.insert(".scroll-styled-h::-webkit-scrollbar-thumb { border-radius: 0 }");
this.css.insert(".antiscroll-scrollbar { border-radius: 0 }");
this.css.insert(".antiscroll-scrollbar-vertical { margin-top: 0 }");
this.css.insert(".antiscroll-scrollbar-horizontal { margin-left: 0 }");
this.css.insert(".app-columns-container::-webkit-scrollbar { height: 9px !important }");
}
if (this.config.revertReplies){
this.css.insert(".activity-header + .tweet .tweet-context { margin-left: -35px }");
this.css.insert(".activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }");
}
if (this.config.revertIcons){ if (this.config.revertIcons){
this.icons = document.createElement("style"); this.icons = document.createElement("style");
this.icons.innerHTML = ` this.icons.innerHTML = `
@@ -497,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-header .column-type-icon { bottom: 26px !important } .column-type-icon { bottom: 26px !important }
.is-options-open .column-type-icon { bottom: 25px !important }
.tweet-footer { margin-top: 6px !important }`; .tweet-footer { margin-top: 6px !important }`;
document.head.appendChild(this.icons); document.head.appendChild(this.icons);
@@ -540,11 +503,6 @@ enabled(){
.txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important } .txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important }
.avatar { border-radius: ${this.config.avatarRadius}% !important } .avatar { border-radius: ${this.config.avatarRadius}% !important }
${this.config.revertReplies ? `
.activity-header + .tweet .tweet-context { margin-left: -35px }
.activity-header + .tweet .tweet-context .obj-left { margin-right: 5px }
` : ``}
${this.config.revertIcons ? ` ${this.config.revertIcons ? `
@font-face { font-family: 'tweetdeckold'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal } @font-face { font-family: 'tweetdeckold'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal }
.icon-reply:before{content:"\\f006";font-family:tweetdeckold} .icon-reply:before{content:"\\f006";font-family:tweetdeckold}
@@ -575,6 +533,32 @@ ready(){
// modal // modal
$("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent); $("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent);
$(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>'); $(".js-app").append('<div id="td-design-plugin-modal" class="js-modal settings-modal ovl scroll-v scroll-styled-v"></div>');
// global settings override
var me = this;
this.prevFuncSettingsGetInfo = TD.components.GlobalSettings.prototype.getInfo;
this.prevFuncSettingsSwitchTab = TD.components.GlobalSettings.prototype.switchTab;
TD.components.GlobalSettings.prototype.getInfo = function(){
let data = me.prevFuncSettingsGetInfo.apply(this, arguments);
data.tabs.push({
title: "Layout & Design",
action: "tdp-edit-design"
});
return data;
};
TD.components.GlobalSettings.prototype.switchTab = function(tab){
if (tab === "tdp-edit-design"){
me.openEditDesignDialog();
}
else{
return me.prevFuncSettingsSwitchTab.apply(this, arguments);
}
};
} }
disabled(){ disabled(){
@@ -598,6 +582,9 @@ disabled(){
$(window).off("focus", this.onWindowFocusEvent); $(window).off("focus", this.onWindowFocusEvent);
$(window).off("blur", this.onWindowBlurEvent); $(window).off("blur", this.onWindowBlurEvent);
TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo;
TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab;
$("[data-action='settings-menu']").off("click", this.onSettingsMenuClickedEvent); $("[data-action='settings-menu']").off("click", this.onSettingsMenuClickedEvent);
$("#td-design-plugin-modal").remove(); $("#td-design-plugin-modal").remove();
} }

View File

@@ -88,10 +88,6 @@
<input data-td-key="moveTweetActionsToRight" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="moveTweetActionsToRight" class="js-theme-checkbox touch-larger-label" type="checkbox">
Tweet actions on the right side Tweet actions on the right side
</label> </label>
<label class="checkbox">
<input data-td-key="revertReplies" data-td-reload class="js-theme-checkbox touch-larger-label" type="checkbox">
Revert reply style (reloads page)
</label>
<!-- DESIGN --> <!-- DESIGN -->
@@ -102,10 +98,6 @@
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
Theme color tweaks Theme color tweaks
</label> </label>
<label class="checkbox">
<input data-td-key="roundedScrollBars" class="js-theme-checkbox touch-larger-label" type="checkbox">
Rounded scroll bars
</label>
<label class="checkbox"> <label class="checkbox">
<input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox">
Revert icon design Revert icon design
@@ -130,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

@@ -29,13 +29,13 @@ enabled(){
// styles // styles
this.css = window.TDPF_createCustomStyle(this); this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 2px 2px 3px 3px; font-size: 24px; z-index: 9999 }"); this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }");
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }"); this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
this.css.insert(".emoji-keyboard-list .separator { height: 26px }"); this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }"); this.css.insert(".emoji-keyboard-list img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.1em; cursor: pointer }");
this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 2px 2px 0 0 }"); this.css.insert(".emoji-keyboard-search { height: auto; padding: 4px 10px 8px; background-color: #292f33; border-radius: 1px 1px 0 0 }");
this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }"); this.css.insert(".emoji-keyboard-search input { width: 100%; border-radius: 1px; }");
this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 2px 2px }"); this.css.insert(".emoji-keyboard-skintones { height: 1.3em; text-align: center; background-color: #292f33; border-radius: 0 0 1px 1px }");
this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }"); this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }"); this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }"); this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");

View File

@@ -8,7 +8,7 @@ Templates
chylex chylex
[version] [version]
1.0 1.0.1
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -53,8 +53,10 @@ enabled(){
this.css.insert(".templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }"); this.css.insert(".templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
this.css.insert(".templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }"); this.css.insert(".templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }");
this.css.insert(".templates-modal > div { display: flex; flex-direction: column; }"); this.css.insert(".templates-modal > div { display: flex; flex-direction: column; }");
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; text-align: right; }");
this.css.insert(".templates-modal-bottom button { margin-left: 4px; }"); this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
this.css.insert(".template-list .templates-modal-bottom { display: flex; justify-content: space-between; }");
this.css.insert(".template-editor .templates-modal-bottom { text-align: right; }");
this.css.insert(".template-list { height: 100%; flex: 1 1 auto; }"); this.css.insert(".template-list { height: 100%; flex: 1 1 auto; }");
this.css.insert(".template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }"); this.css.insert(".template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
@@ -265,6 +267,7 @@ enabled(){
<ul></ul> <ul></ul>
<div class="templates-modal-bottom"> <div class="templates-modal-bottom">
<button data-action="close" class="btn" style="background-color:#d2d2d2;border-color:#ccd0d2"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button> <button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
</div> </div>
</div> </div>
@@ -298,7 +301,7 @@ enabled(){
<div class="templates-modal-bottom"> <div class="templates-modal-bottom">
<button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button> <button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
<button data-action="editor-confirm" class="btn btn-positive"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button> <button data-action="editor-confirm" class="btn btn-positive" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
</div> </div>
</div> </div>
</div> </div>
@@ -392,6 +395,10 @@ enabled(){
toggleEditor(); toggleEditor();
onTemplatesUpdated(true); onTemplatesUpdated(true);
break; break;
case "close":
hideTemplateModal();
break;
} }
$(this).blur(); $(this).blur();

View File

@@ -56,7 +56,7 @@
// //
var appendToFunction = function(func, extension){ var appendToFunction = function(func, extension){
return function(){ return function(){
var res = func.apply(this, arguments); let res = func.apply(this, arguments);
extension.apply(this, arguments); extension.apply(this, arguments);
return res; return res;
}; };
@@ -66,7 +66,7 @@
// Function: Returns true if an object has a specified property, otherwise returns false without throwing an error. // Function: Returns true if an object has a specified property, otherwise returns false without throwing an error.
// //
var ensurePropertyExists = function(obj, ...chain){ var ensurePropertyExists = function(obj, ...chain){
for(var index = 0; index < chain.length; index++){ for(let index = 0; index < chain.length; index++){
if (!obj.hasOwnProperty(chain[index])){ if (!obj.hasOwnProperty(chain[index])){
$TD.crashDebug("Missing property "+chain[index]+" in chain [obj]."+chain.join(".")); $TD.crashDebug("Missing property "+chain[index]+" in chain [obj]."+chain.join("."));
return false; return false;
@@ -124,25 +124,50 @@
return false; return false;
}; };
let fixMedia = (html, media) => {
return html.find(".js-media a[data-media-entity-id='"+media.mediaId+"']").css("background-image", 'url("'+media.thumb()+'")').removeClass("is-zoomable");
};
return function(column, tweet){ return function(column, tweet){
if (checkRecentTweet(tweet.id)){ if (checkRecentTweet(tweet.id)){
return; return;
} }
if (column.model.getHasNotification()){ if (column.model.getHasNotification()){
let previews = $TDX.notificationMediaPreviews;
let html = $(tweet.render({ let html = $(tweet.render({
withFooter: false, withFooter: false,
withTweetActions: false, withTweetActions: false,
withMediaPreview: true, withMediaPreview: true,
isMediaPreviewOff: true, isMediaPreviewOff: !previews,
isMediaPreviewSmall: false, isMediaPreviewSmall: previews,
isMediaPreviewLarge: false isMediaPreviewLarge: false,
isMediaPreviewCompact: false,
isMediaPreviewInQuoted: previews,
thumbSizeClass: "media-size-medium"
})); }));
html.css("border", "0"); html.css("border", "0");
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
html.find(".js-media").last().remove(); // and quoted tweets still show media previews, nice nice html.find(".js-quote-detail").removeClass("is-actionable margin-b--8"); // prevent quoted tweets from changing the cursor and reduce bottom margin
html.find(".js-quote-detail").removeClass("is-actionable"); // prevent quoted tweets from changing the cursor
if (previews){
html.find(".reverse-image-search").remove();
for(let media of tweet.getMedia()){
fixMedia(html, media);
}
if (tweet.quotedTweet){
for(let media of tweet.quotedTweet.getMedia()){
fixMedia(html, media).addClass("media-size-medium");
}
}
}
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;
@@ -210,6 +235,9 @@
onAppReady.push(function(){ onAppReady.push(function(){
document.documentElement.setAttribute("data-td-theme", TD.settings.getTheme()); document.documentElement.setAttribute("data-td-theme", TD.settings.getTheme());
$TD.loadFontSizeClass(TD.settings.getFontSize());
$TD.loadNotificationHeadContents(getNotificationHeadContents());
}); });
// //
@@ -237,12 +265,12 @@
onAppReady.push(function(){ onAppReady.push(function(){
$("[data-action='settings-menu']").click(function(){ $("[data-action='settings-menu']").click(function(){
setTimeout(function(){ setTimeout(function(){
var menu = $(".js-dropdown-content").children("ul").first(); let menu = $(".js-dropdown-content").children("ul").first();
if (menu.length === 0)return; if (menu.length === 0)return;
menu.children(".drp-h-divider").last().before('<li class="is-selectable" data-std><a href="#" data-action="tweetduck">TweetDuck</a></li>'); menu.children(".drp-h-divider").last().before('<li class="is-selectable" data-std><a href="#" data-action="tweetduck">TweetDuck</a></li>');
var button = menu.children("[data-std]"); let button = menu.children("[data-std]");
button.on("click", "a", function(){ button.on("click", "a", function(){
$TD.openContextMenu(); $TD.openContextMenu();
@@ -272,7 +300,7 @@
var me = $(this); var me = $(this);
if (e.type === "mouseenter"){ if (e.type === "mouseenter"){
var text = me.text(); let text = me.text();
if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
return; return;
@@ -280,7 +308,7 @@
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){ tooltipTimer = window.setTimeout(function(){
var expanded = me.attr("data-full-url"); let expanded = me.attr("data-full-url");
expanded = cutStart(expanded, "https://"); expanded = cutStart(expanded, "https://");
expanded = cutStart(expanded, "http://"); expanded = cutStart(expanded, "http://");
expanded = cutStart(expanded, "www."); expanded = cutStart(expanded, "www.");
@@ -298,7 +326,7 @@
} }
else if (e.type === "mouseleave"){ else if (e.type === "mouseleave"){
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
var prevText = me.attr("td-prev-text"); let prevText = me.attr("td-prev-text");
if (prevText){ if (prevText){
me.text(prevText); me.text(prevText);
@@ -323,17 +351,28 @@
})(); })();
// //
// Block: Allow bypassing of t.co in context menus. // Block: Allow bypassing of t.co and include media previews in context menus.
// //
$(document.body).delegate("a", "contextmenu", function(){ $(document.body).delegate("a", "contextmenu", function(){
$TD.setLastRightClickedLink($(this).attr("data-full-url") || ""); $TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
}); });
$(document.body).delegate("a.js-media-image-link", "contextmenu", function(){
let me = $(this)[0];
if (me.firstElementChild){
$TD.setLastRightClickedImage(me.firstElementChild.getAttribute("src"));
}
else{
$TD.setLastRightClickedImage(me.style.backgroundImage.replace(/url\(['"]?(.*?)['"]?\)/, "$1"));
}
});
// //
// Block: Hook into the notification sound effect. // Block: Hook into the notification sound effect.
// //
(function(){ (function(){
var soundEle = document.getElementById("update-sound"); let soundEle = document.getElementById("update-sound");
soundEle.play = prependToFunction(soundEle.play, function(){ soundEle.play = prependToFunction(soundEle.play, function(){
return $TDX.muteNotifications || $TDX.hasCustomNotificationSound; return $TDX.muteNotifications || $TDX.hasCustomNotificationSound;
@@ -352,12 +391,12 @@
return !!highlightedColumnObj; return !!highlightedColumnObj;
}; };
var updateHighlightedTweet = function(ele, obj, link, embeddedLink){ var updateHighlightedTweet = function(ele, obj, link, embeddedLink, imageList){
highlightedTweetEle = ele; highlightedTweetEle = ele;
highlightedTweetObj = obj; highlightedTweetObj = obj;
if (lastTweet !== link){ if (lastTweet !== link){
$TD.setLastHighlightedTweet(link, embeddedLink); $TD.setLastHighlightedTweet(link, embeddedLink, imageList);
lastTweet = link; lastTweet = link;
} }
}; };
@@ -375,28 +414,30 @@
app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){ app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
if (e.type === "mouseenter"){ if (e.type === "mouseenter"){
var me = $(this); let me = $(this);
if (!me[0].hasAttribute("data-account-key") || (!highlightedColumnObj && !updateHighlightedColumn(me.closest("section.js-column")))){ if (!me[0].hasAttribute("data-account-key") || (!highlightedColumnObj && !updateHighlightedColumn(me.closest("section.js-column")))){
return; return;
} }
var tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key")); let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
if (tweet){ if (tweet){
if (tweet.chirpType === TD.services.ChirpBase.TWEET){ if (tweet.chirpType === TD.services.ChirpBase.TWEET){
var link = tweet.getChirpURL(); let link = tweet.getChirpURL();
var embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : ""; let embedded = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
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?
updateHighlightedTweet(me, tweet, link || "", embedded || ""); updateHighlightedTweet(me, tweet, link || "", embedded || "", 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, "", "", "");
} }
}); });
})(); })();
@@ -419,11 +460,11 @@
window.TDGF_triggerScreenshot = function(){ window.TDGF_triggerScreenshot = function(){
if (selectedTweet){ if (selectedTweet){
var tweetWidth = Math.floor(selectedTweet.width()); let tweetWidth = Math.floor(selectedTweet.width());
var parent = selectedTweet.parent(); let parent = selectedTweet.parent();
var isDetail = parent.hasClass("js-tweet-detail"); let isDetail = parent.hasClass("js-tweet-detail");
var isReply = !isDetail && (parent.hasClass("js-replies-to") || parent.hasClass("js-replies-before")); let isReply = !isDetail && (parent.hasClass("js-replies-to") || parent.hasClass("js-replies-before"));
selectedTweet = selectedTweet.clone(); selectedTweet = selectedTweet.clone();
selectedTweet.children().first().addClass($(document.documentElement).attr("class")).css("padding-bottom", "0"); selectedTweet.children().first().addClass($(document.documentElement).attr("class")).css("padding-bottom", "0");
@@ -457,13 +498,13 @@
selectedTweet.find(".js-poll-link").remove(); selectedTweet.find(".js-poll-link").remove();
selectedTweet.find(".td-screenshot-remove").remove(); selectedTweet.find(".td-screenshot-remove").remove();
var testTweet = selectedTweet.clone().css({ let testTweet = selectedTweet.clone().css({
position: "absolute", position: "absolute",
left: "-999px", left: "-999px",
width: tweetWidth+"px" width: tweetWidth+"px"
}).appendTo(document.body); }).appendTo(document.body);
var realHeight = Math.floor(testTweet.height()); let realHeight = Math.floor(testTweet.height());
testTweet.remove(); testTweet.remove();
$TD.screenshotTweet(selectedTweet.html(), tweetWidth, realHeight); $TD.screenshotTweet(selectedTweet.html(), tweetWidth, realHeight);
@@ -499,31 +540,33 @@
}; };
var tryCloseModal1 = function(){ var tryCloseModal1 = function(){
var modal = $("#open-modal"); let modal = $("#open-modal");
return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal); return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal);
}; };
var tryCloseModal2 = function(){ var tryCloseModal2 = function(){
var modal = $(".js-modals-container"); let modal = $(".js-modals-container");
return modal.length && tryClickSelector("a.mdl-dismiss", modal); return modal.length && tryClickSelector("a.mdl-dismiss", modal);
}; };
var tryCloseHighlightedColumn = function(){ var tryCloseHighlightedColumn = function(){
if (highlightedColumnEle){ if (highlightedColumnEle){
var column = highlightedColumnEle.closest(".js-column"); let column = highlightedColumnEle.closest(".js-column");
return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column)); return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
} }
}; };
window.TDGF_onMouseClickExtra = function(button){ window.TDGF_onMouseClickExtra = function(button){
if (button === 1){ // back button if (button === 1){ // back button
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back", ".js-modal-panel") ||
tryClickSelector(".is-shifted-1 .js-column-back", ".js-modal-panel") ||
tryCloseModal1() || tryCloseModal1() ||
tryCloseModal2() || tryCloseModal2() ||
tryClickSelector(".js-inline-compose-close") || tryClickSelector(".js-inline-compose-close") ||
tryCloseHighlightedColumn() || tryCloseHighlightedColumn() ||
tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") || tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") || tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") ||
$(".js-column-back").click(); $(".is-shifted-1 .js-column-back").click();
} }
else if (button === 2){ // forward button else if (button === 2){ // forward button
if (highlightedTweetEle){ if (highlightedTweetEle){
@@ -538,7 +581,7 @@
// //
$(document).on("dataTweetSent", function(e, data){ $(document).on("dataTweetSent", function(e, data){
if (data.response.state && data.response.state === "scheduled"){ if (data.response.state && data.response.state === "scheduled"){
var column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled"); let column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");
if (column){ if (column){
setTimeout(function(){ setTimeout(function(){
@@ -591,16 +634,18 @@
})(); })();
// //
// Block: Swap shift key functionality for selecting accounts. // Block: Swap shift key functionality for selecting accounts, and refocus the textbox afterwards.
// //
onAppReady.push(function(){ onAppReady.push(function(){
var toggleEventShiftKey = function(e){ var onAccountClick = function(e){
if ($TDX.switchAccountSelectors){ if ($TDX.switchAccountSelectors){
e.shiftKey = !e.shiftKey; e.shiftKey = !e.shiftKey;
} }
$(".js-compose-text", ".js-docked-compose").focus();
}; };
$(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", toggleEventShiftKey); $(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", onAccountClick);
if (!ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts")){ if (!ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts")){
return; return;
@@ -609,7 +654,7 @@
TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){ TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){
if (!this.$node.attr("td-account-selector-hook")){ if (!this.$node.attr("td-account-selector-hook")){
this.$node.attr("td-account-selector-hook", "1"); this.$node.attr("td-account-selector-hook", "1");
this.$node.delegate(".js-account-item", "click", toggleEventShiftKey); this.$node.delegate(".js-account-item", "click", onAccountClick);
} }
}); });
}); });
@@ -645,41 +690,57 @@
// Block: Inject custom CSS and layout into the page. // Block: Inject custom CSS and layout into the page.
// //
(function(){ (function(){
var styleOfficial = document.createElement("style"); let styleOfficial = document.createElement("style");
document.head.appendChild(styleOfficial); document.head.appendChild(styleOfficial);
styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls let addRule = (rule) => {
styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names styleOfficial.sheet.insertRule(rule, 0);
styleOfficial.sheet.insertRule(".txt-base-smallest .sprite-verified-mini { width: 13px !important; height: 13px !important; background-position: -223px -99px !important; }", 0); // fix cut off badge icon when zoomed in };
styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment
styleOfficial.sheet.insertRule(".sprite-logo { background-position: -5px -46px !important; }", 0); // fix TweetDeck logo on certain zoom levels
styleOfficial.sheet.insertRule(".app-navigator .tooltip { display: none !important; }", 0); // hide broken tooltips in the menu
styleOfficial.sheet.insertRule(".account-inline .username { vertical-align: 10%; }", 0); // move usernames a bit higher
styleOfficial.sheet.insertRule(".btn-compose, .app-search-fake, .app-search-input { border-radius: 1px; }", 0); // use consistent menu button radius addRule("a[data-full-url] { word-break: break-all; }"); // break long urls
styleOfficial.sheet.insertRule(".is-condensed .app-header-inner { padding-top: 10px !important; }", 0); // add extra padding to menu buttons when condensed addRule(".keyboard-shortcut-list { vertical-align: top; }"); // fix keyboard navigation alignment
styleOfficial.sheet.insertRule(".is-condensed .btn-compose { padding: 8px !important; }", 0); // fix compose button icon when condensed addRule(".account-inline .username { vertical-align: 10%; }"); // move usernames a bit higher
styleOfficial.sheet.insertRule(".app-header:not(.is-condensed) .nav-user-info { padding: 0 5px; }", 0); // add padding to user info addRule(".character-count-compose { width: 40px !important; }"); // fix strangely wide character count element
styleOfficial.sheet.insertRule(".app-title { display: none; }", 0); // hide TweetDeck logo addRule(".column-nav-link .attribution { position: absolute; }"); // fix cut off account names
styleOfficial.sheet.insertRule(".nav-user-info { bottom: 10px !important; }", 0); // move user info 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
styleOfficial.sheet.insertRule(".app-navigator { bottom: 50px !important; }", 0); // move navigation
styleOfficial.sheet.insertRule(".column-navigator-overflow { bottom: 192px !important; }", 0); // move column list
styleOfficial.sheet.insertRule(".column .column-header { height: 49px !important; }", 0); // fix one pixel space below column header 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
styleOfficial.sheet.insertRule(".column:not(.is-options-open) .column-header { border-bottom: none; }", 0); // fix one pixel space below column header addRule(".dropdown-menu, .list-item-last { border-radius: 0 !important; }"); // square-ify dropdowns
addRule(".prf-header { border-radius: 0; }"); // fix user account header border
styleOfficial.sheet.insertRule(".activity-header { align-items: center !important; margin-bottom: 4px; }", 0); // tweak alignment of avatar and text in notifications addRule(".scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar { border-radius: 0; }"); // square-ify scroll bars
styleOfficial.sheet.insertRule(".activity-header .tweet-timestamp { line-height: unset }", 0); // fix timestamp position in notifications addRule(".antiscroll-scrollbar-vertical { margin-top: 0; }"); // square-ify scroll bars
addRule(".antiscroll-scrollbar-horizontal { margin-left: 0; }"); // square-ify scroll bars
addRule(".scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar { width: 8px; }"); // square-ify scroll bars
addRule(".scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar { height: 8px; }"); // square-ify scroll bars
addRule(".app-columns-container::-webkit-scrollbar { height: 9px !important; }"); // square-ify scroll bars
styleOfficial.sheet.insertRule(".app-columns-container::-webkit-scrollbar-track { border-left: 0; }", 0); // remove weird border in the column container scrollbar addRule(".is-condensed .app-header-inner { padding-top: 10px !important; }"); // add extra padding to menu buttons when condensed
styleOfficial.sheet.insertRule(".app-columns-container { bottom: 0 !important; }", 0); // move column container scrollbar to bottom to fit updated style addRule(".is-condensed .btn-compose { padding: 8px !important; }"); // fix compose button icon when condensed
addRule(".app-header:not(.is-condensed) .nav-user-info { padding: 0 5px; }"); // add padding to user info
styleOfficial.sheet.insertRule(".js-column-header .column-header-link { padding: 0; }", 0); // fix column header tooltip hover box addRule(".app-title { display: none; }"); // hide TweetDeck logo
styleOfficial.sheet.insertRule(".js-column-header .column-header-link .icon { padding: 9px 4px; width: calc(1em + 8px); height: 100%; box-sizing: border-box; }", 0); // fix column header tooltip hover box addRule(".nav-user-info { bottom: 10px !important; }"); // move user info
addRule(".app-navigator { bottom: 50px !important; }"); // move navigation
addRule(".column-navigator-overflow { bottom: 192px !important; }"); // move column list
addRule(".app-navigator .tooltip { display: none !important; }"); // hide broken tooltips in the menu
styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']), .is-gif .js-media-gif-container { cursor: alias; }", 0); // change cursor on unsupported videos addRule(".column .column-header { height: 49px !important; }"); // fix one pixel space below column header
styleOfficial.sheet.insertRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #bd3d37; }", 0); // change play icon color on unsupported videos addRule(".column:not(.is-options-open) .column-header { border-bottom: none; }"); // fix one pixel space below column header
addRule(".is-options-open .column-type-icon { bottom: 27px; }"); // fix one pixel space below column header
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(".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(".js-column-header .column-header-link { padding: 0; }"); // fix column header tooltip hover box
addRule(".js-column-header .column-header-link .icon { padding: 9px 4px; width: calc(1em + 8px); height: 100%; box-sizing: border-box; }"); // fix column header tooltip hover box
addRule(".is-video a:not([href*='youtu']), .is-gif .js-media-gif-container { cursor: alias; }"); // change cursor on unsupported videos
addRule(".is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { color: #bd3d37; }"); // change play icon color on unsupported videos
window.TDGF_reinjectCustomCSS = function(styles){ window.TDGF_reinjectCustomCSS = function(styles){
$("#tweetduck-custom-css").remove(); $("#tweetduck-custom-css").remove();
@@ -719,7 +780,7 @@
!ensurePropertyExists(TD, "ui", "Column", "prototype", "playGifIfNotManuallyPaused"))return; !ensurePropertyExists(TD, "ui", "Column", "prototype", "playGifIfNotManuallyPaused"))return;
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){ TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
var media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId); let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
if (media && media.isVideo && media.service !== "youtube"){ if (media && media.isVideo && media.service !== "youtube"){
$TD.openBrowser(this.clickedLink); $TD.openBrowser(this.clickedLink);
@@ -738,8 +799,8 @@
TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused"); TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused");
app.delegate(".js-gif-play", "click", function(e){ app.delegate(".js-gif-play", "click", function(e){
var parent = $(e.target).closest(".js-tweet").first(); let parent = $(e.target).closest(".js-tweet").first();
var link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first(); let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
$TD.openBrowser(link.attr("href")); $TD.openBrowser(link.attr("href"));
e.stopPropagation(); e.stopPropagation();
@@ -750,7 +811,7 @@
// Block: Fix youtu.be previews not showing up for https links. // Block: Fix youtu.be previews not showing up for https links.
// //
if (ensurePropertyExists(TD, "services", "TwitterMedia")){ if (ensurePropertyExists(TD, "services", "TwitterMedia")){
var media = TD.services.TwitterMedia; let media = TD.services.TwitterMedia;
if (!ensurePropertyExists(media, "YOUTUBE_TINY_RE") || if (!ensurePropertyExists(media, "YOUTUBE_TINY_RE") ||
!ensurePropertyExists(media, "YOUTUBE_LONG_RE") || !ensurePropertyExists(media, "YOUTUBE_LONG_RE") ||
@@ -842,9 +903,7 @@
// //
$(document).one("TD.ready", function(){ $(document).one("TD.ready", function(){
onAppReady.forEach(func => func()); onAppReady.forEach(func => func());
onAppReady = null;
$TD.loadFontSizeClass(TD.settings.getFontSize());
$TD.loadNotificationHeadContents(getNotificationHeadContents());
if (window.TD_PLUGINS){ if (window.TD_PLUGINS){
window.TD_PLUGINS.onReady(); window.TD_PLUGINS.onReady();

View File

@@ -11,22 +11,30 @@
var style = document.createElement("style"); var style = document.createElement("style");
document.head.appendChild(style); document.head.appendChild(style);
style.sheet.insertRule("body { overflow: hidden !important; }", 0); // remove scrollbar let addRule = (rule) => {
style.sheet.insertRule(".topbar { display: none !important; }", 0); // hide top bar style.sheet.insertRule(rule, 0);
style.sheet.insertRule(".page-canvas, .buttons, .btn, input { border-radius: 0 !important; }", 0); // sharpen borders };
style.sheet.insertRule("input { padding: 5px 8px 4px !important; }", 0); // tweak input padding
style.sheet.insertRule("#doc { width: 100%; height: 100%; margin: 0; position: absolute; display: table; }", 0); // center everything addRule("body { overflow: hidden !important; background-color: #1c6399 !important; }"); // remove scrollbar and change background
style.sheet.insertRule("#page-outer { display: table-cell; vertical-align: middle; }", 0); // center everything addRule(".page-canvas { box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important; }"); // change page box shadow
style.sheet.insertRule("#page-container { padding: 0 20px !important; width: 100% !important; box-sizing: border-box !important; }", 0); // center everything addRule(".topbar { display: none !important; }"); // hide top bar
style.sheet.insertRule(".page-canvas { margin: 0 auto !important; }", 0); // center everything
addRule(".page-canvas, .buttons, .btn, input { border-radius: 0 !important; }"); // sharpen borders
addRule("input { padding: 5px 8px 4px !important; }"); // tweak input padding
addRule("button[type='submit'] { border: 1px solid rgba(0, 0, 0, 0.3) !important; border-radius: 0 !important; }"); // style buttons
addRule("#doc { width: 100%; height: 100%; margin: 0; position: absolute; display: table; }"); // center everything
addRule("#page-outer { display: table-cell; vertical-align: middle; }"); // center everything
addRule("#page-container { padding: 0 20px !important; width: 100% !important; box-sizing: border-box !important; }"); // center everything
addRule(".page-canvas { margin: 0 auto !important; }"); // center everything
if (location.pathname === "/logout"){ if (location.pathname === "/logout"){
style.sheet.insertRule(".page-canvas { width: auto !important; max-width: 888px; }", 0); // fix min width addRule(".page-canvas { width: auto !important; max-width: 888px; }"); // fix min width
style.sheet.insertRule(".signout-wrapper { width: auto !important; }", 0); // fix min width addRule(".signout-wrapper { width: auto !important; margin: 0 auto !important; }"); // fix min width and margins
style.sheet.insertRule(".btn { margin: 0 4px !important; }", 0); // add margin around buttons addRule(".signout { margin: 60px 0 54px !important; }"); // fix dialog margins
style.sheet.insertRule(".btn.cancel { border: 1px solid #bbc1c5 !important; }", 0); // add border to cancel button addRule(".buttons { padding-bottom: 0 !important; }"); // fix dialog margins
style.sheet.insertRule(".aside p { display: none; }", 0); // hide text below the logout dialog addRule(".aside { display: none; }"); // hide elements around logout dialog
addRule(".buttons button, .buttons a { display: inline-block; margin: 0 4px !important; border: 1px solid rgba(0, 0, 0, 0.3) !important; border-radius: 0 !important; }"); // style buttons
} }
}; };

View File

@@ -88,7 +88,7 @@
<Compile Include="Core\Controls\NumericUpDownEx.cs"> <Compile Include="Core\Controls\NumericUpDownEx.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="Core\Handling\BrowserProcessHandler.cs" /> <Compile Include="Core\Handling\General\BrowserProcessHandler.cs" />
<Compile Include="Core\Handling\ContextMenuBase.cs" /> <Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" /> <Compile Include="Core\Handling\ContextMenuBrowser.cs" />
<Compile Include="Core\FormBrowser.cs"> <Compile Include="Core\FormBrowser.cs">
@@ -97,8 +97,9 @@
<Compile Include="Core\FormBrowser.Designer.cs"> <Compile Include="Core\FormBrowser.Designer.cs">
<DependentUpon>FormBrowser.cs</DependentUpon> <DependentUpon>FormBrowser.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" /> <Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
<Compile Include="Core\Handling\RequestHandler.cs" /> <Compile Include="Core\Handling\General\RequestHandlerBase.cs" />
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" /> <Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
<Compile Include="Core\Notification\FormNotificationMain.cs"> <Compile Include="Core\Notification\FormNotificationMain.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
@@ -113,8 +114,8 @@
<DependentUpon>FormNotificationBase.cs</DependentUpon> <DependentUpon>FormNotificationBase.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Handling\ContextMenuNotification.cs" /> <Compile Include="Core\Handling\ContextMenuNotification.cs" />
<Compile Include="Core\Handling\JavaScriptDialogHandler.cs" /> <Compile Include="Core\Handling\General\JavaScriptDialogHandler.cs" />
<Compile Include="Core\Handling\LifeSpanHandler.cs" /> <Compile Include="Core\Handling\General\LifeSpanHandler.cs" />
<Compile Include="Core\Notification\FormNotificationTweet.cs"> <Compile Include="Core\Notification\FormNotificationTweet.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -167,6 +168,7 @@
</Compile> </Compile>
<Compile Include="Core\Utils\BrowserProcesses.cs" /> <Compile Include="Core\Utils\BrowserProcesses.cs" />
<Compile Include="Core\Utils\StringUtils.cs" /> <Compile Include="Core\Utils\StringUtils.cs" />
<Compile Include="Core\Utils\TwitterUtils.cs" />
<Compile Include="Data\CombinedFileStream.cs" /> <Compile Include="Data\CombinedFileStream.cs" />
<Compile Include="Core\Other\Settings\Export\ExportFileFlags.cs" /> <Compile Include="Core\Other\Settings\Export\ExportFileFlags.cs" />
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" /> <Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
@@ -199,14 +201,12 @@
</Compile> </Compile>
<Compile Include="Core\Bridge\CallbackBridge.cs" /> <Compile Include="Core\Bridge\CallbackBridge.cs" />
<Compile Include="Data\CommandLineArgs.cs" /> <Compile Include="Data\CommandLineArgs.cs" />
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs"> <Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" /> <Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
<Compile Include="Data\Serialization\FileSerializer.cs" /> <Compile Include="Data\Serialization\FileSerializer.cs" />
<Compile Include="Data\InjectedHTML.cs" /> <Compile Include="Data\InjectedHTML.cs" />
<Compile Include="Data\Serialization\ISerializedObject.cs" />
<Compile Include="Data\Serialization\ITypeConverter.cs" /> <Compile Include="Data\Serialization\ITypeConverter.cs" />
<Compile Include="Data\Serialization\SingleTypeConverter.cs" /> <Compile Include="Data\Serialization\SingleTypeConverter.cs" />
<Compile Include="Data\TwoKeyDictionary.cs" /> <Compile Include="Data\TwoKeyDictionary.cs" />

View File

@@ -30,7 +30,7 @@ namespace TweetDuck.Updates{
} }
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){ if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "update.js"); ScriptLoader.ExecuteFile(e.Frame, "update.js");
Check(false); Check(false);
} }

View File

@@ -1,33 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TweetDuck.Core.Utils;
using TweetDuck.Data;
namespace UnitTests.Core{
[TestClass]
public class TestCommandLineArgsParser{
[TestMethod]
public void TestEmptyString(){
Assert.AreEqual(0, CommandLineArgsParser.ReadCefArguments("").Count);
Assert.AreEqual(0, CommandLineArgsParser.ReadCefArguments(" ").Count);
}
[TestMethod]
public void TestValidString(){
CommandLineArgs args = CommandLineArgsParser.ReadCefArguments("--aaa --bbb --first-value=123 --SECOND-VALUE=\"a b c d e\"\r\n--ccc");
// cef has no flags, flag arguments have a value of 1
// the processing removes all dashes in front of each key
Assert.AreEqual(5, args.Count);
Assert.IsTrue(args.HasValue("aaa"));
Assert.IsTrue(args.HasValue("bbb"));
Assert.IsTrue(args.HasValue("ccc"));
Assert.IsTrue(args.HasValue("first-value"));
Assert.IsTrue(args.HasValue("second-value"));
Assert.AreEqual("1", args.GetValue("aaa", string.Empty));
Assert.AreEqual("1", args.GetValue("bbb", string.Empty));
Assert.AreEqual("1", args.GetValue("ccc", string.Empty));
Assert.AreEqual("123", args.GetValue("first-value", string.Empty));
Assert.AreEqual("a b c d e", args.GetValue("second-value", string.Empty));
}
}
}

View File

@@ -0,0 +1,39 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TweetDuck.Core.Utils;
namespace UnitTests.Core{
[TestClass]
public class TestTwitterUtils{
[TestMethod]
public void TestAccountRegex(){
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc"));
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/chylexmc"));
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc/"));
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/chylexmc/"));
Assert.AreEqual("chylexmc", TwitterUtils.RegexAccount.Match("http://twitter.com/chylexmc").Groups[1].Value);
Assert.AreEqual("123", TwitterUtils.RegexAccount.Match("http://twitter.com/123").Groups[1].Value);
Assert.AreEqual("_", TwitterUtils.RegexAccount.Match("http://twitter.com/_").Groups[1].Value);
Assert.AreEqual("Abc_123", TwitterUtils.RegexAccount.Match("http://twitter.com/Abc_123").Groups[1].Value);
Assert.AreEqual("Abc_123", TwitterUtils.RegexAccount.Match("https://twitter.com/Abc_123/").Groups[1].Value);
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/"));
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc/status"));
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://nottwitter.com/chylexmc"));
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("www.twitter.com/chylexmc"));
}
[TestMethod]
public void TestImageQualityLink(){
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetImageLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Default));
Assert.AreEqual("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.GetImageLink("https://pbs.twimg.com/profile_images/123.jpg", TwitterUtils.ImageQuality.Orig));
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Default));
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:small", TwitterUtils.ImageQuality.Orig));
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Default));
Assert.AreEqual("https://pbs.twimg.com/media/123.jpg:orig", TwitterUtils.GetImageLink("https://pbs.twimg.com/media/123.jpg:large", TwitterUtils.ImageQuality.Orig));
}
}
}

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using TweetDuck.Data; using TweetDuck.Data;
@@ -139,5 +140,31 @@ namespace UnitTests.Data{
Directory.Delete("cfs_directory", true); Directory.Delete("cfs_directory", true);
} }
[TestMethod]
public void TestLongIdentifierSuccess(){
TestUtils.WriteText("cfs_long_identifier_fail_in", "test");
string identifier = string.Join("", Enumerable.Repeat("x", 255));
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_long_identifier_success"))){
cfs.WriteFile(identifier, "cfs_long_identifier_fail_in");
cfs.Flush();
}
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_long_identifier_success"))){
Assert.AreEqual(identifier, cfs.ReadFile().Identifier);
}
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void TestLongIdentifierFail(){
TestUtils.WriteText("cfs_long_identifier_fail_in", "test");
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_long_identifier_fail"))){
cfs.WriteFile(string.Join("", Enumerable.Repeat("x", 256)), "cfs_long_identifier_fail_in");
}
}
} }
} }

View File

@@ -153,5 +153,30 @@ namespace UnitTests.Data{
Assert.IsTrue(args.HasValue("-value")); Assert.IsTrue(args.HasValue("-value"));
Assert.AreEqual("Here is some text!", args.GetValue("-value", string.Empty)); Assert.AreEqual("Here is some text!", args.GetValue("-value", string.Empty));
} }
[TestMethod]
public void TestCefEmptyString(){
Assert.AreEqual(0, CommandLineArgs.ReadCefArguments("").Count);
Assert.AreEqual(0, CommandLineArgs.ReadCefArguments(" ").Count);
}
[TestMethod]
public void TestCefValidString(){
CommandLineArgs args = CommandLineArgs.ReadCefArguments("--aaa --bbb --first-value=123 --SECOND-VALUE=\"a b c d e\"\r\n--ccc");
// cef has no flags, flag arguments have a value of 1
// the processing removes all dashes in front of each key
Assert.AreEqual(5, args.Count);
Assert.IsTrue(args.HasValue("aaa"));
Assert.IsTrue(args.HasValue("bbb"));
Assert.IsTrue(args.HasValue("ccc"));
Assert.IsTrue(args.HasValue("first-value"));
Assert.IsTrue(args.HasValue("second-value"));
Assert.AreEqual("1", args.GetValue("aaa", string.Empty));
Assert.AreEqual("1", args.GetValue("bbb", string.Empty));
Assert.AreEqual("1", args.GetValue("ccc", string.Empty));
Assert.AreEqual("123", args.GetValue("first-value", string.Empty));
Assert.AreEqual("a b c d e", args.GetValue("second-value", string.Empty));
}
} }
} }

View File

@@ -10,16 +10,12 @@ namespace UnitTests.Data{
A, B, C, D, E A, B, C, D, E
} }
private class SerializationTestBasic : ISerializedObject{ private class SerializationTestBasic{
public bool TestBool { get; set; } public bool TestBool { get; set; }
public int TestInt { get; set; } public int TestInt { get; set; }
public string TestString { get; set; } public string TestString { get; set; }
public string TestStringNull { get; set; } public string TestStringNull { get; set; }
public TestEnum TestEnum { get; set; } public TestEnum TestEnum { get; set; }
bool ISerializedObject.OnReadUnknownProperty(string property, string value){
return false;
}
} }
[TestMethod] [TestMethod]

View File

@@ -48,10 +48,10 @@
</Choose> </Choose>
<ItemGroup> <ItemGroup>
<Compile Include="Core\TestStringUtils.cs" /> <Compile Include="Core\TestStringUtils.cs" />
<Compile Include="Core\TestTwitterUtils.cs" />
<Compile Include="Data\TestCombinedFileStream.cs" /> <Compile Include="Data\TestCombinedFileStream.cs" />
<Compile Include="Core\TestBrowserUtils.cs" /> <Compile Include="Core\TestBrowserUtils.cs" />
<Compile Include="Data\TestCommandLineArgs.cs" /> <Compile Include="Data\TestCommandLineArgs.cs" />
<Compile Include="Core\TestCommandLineArgsParser.cs" />
<Compile Include="Data\TestFileSerializer.cs" /> <Compile Include="Data\TestFileSerializer.cs" />
<Compile Include="Data\TestInjectedHTML.cs" /> <Compile Include="Data\TestInjectedHTML.cs" />
<Compile Include="Data\TestTwoKeyDictionary.cs" /> <Compile Include="Data\TestTwoKeyDictionary.cs" />