mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
83 Commits
Author | SHA1 | Date | |
---|---|---|---|
c74c168c96 | |||
40b53fa40c | |||
3481cc0349 | |||
09abd889e9 | |||
330bbfbb31 | |||
6b7b690476 | |||
cea72801a7 | |||
04369e22a7 | |||
f1b16eab9a | |||
13646d9c90 | |||
17d762ce91 | |||
edb40adaa1 | |||
bc0809994a | |||
a3e3d517b0 | |||
d8b63a54ca | |||
b81e7583eb | |||
51f9ba3642 | |||
296626f7c7 | |||
5b2daf9746 | |||
9a6b615174 | |||
18f8d5b269 | |||
2867a875c9 | |||
ee2f5ae8cb | |||
bd5c301fb9 | |||
6df68629f7 | |||
be08fd4445 | |||
6f1afb94fb | |||
7401b8a52d | |||
c83b62ebaa | |||
108cf8923e | |||
4e26fd9d56 | |||
8c9168a4bf | |||
97da0b79e4 | |||
d7e5f6876b | |||
1b92b112e2 | |||
ca55119531 | |||
d9da14b5dc | |||
512b5666ac | |||
64977964e8 | |||
2bc13e0de6 | |||
b90c5f17cf | |||
7d8d0bd43b | |||
![]() |
54c1137927 | ||
e6655219ee | |||
5896f8e35a | |||
934cba7251 | |||
9cc1a11bef | |||
c1bc956d6d | |||
351b174b86 | |||
0b4aaf80dc | |||
c10c185817 | |||
327ef1cbee | |||
15eb823c7f | |||
54613e5242 | |||
df1352cbe3 | |||
0559afd972 | |||
afffca020e | |||
d663cc3f64 | |||
110d41e393 | |||
1a8823f592 | |||
6374a852b0 | |||
a10c7dd7c3 | |||
547c7ea417 | |||
760607995a | |||
4704197c09 | |||
093ac1ac40 | |||
9ed8b0d904 | |||
7346ce370d | |||
adefdadc19 | |||
703bce2d00 | |||
97928ecd84 | |||
be9ea7f64a | |||
ec2aaa8789 | |||
ab14b72526 | |||
d8e304f3c1 | |||
ea53ce361f | |||
2fce80b347 | |||
373c0b1cc3 | |||
e5e1b7e608 | |||
7e9221c9e0 | |||
6b849f854e | |||
831f6bc744 | |||
d282a7a537 |
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
*.txt text eof=lf
|
||||||
|
*.cs diff=csharp
|
@@ -4,7 +4,6 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetLib.Communication;
|
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
@@ -136,15 +135,13 @@ namespace TweetDuck.Configuration{
|
|||||||
// Locking process
|
// Locking process
|
||||||
|
|
||||||
public bool RestoreLockingProcess(int failTimeout){
|
public bool RestoreLockingProcess(int failTimeout){
|
||||||
if (lockingProcess != null){
|
if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||||
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
||||||
Comms.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
|
||||||
|
|
||||||
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -166,16 +163,13 @@ namespace TweetDuck.Configuration{
|
|||||||
lockingProcess = null;
|
lockingProcess = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}catch(Exception ex){
|
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
||||||
if (ex is InvalidOperationException || ex is Win32Exception){
|
|
||||||
if (lockingProcess != null){
|
if (lockingProcess != null){
|
||||||
bool hasExited = CheckLockingProcessExited();
|
bool hasExited = CheckLockingProcessExited();
|
||||||
lockingProcess.Dispose();
|
lockingProcess.Dispose();
|
||||||
return hasExited;
|
return hasExited;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
using TweetDuck.Data.Serialization;
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
@@ -32,14 +31,11 @@ namespace TweetDuck.Configuration{
|
|||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Save(){
|
public void Save(){
|
||||||
try{
|
try{
|
||||||
WindowsUtils.CreateDirectoryForFile(file);
|
|
||||||
Serializer.Write(file, this);
|
Serializer.Write(file, this);
|
||||||
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);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +43,7 @@ namespace TweetDuck.Configuration{
|
|||||||
SystemConfig config = new SystemConfig(file);
|
SystemConfig config = new SystemConfig(file);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
Serializer.Read(file, config);
|
Serializer.ReadIfExists(file, config);
|
||||||
}catch(FileNotFoundException){
|
|
||||||
}catch(DirectoryNotFoundException){
|
|
||||||
}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);
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetDuck.Core;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Data.Serialization;
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class UserConfig{
|
sealed class UserConfig{
|
||||||
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ HandleUnknownProperties = HandleUnknownProperties };
|
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
|
||||||
|
|
||||||
private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
|
|
||||||
data.Remove("EnableBrowserGCReload");
|
|
||||||
data.Remove("BrowserMemoryThreshold");
|
|
||||||
|
|
||||||
if (data.Count == 0){
|
|
||||||
obj.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static UserConfig(){
|
static UserConfig(){
|
||||||
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
|
||||||
@@ -46,6 +36,7 @@ namespace TweetDuck.Configuration{
|
|||||||
|
|
||||||
public bool FirstRun { get; set; } = true;
|
public bool FirstRun { get; set; } = true;
|
||||||
public bool AllowDataCollection { get; set; } = false;
|
public bool AllowDataCollection { get; set; } = false;
|
||||||
|
public bool ShowDataCollectionNotification { get; set; } = true;
|
||||||
|
|
||||||
public WindowState BrowserWindow { get; set; } = new WindowState();
|
public WindowState BrowserWindow { get; set; } = new WindowState();
|
||||||
public WindowState PluginsWindow { get; set; } = new WindowState();
|
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||||
@@ -54,6 +45,7 @@ namespace TweetDuck.Configuration{
|
|||||||
public bool SwitchAccountSelectors { get; set; } = true;
|
public bool SwitchAccountSelectors { get; set; } = true;
|
||||||
public bool OpenSearchInFirstColumn { get; set; } = true;
|
public bool OpenSearchInFirstColumn { get; set; } = true;
|
||||||
public bool BestImageQuality { get; set; } = true;
|
public bool BestImageQuality { get; set; } = true;
|
||||||
|
public bool EnableAnimatedImages { get; set; } = true;
|
||||||
public bool EnableSpellCheck { get; set; } = false;
|
public bool EnableSpellCheck { get; set; } = false;
|
||||||
public int VideoPlayerVolume { get; set; } = 50;
|
public int VideoPlayerVolume { get; set; } = 50;
|
||||||
private int _zoomLevel = 100;
|
private int _zoomLevel = 100;
|
||||||
@@ -125,8 +117,6 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double ZoomMultiplier => _zoomLevel/100.0;
|
|
||||||
|
|
||||||
public TrayIcon.Behavior TrayBehavior{
|
public TrayIcon.Behavior TrayBehavior{
|
||||||
get => _trayBehavior;
|
get => _trayBehavior;
|
||||||
|
|
||||||
@@ -152,10 +142,8 @@ namespace TweetDuck.Configuration{
|
|||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Save(){
|
public void Save(){
|
||||||
try{
|
try{
|
||||||
WindowsUtils.CreateDirectoryForFile(file);
|
|
||||||
|
|
||||||
if (File.Exists(file)){
|
if (File.Exists(file)){
|
||||||
string backupFile = GetBackupFile(file);
|
string backupFile = GetBackupFile(file);
|
||||||
File.Delete(backupFile);
|
File.Delete(backupFile);
|
||||||
@@ -163,29 +151,23 @@ namespace TweetDuck.Configuration{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Serializer.Write(file, this);
|
Serializer.Write(file, this);
|
||||||
return true;
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Reload(){
|
public void Reload(){
|
||||||
try{
|
try{
|
||||||
LoadInternal(false);
|
LoadInternal(false);
|
||||||
return true;
|
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
try{
|
try{
|
||||||
Serializer.Write(file, new UserConfig(file));
|
Serializer.Write(file, new UserConfig(file));
|
||||||
LoadInternal(false);
|
LoadInternal(false);
|
||||||
return true;
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
|
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,8 +11,8 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
sealed class TweetDeckBridge{
|
sealed class TweetDeckBridge{
|
||||||
public static string FontSizeClass;
|
public static string FontSize { get; private set; }
|
||||||
public static string NotificationHeadContents;
|
public static string NotificationHeadLayout { get; private set; }
|
||||||
|
|
||||||
public static string LastHighlightedTweetUrl = string.Empty;
|
public static string LastHighlightedTweetUrl = string.Empty;
|
||||||
public static string LastHighlightedQuoteUrl = string.Empty;
|
public static string LastHighlightedQuoteUrl = string.Empty;
|
||||||
@@ -25,7 +25,7 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
||||||
|
|
||||||
public static void ResetStaticProperties(){
|
public static void ResetStaticProperties(){
|
||||||
FontSizeClass = NotificationHeadContents = null;
|
FontSize = NotificationHeadLayout = null;
|
||||||
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +56,11 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadFontSizeClass(string fsClass){
|
public void LoadNotificationLayout(string fontSize, string headLayout){
|
||||||
form.InvokeAsyncSafe(() => FontSizeClass = fsClass);
|
form.InvokeAsyncSafe(() => {
|
||||||
}
|
FontSize = fontSize;
|
||||||
|
NotificationHeadLayout = headLayout;
|
||||||
public void LoadNotificationHeadContents(string headContents){
|
});
|
||||||
form.InvokeAsyncSafe(() => NotificationHeadContents = headContents);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastRightClickInfo(string type, string link){
|
public void SetLastRightClickInfo(string type, string link){
|
||||||
@@ -118,8 +117,8 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayVideo(string url){
|
public void PlayVideo(string url, string username){
|
||||||
form.InvokeAsyncSafe(() => form.PlayVideo(url));
|
form.InvokeAsyncSafe(() => form.PlayVideo(url, username));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FixClipboard(){
|
public void FixClipboard(){
|
||||||
@@ -149,13 +148,9 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
|
|
||||||
public void CrashDebug(string message){
|
public void CrashDebug(string message){
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Log(message);
|
System.Diagnostics.Debug.WriteLine(message);
|
||||||
System.Diagnostics.Debugger.Break();
|
System.Diagnostics.Debugger.Break();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(string data){
|
|
||||||
System.Diagnostics.Debug.WriteLine(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Controls{
|
namespace TweetDuck.Core.Controls{
|
||||||
@@ -13,7 +14,7 @@ namespace TweetDuck.Core.Controls{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SetValueInstant(int value){
|
public void SetValueInstant(int value){
|
||||||
ControlExtensions.SetValueInstant(this, value);
|
ControlExtensions.SetValueInstant(this, Math.Max(Minimum, Math.Min(Maximum, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPaint(PaintEventArgs e){
|
protected override void OnPaint(PaintEventArgs e){
|
||||||
|
4
Core/FormBrowser.Designer.cs
generated
4
Core/FormBrowser.Designer.cs
generated
@@ -24,7 +24,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.trayIcon = new TweetDuck.Core.TrayIcon(this.components);
|
this.trayIcon = new TweetDuck.Core.Other.TrayIcon(this.components);
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.timerResize = new System.Windows.Forms.Timer(this.components);
|
this.timerResize = new System.Windows.Forms.Timer(this.components);
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private TrayIcon trayIcon;
|
private TweetDuck.Core.Other.TrayIcon trayIcon;
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
private System.Windows.Forms.Timer timerResize;
|
private System.Windows.Forms.Timer timerResize;
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,19 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
using CefSharp.WinForms;
|
|
||||||
using System;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
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;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Other.Management;
|
using TweetDuck.Core.Other.Management;
|
||||||
using TweetDuck.Core.Other.Settings;
|
using TweetDuck.Core.Other.Settings;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Enums;
|
|
||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetDuck.Resources;
|
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetLib.Audio;
|
using TweetLib.Audio;
|
||||||
|
|
||||||
@@ -44,20 +40,19 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
public string UpdateInstallerPath { get; private set; }
|
public string UpdateInstallerPath { get; private set; }
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly TweetDeckBrowser browser;
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private readonly FormNotificationTweet notification;
|
private readonly FormNotificationTweet notification;
|
||||||
private readonly ContextMenu contextMenu;
|
private readonly ContextMenu contextMenu;
|
||||||
private readonly MemoryUsageTracker memoryUsageTracker;
|
|
||||||
|
|
||||||
private bool isLoaded;
|
private bool isLoaded;
|
||||||
private bool isBrowserReady;
|
|
||||||
private FormWindowState prevState;
|
private FormWindowState prevState;
|
||||||
|
|
||||||
private TweetScreenshotManager notificationScreenshotManager;
|
private TweetScreenshotManager notificationScreenshotManager;
|
||||||
private SoundNotification soundNotification;
|
private SoundNotification soundNotification;
|
||||||
private VideoPlayer videoPlayer;
|
private VideoPlayer videoPlayer;
|
||||||
|
private AnalyticsManager analytics;
|
||||||
|
|
||||||
public FormBrowser(UpdaterSettings updaterSettings){
|
public FormBrowser(UpdaterSettings updaterSettings){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -67,59 +62,26 @@ namespace TweetDuck.Core{
|
|||||||
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.Executed += plugins_Executed;
|
this.plugins.Executed += plugins_Executed;
|
||||||
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
|
||||||
this.plugins.Reload();
|
this.plugins.Reload();
|
||||||
|
|
||||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
this.notification = new FormNotificationTweet(this, plugins);
|
||||||
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
|
|
||||||
|
|
||||||
this.notification = new FormNotificationTweet(this, plugins){
|
|
||||||
#if DEBUG
|
|
||||||
CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt
|
|
||||||
#else
|
|
||||||
CanMoveWindow = () => false
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
|
this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge(this, notification));
|
||||||
DialogHandler = new FileDialogHandler(),
|
this.browser.PageLoaded += browser_PageLoaded;
|
||||||
DragHandler = new DragHandlerBrowser(),
|
|
||||||
MenuHandler = new ContextMenuBrowser(this),
|
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
|
||||||
KeyboardHandler = new KeyboardHandlerBrowser(this),
|
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
|
||||||
RequestHandler = new RequestHandlerBrowser()
|
|
||||||
};
|
|
||||||
|
|
||||||
#if DEBUG
|
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
|
||||||
this.browser.FrameLoadStart += browser_FrameLoadStart;
|
|
||||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
|
||||||
this.browser.LoadError += browser_LoadError;
|
|
||||||
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
|
|
||||||
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
|
||||||
|
|
||||||
browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
|
||||||
browser.Dock = DockStyle.None;
|
|
||||||
browser.Location = ControlExtensions.InvisibleLocation;
|
|
||||||
Controls.Add(browser);
|
|
||||||
|
|
||||||
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
||||||
|
|
||||||
Disposed += (sender, args) => {
|
Disposed += (sender, args) => {
|
||||||
memoryUsageTracker.Dispose();
|
|
||||||
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
contextMenu.Dispose();
|
contextMenu.Dispose();
|
||||||
|
|
||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
soundNotification?.Dispose();
|
soundNotification?.Dispose();
|
||||||
videoPlayer?.Dispose();
|
videoPlayer?.Dispose();
|
||||||
|
analytics?.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
|
||||||
@@ -128,10 +90,7 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
|
|
||||||
Config.MuteToggled += Config_MuteToggled;
|
this.updates = browser.CreateUpdateHandler(updaterSettings);
|
||||||
Config.ZoomLevelChanged += Config_ZoomLevelChanged;
|
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, updaterSettings);
|
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||||
|
|
||||||
@@ -154,13 +113,9 @@ namespace TweetDuck.Core{
|
|||||||
Config.BrowserWindow.Restore(this, true);
|
Config.BrowserWindow.Restore(this, true);
|
||||||
prevState = WindowState;
|
prevState = WindowState;
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBrowserReady(){
|
if (Config.AllowDataCollection){
|
||||||
if (!isBrowserReady){
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
browser.Location = Point.Empty;
|
|
||||||
browser.Dock = DockStyle.Fill;
|
|
||||||
isBrowserReady = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,69 +123,7 @@ namespace TweetDuck.Core{
|
|||||||
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
// active event handlers
|
// event handlers
|
||||||
|
|
||||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
|
||||||
if (!e.IsLoading){
|
|
||||||
foreach(string word in TwitterUtils.DictionaryWords){
|
|
||||||
browser.AddWordToDictionary(word);
|
|
||||||
}
|
|
||||||
|
|
||||||
BeginInvoke(new Action(OnBrowserReady));
|
|
||||||
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
|
||||||
if (e.Frame.IsMain){
|
|
||||||
memoryUsageTracker.Stop();
|
|
||||||
|
|
||||||
if (Config.ZoomLevel != 100){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
|
||||||
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
|
|
||||||
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
|
|
||||||
|
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
|
||||||
TweetDeckBridge.RestoreSessionData(e.Frame);
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
|
||||||
InjectBrowserCSS();
|
|
||||||
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
|
||||||
|
|
||||||
TweetDeckBridge.ResetStaticProperties();
|
|
||||||
|
|
||||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
|
||||||
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.FirstRun){
|
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
|
||||||
if (e.ErrorCode == CefErrorCode.Aborted){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
|
||||||
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
|
||||||
|
|
||||||
if (errorPage != null){
|
|
||||||
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void timerResize_Tick(object sender, EventArgs e){
|
private void timerResize_Tick(object sender, EventArgs e){
|
||||||
FormBrowser_ResizeEnd(this, e); // also stops timer
|
FormBrowser_ResizeEnd(this, e); // also stops timer
|
||||||
@@ -300,14 +193,6 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_MuteToggled(object sender, EventArgs e){
|
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
|
||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
}
|
}
|
||||||
@@ -323,6 +208,38 @@ namespace TweetDuck.Core{
|
|||||||
ForceClose();
|
ForceClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void browser_PageLoaded(object sender, EventArgs e){
|
||||||
|
if (Config.ShowDataCollectionNotification){
|
||||||
|
this.InvokeAsyncSafe(() => {
|
||||||
|
if (!Config.FirstRun && Config.AllowDataCollection){
|
||||||
|
FormMessage form = new FormMessage("Anonymous Data Update", "Hi! You can now review your anonymous data report, and opt-out if you've changed your mind. Collected data will be used to focus development on most commonly used features. If you want to opt-out but still support the project, any feedback and donations are appreciated.", MessageBoxIcon.Information);
|
||||||
|
form.AddButton("OK", ControlType.Accept | ControlType.Focused);
|
||||||
|
|
||||||
|
Button btnReviewSettings = new Button{
|
||||||
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
||||||
|
Font = SystemFonts.MessageBoxFont,
|
||||||
|
Location = new Point(9, 12),
|
||||||
|
Margin = new Padding(0, 0, 48, 0),
|
||||||
|
Size = new Size(160, 26),
|
||||||
|
Text = "Review Feedback Options",
|
||||||
|
UseVisualStyleBackColor = true
|
||||||
|
};
|
||||||
|
|
||||||
|
btnReviewSettings.Click += (sender2, args2) => {
|
||||||
|
form.Close();
|
||||||
|
OpenSettings(typeof(TabSettingsFeedback));
|
||||||
|
};
|
||||||
|
|
||||||
|
form.AddActionControl(btnReviewSettings);
|
||||||
|
ShowChildForm(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.ShowDataCollectionNotification = false;
|
||||||
|
Config.Save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
||||||
if (e.HasErrors){
|
if (e.HasErrors){
|
||||||
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
|
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK);
|
||||||
@@ -339,10 +256,6 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
|
||||||
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
FormManager.CloseAllDialogs();
|
FormManager.CloseAllDialogs();
|
||||||
@@ -400,12 +313,13 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
|
if (browser.Ready && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
|
||||||
if (videoPlayer != null && videoPlayer.Running){
|
if (videoPlayer != null && videoPlayer.Running){
|
||||||
videoPlayer.Close();
|
videoPlayer.Close();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
|
browser.OnMouseClickExtra(m.WParam);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserExtraMouseButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -414,11 +328,7 @@ namespace TweetDuck.Core{
|
|||||||
base.WndProc(ref m);
|
base.WndProc(ref m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// notification helpers
|
// bridge methods
|
||||||
|
|
||||||
public FormNotificationMain CreateNotificationForm(bool enableContextMenu){
|
|
||||||
return new FormNotificationMain(this, plugins, enableContextMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PauseNotification(){
|
public void PauseNotification(){
|
||||||
notification.PauseNotification();
|
notification.PauseNotification();
|
||||||
@@ -428,22 +338,28 @@ namespace TweetDuck.Core{
|
|||||||
notification.ResumeNotification();
|
notification.ResumeNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
// javascript calls
|
|
||||||
|
|
||||||
public void InjectBrowserCSS(){
|
|
||||||
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReinjectCustomCSS(string css){
|
public void ReinjectCustomCSS(string css){
|
||||||
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
browser.ReinjectCustomCSS(css);
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateProperties(PropertyBridge.Environment environment){
|
|
||||||
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadToTweetDeck(){
|
public void ReloadToTweetDeck(){
|
||||||
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
|
browser.ReloadToTweetDeck();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerTweetScreenshot(){
|
||||||
|
browser.TriggerTweetScreenshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadColumns(){
|
||||||
|
browser.ReloadColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyROT13(){
|
||||||
|
browser.ApplyROT13();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
|
||||||
|
analytics?.TriggerEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback handlers
|
// callback handlers
|
||||||
@@ -452,7 +368,12 @@ namespace TweetDuck.Core{
|
|||||||
if (Config.FirstRun){
|
if (Config.FirstRun){
|
||||||
Config.FirstRun = false;
|
Config.FirstRun = false;
|
||||||
Config.AllowDataCollection = allowDataCollection;
|
Config.AllowDataCollection = allowDataCollection;
|
||||||
|
Config.ShowDataCollectionNotification = false;
|
||||||
Config.Save();
|
Config.Save();
|
||||||
|
|
||||||
|
if (allowDataCollection && analytics == null){
|
||||||
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showGuide){
|
if (showGuide){
|
||||||
@@ -472,7 +393,7 @@ namespace TweetDuck.Core{
|
|||||||
if (!FormManager.TryBringToFront<FormSettings>()){
|
if (!FormManager.TryBringToFront<FormSettings>()){
|
||||||
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
|
||||||
|
|
||||||
FormSettings form = new FormSettings(this, plugins, updates, startTab);
|
FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab);
|
||||||
|
|
||||||
form.FormClosed += (sender, args) => {
|
form.FormClosed += (sender, args) => {
|
||||||
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
|
||||||
@@ -486,11 +407,16 @@ namespace TweetDuck.Core{
|
|||||||
trayIcon.HasNotifications = false;
|
trayIcon.HasNotifications = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Program.SystemConfig.EnableBrowserGCReload){
|
browser.RefreshMemoryTracker();
|
||||||
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
|
|
||||||
|
if (Config.AllowDataCollection){
|
||||||
|
if (analytics == null){
|
||||||
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
}
|
}
|
||||||
else{
|
}
|
||||||
memoryUsageTracker.Stop();
|
else if (analytics != null){
|
||||||
|
analytics.Dispose();
|
||||||
|
analytics = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form.ShouldReloadBrowser){
|
if (form.ShouldReloadBrowser){
|
||||||
@@ -498,25 +424,28 @@ namespace TweetDuck.Core{
|
|||||||
plugins.Reload(); // also reloads the browser
|
plugins.Reload(); // also reloads the browser
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
UpdateProperties(PropertyBridge.Environment.Browser);
|
browser.UpdateProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.RequiresResize = true;
|
notification.RequiresResize = true;
|
||||||
form.Dispose();
|
form.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenOptions);
|
||||||
ShowChildForm(form);
|
ShowChildForm(form);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenAbout(){
|
public void OpenAbout(){
|
||||||
if (!FormManager.TryBringToFront<FormAbout>()){
|
if (!FormManager.TryBringToFront<FormAbout>()){
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenAbout);
|
||||||
ShowChildForm(new FormAbout());
|
ShowChildForm(new FormAbout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenPlugins(){
|
public void OpenPlugins(){
|
||||||
if (!FormManager.TryBringToFront<FormPlugins>()){
|
if (!FormManager.TryBringToFront<FormPlugins>()){
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenPlugins);
|
||||||
ShowChildForm(new FormPlugins(plugins));
|
ShowChildForm(new FormPlugins(plugins));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -539,9 +468,11 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
soundNotification.SetVolume(Config.NotificationSoundVolume);
|
||||||
soundNotification.Play(Config.NotificationSoundPath);
|
soundNotification.Play(Config.NotificationSoundPath);
|
||||||
|
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.SoundNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayVideo(string url){
|
public void PlayVideo(string url, string username){
|
||||||
if (string.IsNullOrEmpty(url)){
|
if (string.IsNullOrEmpty(url)){
|
||||||
videoPlayer?.Close();
|
videoPlayer?.Close();
|
||||||
return;
|
return;
|
||||||
@@ -551,16 +482,12 @@ namespace TweetDuck.Core{
|
|||||||
videoPlayer = new VideoPlayer(this);
|
videoPlayer = new VideoPlayer(this);
|
||||||
|
|
||||||
videoPlayer.ProcessExited += (sender, args) => {
|
videoPlayer.ProcessExited += (sender, args) => {
|
||||||
browser.GetBrowser().GetHost().SendFocusEvent(true);
|
browser.HideVideoOverlay(true);
|
||||||
HideVideoOverlay();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
videoPlayer.Launch(url);
|
videoPlayer.Launch(url, username);
|
||||||
}
|
TriggerAnalyticsEvent(AnalyticsFile.Event.VideoPlay);
|
||||||
|
|
||||||
public void HideVideoOverlay(){
|
|
||||||
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ProcessBrowserKey(Keys key){
|
public bool ProcessBrowserKey(Keys key){
|
||||||
@@ -575,15 +502,14 @@ namespace TweetDuck.Core{
|
|||||||
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
|
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
|
||||||
Activate();
|
Activate();
|
||||||
|
|
||||||
using(IFrame frame = browser.GetBrowser().MainFrame){
|
if (!browser.IsTweetDeckWebsite){
|
||||||
if (!TwitterUtils.IsTweetDeckWebsite(frame)){
|
|
||||||
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
|
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
notification.FinishCurrentNotification();
|
notification.FinishCurrentNotification();
|
||||||
browser.ExecuteScriptAsync("window.TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
browser.ShowTweetDetail(columnId, chirpId, fallbackUrl);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
public void OnTweetScreenshotReady(string html, int width, int height){
|
||||||
@@ -592,6 +518,7 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
notificationScreenshotManager.Trigger(html, width, height);
|
notificationScreenshotManager.Trigger(html, width, height);
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetScreenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisplayTooltip(string text){
|
public void DisplayTooltip(string text){
|
||||||
@@ -604,9 +531,5 @@ namespace TweetDuck.Core{
|
|||||||
toolTip.Show(text, this, position);
|
toolTip.Show(text, this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerTweetScreenshot(){
|
|
||||||
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
abstract class ContextMenuBase : IContextMenuHandler{
|
||||||
@@ -31,10 +32,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
|
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
|
||||||
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
|
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
|
||||||
private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502;
|
private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502;
|
||||||
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26503;
|
private const CefMenuCommand MenuViewImage = (CefMenuCommand)26503;
|
||||||
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26504;
|
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26504;
|
||||||
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505;
|
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505;
|
||||||
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506;
|
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
||||||
|
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
||||||
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||||
|
|
||||||
private string[] lastHighlightedTweetAuthors;
|
private string[] lastHighlightedTweetAuthors;
|
||||||
@@ -79,6 +81,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
|
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
|
||||||
|
model.AddItem(MenuViewImage, "View image in photo viewer");
|
||||||
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
|
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
|
||||||
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
||||||
model.AddItem(MenuSaveMedia, TextSave("image"));
|
model.AddItem(MenuSaveMedia, TextSave("image"));
|
||||||
@@ -114,9 +117,35 @@ namespace TweetDuck.Core.Handling{
|
|||||||
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MenuViewImage:
|
||||||
|
string url = GetMediaLink(parameters);
|
||||||
|
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
|
||||||
|
|
||||||
|
void ViewFile(){
|
||||||
|
string ext = Path.GetExtension(file);
|
||||||
|
|
||||||
|
if (TwitterUtils.ValidImageExtensions.Contains(ext)){
|
||||||
|
WindowsUtils.OpenAssociatedProgram(file);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(file)){
|
||||||
|
ViewFile();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => {
|
||||||
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case MenuSaveMedia:
|
case MenuSaveMedia:
|
||||||
if (IsVideo){
|
if (IsVideo){
|
||||||
TwitterUtils.DownloadVideo(GetMediaLink(parameters));
|
TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
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.Other.Analytics;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
@@ -17,6 +18,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
|
||||||
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
|
||||||
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
|
||||||
|
private const CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615;
|
||||||
|
|
||||||
private const string TitleReloadBrowser = "Reload browser";
|
private const string TitleReloadBrowser = "Reload browser";
|
||||||
private const string TitleMuteNotifications = "Mute notifications";
|
private const string TitleMuteNotifications = "Mute notifications";
|
||||||
@@ -41,6 +43,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
||||||
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
||||||
|
model.AddSeparator();
|
||||||
|
model.AddItem(MenuInputApplyROT13, "Apply ROT13");
|
||||||
|
}
|
||||||
|
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +96,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
|
form.InvokeAsyncSafe(() => form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
@@ -136,6 +145,10 @@ namespace TweetDuck.Core.Handling{
|
|||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
SetClipboardText(form, lastHighlightedQuoteUrl);
|
SetClipboardText(form, lastHighlightedQuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case MenuInputApplyROT13:
|
||||||
|
form.InvokeAsyncSafe(form.ApplyROT13);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -153,6 +166,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
menu.Popup += (sender, args) => {
|
menu.Popup += (sender, args) => {
|
||||||
menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications;
|
menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications;
|
||||||
|
form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu);
|
||||||
};
|
};
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
sealed class ContextMenuNotification : ContextMenuBase{
|
sealed class ContextMenuNotification : ContextMenuBase{
|
||||||
@@ -48,13 +49,16 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (HasDevTools){
|
if (HasDevTools){
|
||||||
model.AddSeparator();
|
AddSeparator(model);
|
||||||
AddDebugMenuItems(model);
|
AddDebugMenuItems(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
form.InvokeAsyncSafe(() => form.ContextMenuOpen = true);
|
form.InvokeAsyncSafe(() => {
|
||||||
|
form.ContextMenuOpen = true;
|
||||||
|
form.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationContextMenu);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
|
||||||
|
@@ -1,14 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling.General{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
||||||
void IBrowserProcessHandler.OnContextInitialized(){
|
public static Task UpdatePrefs(){
|
||||||
|
return Cef.UIThreadTaskFactory.StartNew(UpdatePrefsInternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdatePrefsInternal(){
|
||||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||||
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
||||||
|
ctx.SetPreference("settings.a11y.animation_policy", Program.UserConfig.EnableAnimatedImages ? "allowed" : "none", out string _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IBrowserProcessHandler.OnContextInitialized(){
|
||||||
|
UpdatePrefsInternal();
|
||||||
|
}
|
||||||
|
|
||||||
void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
|
void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
|
||||||
void IDisposable.Dispose(){}
|
void IDisposable.Dispose(){}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling {
|
namespace TweetDuck.Core.Handling {
|
||||||
sealed class KeyboardHandlerNotification : IKeyboardHandler{
|
sealed class KeyboardHandlerNotification : IKeyboardHandler{
|
||||||
@@ -11,19 +12,26 @@ namespace TweetDuck.Core.Handling {
|
|||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TriggerKeyboardShortcutAnalytics(){
|
||||||
|
notification.InvokeAsyncSafe(() => notification.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationKeyboardShortcut));
|
||||||
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
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://")){
|
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
||||||
switch((Keys)windowsKeyCode){
|
switch((Keys)windowsKeyCode){
|
||||||
case Keys.Enter:
|
case Keys.Enter:
|
||||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Keys.Escape:
|
case Keys.Escape:
|
||||||
notification.InvokeAsyncSafe(() => notification.HideNotification(true));
|
notification.InvokeAsyncSafe(notification.HideNotification);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Keys.Space:
|
case Keys.Space:
|
||||||
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
46
Core/Notification/Example/FormNotificationExample.cs
Normal file
46
Core/Notification/Example/FormNotificationExample.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Notification.Example{
|
||||||
|
sealed class FormNotificationExample : FormNotificationMain{
|
||||||
|
public override bool RequiresResize => true;
|
||||||
|
protected override bool CanDragWindow => Program.UserConfig.NotificationPosition == TweetNotification.Position.Custom;
|
||||||
|
|
||||||
|
protected override FormBorderStyle NotificationBorderStyle{
|
||||||
|
get{
|
||||||
|
if (Program.UserConfig.NotificationSize == TweetNotification.Size.Custom){
|
||||||
|
switch(base.NotificationBorderStyle){
|
||||||
|
case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable;
|
||||||
|
case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.NotificationBorderStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly TweetNotification exampleNotification;
|
||||||
|
|
||||||
|
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
|
||||||
|
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
exampleNotification = TweetNotification.Example(exampleTweetHTML, 95);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowExampleNotification(bool reset){
|
||||||
|
if (reset){
|
||||||
|
LoadTweet(exampleNotification);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
PrepareAndDisplayWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,7 @@ 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.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Other.Management;
|
using TweetDuck.Core.Other.Management;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
partial class FormNotificationBase : Form{
|
partial class FormNotificationBase : Form{
|
||||||
protected static int FontSizeLevel{
|
protected static int FontSizeLevel{
|
||||||
get{
|
get{
|
||||||
switch(TweetDeckBridge.FontSizeClass){
|
switch(TweetDeckBridge.FontSize){
|
||||||
case "largest": return 4;
|
case "largest": return 4;
|
||||||
case "large": return 3;
|
case "large": return 3;
|
||||||
case "small": return 1;
|
case "small": return 1;
|
||||||
@@ -25,7 +26,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Point PrimaryLocation{
|
protected virtual Point PrimaryLocation{
|
||||||
get{
|
get{
|
||||||
UserConfig config = Program.UserConfig;
|
UserConfig config = Program.UserConfig;
|
||||||
Screen screen;
|
Screen screen;
|
||||||
@@ -66,31 +67,39 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
|
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
|
||||||
|
protected virtual bool CanDragWindow => true;
|
||||||
|
|
||||||
public new Point Location{
|
public new Point Location{
|
||||||
get => base.Location;
|
get{
|
||||||
|
return base.Location;
|
||||||
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
|
||||||
FormBorderStyle = GetBorderStyle(CanResizeWindow);
|
FormBorderStyle = NotificationBorderStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanResizeWindow{
|
protected virtual FormBorderStyle NotificationBorderStyle{
|
||||||
get => FormBorderStyle == FormBorderStyle.Sizable || FormBorderStyle == FormBorderStyle.SizableToolWindow;
|
get{
|
||||||
set => FormBorderStyle = GetBorderStyle(value);
|
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
|
||||||
|
return FormBorderStyle.FixedSingle;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return FormBorderStyle.FixedToolWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<bool> CanMoveWindow { get; set; } = () => true;
|
|
||||||
protected override bool ShowWithoutActivation => true;
|
protected override bool ShowWithoutActivation => true;
|
||||||
|
|
||||||
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier;
|
protected float DpiScale { get; }
|
||||||
|
protected double SizeScale => DpiScale*Program.UserConfig.ZoomLevel/100.0;
|
||||||
|
|
||||||
protected readonly FormBrowser owner;
|
protected readonly FormBrowser owner;
|
||||||
protected readonly ChromiumWebBrowser browser;
|
protected readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
|
||||||
private readonly float dpiScale;
|
|
||||||
|
|
||||||
private TweetNotification currentNotification;
|
private TweetNotification currentNotification;
|
||||||
private int pauseCounter;
|
private int pauseCounter;
|
||||||
@@ -120,13 +129,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
this.browser.ClientSize = ClientSize;
|
this.browser.ClientSize = ClientSize;
|
||||||
this.browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
|
this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this.dpiScale = this.GetDPIScale();
|
DpiScale = this.GetDPIScale();
|
||||||
|
|
||||||
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
||||||
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
||||||
@@ -143,20 +152,24 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
protected override void WndProc(ref Message m){
|
||||||
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
|
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanDragWindow){ // WM_SYSCOMMAND, SC_MOVE
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.WndProc(ref m);
|
base.WndProc(ref m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
|
||||||
|
owner.TriggerAnalyticsEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
|
|
||||||
private void owner_FormClosed(object sender, FormClosedEventArgs e){
|
private void owner_FormClosed(object sender, FormClosedEventArgs e){
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
private void browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
|
||||||
if (e.IsBrowserInitialized){
|
if (e.IsBrowserInitialized){
|
||||||
Initialized?.Invoke(this, EventArgs.Empty);
|
Initialized?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
@@ -167,11 +180,8 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
// notification methods
|
// notification methods
|
||||||
|
|
||||||
public virtual void HideNotification(bool loadBlank){
|
public virtual void HideNotification(){
|
||||||
if (loadBlank){
|
|
||||||
browser.Load("about:blank");
|
browser.Load("about:blank");
|
||||||
}
|
|
||||||
|
|
||||||
Location = ControlExtensions.InvisibleLocation;
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
currentNotification = null;
|
currentNotification = null;
|
||||||
}
|
}
|
||||||
@@ -233,14 +243,5 @@ namespace TweetDuck.Core.Notification{
|
|||||||
toolTip.Show(text, this, position);
|
toolTip.Show(text, this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FormBorderStyle GetBorderStyle(bool sizable){
|
|
||||||
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
|
|
||||||
return sizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedSingle;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return sizable ? FormBorderStyle.SizableToolWindow : FormBorderStyle.FixedToolWindow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -49,7 +49,7 @@
|
|||||||
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
|
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
|
||||||
this.progressBarTimer.Maximum = 1000;
|
this.progressBarTimer.Maximum = 1000;
|
||||||
this.progressBarTimer.Name = "progressBarTimer";
|
this.progressBarTimer.Name = "progressBarTimer";
|
||||||
this.progressBarTimer.Size = new System.Drawing.Size(284, TimerBarHeight);
|
this.progressBarTimer.Size = new System.Drawing.Size(284, 4);
|
||||||
this.progressBarTimer.TabIndex = 1;
|
this.progressBarTimer.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// FormNotification
|
// FormNotification
|
||||||
|
@@ -5,6 +5,7 @@ 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.Handling;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
@@ -12,14 +13,14 @@ using TweetDuck.Plugins.Enums;
|
|||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
partial class FormNotificationMain : FormNotificationBase{
|
abstract partial class FormNotificationMain : FormNotificationBase{
|
||||||
private const string NotificationScriptFile = "notification.js";
|
private const string NotificationScriptFile = "notification.js";
|
||||||
private const int TimerBarHeight = 4;
|
|
||||||
|
|
||||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||||
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
private readonly int timerBarHeight;
|
||||||
|
|
||||||
protected int timeLeft, totalTime;
|
protected int timeLeft, totalTime;
|
||||||
protected bool pausedDuringNotification;
|
protected bool pausedDuringNotification;
|
||||||
@@ -31,9 +32,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private bool? prevDisplayTimer;
|
private bool? prevDisplayTimer;
|
||||||
private int? prevFontSize;
|
private int? prevFontSize;
|
||||||
|
|
||||||
public bool RequiresResize{
|
public virtual bool RequiresResize{
|
||||||
get{
|
get{
|
||||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel || CanResizeWindow;
|
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
@@ -72,14 +73,13 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Size BrowserSize{
|
public Size BrowserSize => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize;
|
||||||
get => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-TimerBarHeight) : ClientSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.plugins = pluginManager;
|
this.plugins = pluginManager;
|
||||||
|
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
||||||
|
|
||||||
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||||
|
|
||||||
@@ -128,6 +128,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockXButtonUp = true;
|
blockXButtonUp = true;
|
||||||
|
this.InvokeAsyncSafe(() => TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationExtraMouseButton));
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){
|
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){
|
||||||
@@ -148,7 +149,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
if (e.CloseReason == CloseReason.UserClosing){
|
if (e.CloseReason == CloseReason.UserClosing){
|
||||||
HideNotification(true);
|
HideNotification();
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,12 +177,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void timerHideProgress_Tick(object sender, EventArgs e){
|
private void timerHideProgress_Tick(object sender, EventArgs e){
|
||||||
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
|
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
timeLeft -= timerProgress.Interval;
|
timeLeft -= timerProgress.Interval;
|
||||||
|
|
||||||
int value = BrowserUtils.Scale(1025, (totalTime-timeLeft)/(double)totalTime);
|
int value = BrowserUtils.Scale(progressBarTimer.Maximum+25, (totalTime-timeLeft)/(double)totalTime);
|
||||||
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
|
progressBarTimer.SetValueInstant(Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value);
|
||||||
|
|
||||||
if (timeLeft <= 0){
|
if (timeLeft <= 0){
|
||||||
FinishCurrentNotification();
|
FinishCurrentNotification();
|
||||||
@@ -194,21 +197,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
LoadTweet(notification);
|
LoadTweet(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowNotificationForSettings(bool reset){
|
public override void HideNotification(){
|
||||||
if (reset){
|
base.HideNotification();
|
||||||
LoadTweet(TweetNotification.ExampleTweet);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
PrepareAndDisplayWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateTitle();
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
||||||
}
|
|
||||||
|
|
||||||
public override void HideNotification(bool loadBlank){
|
|
||||||
base.HideNotification(loadBlank);
|
|
||||||
|
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
|
||||||
timerProgress.Stop();
|
timerProgress.Stop();
|
||||||
totalTime = 0;
|
totalTime = 0;
|
||||||
|
|
||||||
@@ -251,14 +243,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
protected override void LoadTweet(TweetNotification tweet){
|
protected override void LoadTweet(TweetNotification tweet){
|
||||||
timerProgress.Stop();
|
timerProgress.Stop();
|
||||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
||||||
|
|
||||||
base.LoadTweet(tweet);
|
base.LoadTweet(tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetNotificationSize(int width, int height){
|
protected override void SetNotificationSize(int width, int height){
|
||||||
if (Program.UserConfig.DisplayNotificationTimer){
|
if (Program.UserConfig.DisplayNotificationTimer){
|
||||||
ClientSize = new Size(width, height+TimerBarHeight);
|
ClientSize = new Size(width, height+timerBarHeight);
|
||||||
progressBarTimer.Visible = true;
|
progressBarTimer.Visible = true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -269,7 +261,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
browser.ClientSize = new Size(width, height);
|
browser.ClientSize = new Size(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrepareAndDisplayWindow(){
|
protected void PrepareAndDisplayWindow(){
|
||||||
if (RequiresResize){
|
if (RequiresResize){
|
||||||
RequiresResize = false;
|
RequiresResize = false;
|
||||||
SetNotificationSize(BaseClientWidth, BaseClientHeight);
|
SetNotificationSize(BaseClientWidth, BaseClientHeight);
|
||||||
|
@@ -40,7 +40,6 @@ namespace TweetDuck.Core.Notification {
|
|||||||
//
|
//
|
||||||
// FormNotificationTweet
|
// FormNotificationTweet
|
||||||
//
|
//
|
||||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotificationTweet_FormClosing);
|
|
||||||
this.ResumeLayout(true);
|
this.ResumeLayout(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
@@ -10,10 +11,24 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private const int NonIntrusiveIdleLimit = 30;
|
private const int NonIntrusiveIdleLimit = 30;
|
||||||
private const int TrimMinimum = 32;
|
private const int TrimMinimum = 32;
|
||||||
|
|
||||||
|
protected override Point PrimaryLocation => hasTemporarilyMoved && IsNotificationVisible ? Location : base.PrimaryLocation;
|
||||||
private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position);
|
private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position);
|
||||||
|
|
||||||
|
protected override bool CanDragWindow{
|
||||||
|
get{
|
||||||
|
if (ModifierKeys.HasFlag(Keys.Alt)){
|
||||||
|
hasTemporarilyMoved = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
||||||
private bool needsTrim;
|
private bool needsTrim;
|
||||||
|
private bool hasTemporarilyMoved;
|
||||||
|
|
||||||
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
|
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -26,18 +41,18 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormNotificationTweet_FormClosing(object sender, FormClosingEventArgs e){
|
protected override void WndProc(ref Message m){
|
||||||
if (e.CloseReason == CloseReason.UserClosing){
|
if (m.Msg == 0x00A7){ // WM_NCMBUTTONDOWN
|
||||||
tweetQueue.Clear(); // already canceled
|
int hitTest = m.WParam.ToInt32();
|
||||||
TrimQueue();
|
|
||||||
|
if (hitTest == 2 || hitTest == 20){ // HTCAPTION, HTCLOSE
|
||||||
|
hasTemporarilyMoved = false;
|
||||||
|
MoveToVisibleLocation();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TrimQueue(){
|
base.WndProc(ref m);
|
||||||
if (needsTrim){
|
|
||||||
tweetQueue.TrimExcess();
|
|
||||||
needsTrim = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
@@ -68,11 +83,9 @@ namespace TweetDuck.Core.Notification{
|
|||||||
// notification methods
|
// notification methods
|
||||||
|
|
||||||
public override void ShowNotification(TweetNotification notification){
|
public override void ShowNotification(TweetNotification notification){
|
||||||
if (IsPaused){
|
|
||||||
tweetQueue.Enqueue(notification);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
tweetQueue.Enqueue(notification);
|
tweetQueue.Enqueue(notification);
|
||||||
|
|
||||||
|
if (!IsPaused){
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
|
|
||||||
if (totalTime == 0){
|
if (totalTime == 0){
|
||||||
@@ -81,6 +94,19 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
needsTrim |= tweetQueue.Count >= TrimMinimum;
|
needsTrim |= tweetQueue.Count >= TrimMinimum;
|
||||||
|
TriggerAnalyticsEvent(AnalyticsFile.Event.DesktopNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HideNotification(){
|
||||||
|
base.HideNotification();
|
||||||
|
tweetQueue.Clear();
|
||||||
|
|
||||||
|
if (needsTrim){
|
||||||
|
tweetQueue.TrimExcess();
|
||||||
|
needsTrim = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTemporarilyMoved = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FinishCurrentNotification(){
|
public override void FinishCurrentNotification(){
|
||||||
@@ -88,8 +114,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
LoadNextNotification();
|
LoadNextNotification();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
HideNotification(true);
|
HideNotification();
|
||||||
TrimQueue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,6 +12,8 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Notification.Screenshot{
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
sealed class FormNotificationScreenshotable : FormNotificationBase{
|
||||||
|
protected override bool CanDragWindow => false;
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
|
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
|
||||||
|
@@ -42,10 +42,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins){
|
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins);
|
||||||
CanMoveWindow = () => false
|
|
||||||
};
|
|
||||||
|
|
||||||
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
|
||||||
screenshot.Show();
|
screenshot.Show();
|
||||||
timeout.Start();
|
timeout.Start();
|
||||||
|
@@ -5,25 +5,11 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
sealed class TweetNotification{
|
sealed class TweetNotification{
|
||||||
private const string DefaultFontSizeClass = "medium";
|
private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><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 DefaultHeadContents = @"<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 static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
|
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
|
||||||
|
|
||||||
private static string ExampleTweetHTML;
|
public static TweetNotification Example(string html, int characters){
|
||||||
|
return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true);
|
||||||
public static TweetNotification ExampleTweet{
|
|
||||||
get{
|
|
||||||
if (ExampleTweetHTML == null){
|
|
||||||
ExampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
ExampleTweetHTML = ExampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TweetNotification(string.Empty, string.Empty, "Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Position{
|
public enum Position{
|
||||||
@@ -67,8 +53,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
build.Append("<!DOCTYPE html>");
|
build.Append("<!DOCTYPE html>");
|
||||||
build.Append("<html class='os-windows txt-base-").Append(TweetDeckBridge.FontSizeClass ?? DefaultFontSizeClass).Append("'>");
|
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
|
||||||
build.Append("<head>").Append(TweetDeckBridge.NotificationHeadContents ?? DefaultHeadContents);
|
|
||||||
|
|
||||||
if (enableCustomCSS){
|
if (enableCustomCSS){
|
||||||
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
||||||
@@ -85,7 +70,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
build.Append(' ').Append(bodyClasses);
|
build.Append(' ').Append(bodyClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%;height:auto;overflow:initial;'>");
|
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;height:auto!important;overflow:initial!important;'>");
|
||||||
build.Append(html);
|
build.Append(html);
|
||||||
build.Append("</div></body>");
|
build.Append("</div></body>");
|
||||||
build.Append("</html>");
|
build.Append("</html>");
|
||||||
|
101
Core/Other/Analytics/AnalyticsFile.cs
Normal file
101
Core/Other/Analytics/AnalyticsFile.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using TweetDuck.Data.Serialization;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
sealed class AnalyticsFile{
|
||||||
|
private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>();
|
||||||
|
|
||||||
|
static AnalyticsFile(){
|
||||||
|
Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{
|
||||||
|
ConvertToString = value => value.ToBinary().ToString(),
|
||||||
|
ConvertToObject = value => DateTime.FromBinary(long.Parse(value))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Event{
|
||||||
|
OpenOptions, OpenPlugins, OpenAbout, OpenGuide,
|
||||||
|
DesktopNotification, SoundNotification,
|
||||||
|
BrowserContextMenu, BrowserExtraMouseButton,
|
||||||
|
NotificationContextMenu, NotificationExtraMouseButton, NotificationKeyboardShortcut,
|
||||||
|
TweetScreenshot, TweetDetail, VideoPlay, GCReload
|
||||||
|
}
|
||||||
|
|
||||||
|
// STATE PROPERTIES
|
||||||
|
|
||||||
|
public DateTime LastDataCollection { get; set; } = DateTime.MinValue;
|
||||||
|
public string LastCollectionVersion { get; set; } = null;
|
||||||
|
public string LastCollectionMessage { get; set; } = null;
|
||||||
|
|
||||||
|
// USAGE DATA
|
||||||
|
|
||||||
|
public int CountOpenOptions { get; private set; } = 0;
|
||||||
|
public int CountOpenPlugins { get; private set; } = 0;
|
||||||
|
public int CountOpenAbout { get; private set; } = 0;
|
||||||
|
public int CountOpenGuide { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int CountDesktopNotifications { get; private set; } = 0;
|
||||||
|
public int CountSoundNotifications { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int CountBrowserContextMenus { get; private set; } = 0;
|
||||||
|
public int CountBrowserExtraMouseButtons { get; private set; } = 0;
|
||||||
|
public int CountNotificationContextMenus { get; private set; } = 0;
|
||||||
|
public int CountNotificationExtraMouseButtons { get; private set; } = 0;
|
||||||
|
public int CountNotificationKeyboardShortcuts { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int CountTweetScreenshots { get; private set; } = 0;
|
||||||
|
public int CountTweetDetails { get; private set; } = 0;
|
||||||
|
public int CountVideoPlays { get; private set; } = 0;
|
||||||
|
public int CountGCReloads { get; private set; } = 0;
|
||||||
|
|
||||||
|
// END OF DATA
|
||||||
|
|
||||||
|
private readonly string file;
|
||||||
|
|
||||||
|
private AnalyticsFile(string file){
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerEvent(Event e){
|
||||||
|
switch(e){
|
||||||
|
case Event.OpenOptions: ++CountOpenOptions; break;
|
||||||
|
case Event.OpenPlugins: ++CountOpenPlugins; break;
|
||||||
|
case Event.OpenAbout: ++CountOpenAbout; break;
|
||||||
|
case Event.OpenGuide: ++CountOpenGuide; break;
|
||||||
|
|
||||||
|
case Event.DesktopNotification: ++CountDesktopNotifications; break;
|
||||||
|
case Event.SoundNotification: ++CountSoundNotifications; break;
|
||||||
|
|
||||||
|
case Event.BrowserContextMenu: ++CountBrowserContextMenus; break;
|
||||||
|
case Event.BrowserExtraMouseButton: ++CountBrowserExtraMouseButtons; break;
|
||||||
|
case Event.NotificationContextMenu: ++CountNotificationContextMenus; break;
|
||||||
|
case Event.NotificationExtraMouseButton: ++CountNotificationExtraMouseButtons; break;
|
||||||
|
case Event.NotificationKeyboardShortcut: ++CountNotificationKeyboardShortcuts; break;
|
||||||
|
|
||||||
|
case Event.TweetScreenshot: ++CountTweetScreenshots; break;
|
||||||
|
case Event.TweetDetail: ++CountTweetDetails; break;
|
||||||
|
case Event.VideoPlay: ++CountVideoPlays; break;
|
||||||
|
case Event.GCReload: ++CountGCReloads; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(){
|
||||||
|
try{
|
||||||
|
Serializer.Write(file, this);
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnalyticsFile Load(string file){
|
||||||
|
AnalyticsFile config = new AnalyticsFile(file);
|
||||||
|
|
||||||
|
try{
|
||||||
|
Serializer.ReadIfExists(file, config);
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
Core/Other/Analytics/AnalyticsManager.cs
Normal file
127
Core/Other/Analytics/AnalyticsManager.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
sealed class AnalyticsManager : IDisposable{
|
||||||
|
private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(7);
|
||||||
|
private static readonly Uri CollectionUrl = new Uri("https://tweetduck.chylex.com/breadcrumb/report");
|
||||||
|
|
||||||
|
public AnalyticsFile File { get; }
|
||||||
|
|
||||||
|
private readonly FormBrowser browser;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
private readonly Timer currentTimer, saveTimer;
|
||||||
|
|
||||||
|
public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){
|
||||||
|
this.browser = browser;
|
||||||
|
this.plugins = plugins;
|
||||||
|
this.File = AnalyticsFile.Load(file);
|
||||||
|
|
||||||
|
this.currentTimer = new Timer{ SynchronizingObject = browser };
|
||||||
|
this.currentTimer.Elapsed += currentTimer_Elapsed;
|
||||||
|
|
||||||
|
this.saveTimer = new Timer{ SynchronizingObject = browser, Interval = 60000 };
|
||||||
|
this.saveTimer.Elapsed += saveTimer_Elapsed;
|
||||||
|
|
||||||
|
if (this.File.LastCollectionVersion != Program.VersionTag){
|
||||||
|
ScheduleReportIn(TimeSpan.FromHours(12), string.Empty);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
RestartTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
if (saveTimer.Enabled){
|
||||||
|
File.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTimer.Dispose();
|
||||||
|
saveTimer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerEvent(AnalyticsFile.Event e){
|
||||||
|
File.TriggerEvent(e);
|
||||||
|
saveTimer.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveTimer_Elapsed(object sender, ElapsedEventArgs e){
|
||||||
|
saveTimer.Stop();
|
||||||
|
File.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScheduleReportIn(TimeSpan delay, string message = null){
|
||||||
|
SetLastDataCollectionTime(DateTime.Now.Subtract(CollectionInterval).Add(delay), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetLastDataCollectionTime(DateTime dt, string message = null){
|
||||||
|
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
|
||||||
|
File.LastCollectionVersion = Program.VersionTag;
|
||||||
|
File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture);
|
||||||
|
|
||||||
|
File.Save();
|
||||||
|
RestartTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestartTimer(){
|
||||||
|
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||||
|
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes));
|
||||||
|
|
||||||
|
currentTimer.Interval = Math.Max(minutesTillNext, 1)*60000;
|
||||||
|
currentTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void currentTimer_Elapsed(object sender, ElapsedEventArgs e){
|
||||||
|
currentTimer.Stop();
|
||||||
|
|
||||||
|
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||||
|
|
||||||
|
if (Math.Floor(diff.TotalMinutes) >= CollectionInterval.TotalMinutes){
|
||||||
|
SendReport();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
RestartTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendReport(){
|
||||||
|
AnalyticsReportGenerator.ExternalInfo info = AnalyticsReportGenerator.ExternalInfo.From(browser);
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => {
|
||||||
|
AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins);
|
||||||
|
BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
|
||||||
|
}).ContinueWith(task => browser.InvokeAsyncSafe(() => {
|
||||||
|
if (task.Status == TaskStatus.RanToCompletion){
|
||||||
|
SetLastDataCollectionTime(DateTime.Now);
|
||||||
|
}
|
||||||
|
else if (task.Exception != null){
|
||||||
|
string message = null;
|
||||||
|
|
||||||
|
if (task.Exception.InnerException is WebException e){
|
||||||
|
switch(e.Status){
|
||||||
|
case WebExceptionStatus.ConnectFailure:
|
||||||
|
message = "Connection Error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WebExceptionStatus.NameResolutionFailure:
|
||||||
|
message = "DNS Error";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WebExceptionStatus.ProtocolError:
|
||||||
|
HttpWebResponse response = e.Response as HttpWebResponse;
|
||||||
|
message = "HTTP Error "+(response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: "+(task.Exception.InnerException?.Message ?? task.Exception.Message));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
Core/Other/Analytics/AnalyticsReport.cs
Normal file
57
Core/Other/Analytics/AnalyticsReport.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
sealed class AnalyticsReport : IEnumerable{
|
||||||
|
private OrderedDictionary data = new OrderedDictionary(32);
|
||||||
|
private int separators;
|
||||||
|
|
||||||
|
public void Add(int ignored){ // adding separators to pretty print
|
||||||
|
data.Add((++separators).ToString(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(string key, string value){
|
||||||
|
data.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnalyticsReport FinalizeReport(){
|
||||||
|
if (!data.IsReadOnly){
|
||||||
|
data = data.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator GetEnumerator(){
|
||||||
|
return data.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NameValueCollection ToNameValueCollection(){
|
||||||
|
NameValueCollection collection = new NameValueCollection();
|
||||||
|
|
||||||
|
foreach(DictionaryEntry entry in data){
|
||||||
|
if (entry.Value != null){
|
||||||
|
collection.Add(((string)entry.Key).ToLower().Replace(' ', '_'), (string)entry.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString(){
|
||||||
|
StringBuilder build = new StringBuilder();
|
||||||
|
|
||||||
|
foreach(DictionaryEntry entry in data){
|
||||||
|
if (entry.Value == null){
|
||||||
|
build.AppendLine();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
build.AppendLine(entry.Key+": "+entry.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return build.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
297
Core/Other/Analytics/AnalyticsReportGenerator.cs
Normal file
297
Core/Other/Analytics/AnalyticsReportGenerator.cs
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Analytics{
|
||||||
|
static class AnalyticsReportGenerator{
|
||||||
|
public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){
|
||||||
|
Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData;
|
||||||
|
|
||||||
|
return new AnalyticsReport{
|
||||||
|
{ "App Version" , Program.VersionTag },
|
||||||
|
{ "App Type" , Program.IsPortable ? "portable" : "installed" },
|
||||||
|
0,
|
||||||
|
{ "System Name" , SystemName },
|
||||||
|
{ "System Edition" , SystemEdition },
|
||||||
|
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
|
||||||
|
{ "System Build" , SystemBuild },
|
||||||
|
{ "System Locale" , Program.Culture.Name.ToLower() },
|
||||||
|
0,
|
||||||
|
{ "RAM" , Exact(RamSize) },
|
||||||
|
{ "GPU" , GpuVendor },
|
||||||
|
0,
|
||||||
|
{ "Screen Count" , Exact(Screen.AllScreens.Length) },
|
||||||
|
{ "Screen Resolution" , info.Resolution ?? "(unknown)" },
|
||||||
|
{ "Screen DPI" , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
|
||||||
|
0,
|
||||||
|
{ "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) },
|
||||||
|
{ "Browser GC Reload" , Bool(SysConfig.EnableBrowserGCReload) },
|
||||||
|
{ "Browser GC Threshold" , Exact(SysConfig.BrowserMemoryThreshold) },
|
||||||
|
0,
|
||||||
|
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
|
||||||
|
{ "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
|
||||||
|
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
|
||||||
|
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
|
||||||
|
{ "Spell Check" , Bool(UserConfig.EnableSpellCheck) },
|
||||||
|
{ "Zoom" , Exact(UserConfig.ZoomLevel) },
|
||||||
|
0,
|
||||||
|
{ "Updates" , Bool(UserConfig.EnableUpdateCheck) },
|
||||||
|
{ "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
|
||||||
|
0,
|
||||||
|
{ "Tray" , TrayMode },
|
||||||
|
{ "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) },
|
||||||
|
0,
|
||||||
|
{ "Notification Position" , NotificationPosition },
|
||||||
|
{ "Notification Size" , NotificationSize },
|
||||||
|
{ "Notification Timer" , NotificationTimer },
|
||||||
|
{ "Notification Timer Speed" , RoundUp(UserConfig.NotificationDurationValue, 5) },
|
||||||
|
{ "Notification Scroll Speed" , Exact(UserConfig.NotificationScrollSpeed) },
|
||||||
|
{ "Notification Column Title" , Bool(UserConfig.DisplayNotificationColumn) },
|
||||||
|
{ "Notification Media Previews" , Bool(UserConfig.NotificationMediaPreviews) },
|
||||||
|
{ "Notification Link Skip" , Bool(UserConfig.NotificationSkipOnLinkClick) },
|
||||||
|
{ "Notification Non-Intrusive" , Bool(UserConfig.NotificationNonIntrusiveMode) },
|
||||||
|
{ "Notification Idle Pause" , Exact(UserConfig.NotificationIdlePauseSeconds) },
|
||||||
|
{ "Custom Sound Notification" , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) },
|
||||||
|
0,
|
||||||
|
{ "Program Arguments" , List(ProgramArguments) },
|
||||||
|
{ "Custom CEF Arguments" , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) },
|
||||||
|
{ "Custom Browser CSS" , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
|
||||||
|
{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
|
||||||
|
0,
|
||||||
|
{ "Plugins All" , List(plugins.Plugins.Select(plugin => plugin.Identifier)) },
|
||||||
|
{ "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(plugin => plugin.Identifier)) },
|
||||||
|
0,
|
||||||
|
{ "Theme" , Dict(editLayoutDesign, "_theme", "light/def") },
|
||||||
|
{ "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") },
|
||||||
|
{ "Font Size" , Dict(editLayoutDesign, "fontSize", "12px/def") },
|
||||||
|
{ "Large Quote Font" , Dict(editLayoutDesign, "increaseQuoteTextSize", "false/def") },
|
||||||
|
{ "Small Compose Font" , Dict(editLayoutDesign, "smallComposeTextSize", "false/def") },
|
||||||
|
{ "Avatar Radius" , Dict(editLayoutDesign, "avatarRadius", "2/def") },
|
||||||
|
{ "Hide Tweet Actions" , Dict(editLayoutDesign, "hideTweetActions", "true/def") },
|
||||||
|
{ "Move Tweet Actions" , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") },
|
||||||
|
{ "Theme Color Tweaks" , Dict(editLayoutDesign, "themeColorTweaks", "true/def") },
|
||||||
|
{ "Revert Icons" , Dict(editLayoutDesign, "revertIcons", "true/def") },
|
||||||
|
{ "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations", "true/def") },
|
||||||
|
{ "Reply Account Mode" , ReplyAccountConfigFromPlugin },
|
||||||
|
{ "Template Count" , Exact(TemplateCountFromPlugin) },
|
||||||
|
0,
|
||||||
|
{ "Opened Options" , LogRound(file.CountOpenOptions, 4) },
|
||||||
|
{ "Opened Plugins" , LogRound(file.CountOpenPlugins, 4) },
|
||||||
|
{ "Opened About" , LogRound(file.CountOpenAbout, 4) },
|
||||||
|
{ "Opened Guide" , LogRound(file.CountOpenGuide, 4) },
|
||||||
|
{ "Desktop Notifications" , LogRound(file.CountDesktopNotifications, 5) },
|
||||||
|
{ "Sound Notifications" , LogRound(file.CountSoundNotifications, 5) },
|
||||||
|
{ "Browser Context Menus" , LogRound(file.CountBrowserContextMenus, 2) },
|
||||||
|
{ "Browser Extra Mouse Buttons" , LogRound(file.CountBrowserExtraMouseButtons, 2) },
|
||||||
|
{ "Notification Context Menus" , LogRound(file.CountNotificationContextMenus, 2) },
|
||||||
|
{ "Notification Extra Mouse Buttons" , LogRound(file.CountNotificationExtraMouseButtons, 2) },
|
||||||
|
{ "Notification Keyboard Shortcuts" , LogRound(file.CountNotificationKeyboardShortcuts, 2) },
|
||||||
|
{ "Tweet Screenshots" , LogRound(file.CountTweetScreenshots, 2) },
|
||||||
|
{ "Tweet Details" , LogRound(file.CountTweetDetails, 2) },
|
||||||
|
{ "Video Plays" , LogRound(file.CountVideoPlays, 4) },
|
||||||
|
{ "GC Reloads" , LogRound(file.CountGCReloads, 4) }
|
||||||
|
}.FinalizeReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UserConfig UserConfig => Program.UserConfig;
|
||||||
|
private static SystemConfig SysConfig => Program.SystemConfig;
|
||||||
|
|
||||||
|
private static string Bool(bool value) => value ? "on" : "off";
|
||||||
|
private static string Exact(int value) => value.ToString();
|
||||||
|
private static string RoundUp(int value, int multiple) => (multiple*(int)Math.Ceiling((double)value/multiple)).ToString();
|
||||||
|
private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
|
||||||
|
private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def;
|
||||||
|
private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)"));
|
||||||
|
|
||||||
|
private static string SystemName { get; }
|
||||||
|
private static string SystemEdition { get; }
|
||||||
|
private static string SystemBuild { get; }
|
||||||
|
private static int RamSize { get; }
|
||||||
|
private static string GpuVendor { get; }
|
||||||
|
private static string[] ProgramArguments { get; }
|
||||||
|
|
||||||
|
static AnalyticsReportGenerator(){
|
||||||
|
string osName, osEdition, osBuild;
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
osName = key.GetValue("ProductName") as string;
|
||||||
|
osEdition = key.GetValue("EditionID") as string;
|
||||||
|
osBuild = key.GetValue("CurrentBuild") as string;
|
||||||
|
|
||||||
|
if (osName != null && osEdition != null){
|
||||||
|
osName = osName.Replace(osEdition, "").TrimEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
osName = osEdition = osBuild = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemName = osName ?? "Windows (unknown)";
|
||||||
|
SystemEdition = osEdition ?? "(unknown)";
|
||||||
|
SystemBuild = osBuild ?? "(unknown)";
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){
|
||||||
|
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||||
|
RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
RamSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string gpu = null;
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){
|
||||||
|
foreach(ManagementBaseObject obj in searcher.Get()){
|
||||||
|
string vendor = obj["Caption"] as string;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(vendor)){
|
||||||
|
gpu = vendor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// rip
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuVendor = gpu ?? "(unknown)";
|
||||||
|
|
||||||
|
Dictionary<string, string> args = new Dictionary<string, string>();
|
||||||
|
Arguments.GetCurrentClean().ToDictionary(args);
|
||||||
|
ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TrayMode{
|
||||||
|
get{
|
||||||
|
switch(UserConfig.TrayBehavior){
|
||||||
|
case TrayIcon.Behavior.DisplayOnly: return "icon";
|
||||||
|
case TrayIcon.Behavior.MinimizeToTray: return "minimize";
|
||||||
|
case TrayIcon.Behavior.CloseToTray: return "close";
|
||||||
|
case TrayIcon.Behavior.Combined: return "combined";
|
||||||
|
default: return "off";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NotificationPosition{
|
||||||
|
get{
|
||||||
|
switch(UserConfig.NotificationPosition){
|
||||||
|
case TweetNotification.Position.TopLeft: return "top left";
|
||||||
|
case TweetNotification.Position.TopRight: return "top right";
|
||||||
|
case TweetNotification.Position.BottomLeft: return "bottom left";
|
||||||
|
case TweetNotification.Position.BottomRight: return "bottom right";
|
||||||
|
default: return "custom";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NotificationSize{
|
||||||
|
get{
|
||||||
|
switch(UserConfig.NotificationSize){
|
||||||
|
case TweetNotification.Size.Auto: return "auto";
|
||||||
|
default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NotificationTimer{
|
||||||
|
get{
|
||||||
|
if (!UserConfig.DisplayNotificationTimer){
|
||||||
|
return "off";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return UserConfig.NotificationTimerCountDown ? "count down" : "count up";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, string> EditLayoutDesignPluginData{
|
||||||
|
get{
|
||||||
|
Dictionary<string, string> dict = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
try{
|
||||||
|
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json"));
|
||||||
|
|
||||||
|
foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){
|
||||||
|
dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"');
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// rip
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int TemplateCountFromPlugin{
|
||||||
|
get{
|
||||||
|
try{
|
||||||
|
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json"));
|
||||||
|
return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":"));
|
||||||
|
}catch{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReplyAccountConfigFromPlugin{
|
||||||
|
get{
|
||||||
|
try{
|
||||||
|
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", "");
|
||||||
|
|
||||||
|
Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)");
|
||||||
|
Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline);
|
||||||
|
|
||||||
|
if (!matchType.Success){
|
||||||
|
return "(unknown)";
|
||||||
|
}
|
||||||
|
|
||||||
|
string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
|
||||||
|
return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType;
|
||||||
|
}catch{
|
||||||
|
return "(unknown)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExternalInfo{
|
||||||
|
public static ExternalInfo From(Form form){
|
||||||
|
if (form == null){
|
||||||
|
return new ExternalInfo();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Screen screen = Screen.FromControl(form);
|
||||||
|
int dpi;
|
||||||
|
|
||||||
|
using(Graphics graphics = form.CreateGraphics()){
|
||||||
|
dpi = (int)graphics.DpiY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExternalInfo{
|
||||||
|
Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height,
|
||||||
|
DPI = dpi
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Resolution { get; private set; }
|
||||||
|
public int? DPI { get; private set; }
|
||||||
|
|
||||||
|
private ExternalInfo(){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
@@ -21,7 +20,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string);
|
BrowserUtils.OpenExternalBrowser(e.Link.LinkData as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){
|
private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){
|
||||||
|
@@ -6,11 +6,12 @@ using CefSharp.WinForms;
|
|||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
sealed partial class FormGuide : Form{
|
sealed partial class FormGuide : Form{
|
||||||
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v1/";
|
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
|
|
||||||
@@ -24,6 +25,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
if (owner != null){
|
if (owner != null){
|
||||||
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
|
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
|
||||||
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
||||||
|
|
||||||
|
owner.TriggerAnalyticsEvent(AnalyticsFile.Event.OpenGuide);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser(GuideUrl){
|
this.browser = new ChromiumWebBrowser(GuideUrl){
|
||||||
|
@@ -35,8 +35,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ReloadPluginList(){
|
private void ReloadPluginList(){
|
||||||
flowLayoutPlugins.SuspendLayout();
|
|
||||||
flowLayoutPlugins.Controls.Clear();
|
flowLayoutPlugins.Controls.Clear();
|
||||||
|
flowLayoutPlugins.SuspendLayout();
|
||||||
|
|
||||||
foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){
|
foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){
|
||||||
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin));
|
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin));
|
||||||
|
@@ -3,6 +3,9 @@ using System.Collections.Generic;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Notification.Example;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
using TweetDuck.Core.Other.Settings;
|
using TweetDuck.Core.Other.Settings;
|
||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
@@ -21,7 +24,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
public bool ShouldReloadBrowser { get; private set; }
|
public bool ShouldReloadBrowser { get; private set; }
|
||||||
|
|
||||||
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){
|
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Options";
|
Text = Program.BrandName+" Options";
|
||||||
@@ -33,20 +36,21 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
||||||
|
|
||||||
AddButton("General", () => new TabSettingsGeneral(updates));
|
AddButton("General", () => new TabSettingsGeneral(this.browser, updates));
|
||||||
AddButton("System Tray", () => new TabSettingsTray());
|
AddButton("System Tray", () => new TabSettingsTray());
|
||||||
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false)));
|
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
|
||||||
AddButton("Sounds", () => new TabSettingsSounds());
|
AddButton("Sounds", () => new TabSettingsSounds());
|
||||||
AddButton("Feedback", () => new TabSettingsFeedback());
|
AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
|
||||||
AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS));
|
AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS));
|
||||||
|
|
||||||
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
|
currentTab.Control.OnClosing();
|
||||||
|
|
||||||
foreach(SettingsTab tab in tabs.Values){
|
foreach(SettingsTab tab in tabs.Values){
|
||||||
if (tab.IsInitialized){
|
if (tab.IsInitialized){
|
||||||
tab.Control.OnClosing();
|
|
||||||
tab.Control.Dispose();
|
tab.Control.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,20 +60,21 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnManageOptions_Click(object sender, EventArgs e){
|
private void btnManageOptions_Click(object sender, EventArgs e){
|
||||||
foreach(SettingsTab tab in tabs.Values){
|
currentTab.Control.OnClosing();
|
||||||
if (tab.IsInitialized){
|
|
||||||
tab.Control.OnClosing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
|
||||||
FormClosing -= FormSettings_FormClosing;
|
FormClosing -= FormSettings_FormClosing;
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
browser.ResumeNotification();
|
browser.ResumeNotification();
|
||||||
|
|
||||||
|
BrowserProcessHandler.UpdatePrefs();
|
||||||
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
|
ShouldReloadBrowser = dialog.ShouldReloadBrowser;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
FormClosing += FormSettings_FormClosing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +118,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
private void SelectTab(SettingsTab tab){
|
private void SelectTab(SettingsTab tab){
|
||||||
if (currentTab != null){
|
if (currentTab != null){
|
||||||
currentTab.Button.BackColor = SystemColors.Control;
|
currentTab.Button.BackColor = SystemColors.Control;
|
||||||
|
currentTab.Control.OnClosing();
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;
|
tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;
|
||||||
@@ -157,11 +163,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
private sealed class SettingsTab{
|
private sealed class SettingsTab{
|
||||||
public Button Button { get; }
|
public Button Button { get; }
|
||||||
|
|
||||||
public BaseTabSettings Control{
|
public BaseTabSettings Control => control ?? (control = constructor());
|
||||||
get => control ?? (control = constructor());
|
|
||||||
set => control = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsInitialized => control != null;
|
public bool IsInitialized => control != null;
|
||||||
|
|
||||||
private readonly Func<BaseTabSettings> constructor;
|
private readonly Func<BaseTabSettings> constructor;
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using System.Windows.Forms;
|
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Management{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
@@ -12,7 +13,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
private readonly string script;
|
private readonly string script;
|
||||||
private readonly Timer timer;
|
private readonly Timer timer;
|
||||||
private Form owner;
|
private FormBrowser owner;
|
||||||
private IBrowser browser;
|
private IBrowser browser;
|
||||||
|
|
||||||
private long threshold;
|
private long threshold;
|
||||||
@@ -25,11 +26,11 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
this.timer.Elapsed += timer_Elapsed;
|
this.timer.Elapsed += timer_Elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Form owner, IBrowser browser, int thresholdMB){
|
public void Start(ChromiumWebBrowser control, int thresholdMB){
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
this.owner = owner;
|
this.owner = (FormBrowser)control.Parent; // TODO ugly
|
||||||
this.browser = browser;
|
this.browser = control.GetBrowser();
|
||||||
this.threshold = thresholdMB*1024L*1024L;
|
this.threshold = thresholdMB*1024L*1024L;
|
||||||
this.timer.SynchronizingObject = owner;
|
this.timer.SynchronizingObject = owner;
|
||||||
this.timer.Start();
|
this.timer.Start();
|
||||||
@@ -70,6 +71,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
if (response.Success && (response.Result as bool? ?? false)){
|
if (response.Success && (response.Result as bool? ?? false)){
|
||||||
SetNeedsCleanup(false);
|
SetNeedsCleanup(false);
|
||||||
|
owner.InvokeAsyncSafe(() => owner.TriggerAnalyticsEvent(Analytics.AnalyticsFile.Event.GCReload));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,6 @@ using TweetLib.Communication;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Other.Management{
|
namespace TweetDuck.Core.Other.Management{
|
||||||
sealed class VideoPlayer : IDisposable{
|
sealed class VideoPlayer : IDisposable{
|
||||||
private readonly string PlayerExe = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe");
|
|
||||||
|
|
||||||
public bool Running{
|
public bool Running{
|
||||||
get{
|
get{
|
||||||
if (currentProcess == null){
|
if (currentProcess == null){
|
||||||
@@ -25,6 +23,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
private readonly Form owner;
|
private readonly Form owner;
|
||||||
private string lastUrl;
|
private string lastUrl;
|
||||||
|
private string lastUsername;
|
||||||
|
|
||||||
private Process currentProcess;
|
private Process currentProcess;
|
||||||
private DuplexPipe.Server currentPipe;
|
private DuplexPipe.Server currentPipe;
|
||||||
@@ -35,20 +34,21 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
this.owner.FormClosing += owner_FormClosing;
|
this.owner.FormClosing += owner_FormClosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Launch(string url){
|
public void Launch(string url, string username){
|
||||||
if (Running){
|
if (Running){
|
||||||
Destroy();
|
Destroy();
|
||||||
isClosing = false;
|
isClosing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastUrl = url;
|
lastUrl = url;
|
||||||
|
lastUsername = username;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
currentPipe = DuplexPipe.CreateServer();
|
currentPipe = DuplexPipe.CreateServer();
|
||||||
currentPipe.DataIn += currentPipe_DataIn;
|
currentPipe.DataIn += currentPipe_DataIn;
|
||||||
|
|
||||||
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
||||||
FileName = PlayerExe,
|
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
||||||
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
@@ -84,7 +84,7 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "download":
|
case "download":
|
||||||
TwitterUtils.DownloadVideo(lastUrl);
|
TwitterUtils.DownloadVideo(lastUrl, lastUsername);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "rip":
|
case "rip":
|
||||||
@@ -157,14 +157,14 @@ namespace TweetDuck.Core.Other.Management{
|
|||||||
|
|
||||||
switch(exitCode){
|
switch(exitCode){
|
||||||
case 3: // CODE_LAUNCH_FAIL
|
case 3: // CODE_LAUNCH_FAIL
|
||||||
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
|
||||||
BrowserUtils.OpenExternalBrowser(lastUrl);
|
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: // CODE_MEDIA_ERROR
|
case 4: // CODE_MEDIA_ERROR
|
||||||
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){
|
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
|
||||||
BrowserUtils.OpenExternalBrowser(lastUrl);
|
BrowserUtils.OpenExternalBrowser(lastUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
90
Core/Other/Settings/Dialogs/DialogSettingsAnalytics.Designer.cs
generated
Normal file
90
Core/Other/Settings/Dialogs/DialogSettingsAnalytics.Designer.cs
generated
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
namespace TweetDuck.Core.Other.Settings.Dialogs {
|
||||||
|
partial class DialogSettingsAnalytics {
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing) {
|
||||||
|
if (disposing && (components != null)) {
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent() {
|
||||||
|
this.textBoxReport = new System.Windows.Forms.TextBox();
|
||||||
|
this.btnClose = new System.Windows.Forms.Button();
|
||||||
|
this.labelInfo = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// textBoxReport
|
||||||
|
//
|
||||||
|
this.textBoxReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.textBoxReport.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
|
this.textBoxReport.Location = new System.Drawing.Point(12, 41);
|
||||||
|
this.textBoxReport.Multiline = true;
|
||||||
|
this.textBoxReport.Name = "textBoxReport";
|
||||||
|
this.textBoxReport.ReadOnly = true;
|
||||||
|
this.textBoxReport.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||||
|
this.textBoxReport.Size = new System.Drawing.Size(460, 480);
|
||||||
|
this.textBoxReport.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// btnClose
|
||||||
|
//
|
||||||
|
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnClose.Location = new System.Drawing.Point(416, 527);
|
||||||
|
this.btnClose.Name = "btnClose";
|
||||||
|
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnClose.Size = new System.Drawing.Size(56, 23);
|
||||||
|
this.btnClose.TabIndex = 2;
|
||||||
|
this.btnClose.Text = "Close";
|
||||||
|
this.btnClose.UseVisualStyleBackColor = true;
|
||||||
|
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
|
||||||
|
//
|
||||||
|
// labelInfo
|
||||||
|
//
|
||||||
|
this.labelInfo.Location = new System.Drawing.Point(12, 9);
|
||||||
|
this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
|
||||||
|
this.labelInfo.Name = "labelInfo";
|
||||||
|
this.labelInfo.Size = new System.Drawing.Size(460, 26);
|
||||||
|
this.labelInfo.TabIndex = 0;
|
||||||
|
this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly once every wee" +
|
||||||
|
"k.\r\nSome numbers in the report were made imprecise on purpose.";
|
||||||
|
//
|
||||||
|
// DialogSettingsAnalytics
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(484, 562);
|
||||||
|
this.Controls.Add(this.labelInfo);
|
||||||
|
this.Controls.Add(this.btnClose);
|
||||||
|
this.Controls.Add(this.textBoxReport);
|
||||||
|
this.MinimumSize = new System.Drawing.Size(450, 340);
|
||||||
|
this.Name = "DialogSettingsAnalytics";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.TextBox textBoxReport;
|
||||||
|
private System.Windows.Forms.Button btnClose;
|
||||||
|
private System.Windows.Forms.Label labelInfo;
|
||||||
|
}
|
||||||
|
}
|
24
Core/Other/Settings/Dialogs/DialogSettingsAnalytics.cs
Normal file
24
Core/Other/Settings/Dialogs/DialogSettingsAnalytics.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
|
sealed partial class DialogSettingsAnalytics : Form{
|
||||||
|
public string CefArgs => textBoxReport.Text;
|
||||||
|
|
||||||
|
public DialogSettingsAnalytics(AnalyticsReport report){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Text = Program.BrandName+" Options - Analytics Report";
|
||||||
|
|
||||||
|
textBoxReport.EnableMultilineShortcuts();
|
||||||
|
textBoxReport.Text = report.ToString().TrimEnd();
|
||||||
|
textBoxReport.Select(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnClose_Click(object sender, EventArgs e){
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnOpenWiki_Click(object sender, EventArgs e){
|
private void btnOpenWiki_Click(object sender, EventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki");
|
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
@@ -19,7 +19,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnHelp_Click(object sender, EventArgs e){
|
private void btnHelp_Click(object sender, EventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe("http://peter.sh/experiments/chromium-command-line-switches/");
|
BrowserUtils.OpenExternalBrowser("http://peter.sh/experiments/chromium-command-line-switches/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
|
|
||||||
set{
|
set{
|
||||||
// this will call events and SetFlag, which also updates the UI
|
// this will call events and SetFlag, which also updates the UI
|
||||||
cbConfig.Checked = value.HasFlag(ExportFileFlags.Config);
|
cbConfig.Checked = value.HasFlag(ExportFileFlags.UserConfig);
|
||||||
cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
|
cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
|
||||||
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
|
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cbConfig_CheckedChanged(object sender, EventArgs e){
|
private void cbConfig_CheckedChanged(object sender, EventArgs e){
|
||||||
SetFlag(ExportFileFlags.Config, cbConfig.Checked);
|
SetFlag(ExportFileFlags.UserConfig, cbConfig.Checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cbSession_CheckedChanged(object sender, EventArgs e){
|
private void cbSession_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -63,7 +63,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
currentState = State.Reset;
|
currentState = State.Reset;
|
||||||
|
|
||||||
Text = "Restore Defaults";
|
Text = "Restore Defaults";
|
||||||
Flags = ExportFileFlags.Config;
|
Flags = ExportFileFlags.UserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import
|
// Import
|
||||||
@@ -108,10 +108,18 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
|
|
||||||
case State.Reset:
|
case State.Reset:
|
||||||
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
|
||||||
if (Flags.HasFlag(ExportFileFlags.Config)){
|
if (Flags.HasFlag(ExportFileFlags.UserConfig)){
|
||||||
Program.ResetConfig();
|
Program.ResetConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
try{
|
||||||
|
File.Delete(Program.SystemConfigFilePath);
|
||||||
|
}catch(Exception ex){
|
||||||
|
Program.Reporter.HandleException("System Config Reset Error", "Could not delete system config.", true, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Flags.HasFlag(ExportFileFlags.PluginData)){
|
if (Flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
try{
|
try{
|
||||||
File.Delete(Program.PluginConfigFilePath);
|
File.Delete(Program.PluginConfigFilePath);
|
||||||
@@ -124,6 +132,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
if (Flags.HasFlag(ExportFileFlags.Session)){
|
if (Flags.HasFlag(ExportFileFlags.Session)){
|
||||||
Program.Restart(Arguments.ArgDeleteCookies);
|
Program.Restart(Arguments.ArgDeleteCookies);
|
||||||
}
|
}
|
||||||
|
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
Program.Restart();
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
ShouldReloadBrowser = true;
|
ShouldReloadBrowser = true;
|
||||||
}
|
}
|
||||||
@@ -139,8 +150,13 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Program.UserConfig.Reload();
|
Program.UserConfig.Reload();
|
||||||
|
|
||||||
if (importManager.IsRestarting){
|
if (importManager.IsRestarting){
|
||||||
|
if (Flags.HasFlag(ExportFileFlags.Session)){
|
||||||
Program.Restart(Arguments.ArgImportCookies);
|
Program.Restart(Arguments.ArgImportCookies);
|
||||||
}
|
}
|
||||||
|
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
Program.Restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
ShouldReloadBrowser = true;
|
ShouldReloadBrowser = true;
|
||||||
}
|
}
|
||||||
@@ -171,6 +187,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
|
Program.SystemConfig.Save();
|
||||||
|
|
||||||
ExportManager manager = new ExportManager(file, plugins);
|
ExportManager manager = new ExportManager(file, plugins);
|
||||||
|
|
||||||
if (!manager.Export(Flags)){
|
if (!manager.Export(Flags)){
|
||||||
|
@@ -4,9 +4,10 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
[Flags]
|
[Flags]
|
||||||
enum ExportFileFlags{
|
enum ExportFileFlags{
|
||||||
None = 0,
|
None = 0,
|
||||||
Config = 1,
|
UserConfig = 1,
|
||||||
Session = 2,
|
SystemConfig = 2, // TODO implement later
|
||||||
PluginData = 4,
|
Session = 4,
|
||||||
All = Config|Session|PluginData
|
PluginData = 8,
|
||||||
|
All = UserConfig|SystemConfig|Session|PluginData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,10 +25,14 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
public bool Export(ExportFileFlags flags){
|
public bool Export(ExportFileFlags flags){
|
||||||
try{
|
try{
|
||||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
if (flags.HasFlag(ExportFileFlags.Config)){
|
if (flags.HasFlag(ExportFileFlags.UserConfig)){
|
||||||
stream.WriteFile("config", Program.UserConfigFilePath);
|
stream.WriteFile("config", Program.UserConfigFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
stream.WriteFile("system", Program.SystemConfigFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
stream.WriteFile("plugin.config", Program.PluginConfigFilePath);
|
stream.WriteFile("plugin.config", Program.PluginConfigFilePath);
|
||||||
|
|
||||||
@@ -67,7 +71,11 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
while((key = stream.SkipFile()) != null){
|
while((key = stream.SkipFile()) != null){
|
||||||
switch(key){
|
switch(key){
|
||||||
case "config":
|
case "config":
|
||||||
flags |= ExportFileFlags.Config;
|
flags |= ExportFileFlags.UserConfig;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "system":
|
||||||
|
flags |= ExportFileFlags.SystemConfig;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "plugin.config":
|
case "plugin.config":
|
||||||
@@ -99,12 +107,20 @@ namespace TweetDuck.Core.Other.Settings.Export{
|
|||||||
while((entry = stream.ReadFile()) != null){
|
while((entry = stream.ReadFile()) != null){
|
||||||
switch(entry.KeyName){
|
switch(entry.KeyName){
|
||||||
case "config":
|
case "config":
|
||||||
if (flags.HasFlag(ExportFileFlags.Config)){
|
if (flags.HasFlag(ExportFileFlags.UserConfig)){
|
||||||
entry.WriteToFile(Program.UserConfigFilePath);
|
entry.WriteToFile(Program.UserConfigFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "system":
|
||||||
|
if (flags.HasFlag(ExportFileFlags.SystemConfig)){
|
||||||
|
entry.WriteToFile(Program.SystemConfigFilePath);
|
||||||
|
IsRestarting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case "plugin.config":
|
case "plugin.config":
|
||||||
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
if (flags.HasFlag(ExportFileFlags.PluginData)){
|
||||||
entry.WriteToFile(Program.PluginConfigFilePath);
|
entry.WriteToFile(Program.PluginConfigFilePath);
|
||||||
|
@@ -29,14 +29,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
|
||||||
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
|
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold);
|
||||||
|
|
||||||
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
|
BrowserCache.CalculateCacheSize(task => {
|
||||||
if (bytes == -1L){
|
string text = task.IsCompleted ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "(unknown size)";
|
||||||
btnClearCache.Text = "Clear Cache (unknown size)";
|
this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})");
|
||||||
}
|
});
|
||||||
else{
|
|
||||||
btnClearCache.Text = "Clear Cache ("+(int)Math.Ceiling(bytes/(1024.0*1024.0))+" MB)";
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
|
56
Core/Other/Settings/TabSettingsFeedback.Designer.cs
generated
56
Core/Other/Settings/TabSettingsFeedback.Designer.cs
generated
@@ -25,12 +25,14 @@
|
|||||||
private void InitializeComponent() {
|
private void InitializeComponent() {
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.panelFeedback = new System.Windows.Forms.Panel();
|
this.panelFeedback = new System.Windows.Forms.Panel();
|
||||||
|
this.labelDataCollectionMessage = new System.Windows.Forms.Label();
|
||||||
|
this.btnViewReport = new System.Windows.Forms.Button();
|
||||||
|
this.btnSendFeedback = new System.Windows.Forms.Button();
|
||||||
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
|
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
|
||||||
this.checkDataCollection = new System.Windows.Forms.CheckBox();
|
this.checkDataCollection = new System.Windows.Forms.CheckBox();
|
||||||
this.labelDataCollection = new System.Windows.Forms.Label();
|
this.labelDataCollection = new System.Windows.Forms.Label();
|
||||||
this.labelFeedback = new System.Windows.Forms.Label();
|
this.labelFeedback = new System.Windows.Forms.Label();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.btnSendFeedback = new System.Windows.Forms.Button();
|
|
||||||
this.panelFeedback.SuspendLayout();
|
this.panelFeedback.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
@@ -38,15 +40,48 @@
|
|||||||
//
|
//
|
||||||
this.panelFeedback.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.panelFeedback.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.panelFeedback.Controls.Add(this.labelDataCollectionMessage);
|
||||||
|
this.panelFeedback.Controls.Add(this.btnViewReport);
|
||||||
this.panelFeedback.Controls.Add(this.btnSendFeedback);
|
this.panelFeedback.Controls.Add(this.btnSendFeedback);
|
||||||
this.panelFeedback.Controls.Add(this.labelDataCollectionLink);
|
this.panelFeedback.Controls.Add(this.labelDataCollectionLink);
|
||||||
this.panelFeedback.Controls.Add(this.checkDataCollection);
|
this.panelFeedback.Controls.Add(this.checkDataCollection);
|
||||||
this.panelFeedback.Controls.Add(this.labelDataCollection);
|
this.panelFeedback.Controls.Add(this.labelDataCollection);
|
||||||
this.panelFeedback.Location = new System.Drawing.Point(9, 31);
|
this.panelFeedback.Location = new System.Drawing.Point(9, 31);
|
||||||
this.panelFeedback.Name = "panelFeedback";
|
this.panelFeedback.Name = "panelFeedback";
|
||||||
this.panelFeedback.Size = new System.Drawing.Size(322, 80);
|
this.panelFeedback.Size = new System.Drawing.Size(322, 188);
|
||||||
this.panelFeedback.TabIndex = 1;
|
this.panelFeedback.TabIndex = 1;
|
||||||
//
|
//
|
||||||
|
// labelDataCollectionMessage
|
||||||
|
//
|
||||||
|
this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 114);
|
||||||
|
this.labelDataCollectionMessage.Margin = new System.Windows.Forms.Padding(6);
|
||||||
|
this.labelDataCollectionMessage.Name = "labelDataCollectionMessage";
|
||||||
|
this.labelDataCollectionMessage.Size = new System.Drawing.Size(310, 67);
|
||||||
|
this.labelDataCollectionMessage.TabIndex = 5;
|
||||||
|
//
|
||||||
|
// btnViewReport
|
||||||
|
//
|
||||||
|
this.btnViewReport.AutoSize = true;
|
||||||
|
this.btnViewReport.Location = new System.Drawing.Point(6, 82);
|
||||||
|
this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
|
this.btnViewReport.Name = "btnViewReport";
|
||||||
|
this.btnViewReport.Size = new System.Drawing.Size(144, 23);
|
||||||
|
this.btnViewReport.TabIndex = 4;
|
||||||
|
this.btnViewReport.Text = "View My Analytics Report";
|
||||||
|
this.btnViewReport.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// btnSendFeedback
|
||||||
|
//
|
||||||
|
this.btnSendFeedback.AutoSize = true;
|
||||||
|
this.btnSendFeedback.Location = new System.Drawing.Point(5, 3);
|
||||||
|
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
|
this.btnSendFeedback.Name = "btnSendFeedback";
|
||||||
|
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
|
||||||
|
this.btnSendFeedback.TabIndex = 0;
|
||||||
|
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
|
||||||
|
this.btnSendFeedback.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// labelDataCollectionLink
|
// labelDataCollectionLink
|
||||||
//
|
//
|
||||||
this.labelDataCollectionLink.AutoSize = true;
|
this.labelDataCollectionLink.AutoSize = true;
|
||||||
@@ -60,7 +95,6 @@
|
|||||||
this.labelDataCollectionLink.TabStop = true;
|
this.labelDataCollectionLink.TabStop = true;
|
||||||
this.labelDataCollectionLink.Text = "(learn more)";
|
this.labelDataCollectionLink.Text = "(learn more)";
|
||||||
this.labelDataCollectionLink.UseCompatibleTextRendering = true;
|
this.labelDataCollectionLink.UseCompatibleTextRendering = true;
|
||||||
this.labelDataCollectionLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.labelDataCollectionLink_LinkClicked);
|
|
||||||
//
|
//
|
||||||
// checkDataCollection
|
// checkDataCollection
|
||||||
//
|
//
|
||||||
@@ -94,18 +128,6 @@
|
|||||||
this.labelFeedback.TabIndex = 0;
|
this.labelFeedback.TabIndex = 0;
|
||||||
this.labelFeedback.Text = "Feedback";
|
this.labelFeedback.Text = "Feedback";
|
||||||
//
|
//
|
||||||
// btnSendFeedback
|
|
||||||
//
|
|
||||||
this.btnSendFeedback.AutoSize = true;
|
|
||||||
this.btnSendFeedback.Location = new System.Drawing.Point(5, 3);
|
|
||||||
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
|
||||||
this.btnSendFeedback.Name = "btnSendFeedback";
|
|
||||||
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
|
||||||
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
|
|
||||||
this.btnSendFeedback.TabIndex = 0;
|
|
||||||
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
|
|
||||||
this.btnSendFeedback.UseVisualStyleBackColor = true;
|
|
||||||
//
|
|
||||||
// TabSettingsFeedback
|
// TabSettingsFeedback
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -113,7 +135,7 @@
|
|||||||
this.Controls.Add(this.panelFeedback);
|
this.Controls.Add(this.panelFeedback);
|
||||||
this.Controls.Add(this.labelFeedback);
|
this.Controls.Add(this.labelFeedback);
|
||||||
this.Name = "TabSettingsFeedback";
|
this.Name = "TabSettingsFeedback";
|
||||||
this.Size = new System.Drawing.Size(340, 122);
|
this.Size = new System.Drawing.Size(340, 230);
|
||||||
this.panelFeedback.ResumeLayout(false);
|
this.panelFeedback.ResumeLayout(false);
|
||||||
this.panelFeedback.PerformLayout();
|
this.panelFeedback.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
@@ -130,5 +152,7 @@
|
|||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
private System.Windows.Forms.LinkLabel labelDataCollectionLink;
|
private System.Windows.Forms.LinkLabel labelDataCollectionLink;
|
||||||
private System.Windows.Forms.Button btnSendFeedback;
|
private System.Windows.Forms.Button btnSendFeedback;
|
||||||
|
private System.Windows.Forms.Button btnViewReport;
|
||||||
|
private System.Windows.Forms.Label labelDataCollectionMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +1,40 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
sealed partial class TabSettingsFeedback : BaseTabSettings{
|
sealed partial class TabSettingsFeedback : BaseTabSettings{
|
||||||
public TabSettingsFeedback(){
|
private readonly AnalyticsFile analyticsFile;
|
||||||
|
private readonly AnalyticsReportGenerator.ExternalInfo analyticsInfo;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
|
||||||
|
public TabSettingsFeedback(AnalyticsManager analytics, AnalyticsReportGenerator.ExternalInfo analyticsInfo, PluginManager plugins){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.analyticsFile = analytics?.File ?? AnalyticsFile.Load(Program.AnalyticsFilePath);
|
||||||
|
this.analyticsInfo = analyticsInfo;
|
||||||
|
this.plugins = plugins;
|
||||||
|
|
||||||
checkDataCollection.Checked = Config.AllowDataCollection;
|
checkDataCollection.Checked = Config.AllowDataCollection;
|
||||||
|
|
||||||
|
if (analytics != null){
|
||||||
|
string collectionTime = analyticsFile.LastCollectionMessage;
|
||||||
|
labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: "+collectionTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
btnSendFeedback.Click += btnSendFeedback_Click;
|
btnSendFeedback.Click += btnSendFeedback_Click;
|
||||||
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
|
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
|
||||||
|
labelDataCollectionLink.LinkClicked += labelDataCollectionLink_LinkClicked;
|
||||||
|
btnViewReport.Click += btnViewReport_Click;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnSendFeedback_Click(object sender, EventArgs e){
|
private void btnSendFeedback_Click(object sender, EventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/issues/new");
|
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/issues/new");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
|
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -24,7 +42,13 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
|
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnViewReport_Click(object sender, EventArgs e){
|
||||||
|
using(DialogSettingsAnalytics dialog = new DialogSettingsAnalytics(AnalyticsReportGenerator.Create(analyticsFile, analyticsInfo, plugins))){
|
||||||
|
dialog.ShowDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
39
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -40,6 +40,7 @@
|
|||||||
this.panelUI = new System.Windows.Forms.Panel();
|
this.panelUI = new System.Windows.Forms.Panel();
|
||||||
this.panelUpdates = new System.Windows.Forms.Panel();
|
this.panelUpdates = new System.Windows.Forms.Panel();
|
||||||
this.labelUpdates = new System.Windows.Forms.Label();
|
this.labelUpdates = new System.Windows.Forms.Label();
|
||||||
|
this.checkAnimatedAvatars = 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();
|
||||||
@@ -60,11 +61,11 @@
|
|||||||
// checkSpellCheck
|
// checkSpellCheck
|
||||||
//
|
//
|
||||||
this.checkSpellCheck.AutoSize = true;
|
this.checkSpellCheck.AutoSize = true;
|
||||||
this.checkSpellCheck.Location = new System.Drawing.Point(6, 97);
|
this.checkSpellCheck.Location = new System.Drawing.Point(6, 120);
|
||||||
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 = 4;
|
this.checkSpellCheck.TabIndex = 5;
|
||||||
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;
|
||||||
@@ -95,11 +96,11 @@
|
|||||||
// labelZoomValue
|
// labelZoomValue
|
||||||
//
|
//
|
||||||
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
|
||||||
this.labelZoomValue.Location = new System.Drawing.Point(147, 146);
|
this.labelZoomValue.Location = new System.Drawing.Point(147, 169);
|
||||||
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 = 7;
|
this.labelZoomValue.TabIndex = 8;
|
||||||
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.");
|
||||||
@@ -145,24 +146,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, 145);
|
this.trackBarZoom.Location = new System.Drawing.Point(3, 168);
|
||||||
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 = 6;
|
this.trackBarZoom.TabIndex = 7;
|
||||||
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, 129);
|
this.labelZoom.Location = new System.Drawing.Point(3, 152);
|
||||||
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 = 5;
|
this.labelZoom.TabIndex = 6;
|
||||||
this.labelZoom.Text = "Zoom";
|
this.labelZoom.Text = "Zoom";
|
||||||
//
|
//
|
||||||
// zoomUpdateTimer
|
// zoomUpdateTimer
|
||||||
@@ -185,6 +186,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.checkAnimatedAvatars);
|
||||||
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
|
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn);
|
||||||
this.panelUI.Controls.Add(this.checkBestImageQuality);
|
this.panelUI.Controls.Add(this.checkBestImageQuality);
|
||||||
this.panelUI.Controls.Add(this.checkExpandLinks);
|
this.panelUI.Controls.Add(this.checkExpandLinks);
|
||||||
@@ -195,7 +197,7 @@
|
|||||||
this.panelUI.Controls.Add(this.labelZoomValue);
|
this.panelUI.Controls.Add(this.labelZoomValue);
|
||||||
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, 179);
|
this.panelUI.Size = new System.Drawing.Size(322, 205);
|
||||||
this.panelUI.TabIndex = 1;
|
this.panelUI.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// panelUpdates
|
// panelUpdates
|
||||||
@@ -204,7 +206,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, 257);
|
this.panelUpdates.Location = new System.Drawing.Point(8, 283);
|
||||||
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 = 3;
|
this.panelUpdates.TabIndex = 3;
|
||||||
@@ -213,13 +215,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, 234);
|
this.labelUpdates.Location = new System.Drawing.Point(5, 260);
|
||||||
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 = 2;
|
this.labelUpdates.TabIndex = 2;
|
||||||
this.labelUpdates.Text = "Updates";
|
this.labelUpdates.Text = "Updates";
|
||||||
//
|
//
|
||||||
|
// checkAnimatedAvatars
|
||||||
|
//
|
||||||
|
this.checkAnimatedAvatars.AutoSize = true;
|
||||||
|
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 97);
|
||||||
|
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
|
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
||||||
|
this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
|
||||||
|
this.checkAnimatedAvatars.TabIndex = 4;
|
||||||
|
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
||||||
|
this.toolTip.SetToolTip(this.checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs.");
|
||||||
|
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// TabSettingsGeneral
|
// TabSettingsGeneral
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -229,7 +243,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, 322);
|
this.Size = new System.Drawing.Size(340, 348);
|
||||||
((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();
|
||||||
@@ -258,5 +272,6 @@
|
|||||||
private System.Windows.Forms.Label labelUpdates;
|
private System.Windows.Forms.Label labelUpdates;
|
||||||
private System.Windows.Forms.CheckBox checkBestImageQuality;
|
private System.Windows.Forms.CheckBox checkBestImageQuality;
|
||||||
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
|
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
|
||||||
|
private System.Windows.Forms.CheckBox checkAnimatedAvatars;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
sealed partial class TabSettingsGeneral : BaseTabSettings{
|
||||||
|
private readonly FormBrowser browser;
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private int updateCheckEventId = -1;
|
private int updateCheckEventId = -1;
|
||||||
|
|
||||||
public TabSettingsGeneral(UpdateHandler updates){
|
public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.browser = browser;
|
||||||
|
|
||||||
this.updates = updates;
|
this.updates = updates;
|
||||||
this.updates.CheckFinished += updates_CheckFinished;
|
this.updates.CheckFinished += updates_CheckFinished;
|
||||||
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
|
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
|
||||||
@@ -22,6 +26,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
||||||
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
||||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
|
checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
|
||||||
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
checkSpellCheck.Checked = Config.EnableSpellCheck;
|
||||||
|
|
||||||
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
|
||||||
@@ -32,6 +37,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
||||||
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
||||||
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||||
|
checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
|
||||||
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
|
||||||
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
||||||
|
|
||||||
@@ -59,9 +65,14 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.BestImageQuality = checkBestImageQuality.Checked;
|
Config.BestImageQuality = checkBestImageQuality.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
|
||||||
|
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => browser.ReloadColumns());
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
BrowserProcessHandler.UpdatePrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarZoom_ValueChanged(object sender, EventArgs e){
|
private void trackBarZoom_ValueChanged(object sender, EventArgs e){
|
||||||
|
@@ -2,21 +2,22 @@
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Notification.Example;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
sealed partial class TabSettingsNotifications : BaseTabSettings{
|
sealed partial class TabSettingsNotifications : BaseTabSettings{
|
||||||
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
|
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
|
||||||
|
|
||||||
private readonly FormNotificationMain notification;
|
private readonly FormNotificationExample notification;
|
||||||
|
|
||||||
public TabSettingsNotifications(FormNotificationMain notification){
|
public TabSettingsNotifications(FormNotificationExample notification){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.notification = notification;
|
this.notification = notification;
|
||||||
|
|
||||||
this.notification.Initialized += (sender, args) => {
|
this.notification.Initialized += (sender, args) => {
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
this.notification.ShowNotificationForSettings(true);
|
this.notification.ShowExampleNotification(true);
|
||||||
this.notification.Move += notification_Move;
|
this.notification.Move += notification_Move;
|
||||||
this.notification.ResizeEnd += notification_ResizeEnd;
|
this.notification.ResizeEnd += notification_ResizeEnd;
|
||||||
});
|
});
|
||||||
@@ -73,9 +74,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||||
|
|
||||||
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
|
|
||||||
this.notification.CanResizeWindow = radioSizeCustom.Checked;
|
|
||||||
|
|
||||||
Disposed += (sender, args) => this.notification.Dispose();
|
Disposed += (sender, args) => this.notification.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +108,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
|
||||||
if (Parent == null){
|
if (Parent == null){
|
||||||
notification.HideNotification(false);
|
notification.HideNotification();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +129,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
private void notification_ResizeEnd(object sender, EventArgs e){
|
private void notification_ResizeEnd(object sender, EventArgs e){
|
||||||
if (radioSizeCustom.Checked){
|
if (radioSizeCustom.Checked){
|
||||||
Config.CustomNotificationSize = notification.BrowserSize;
|
Config.CustomNotificationSize = notification.BrowserSize;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +140,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
|
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
|
||||||
|
|
||||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true;
|
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void radioLocCustom_Click(object sender, EventArgs e){
|
private void radioLocCustom_Click(object sender, EventArgs e){
|
||||||
@@ -153,7 +151,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.NotificationPosition = TweetNotification.Position.Custom;
|
Config.NotificationPosition = TweetNotification.Position.Custom;
|
||||||
|
|
||||||
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
|
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
|
|
||||||
if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is outside view", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
|
if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is outside view", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
|
||||||
Config.NotificationPosition = TweetNotification.Position.TopRight;
|
Config.NotificationPosition = TweetNotification.Position.TopRight;
|
||||||
@@ -167,10 +165,11 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void radioSize_CheckedChanged(object sender, EventArgs e){
|
private void radioSize_CheckedChanged(object sender, EventArgs e){
|
||||||
if (radioSizeAuto.Checked)Config.NotificationSize = TweetNotification.Size.Auto;
|
if (radioSizeAuto.Checked){
|
||||||
|
Config.NotificationSize = TweetNotification.Size.Auto;
|
||||||
|
}
|
||||||
|
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
notification.CanResizeWindow = false; // must be after ShowNotificationForSettings
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void radioSizeCustom_Click(object sender, EventArgs e){
|
private void radioSizeCustom_Click(object sender, EventArgs e){
|
||||||
@@ -179,9 +178,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Config.NotificationSize = TweetNotification.Size.Custom;
|
Config.NotificationSize = TweetNotification.Size.Custom;
|
||||||
|
notification.ShowExampleNotification(false);
|
||||||
notification.CanResizeWindow = true;
|
|
||||||
notification.ShowNotificationForSettings(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
private void trackBarDuration_ValueChanged(object sender, EventArgs e){
|
||||||
@@ -206,18 +203,18 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void checkColumnName_CheckedChanged(object sender, EventArgs e){
|
private void checkColumnName_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.DisplayNotificationColumn = checkColumnName.Checked;
|
Config.DisplayNotificationColumn = checkColumnName.Checked;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
|
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
|
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
|
||||||
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){
|
private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.NotificationTimerCountDown = checkTimerCountDown.Checked;
|
Config.NotificationTimerCountDown = checkTimerCountDown.Checked;
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
|
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -245,17 +242,17 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
|
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
|
||||||
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
|
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
|
||||||
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
|
||||||
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
|
||||||
notification.ShowNotificationForSettings(false);
|
notification.ShowExampleNotification(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void durationUpdateTimer_Tick(object sender, EventArgs e){
|
private void durationUpdateTimer_Tick(object sender, EventArgs e){
|
||||||
notification.ShowNotificationForSettings(true);
|
notification.ShowExampleNotification(true);
|
||||||
durationUpdateTimer.Stop();
|
durationUpdateTimer.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
namespace TweetDuck.Core {
|
namespace TweetDuck.Core.Other {
|
||||||
partial class TrayIcon {
|
partial class TrayIcon {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required designer variable.
|
/// Required designer variable.
|
@@ -2,7 +2,7 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core.Other{
|
||||||
sealed partial class TrayIcon : Component{
|
sealed partial class TrayIcon : Component{
|
||||||
public enum Behavior{ // keep order
|
public enum Behavior{ // keep order
|
||||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||||
@@ -12,7 +12,9 @@ namespace TweetDuck.Core{
|
|||||||
public event EventHandler ClickClose;
|
public event EventHandler ClickClose;
|
||||||
|
|
||||||
public bool Visible{
|
public bool Visible{
|
||||||
get => notifyIcon.Visible;
|
get{
|
||||||
|
return notifyIcon.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
if (value){
|
if (value){
|
242
Core/TweetDeckBrowser.cs
Normal file
242
Core/TweetDeckBrowser.cs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Core.Bridge;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Management;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
|
using TweetDuck.Plugins.Events;
|
||||||
|
using TweetDuck.Resources;
|
||||||
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core{
|
||||||
|
sealed class TweetDeckBrowser : IDisposable{
|
||||||
|
public bool Ready { get; private set; }
|
||||||
|
|
||||||
|
public bool Enabled{
|
||||||
|
get => browser.Enabled;
|
||||||
|
set => browser.Enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTweetDeckWebsite{
|
||||||
|
get{
|
||||||
|
using(IFrame frame = browser.GetBrowser().MainFrame){
|
||||||
|
return TwitterUtils.IsTweetDeckWebsite(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler PageLoaded;
|
||||||
|
|
||||||
|
private readonly ChromiumWebBrowser browser;
|
||||||
|
private readonly PluginManager plugins;
|
||||||
|
private readonly MemoryUsageTracker memoryUsageTracker;
|
||||||
|
|
||||||
|
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge bridge){
|
||||||
|
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
|
||||||
|
DialogHandler = new FileDialogHandler(),
|
||||||
|
DragHandler = new DragHandlerBrowser(),
|
||||||
|
MenuHandler = new ContextMenuBrowser(owner),
|
||||||
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
|
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
||||||
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
|
RequestHandler = new RequestHandlerBrowser()
|
||||||
|
};
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
|
this.browser.FrameLoadStart += browser_FrameLoadStart;
|
||||||
|
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
|
this.browser.LoadError += browser_LoadError;
|
||||||
|
|
||||||
|
this.browser.RegisterAsyncJsObject("$TD", bridge);
|
||||||
|
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
||||||
|
|
||||||
|
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
||||||
|
this.browser.Dock = DockStyle.None;
|
||||||
|
this.browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
|
||||||
|
owner.Controls.Add(browser);
|
||||||
|
|
||||||
|
this.plugins = plugins;
|
||||||
|
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||||
|
|
||||||
|
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
|
||||||
|
|
||||||
|
Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
|
||||||
|
Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup and management
|
||||||
|
|
||||||
|
private void OnBrowserReady(){
|
||||||
|
if (!Ready){
|
||||||
|
browser.Location = Point.Empty;
|
||||||
|
browser.Dock = DockStyle.Fill;
|
||||||
|
Ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Focus(){
|
||||||
|
browser.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
plugins.PluginChangedState -= plugins_PluginChangedState;
|
||||||
|
|
||||||
|
Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
|
||||||
|
Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
|
||||||
|
|
||||||
|
memoryUsageTracker.Dispose();
|
||||||
|
browser.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
|
||||||
|
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
|
if (!e.IsLoading){
|
||||||
|
foreach(string word in TwitterUtils.DictionaryWords){
|
||||||
|
browser.AddWordToDictionary(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.BeginInvoke(new Action(OnBrowserReady));
|
||||||
|
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
||||||
|
if (e.Frame.IsMain){
|
||||||
|
memoryUsageTracker.Stop();
|
||||||
|
|
||||||
|
if (Program.UserConfig.ZoomLevel != 100){
|
||||||
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
|
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
|
||||||
|
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
|
||||||
|
|
||||||
|
UpdateProperties();
|
||||||
|
TweetDeckBridge.RestoreSessionData(e.Frame);
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "code.js");
|
||||||
|
InjectBrowserCSS();
|
||||||
|
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
|
||||||
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
|
||||||
|
|
||||||
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
|
|
||||||
|
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||||
|
memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Program.UserConfig.FirstRun){
|
||||||
|
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
PageLoaded?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
||||||
|
if (e.ErrorCode == CefErrorCode.Aborted){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
|
||||||
|
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
|
||||||
|
|
||||||
|
if (errorPage != null){
|
||||||
|
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
|
browser.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserConfig_MuteToggled(object sender, EventArgs e){
|
||||||
|
UpdateProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){
|
||||||
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// external handling
|
||||||
|
|
||||||
|
public UpdateHandler CreateUpdateHandler(UpdaterSettings settings){
|
||||||
|
return new UpdateHandler(browser, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshMemoryTracker(){
|
||||||
|
if (Program.SystemConfig.EnableBrowserGCReload){
|
||||||
|
memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
memoryUsageTracker.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HideVideoOverlay(bool focus){
|
||||||
|
if (focus){
|
||||||
|
browser.GetBrowser().GetHost().SendFocusEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// javascript calls
|
||||||
|
|
||||||
|
public void ReloadToTweetDeck(){
|
||||||
|
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateProperties(){
|
||||||
|
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InjectBrowserCSS(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReinjectCustomCSS(string css){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMouseClickExtra(IntPtr param){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerTweetScreenshot(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadColumns(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_reloadColumns()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyROT13(){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_applyROT13()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,15 +8,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
static class BrowserCache{
|
static class BrowserCache{
|
||||||
private static bool ClearOnExit { get; set; }
|
private static bool ClearOnExit { get; set; }
|
||||||
|
|
||||||
private static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache");
|
public static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache");
|
||||||
|
private static IEnumerable<string> CacheFiles => Directory.EnumerateFiles(CacheFolder);
|
||||||
|
|
||||||
private static IEnumerable<string> CacheFiles{
|
public static void CalculateCacheSize(Action<Task<long>> callbackBytes){
|
||||||
get{
|
|
||||||
return Directory.EnumerateFiles(CacheFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CalculateCacheSize(Action<long> callbackBytes){
|
|
||||||
Task<long> task = new Task<long>(() => {
|
Task<long> task = new Task<long>(() => {
|
||||||
return CacheFiles.Select(file => {
|
return CacheFiles.Select(file => {
|
||||||
try{
|
try{
|
||||||
@@ -27,7 +22,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}).Sum();
|
}).Sum();
|
||||||
});
|
});
|
||||||
|
|
||||||
task.ContinueWith(originalTask => callbackBytes(originalTask.Exception == null ? originalTask.Result : -1L), TaskContinuationOptions.ExecuteSynchronously);
|
task.ContinueWith(callbackBytes);
|
||||||
task.Start();
|
task.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
args["disable-gpu-vsync"] = "1";
|
args["disable-gpu-vsync"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
args["disable-extensions"] = "1";
|
args["disable-pdf-extension"] = "1";
|
||||||
args["disable-plugins-discovery"] = "1";
|
args["disable-plugins-discovery"] = "1";
|
||||||
args["enable-system-flash"] = "0";
|
args["enable-system-flash"] = "0";
|
||||||
|
|
||||||
@@ -70,12 +70,12 @@ namespace TweetDuck.Core.Utils{
|
|||||||
|
|
||||||
switch(CheckUrl(url)){
|
switch(CheckUrl(url)){
|
||||||
case UrlCheckResult.Fine:
|
case UrlCheckResult.Fine:
|
||||||
OpenExternalBrowserUnsafe(url);
|
WindowsUtils.OpenAssociatedProgram(url);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UrlCheckResult.Tracking:
|
case UrlCheckResult.Tracking:
|
||||||
if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){
|
if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){
|
||||||
OpenExternalBrowserUnsafe(url);
|
WindowsUtils.OpenAssociatedProgram(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -86,10 +86,6 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenExternalBrowserUnsafe(string url){
|
|
||||||
using(Process.Start(url)){}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetFileNameFromUrl(string url){
|
public static string GetFileNameFromUrl(string url){
|
||||||
string file = Path.GetFileName(new Uri(url).AbsolutePath);
|
string file = Path.GetFileName(new Uri(url).AbsolutePath);
|
||||||
return string.IsNullOrEmpty(file) ? null : file;
|
return string.IsNullOrEmpty(file) ? null : file;
|
||||||
@@ -99,9 +95,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
public static WebClient CreateWebClient(){
|
||||||
WebClient client = new WebClient{ Proxy = null };
|
WebClient client = new WebClient{ Proxy = null };
|
||||||
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
|
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
||||||
|
WebClient client = CreateWebClient();
|
||||||
|
|
||||||
client.DownloadFileCompleted += (sender, args) => {
|
client.DownloadFileCompleted += (sender, args) => {
|
||||||
if (args.Cancelled){
|
if (args.Cancelled){
|
||||||
|
@@ -8,6 +8,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
||||||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
|
||||||
static class NativeMethods{
|
static class NativeMethods{
|
||||||
|
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
||||||
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
|
||||||
|
|
||||||
public const int HWND_TOPMOST = -1;
|
public const int HWND_TOPMOST = -1;
|
||||||
@@ -51,6 +52,12 @@ namespace TweetDuck.Core.Utils{
|
|||||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
|
||||||
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern uint RegisterWindowMessage(string messageName);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
|
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
|
||||||
|
|
||||||
@@ -96,13 +103,18 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
|
||||||
|
PostMessage(HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetMouseHookData(IntPtr ptr){
|
public static int GetMouseHookData(IntPtr ptr){
|
||||||
return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16;
|
return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetIdleSeconds(){
|
public static int GetIdleSeconds(){
|
||||||
LASTINPUTINFO info = new LASTINPUTINFO();
|
LASTINPUTINFO info = new LASTINPUTINFO{
|
||||||
info.cbSize = LASTINPUTINFO.Size;
|
cbSize = LASTINPUTINFO.Size
|
||||||
|
};
|
||||||
|
|
||||||
if (!GetLastInputInfo(ref info)){
|
if (!GetLastInputInfo(ref info)){
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -18,5 +18,16 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
public static string ConvertPascalCaseToScreamingSnakeCase(string str){
|
||||||
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int CountOccurrences(string source, string substring){
|
||||||
|
int count = 0, index = 0;
|
||||||
|
|
||||||
|
while((index = source.IndexOf(substring, index)) != -1){
|
||||||
|
index += substring.Length;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static readonly string[] ValidImageExtensions = {
|
||||||
|
".jpg", ".jpeg", ".png", ".gif"
|
||||||
|
};
|
||||||
|
|
||||||
public enum ImageQuality{
|
public enum ImageQuality{
|
||||||
Default, Orig
|
Default, Orig
|
||||||
}
|
}
|
||||||
@@ -53,6 +57,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetImageFileName(string url){
|
||||||
|
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
||||||
|
}
|
||||||
|
|
||||||
public static void DownloadImage(string url, string username, ImageQuality quality){
|
public static void DownloadImage(string url, string username, ImageQuality quality){
|
||||||
DownloadImages(new string[]{ url }, username, quality);
|
DownloadImages(new string[]{ url }, username, quality);
|
||||||
}
|
}
|
||||||
@@ -65,7 +73,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
string firstImageLink = GetMediaLink(urls[0], quality);
|
string firstImageLink = GetMediaLink(urls[0], quality);
|
||||||
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
|
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
|
||||||
|
|
||||||
string file = BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(firstImageLink));
|
string file = GetImageFileName(firstImageLink);
|
||||||
string ext = Path.GetExtension(file); // includes dot
|
string ext = Path.GetExtension(file); // includes dot
|
||||||
|
|
||||||
string[] fileNameParts = qualityIndex == -1 ? new string[]{
|
string[] fileNameParts = qualityIndex == -1 ? new string[]{
|
||||||
@@ -103,7 +111,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DownloadVideo(string url){
|
public static void DownloadVideo(string url, string username){
|
||||||
string filename = BrowserUtils.GetFileNameFromUrl(url);
|
string filename = BrowserUtils.GetFileNameFromUrl(url);
|
||||||
string ext = Path.GetExtension(filename);
|
string ext = Path.GetExtension(filename);
|
||||||
|
|
||||||
@@ -111,7 +119,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
AutoUpgradeEnabled = true,
|
AutoUpgradeEnabled = true,
|
||||||
OverwritePrompt = true,
|
OverwritePrompt = true,
|
||||||
Title = "Save video",
|
Title = "Save video",
|
||||||
FileName = filename,
|
FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}",
|
||||||
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
@@ -49,17 +50,22 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Process StartProcess(string file, string arguments, bool runElevated){
|
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
||||||
ProcessStartInfo processInfo = new ProcessStartInfo{
|
try{
|
||||||
|
using(Process.Start(new ProcessStartInfo{
|
||||||
FileName = file,
|
FileName = file,
|
||||||
Arguments = arguments
|
Arguments = arguments,
|
||||||
};
|
Verb = runElevated ? "runas" : string.Empty,
|
||||||
|
ErrorDialog = true
|
||||||
if (runElevated){
|
})){
|
||||||
processInfo.Verb = "runas";
|
return true;
|
||||||
|
}
|
||||||
|
}catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user
|
||||||
|
return false;
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Error opening file", e.Message, true, e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Process.Start(processInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
||||||
@@ -104,7 +110,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void ClipboardStripHtmlStyles(){
|
public static void ClipboardStripHtmlStyles(){
|
||||||
if (!Clipboard.ContainsText(TextDataFormat.Html)){
|
if (!Clipboard.ContainsText(TextDataFormat.Html) || !Clipboard.ContainsText(TextDataFormat.UnicodeText)){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Data.Serialization{
|
namespace TweetDuck.Data.Serialization{
|
||||||
sealed class FileSerializer<T>{
|
sealed class FileSerializer<T>{
|
||||||
@@ -28,6 +29,8 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Write(string file, T obj){
|
public void Write(string file, T obj){
|
||||||
|
WindowsUtils.CreateDirectoryForFile(file);
|
||||||
|
|
||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
|
||||||
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
foreach(KeyValuePair<string, PropertyInfo> prop in props){
|
||||||
Type type = prop.Value.PropertyType;
|
Type type = prop.Value.PropertyType;
|
||||||
@@ -54,7 +57,11 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
|
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
|
||||||
|
|
||||||
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
|
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
|
||||||
if (reader.Peek() <= 1){
|
switch(reader.Peek()){
|
||||||
|
case -1:
|
||||||
|
throw new FormatException("File is empty.");
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
throw new FormatException("Input appears to be a binary file.");
|
throw new FormatException("Input appears to be a binary file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +102,13 @@ namespace TweetDuck.Data.Serialization{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReadIfExists(string file, T obj){
|
||||||
|
try{
|
||||||
|
Read(file, obj);
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
}catch(DirectoryNotFoundException){}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class BasicTypeConverter : ITypeConverter{
|
private sealed class BasicTypeConverter : ITypeConverter{
|
||||||
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
|
||||||
switch(Type.GetTypeCode(type)){
|
switch(Type.GetTypeCode(type)){
|
||||||
|
@@ -62,9 +62,7 @@ namespace TweetDuck.Plugins.Controls{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnToggleState_Click(object sender, EventArgs e){
|
private void btnToggleState_Click(object sender, EventArgs e){
|
||||||
bool newState = !pluginManager.Config.IsEnabled(plugin);
|
pluginManager.Config.ToggleEnabled(plugin);
|
||||||
pluginManager.Config.SetEnabled(plugin, newState);
|
|
||||||
|
|
||||||
UpdatePluginState();
|
UpdatePluginState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,12 +7,11 @@ using TweetDuck.Plugins.Enums;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class Plugin{
|
sealed class Plugin{
|
||||||
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
|
||||||
private const string VersionWildcard = "*";
|
private const string VersionWildcard = "*";
|
||||||
|
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
public PluginGroup Group { get; }
|
public PluginGroup Group { get; }
|
||||||
public PluginEnvironment Environments { get; private set; }
|
public PluginEnvironment Environments { get; }
|
||||||
|
|
||||||
public string Name => metadata["NAME"];
|
public string Name => metadata["NAME"];
|
||||||
public string Description => metadata["DESCRIPTION"];
|
public string Description => metadata["DESCRIPTION"];
|
||||||
@@ -23,9 +22,7 @@ namespace TweetDuck.Plugins{
|
|||||||
public string ConfigDefault => metadata["CONFIGDEFAULT"];
|
public string ConfigDefault => metadata["CONFIGDEFAULT"];
|
||||||
public string RequiredVersion => metadata["REQUIRES"];
|
public string RequiredVersion => metadata["REQUIRES"];
|
||||||
|
|
||||||
public bool CanRun{
|
public bool CanRun { get; private set; }
|
||||||
get => canRun ?? (canRun = CheckRequiredVersion(RequiredVersion)).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasConfig{
|
public bool HasConfig{
|
||||||
get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0;
|
get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0;
|
||||||
@@ -56,21 +53,18 @@ namespace TweetDuck.Plugins{
|
|||||||
{ "REQUIRES", VersionWildcard }
|
{ "REQUIRES", VersionWildcard }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool? canRun;
|
private Plugin(string path, string name, PluginGroup group, PluginEnvironment environments){
|
||||||
|
|
||||||
private Plugin(string path, PluginGroup group){
|
|
||||||
string name = Path.GetFileName(path);
|
|
||||||
System.Diagnostics.Debug.Assert(name != null);
|
|
||||||
|
|
||||||
this.pathRoot = path;
|
this.pathRoot = path;
|
||||||
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
|
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
|
||||||
|
|
||||||
this.Identifier = group.GetIdentifierPrefix()+name;
|
this.Identifier = group.GetIdentifierPrefix()+name;
|
||||||
this.Group = group;
|
this.Group = group;
|
||||||
this.Environments = PluginEnvironment.None;
|
this.Environments = environments;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMetadataLoaded(){
|
private void OnMetadataLoaded(){
|
||||||
|
CanRun = CheckRequiredVersion(RequiredVersion);
|
||||||
|
|
||||||
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
|
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
|
||||||
|
|
||||||
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
||||||
@@ -80,7 +74,7 @@ namespace TweetDuck.Plugins{
|
|||||||
Directory.CreateDirectory(dataFolder);
|
Directory.CreateDirectory(dataFolder);
|
||||||
File.Copy(defaultConfigPath, configPath, false);
|
File.Copy(defaultConfigPath, configPath, false);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Plugin Loading Error", "Could not generate a configuration file for '"+Identifier+"' plugin.", true, e);
|
throw new IOException("Could not generate a configuration file for '"+Identifier+"' plugin: "+e.Message, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,89 +132,74 @@ namespace TweetDuck.Plugins{
|
|||||||
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
|
// Static
|
||||||
Plugin plugin = new Plugin(path, group);
|
|
||||||
|
|
||||||
if (!LoadMetadata(path, plugin, out error)){
|
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||||
return null;
|
private static readonly string[] EndTag = { "[END]" };
|
||||||
}
|
|
||||||
|
|
||||||
if (!LoadEnvironments(path, plugin, out error)){
|
public static Plugin CreateFromFolder(string path, PluginGroup group){
|
||||||
return null;
|
Plugin plugin = new Plugin(path, Path.GetFileName(path), group, LoadEnvironments(path));
|
||||||
}
|
LoadMetadata(path, plugin);
|
||||||
|
|
||||||
error = string.Empty;
|
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
|
private static PluginEnvironment LoadEnvironments(string path){
|
||||||
|
PluginEnvironment environments = PluginEnvironment.None;
|
||||||
|
|
||||||
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
||||||
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.Environments == PluginEnvironment.None){
|
if (environments == PluginEnvironment.None){
|
||||||
error = "Plugin has no script files.";
|
throw new ArgumentException("Plugin has no script files");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error = string.Empty;
|
return environments;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] endTag = { "[END]" };
|
private static void LoadMetadata(string path, Plugin plugin){
|
||||||
|
|
||||||
private static bool LoadMetadata(string path, Plugin plugin, out string error){
|
|
||||||
string metaFile = Path.Combine(path, ".meta");
|
string metaFile = Path.Combine(path, ".meta");
|
||||||
|
|
||||||
if (!File.Exists(metaFile)){
|
if (!File.Exists(metaFile)){
|
||||||
error = "Missing .meta file.";
|
throw new ArgumentException("Missing .meta file");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] lines = File.ReadAllLines(metaFile, Encoding.UTF8);
|
string currentTag = null, currentContents = string.Empty;
|
||||||
string currentTag = null, currentContents = "";
|
|
||||||
|
|
||||||
foreach(string line in lines.Concat(endTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
|
||||||
if (line[0] == '[' && line[line.Length-1] == ']'){
|
if (line[0] == '[' && line[line.Length-1] == ']'){
|
||||||
if (currentTag != null){
|
if (currentTag != null){
|
||||||
plugin.metadata[currentTag] = currentContents;
|
plugin.metadata[currentTag] = currentContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
||||||
currentContents = "";
|
currentContents = string.Empty;
|
||||||
|
|
||||||
if (line.Equals(endTag[0])){
|
if (line.Equals(EndTag[0])){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plugin.metadata.ContainsKey(currentTag)){
|
if (!plugin.metadata.ContainsKey(currentTag)){
|
||||||
error = "Invalid metadata tag: "+currentTag;
|
throw new FormatException("Invalid metadata tag: "+currentTag);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentTag != null){
|
else if (currentTag != null){
|
||||||
currentContents = currentContents.Length == 0 ? line : currentContents+"\r\n"+line;
|
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
error = "Missing metadata tag before value: "+line;
|
throw new FormatException("Missing metadata tag before value: "+line);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.Name.Length == 0){
|
if (plugin.Name.Length == 0){
|
||||||
error = "Plugin is missing a name in the .meta file.";
|
throw new FormatException("Plugin is missing a name in the .meta file");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
||||||
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
|
throw new FormatException("Plugin contains invalid version: "+plugin.RequiredVersion);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.OnMetadataLoaded();
|
plugin.OnMetadataLoaded();
|
||||||
|
|
||||||
error = string.Empty;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckRequiredVersion(string requires){
|
private static bool CheckRequiredVersion(string requires){
|
||||||
|
@@ -24,10 +24,28 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleEnabled(Plugin plugin){
|
||||||
|
SetEnabled(plugin, !IsEnabled(plugin));
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsEnabled(Plugin plugin){
|
public bool IsEnabled(Plugin plugin){
|
||||||
return !disabled.Contains(plugin.Identifier);
|
return !disabled.Contains(plugin.Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Save(string file){
|
||||||
|
try{
|
||||||
|
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
||||||
|
writer.WriteLine("#Disabled");
|
||||||
|
|
||||||
|
foreach(string identifier in disabled){
|
||||||
|
writer.WriteLine(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Load(string file){
|
public void Load(string file){
|
||||||
try{
|
try{
|
||||||
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
|
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
|
||||||
@@ -43,30 +61,12 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
}catch(FileNotFoundException){
|
}catch(FileNotFoundException){
|
||||||
disabled.Clear();
|
disabled.Clear();
|
||||||
|
disabled.UnionWith(DefaultDisabled);
|
||||||
foreach(string identifier in DefaultDisabled){
|
|
||||||
disabled.Add(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
Save(file);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save(string file){
|
|
||||||
try{
|
|
||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
|
||||||
writer.WriteLine("#Disabled");
|
|
||||||
|
|
||||||
foreach(string identifier in disabled){
|
|
||||||
writer.WriteLine(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(Exception e){
|
|
||||||
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,8 +9,6 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class PluginManager{
|
sealed class PluginManager{
|
||||||
private const int InvalidToken = 0;
|
|
||||||
|
|
||||||
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
||||||
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
||||||
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
||||||
@@ -35,8 +33,6 @@ namespace TweetDuck.Plugins{
|
|||||||
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
||||||
private readonly Random rand = new Random();
|
private readonly Random rand = new Random();
|
||||||
|
|
||||||
private List<string> loadErrors;
|
|
||||||
|
|
||||||
public PluginManager(string rootPath, string configPath){
|
public PluginManager(string rootPath, string configPath){
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
this.configPath = configPath;
|
this.configPath = configPath;
|
||||||
@@ -68,7 +64,18 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return InvalidToken;
|
int token, attempts = 1000;
|
||||||
|
|
||||||
|
do{
|
||||||
|
token = rand.Next();
|
||||||
|
}while(tokens.ContainsKey(token) && --attempts >= 0);
|
||||||
|
|
||||||
|
if (attempts < 0){
|
||||||
|
token = -tokens.Count-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens[token] = plugin;
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plugin GetPluginFromToken(int token){
|
public Plugin GetPluginFromToken(int token){
|
||||||
@@ -81,28 +88,41 @@ namespace TweetDuck.Plugins{
|
|||||||
plugins.Clear();
|
plugins.Clear();
|
||||||
tokens.Clear();
|
tokens.Clear();
|
||||||
|
|
||||||
loadErrors = new List<string>(2);
|
List<string> loadErrors = new List<string>(2);
|
||||||
|
|
||||||
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official)){
|
IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
|
||||||
plugins.Add(plugin);
|
if (!Directory.Exists(path)){
|
||||||
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom)){
|
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
||||||
plugins.Add(plugin);
|
Plugin plugin;
|
||||||
|
|
||||||
|
try{
|
||||||
|
plugin = Plugin.CreateFromFolder(fullDir, group);
|
||||||
|
}catch(Exception e){
|
||||||
|
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield return plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.UnionWith(LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official));
|
||||||
|
plugins.UnionWith(LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom));
|
||||||
|
|
||||||
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
||||||
if (HasAnyPlugin(environment)){
|
if (!HasAnyPlugin(environment)){
|
||||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
return;
|
||||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
|
||||||
ExecutePluginScripts(frame, environment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecutePluginScripts(IFrame frame, PluginEnvironment environment){
|
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
||||||
|
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
||||||
|
|
||||||
bool includeDisabled = environment.IncludesDisabledPlugins();
|
bool includeDisabled = environment.IncludesDisabledPlugins();
|
||||||
|
|
||||||
if (includeDisabled){
|
if (includeDisabled){
|
||||||
@@ -113,7 +133,10 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
foreach(Plugin plugin in Plugins){
|
foreach(Plugin plugin in Plugins){
|
||||||
string path = plugin.GetScriptPath(environment);
|
string path = plugin.GetScriptPath(environment);
|
||||||
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun)continue;
|
|
||||||
|
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string script;
|
string script;
|
||||||
|
|
||||||
@@ -124,49 +147,10 @@ namespace TweetDuck.Plugins{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int token;
|
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, GetTokenFromPlugin(plugin), environment), "plugin:"+plugin);
|
||||||
|
|
||||||
if (tokens.ContainsValue(plugin)){
|
|
||||||
token = GetTokenFromPlugin(plugin);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
token = GenerateToken();
|
|
||||||
tokens[token] = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, token, environment), "plugin:"+plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
|
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
|
|
||||||
if (!Directory.Exists(path)){
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
|
||||||
Plugin plugin = Plugin.CreateFromFolder(fullDir, group, out string error);
|
|
||||||
|
|
||||||
if (plugin == null){
|
|
||||||
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+error);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
yield return plugin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GenerateToken(){
|
|
||||||
for(int attempt = 0; attempt < 1000; attempt++){
|
|
||||||
int token = rand.Next();
|
|
||||||
|
|
||||||
if (!tokens.ContainsKey(token) && token != InvalidToken){
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -tokens.Count;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
Program.cs
25
Program.cs
@@ -14,14 +14,13 @@ using TweetDuck.Core.Other.Settings.Export;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetLib.Communication;
|
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
static class Program{
|
static class Program{
|
||||||
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.10.1";
|
public const string VersionTag = "1.11.1";
|
||||||
|
|
||||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||||
|
|
||||||
@@ -37,6 +36,7 @@ namespace TweetDuck{
|
|||||||
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
||||||
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
||||||
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
||||||
|
public static string AnalyticsFilePath => Path.Combine(StoragePath, "TD_Analytics.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");
|
||||||
@@ -69,9 +69,10 @@ namespace TweetDuck{
|
|||||||
private static void Main(){
|
private static void Main(){
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
Cef.EnableHighDPISupport();
|
||||||
|
|
||||||
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
|
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||||
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
|
SubProcessMessage = NativeMethods.RegisterWindowMessage("TweetDuckSubProcess");
|
||||||
|
|
||||||
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||||
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
|
||||||
@@ -145,7 +146,6 @@ namespace TweetDuck{
|
|||||||
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||||
|
|
||||||
Cef.EnableHighDPISupport();
|
|
||||||
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
||||||
|
|
||||||
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||||
@@ -166,9 +166,13 @@ namespace TweetDuck{
|
|||||||
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||||
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||||
|
|
||||||
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);
|
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
RestartWithArgsInternal(Arguments.GetCurrentClean());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDataStoragePath(){
|
private static string GetDataStoragePath(){
|
||||||
@@ -211,11 +215,14 @@ namespace TweetDuck{
|
|||||||
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
||||||
if (browserForm == null)return;
|
if (browserForm == null)return;
|
||||||
|
|
||||||
args.AddFlag(Arguments.ArgRestart);
|
|
||||||
|
|
||||||
browserForm.ForceClose();
|
browserForm.ForceClose();
|
||||||
ExitCleanup();
|
|
||||||
|
|
||||||
|
ExitCleanup();
|
||||||
|
RestartWithArgsInternal(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RestartWithArgsInternal(CommandLineArgs args){
|
||||||
|
args.AddFlag(Arguments.ArgRestart);
|
||||||
Process.Start(Application.ExecutablePath, args.ToString());
|
Process.Start(Application.ExecutablePath, args.ToString());
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
|
25
README.md
25
README.md
@@ -1,3 +1,7 @@
|
|||||||
|
# Support
|
||||||
|
|
||||||
|
[Follow TweetDuck on Twitter](https://twitter.com/TryTweetDuck) | [Support via PayPal](https://paypal.me/chylex) | [Support via Patreon](https://www.patreon.com/chylex)
|
||||||
|
|
||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
@@ -25,21 +29,34 @@ To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure yo
|
|||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, open the **Output** view and see which part of the build failed.
|
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
|
||||||
|
|
||||||
After the build succeeds, the **bin/x86/Release** folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#Installers) section for automated installer generation.
|
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
|
||||||
|
|
||||||
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck.
|
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck.
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
There are a few quirks in the build process that may catch you off guard:
|
||||||
|
|
||||||
|
- **Plugin files are not updated automatically**
|
||||||
|
- Since official plugins (`Resources/Plugins`) are not included in the project, Visual Studio will not automatically detect changes in the files
|
||||||
|
- To ensure plugins are updated when testing the app, click **Rebuild Solution** before clicking **Start**
|
||||||
|
- **Error: The command (...) exited with code 1**
|
||||||
|
- If the post-build event fails, open the **Output** tab and look for the cause
|
||||||
|
- Determine if there was an IO error while copying files or modifying folders, or whether the final .ps1 script failed (`Encountered an error while running PostBuild.ps1 on line xyz`)
|
||||||
|
- Some files are checked for invalid characters:
|
||||||
|
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
||||||
|
|
||||||
### Installers
|
### Installers
|
||||||
|
|
||||||
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
|
||||||
|
|
||||||
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
||||||
|
|
||||||
Now you can generate installers after a build by running **bld/RUN BUILD.bat**. Note that despite the name, this will only package the files, you still need to run the [build](#Build) in Visual Studio!
|
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that despite the name, this will only package the files, you still need to run the [build](#build) in Visual Studio!
|
||||||
|
|
||||||
After the window closes, three installers will be generated inside the **bld/Output** folder:
|
After the window closes, three installers will be generated inside the `bld/Output` folder:
|
||||||
* **TweetDuck.exe**
|
* **TweetDuck.exe**
|
||||||
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
|
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
|
||||||
* **TweetDuck.Update.exe**
|
* **TweetDuck.Update.exe**
|
||||||
|
@@ -28,7 +28,7 @@
|
|||||||
<img src="img/app-menu.png" alt="">
|
<img src="img/app-menu.png" alt="">
|
||||||
<p>There are two ways to open the main menu:</p>
|
<p>There are two ways to open the main menu:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Click <em>Settings</em> in the left panel, then select <em>TweetDuck</em></li>
|
<li>Click <em>Settings</em> in the left panel and select <em>TweetDuck</em>, or...</li>
|
||||||
<li><em>Right-click anywhere</em> and you will either see the listed options, or a <em>TweetDuck</em> entry that contains these options</li>
|
<li><em>Right-click anywhere</em> and you will either see the listed options, or a <em>TweetDuck</em> entry that contains these options</li>
|
||||||
</ul>
|
</ul>
|
||||||
<img src="img/settings-dropdown.png" alt="">
|
<img src="img/settings-dropdown.png" alt="">
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<li><em>Save image/video as...</em></li>
|
<li><em>Save image/video as...</em></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>TweetDuck will attempt to fetch the highest quality image when you click any of these options. You can disable that by going to the <em>main menu</em>, selecting <em>Options</em>, and then unchecking <em>Best Image Quality</em>.</p>
|
<p>TweetDuck will attempt to fetch the highest quality image when you click any of these options. You can disable that by going to the <em>main menu</em>, selecting <em>Options</em>, and then unchecking <em>Best Image Quality</em>.</p>
|
||||||
<p>Whenever possible, the username and quality are included in the filename by default, for convenience. After you select a folder and click Save, the image/video will be downloaded in the background. There is no notification for when the download finishes, but you will be notified if it fails.</p>
|
<p>Whenever possible, the username and quality are included in the filename by default for convenience. After you select a folder and click Save, the image/video will be downloaded in the background. There is no notification for when the download finishes, but you will be notified if it fails.</p>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@@ -331,10 +331,10 @@
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary id="popout-replies">How to instantly popout replies</summary>
|
<summary id="popout-replies">How to instantly popout replies or quote tweets</summary>
|
||||||
<div>
|
<div>
|
||||||
<p><em>Middle-click the reply icon</em> to instantly open your reply in the New Tweet panel.</p>
|
<p><em>Middle-click the reply icon</em> to instantly open your reply in the New Tweet panel, or <em>middle-click the retweet icon</em> to quote a tweet instead of retweeting it.</p>
|
||||||
<p>Middle-clicks are usually done by pressing your mouse wheel as if it was a button. When using a laptop touchpad or certain mice, the ways of triggering a middle click vary.</p>
|
<p>Middle-clicking is usually done by pressing your mouse wheel as if it was a button. Some mice or other devices, such as laptop touchpads, may have a dedicated button or button combination instead.</p>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@@ -8,10 +8,10 @@ Edit layout & design
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.1.3
|
1.1.7
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
1.7
|
1.10.2
|
@@ -6,12 +6,15 @@ enabled(){
|
|||||||
this.config = null;
|
this.config = null;
|
||||||
|
|
||||||
this.defaultConfig = {
|
this.defaultConfig = {
|
||||||
|
_theme: "light",
|
||||||
columnWidth: "310px",
|
columnWidth: "310px",
|
||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
hideTweetActions: true,
|
hideTweetActions: true,
|
||||||
moveTweetActionsToRight: true,
|
moveTweetActionsToRight: true,
|
||||||
themeColorTweaks: true,
|
themeColorTweaks: true,
|
||||||
revertIcons: true,
|
revertIcons: true,
|
||||||
|
showCharacterCount: true,
|
||||||
|
increaseQuoteTextSize: false,
|
||||||
smallComposeTextSize: false,
|
smallComposeTextSize: false,
|
||||||
optimizeAnimations: true,
|
optimizeAnimations: true,
|
||||||
avatarRadius: 2
|
avatarRadius: 2
|
||||||
@@ -37,11 +40,13 @@ enabled(){
|
|||||||
this.currentStage = 1;
|
this.currentStage = 1;
|
||||||
}
|
}
|
||||||
else if (this.tmpConfig !== null){
|
else if (this.tmpConfig !== null){
|
||||||
|
let needsResave = !("_theme" in this.tmpConfig);
|
||||||
|
|
||||||
this.config = $.extend(this.defaultConfig, this.tmpConfig);
|
this.config = $.extend(this.defaultConfig, this.tmpConfig);
|
||||||
this.tmpConfig = null;
|
this.tmpConfig = null;
|
||||||
this.reinjectAll();
|
this.reinjectAll();
|
||||||
|
|
||||||
if (this.firstTimeLoad){
|
if (this.firstTimeLoad || needsResave){
|
||||||
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.config));
|
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +64,8 @@ enabled(){
|
|||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$(document).one("dataSettingsValues", () => {
|
$(document).one("dataSettingsValues", () => {
|
||||||
|
this.defaultConfig._theme = TD.settings.getTheme();
|
||||||
|
|
||||||
switch(TD.settings.getColumnWidth()){
|
switch(TD.settings.getColumnWidth()){
|
||||||
case "wide": this.defaultConfig.columnWidth = "350px"; break;
|
case "wide": this.defaultConfig.columnWidth = "350px"; break;
|
||||||
case "narrow": this.defaultConfig.columnWidth = "270px"; break;
|
case "narrow": this.defaultConfig.columnWidth = "270px"; break;
|
||||||
@@ -172,28 +179,44 @@ enabled(){
|
|||||||
}
|
}
|
||||||
// SELECTS
|
// SELECTS
|
||||||
else if (tag === "SELECT"){
|
else if (tag === "SELECT"){
|
||||||
if (!item.val(me.config[key]).val()){
|
let optionCustom = item.find("option[value^='custom']");
|
||||||
let custom = item.find("option[value='custom']");
|
|
||||||
|
|
||||||
if (custom.length === 1){
|
let resetMyValue = () => {
|
||||||
item.val("custom");
|
if (!item.val(me.config[key]).val() && optionCustom.length === 1){
|
||||||
custom.text(getTextForCustom(key));
|
item.val(optionCustom.attr("value"));
|
||||||
}
|
optionCustom.text(getTextForCustom(key));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resetMyValue();
|
||||||
|
|
||||||
item.change(function(){ // TODO change doesn't fire when Custom is already selected
|
item.change(function(){ // TODO change doesn't fire when Custom is already selected
|
||||||
let val = item.val();
|
let val = item.val();
|
||||||
|
|
||||||
if (val === "custom"){
|
if (val === "custom-px"){
|
||||||
val = prompt("Enter custom value:");
|
val = (prompt("Enter custom value (px):") || "").trim();
|
||||||
|
|
||||||
if (val){
|
if (val){
|
||||||
updateKey(key, val);
|
if (val.endsWith("px")){
|
||||||
item.find("option[value='custom']").text(getTextForCustom(key));
|
val = val.slice(0, -2).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^[0-9]+$/.test(val)){
|
||||||
|
updateKey(key, val+"px");
|
||||||
|
optionCustom.text(getTextForCustom(key));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
alert("Invalid value, only px values are supported.");
|
||||||
|
resetMyValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
resetMyValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
updateKey(key, item.val());
|
updateKey(key, item.val());
|
||||||
|
optionCustom.text("Custom");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -217,9 +240,11 @@ enabled(){
|
|||||||
modal.find("[data-td-theme='"+TD.settings.getTheme()+"']").prop("checked", true);
|
modal.find("[data-td-theme='"+TD.settings.getTheme()+"']").prop("checked", true);
|
||||||
|
|
||||||
modal.find("[data-td-theme]").change(function(){
|
modal.find("[data-td-theme]").change(function(){
|
||||||
|
me.config._theme = $(this).attr("data-td-theme");
|
||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
TD.settings.setTheme($(this).attr("data-td-theme"));
|
|
||||||
$(document).trigger("uiToggleTheme");
|
$(document).trigger("uiToggleTheme");
|
||||||
|
me.saveConfig();
|
||||||
me.reinjectAll();
|
me.reinjectAll();
|
||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
@@ -331,7 +356,7 @@ enabled(){
|
|||||||
this.css.insert("#general_settings .cf { display: none !important }");
|
this.css.insert("#general_settings .cf { display: none !important }");
|
||||||
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
|
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
|
||||||
|
|
||||||
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }");
|
this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
|
||||||
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
||||||
|
|
||||||
let notificationScrollbarColor = null;
|
let notificationScrollbarColor = null;
|
||||||
@@ -339,31 +364,39 @@ enabled(){
|
|||||||
if (this.config.themeColorTweaks){
|
if (this.config.themeColorTweaks){
|
||||||
switch(TD.settings.getTheme()){
|
switch(TD.settings.getTheme()){
|
||||||
case "dark":
|
case "dark":
|
||||||
this.css.insert(".app-content, .app-columns-container { background-color: #444448 }");
|
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
|
||||||
this.css.insert(".column-drag-handle { opacity: 0.5 }");
|
this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
|
||||||
this.css.insert(".column-drag-handle:hover { opacity: 1 }");
|
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
|
||||||
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #666 }");
|
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #666 !important }");
|
||||||
notificationScrollbarColor = "666";
|
notificationScrollbarColor = "666";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "light":
|
case "light":
|
||||||
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #d2d6da }");
|
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #d2d6da !important }");
|
||||||
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }");
|
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 !important }");
|
||||||
notificationScrollbarColor = "a5aeb5";
|
notificationScrollbarColor = "a5aeb5";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.showCharacterCount){
|
||||||
|
this.css.insert("#tduck .js-character-count.is-hidden { display: inline !important }");
|
||||||
|
}
|
||||||
|
|
||||||
if (this.config.hideTweetActions){
|
if (this.config.hideTweetActions){
|
||||||
this.css.insert(".tweet-action { opacity: 0; }");
|
this.css.insert(".tweet-action { opacity: 0; }");
|
||||||
this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5; }");
|
this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5 }");
|
||||||
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }");
|
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }");
|
||||||
this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }");
|
this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.moveTweetActionsToRight){
|
if (this.config.moveTweetActionsToRight){
|
||||||
this.css.insert(".tweet-actions { float: right !important; width: auto !important }");
|
this.css.insert("#tduck .tweet-actions { float: right !important; width: auto !important }");
|
||||||
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important }");
|
this.css.insert("#tduck .tweet-actions > li:nth-child(4) { margin-right: 2px !important }");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.increaseQuoteTextSize){
|
||||||
|
this.css.insert(".quoted-tweet { font-size: 1em !important }");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.smallComposeTextSize){
|
if (this.config.smallComposeTextSize){
|
||||||
@@ -371,96 +404,100 @@ enabled(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.revertIcons){
|
if (this.config.revertIcons){
|
||||||
|
let iconData = [
|
||||||
|
[ ".icon-twitter-bird", "00" ],
|
||||||
|
[ ".icon-mention", "01" ],
|
||||||
|
[ ".icon-following", "02" ],
|
||||||
|
[ ".icon-message", "03" ],
|
||||||
|
[ ".icon-home", "04" ],
|
||||||
|
[ ".icon-hashtag", "05" ],
|
||||||
|
[ ".icon-reply", "06" ],
|
||||||
|
[ ".icon-favorite", "55" ],
|
||||||
|
[ ".icon-retweet", "08" ],
|
||||||
|
[ ".icon-drafts", "09" ],
|
||||||
|
[ ".icon-search", "0a" ],
|
||||||
|
[ ".icon-trash", "0c" ],
|
||||||
|
[ ".icon-close", "0d" ],
|
||||||
|
[ ".icon-arrow-r:before,.Icon--caretRight", "0e" ],
|
||||||
|
[ ".icon-arrow-l:before,.Icon--caretLeft", "0f" ],
|
||||||
|
[ ".icon-protected", "13" ],
|
||||||
|
[ ".icon-list", "14" ],
|
||||||
|
[ ".icon-camera", "15" ],
|
||||||
|
[ ".icon-more", "16" ],
|
||||||
|
[ ".icon-settings", "18" ],
|
||||||
|
[ ".icon-notifications", "19" ],
|
||||||
|
[ ".icon-user-dd", "1a" ],
|
||||||
|
[ ".icon-activity", "1c" ],
|
||||||
|
[ ".icon-trending", "1d" ],
|
||||||
|
[ ".icon-minus", "1e" ],
|
||||||
|
[ ".icon-plus", "1f" ],
|
||||||
|
[ ".icon-geo", "20" ],
|
||||||
|
[ ".icon-check", "21" ],
|
||||||
|
[ ".icon-schedule", "22" ],
|
||||||
|
[ ".icon-dot", "23" ],
|
||||||
|
[ ".icon-user", "24" ],
|
||||||
|
[ ".icon-content", "25" ],
|
||||||
|
[ ".icon-arrow-d:before,.Icon--caretDown", "26" ],
|
||||||
|
[ ".icon-arrow-u", "27" ],
|
||||||
|
[ ".icon-share", "28" ],
|
||||||
|
[ ".icon-info", "29" ],
|
||||||
|
[ ".icon-verified", "2a" ],
|
||||||
|
[ ".icon-translator", "2b" ],
|
||||||
|
[ ".icon-blocked", "2c" ],
|
||||||
|
[ ".icon-constrain", "2d" ],
|
||||||
|
[ ".icon-play-video", "2e" ],
|
||||||
|
[ ".icon-empty", "2f" ],
|
||||||
|
[ ".icon-clear-input", "30" ],
|
||||||
|
[ ".icon-compose", "31" ],
|
||||||
|
[ ".icon-mark-read", "32" ],
|
||||||
|
[ ".icon-arrow-r-double", "33" ],
|
||||||
|
[ ".icon-arrow-l-double", "34" ],
|
||||||
|
[ ".icon-follow", "35" ],
|
||||||
|
[ ".icon-image", "36" ],
|
||||||
|
[ ".icon-popout", "37" ],
|
||||||
|
[ ".icon-move", "39" ],
|
||||||
|
[ ".icon-compose-grid", "3a" ],
|
||||||
|
[ ".icon-compose-minigrid", "3b" ],
|
||||||
|
[ ".icon-compose-list", "3c" ],
|
||||||
|
[ ".icon-edit", "40" ],
|
||||||
|
[ ".icon-clear-timeline", "41" ],
|
||||||
|
[ ".icon-sliders", "42" ],
|
||||||
|
[ ".icon-custom-timeline", "43" ],
|
||||||
|
[ ".icon-compose-dm", "44" ],
|
||||||
|
[ ".icon-bg-dot", "45" ],
|
||||||
|
[ ".icon-user-team-mgr", "46" ],
|
||||||
|
[ ".icon-user-switch", "47" ],
|
||||||
|
[ ".icon-conversation", "48" ],
|
||||||
|
[ ".icon-dataminr", "49" ],
|
||||||
|
[ ".icon-link", "4a", ],
|
||||||
|
[ ".icon-flash", "50" ],
|
||||||
|
[ ".icon-pointer-u", "51" ],
|
||||||
|
[ ".icon-analytics", "54" ],
|
||||||
|
[ ".icon-heart", "55" ],
|
||||||
|
[ ".icon-calendar", "56" ],
|
||||||
|
[ ".icon-attachment", "57" ],
|
||||||
|
[ ".icon-play", "58" ],
|
||||||
|
[ ".icon-bookmark", "59" ],
|
||||||
|
[ ".icon-play-badge", "60" ],
|
||||||
|
[ ".icon-gif-badge", "61" ],
|
||||||
|
[ ".icon-poll", "62" ],
|
||||||
|
|
||||||
|
[ ".icon-heart-filled", "55" ],
|
||||||
|
[ ".icon-retweet-filled", "08" ],
|
||||||
|
[ ".icon-list-filled", "14" ],
|
||||||
|
[ ".icon-user-filled", "35" ],
|
||||||
|
];
|
||||||
|
|
||||||
this.icons = document.createElement("style");
|
this.icons = document.createElement("style");
|
||||||
this.icons.innerHTML = `
|
this.icons.innerHTML = `
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'tweetdeckold';
|
font-family: '_of';
|
||||||
src: url("https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff") format("woff");
|
src: url("https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff") format("woff");
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-twitter-bird:before{content:"\\f000";font-family:tweetdeckold}
|
${iconData.map(entry => `#tduck ${entry[0]}:before{content:\"\\f0${entry[1]}\";font-family:_of!important}`).join("")}
|
||||||
.icon-mention:before{content:"\\f001";font-family:tweetdeckold}
|
|
||||||
.icon-following:before{content:"\\f002";font-family:tweetdeckold}
|
|
||||||
.icon-message:before{content:"\\f003";font-family:tweetdeckold}
|
|
||||||
.icon-home:before{content:"\\f004";font-family:tweetdeckold}
|
|
||||||
.icon-hashtag:before{content:"\\f005";font-family:tweetdeckold}
|
|
||||||
.icon-reply:before{content:"\\f006";font-family:tweetdeckold}
|
|
||||||
.icon-favorite:before{content:"\\f055";font-family:tweetdeckold}
|
|
||||||
.icon-retweet:before{content:"\\f008";font-family:tweetdeckold}
|
|
||||||
.icon-drafts:before{content:"\\f009";font-family:tweetdeckold}
|
|
||||||
.icon-search:before{content:"\\f00a";font-family:tweetdeckold}
|
|
||||||
.icon-trash:before{content:"\\f00c";font-family:tweetdeckold}
|
|
||||||
.icon-close:before{content:"\\f00d";font-family:tweetdeckold}
|
|
||||||
.icon-arrow-r:before,.Icon--caretRight:before{content:"\\f00e";font-family:tweetdeckold}
|
|
||||||
.icon-arrow-l:before,.Icon--caretLeft:before{content:"\\f00f";font-family:tweetdeckold}
|
|
||||||
.icon-protected:before{content:"\\f013";font-family:tweetdeckold}
|
|
||||||
.icon-list:before{content:"\\f014";font-family:tweetdeckold}
|
|
||||||
.icon-camera:before{content:"\\f015";font-family:tweetdeckold}
|
|
||||||
.icon-more:before{content:"\\f016";font-family:tweetdeckold}
|
|
||||||
.icon-settings:before{content:"\\f018";font-family:tweetdeckold}
|
|
||||||
.icon-notifications:before{content:"\\f019";font-family:tweetdeckold}
|
|
||||||
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
|
|
||||||
.icon-activity:before{content:"\\f01c";font-family:tweetdeckold}
|
|
||||||
.icon-trending:before{content:"\\f01d";font-family:tweetdeckold}
|
|
||||||
.icon-minus:before{content:"\\f01e";font-family:tweetdeckold}
|
|
||||||
.icon-plus:before{content:"\\f01f";font-family:tweetdeckold}
|
|
||||||
.icon-geo:before{content:"\\f020";font-family:tweetdeckold}
|
|
||||||
.icon-check:before{content:"\\f021";font-family:tweetdeckold}
|
|
||||||
.icon-schedule:before{content:"\\f022";font-family:tweetdeckold}
|
|
||||||
.icon-dot:before{content:"\\f023";font-family:tweetdeckold}
|
|
||||||
.icon-user:before{content:"\\f024";font-family:tweetdeckold}
|
|
||||||
.icon-content:before{content:"\\f025";font-family:tweetdeckold}
|
|
||||||
.icon-arrow-d:before,.Icon--caretDown:before{content:"\\f026";font-family:tweetdeckold}
|
|
||||||
.icon-arrow-u:before{content:"\\f027";font-family:tweetdeckold}
|
|
||||||
.icon-share:before{content:"\\f028";font-family:tweetdeckold}
|
|
||||||
.icon-info:before{content:"\\f029";font-family:tweetdeckold}
|
|
||||||
.icon-verified:before{content:"\\f02a";font-family:tweetdeckold}
|
|
||||||
.icon-translator:before{content:"\\f02b";font-family:tweetdeckold}
|
|
||||||
.icon-blocked:before{content:"\\f02c";font-family:tweetdeckold}
|
|
||||||
.icon-constrain:before{content:"\\f02d";font-family:tweetdeckold}
|
|
||||||
.icon-play-video:before{content:"\\f02e";font-family:tweetdeckold}
|
|
||||||
.icon-empty:before{content:"\\f02f";font-family:tweetdeckold}
|
|
||||||
.icon-clear-input:before{content:"\\f030";font-family:tweetdeckold}
|
|
||||||
.icon-compose:before{content:"\\f031";font-family:tweetdeckold}
|
|
||||||
.icon-mark-read:before{content:"\\f032";font-family:tweetdeckold}
|
|
||||||
.icon-arrow-r-double:before{content:"\\f033";font-family:tweetdeckold}
|
|
||||||
.icon-arrow-l-double:before{content:"\\f034";font-family:tweetdeckold}
|
|
||||||
.icon-follow:before{content:"\\f035";font-family:tweetdeckold}
|
|
||||||
.icon-image:before{content:"\\f036";font-family:tweetdeckold}
|
|
||||||
.icon-popout:before{content:"\\f037";font-family:tweetdeckold}
|
|
||||||
.icon-move:before{content:"\\f039";font-family:tweetdeckold}
|
|
||||||
.icon-compose-grid:before{content:"\\f03a";font-family:tweetdeckold}
|
|
||||||
.icon-compose-minigrid:before{content:"\\f03b";font-family:tweetdeckold}
|
|
||||||
.icon-compose-list:before{content:"\\f03c";font-family:tweetdeckold}
|
|
||||||
.icon-edit:before{content:"\\f040";font-family:tweetdeckold}
|
|
||||||
.icon-clear-timeline:before{content:"\\f041";font-family:tweetdeckold}
|
|
||||||
.icon-sliders:before{content:"\\f042";font-family:tweetdeckold}
|
|
||||||
.icon-custom-timeline:before{content:"\\f043";font-family:tweetdeckold}
|
|
||||||
.icon-compose-dm:before{content:"\\f044";font-family:tweetdeckold}
|
|
||||||
.icon-bg-dot:before{content:"\\f045";font-family:tweetdeckold}
|
|
||||||
.icon-user-team-mgr:before{content:"\\f046";font-family:tweetdeckold}
|
|
||||||
.icon-user-switch:before{content:"\\f047";font-family:tweetdeckold}
|
|
||||||
.icon-conversation:before{content:"\\f048";font-family:tweetdeckold}
|
|
||||||
.icon-dataminr:before{content:"\\f049";font-family:tweetdeckold}
|
|
||||||
.icon-link:before{content:"\\f04a";font-family:tweetdeckold}
|
|
||||||
.icon-flash:before{content:"\\f050";font-family:tweetdeckold}
|
|
||||||
.icon-pointer-u:before{content:"\\f051";font-family:tweetdeckold}
|
|
||||||
.icon-analytics:before{content:"\\f054";font-family:tweetdeckold}
|
|
||||||
.icon-heart:before{content:"\\f055";font-family:tweetdeckold}
|
|
||||||
.icon-calendar:before{content:"\\f056";font-family:tweetdeckold}
|
|
||||||
.icon-attachment:before{content:"\\f057";font-family:tweetdeckold}
|
|
||||||
.icon-play:before{content:"\\f058";font-family:tweetdeckold}
|
|
||||||
.icon-bookmark:before{content:"\\f059";font-family:tweetdeckold}
|
|
||||||
.icon-play-badge:before{content:"\\f060";font-family:tweetdeckold}
|
|
||||||
.icon-gif-badge:before{content:"\\f061";font-family:tweetdeckold}
|
|
||||||
.icon-poll:before{content:"\\f062";font-family:tweetdeckold}
|
|
||||||
|
|
||||||
.icon-heart-filled:before{content:"\\f055";font-family:tweetdeckold}
|
|
||||||
.icon-retweet-filled:before{content:"\\f008";font-family:tweetdeckold}
|
|
||||||
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold}
|
|
||||||
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
|
||||||
|
|
||||||
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
||||||
.column-header .column-type-icon { bottom: 26px !important }
|
.column-header .column-type-icon { bottom: 26px !important }
|
||||||
@@ -507,21 +544,25 @@ enabled(){
|
|||||||
|
|
||||||
$TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", `
|
$TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", `
|
||||||
<style type='text/css'>
|
<style type='text/css'>
|
||||||
.txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important }
|
html[data-td-font] { font-size: ${this.config.fontSize} !important }
|
||||||
.avatar { border-radius: ${this.config.avatarRadius}% !important }
|
.avatar { border-radius: ${this.config.avatarRadius}% !important }
|
||||||
|
|
||||||
|
${this.config.increaseQuoteTextSize ? `
|
||||||
|
.quoted-tweet { font-size: 1em !important }
|
||||||
|
` : ``}
|
||||||
|
|
||||||
${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: '_of'; 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}
|
#tduck .icon-reply:before{content:"\\f006";font-family:_of!important}
|
||||||
.icon-heart-filled:before{content:"\\f055";font-family:tweetdeckold}
|
#tduck .icon-heart-filled:before{content:"\\f055";font-family:_of!important}
|
||||||
.icon-retweet-filled:before{content:"\\f008";font-family:tweetdeckold}
|
#tduck .icon-retweet-filled:before{content:"\\f008";font-family:_of!important}
|
||||||
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold}
|
#tduck .icon-list-filled:before{content:"\\f014";font-family:_of!important}
|
||||||
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
|
#tduck .icon-user-filled:before{content:"\\f035";font-family:_of!important}
|
||||||
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
|
#tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important}
|
||||||
` : ``}
|
` : ``}
|
||||||
|
|
||||||
${notificationScrollbarColor ? `
|
${notificationScrollbarColor ? `
|
||||||
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} }
|
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} !important }
|
||||||
` : ``}
|
` : ``}
|
||||||
</style>`);
|
</style>`);
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="td-modal-panel js-modal-panel mdl s-tall-fixed is-inverted-dark">
|
<div id="edit-design-panel" class="js-modal-panel mdl s-tall-fixed is-inverted-dark">
|
||||||
<header class="js-mdl-header mdl-header js-drag-handle">
|
<header class="js-mdl-header mdl-header js-drag-handle">
|
||||||
<h3 class="mdl-header-title js-header-title">TweetDuck - Layout & Design</h3>
|
<h3 class="mdl-header-title js-header-title">TweetDuck - Layout & Design</h3>
|
||||||
<a href="#" class="mdl-dismiss js-dismiss link-normal-dark">
|
<a href="#" class="mdl-dismiss js-dismiss link-normal-dark">
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</header>
|
</header>
|
||||||
<div class="mdl-inner">
|
<div class="mdl-inner">
|
||||||
<div class="td-modal-content mdl-content js-mdl-content horizontal-flow-container">
|
<div id="edit-design-panel-content" class="mdl-content js-mdl-content horizontal-flow-container">
|
||||||
<div class="td-modal-inner-cols">
|
<div id="edit-design-panel-inner-cols">
|
||||||
<div class="l-column mdl-column">
|
<div class="l-column mdl-column">
|
||||||
|
|
||||||
<!-- THEME -->
|
<!-- THEME -->
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<option value="310px">Medium (310px)</option>
|
<option value="310px">Medium (310px)</option>
|
||||||
<option value="350px">Wide (350px)</option>
|
<option value="350px">Wide (350px)</option>
|
||||||
<option value="400px">Extreme (400px)</option>
|
<option value="400px">Extreme (400px)</option>
|
||||||
<option value="custom">Custom</option>
|
<option value="custom-px">Custom</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<option disabled></option>
|
<option disabled></option>
|
||||||
<optgroup label="Dynamic width">
|
<optgroup label="Dynamic width">
|
||||||
@@ -59,17 +59,15 @@
|
|||||||
<option value="14px">Medium (14px)</option>
|
<option value="14px">Medium (14px)</option>
|
||||||
<option value="15px">Large (15px)</option>
|
<option value="15px">Large (15px)</option>
|
||||||
<option value="16px">Largest (16px)</option>
|
<option value="16px">Largest (16px)</option>
|
||||||
<option value="custom">Custom</option>
|
<option value="custom-px">Custom</option>
|
||||||
</select>
|
</select>
|
||||||
|
<label class="checkbox">
|
||||||
<!-- ADVANCED -->
|
<input data-td-key="increaseQuoteTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
|
Increase quoted tweet font size
|
||||||
<label class="txt-uppercase touch-larger-label">
|
|
||||||
<b>Advanced</b>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input data-td-key="optimizeAnimations" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="smallComposeTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Use more memory for smoother animations
|
Small tweet input font size
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -94,17 +92,27 @@
|
|||||||
<label class="txt-uppercase touch-larger-label">
|
<label class="txt-uppercase touch-larger-label">
|
||||||
<b>Design</b>
|
<b>Design</b>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
|
||||||
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
|
||||||
Theme color tweaks
|
|
||||||
</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
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input data-td-key="smallComposeTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="showCharacterCount" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Small compose tweet font size
|
Always show character count
|
||||||
|
</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
|
Theme color tweaks
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!-- ADVANCED -->
|
||||||
|
|
||||||
|
<label class="txt-uppercase touch-larger-label">
|
||||||
|
<b>Advanced</b>
|
||||||
|
</label>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input data-td-key="optimizeAnimations" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
|
Use more memory for smoother animations
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -151,54 +159,55 @@
|
|||||||
|
|
||||||
/* Containers */
|
/* Containers */
|
||||||
|
|
||||||
.td-modal-panel {
|
#edit-design-panel {
|
||||||
width: 693px;
|
width: 693px;
|
||||||
height: 380px;
|
height: 380px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-modal-inner-cols {
|
#edit-design-panel-inner-cols {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-modal-inner-cols .l-column {
|
#edit-design-panel-inner-cols .l-column {
|
||||||
padding: 15px 9px;
|
padding: 15px 9px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
width: 225px;
|
||||||
font-size: 0; /* fix custom font size breaking the modal layout */
|
font-size: 0; /* fix custom font size breaking the modal layout */
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-modal-inner-cols .l-column:nth-child(2) {
|
#edit-design-panel-inner-cols .l-column:nth-child(3) {
|
||||||
width: 250px;
|
width: 200px;
|
||||||
}
|
|
||||||
|
|
||||||
.td-modal-inner-full {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.td-modal-inner-full .txt-center {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Elements */
|
/* Elements */
|
||||||
|
|
||||||
.td-modal-content label {
|
#edit-design-panel-content label.txt-uppercase {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-modal-content label:first-child {
|
#edit-design-panel-content label.txt-uppercase:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-modal-content label.radio {
|
#edit-design-panel-content label.radio {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 16px 5px 4px;
|
margin: 0 16px 5px 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-modal-content label.checkbox {
|
#edit-design-panel-content label.checkbox {
|
||||||
margin: 0 0 5px 4px;
|
margin: 0 0 5px 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#edit-design-panel-content select + label.checkbox {
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#edit-design-panel-content input.js-theme-checkbox, #edit-design-panel-content input.js-theme-radio {
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Avatar shape */
|
/* Avatar shape */
|
||||||
|
|
||||||
.td-avatar-shape-container {
|
.td-avatar-shape-container {
|
||||||
|
@@ -9,7 +9,7 @@ Emoji keyboard
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.4
|
1.4.1
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -303,23 +303,26 @@ enabled(){
|
|||||||
let firstColon = val.lastIndexOf(':', ele[0].selectionStart);
|
let firstColon = val.lastIndexOf(':', ele[0].selectionStart);
|
||||||
return if firstColon === -1;
|
return if firstColon === -1;
|
||||||
|
|
||||||
let search = val.substring(firstColon+1, ele[0].selectionStart);
|
let search = val.substring(firstColon+1, ele[0].selectionStart).toLowerCase();
|
||||||
return if !/^[a-z_]+$/i.test(search);
|
return if !/^[a-z_]+$/.test(search);
|
||||||
|
|
||||||
let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
|
||||||
return if keywords.length === 0;
|
return if keywords.length === 0;
|
||||||
|
|
||||||
let foundName = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw)));
|
let foundNames = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw)));
|
||||||
|
|
||||||
if (foundName.length === 0){
|
if (foundNames.length === 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (foundNames.length > 1 && foundNames.includes(search)){
|
||||||
|
foundNames = [ search ];
|
||||||
|
}
|
||||||
|
|
||||||
lastEmojiKeyword = `:${search}:`;
|
lastEmojiKeyword = `:${search}:`;
|
||||||
lastEmojiPosition = lastEmojiLength = 0;
|
lastEmojiPosition = lastEmojiLength = 0;
|
||||||
|
|
||||||
if (foundName.length === 1){
|
if (foundNames.length === 1){
|
||||||
let foundIndex = me.emojiNames.indexOf(foundName[0]);
|
let foundIndex = me.emojiNames.indexOf(foundNames[0]);
|
||||||
let foundEmoji;
|
let foundEmoji;
|
||||||
|
|
||||||
for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){
|
for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){
|
||||||
@@ -346,7 +349,7 @@ enabled(){
|
|||||||
lastEmojiLength = foundEmoji.length;
|
lastEmojiLength = foundEmoji.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (foundName.length > 1){
|
else if (foundNames.length > 1){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart));
|
ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart));
|
||||||
ele[0].selectionEnd = ele[0].selectionStart = firstColon;
|
ele[0].selectionEnd = ele[0].selectionStart = firstColon;
|
||||||
|
@@ -8,7 +8,7 @@ Custom reply account
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.2.3
|
1.2.4
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
@@ -20,4 +20,4 @@ configuration.js
|
|||||||
configuration.default.js
|
configuration.default.js
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
1.8
|
1.10.3
|
@@ -6,7 +6,7 @@ enabled(){
|
|||||||
this.lastSelectedAccount = null;
|
this.lastSelectedAccount = null;
|
||||||
|
|
||||||
this.uiComposeTweetEvent = (e, data) => {
|
this.uiComposeTweetEvent = (e, data) => {
|
||||||
return if data.type !== "reply" || data.popFromInline || !("element" in data);
|
return if !(data.type === "reply" || (data.type === "tweet" && "quotedTweet" in data)) || data.popFromInline || !("element" in data);
|
||||||
|
|
||||||
var query;
|
var query;
|
||||||
|
|
||||||
@@ -117,19 +117,9 @@ enabled(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
var events = $._data(document, "events");
|
for(let event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){
|
||||||
|
|
||||||
for(var event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){
|
|
||||||
$(document).on(event, this.uiComposeTweetEvent);
|
$(document).on(event, this.uiComposeTweetEvent);
|
||||||
|
window.TDPF_prioritizeNewestEvent(document, event);
|
||||||
var handlers = events[event];
|
|
||||||
var newHandler = handlers[handlers.length-1];
|
|
||||||
|
|
||||||
for(var index = handlers.length-1; index > 0; index--){
|
|
||||||
handlers[index] = handlers[index-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers[0] = newHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);
|
$(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);
|
||||||
|
@@ -8,7 +8,7 @@ Templates
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.0.2
|
1.0.3
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -50,25 +50,25 @@ enabled(){
|
|||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
|
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
|
||||||
|
|
||||||
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; }");
|
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-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-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; }");
|
||||||
this.css.insert(".template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }");
|
this.css.insert("#template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }");
|
||||||
this.css.insert(".template-list li[data-template] { cursor: pointer; }");
|
this.css.insert("#template-list li[data-template] { cursor: pointer; }");
|
||||||
this.css.insert(".template-list li[data-template]:hover { background-color: #d8d8d8; }");
|
this.css.insert("#template-list li[data-template]:hover { background-color: #d8d8d8; }");
|
||||||
this.css.insert(".template-list li span { white-space: nowrap; }");
|
this.css.insert("#template-list li span { white-space: nowrap; }");
|
||||||
this.css.insert(".template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }");
|
this.css.insert("#template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }");
|
||||||
this.css.insert(".template-list li .icon:hover { opacity: 1; }");
|
this.css.insert("#template-list li .icon:hover { opacity: 1; }");
|
||||||
this.css.insert(".template-list li .template-actions { float: right; }");
|
this.css.insert("#template-list li .template-actions { float: right; }");
|
||||||
|
|
||||||
this.css.insert(".template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 150px; max-width: 400px; background-color: #485865; }");
|
this.css.insert("#template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 150px; max-width: 400px; background-color: #485865; }");
|
||||||
this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }");
|
this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }");
|
||||||
this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }");
|
this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }");
|
||||||
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
|
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
|
||||||
@@ -261,9 +261,9 @@ enabled(){
|
|||||||
$(".manage-templates-btn").addClass("active");
|
$(".manage-templates-btn").addClass("active");
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
<div class="templates-modal-wrap">
|
<div id="templates-modal-wrap">
|
||||||
<div class="templates-modal">
|
<div id="templates-modal">
|
||||||
<div class="template-list">
|
<div id="template-list">
|
||||||
<ul></ul>
|
<ul></ul>
|
||||||
|
|
||||||
<div class="templates-modal-bottom">
|
<div class="templates-modal-bottom">
|
||||||
@@ -272,7 +272,7 @@ enabled(){
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="template-editor invisible">
|
<div id="template-editor" class="invisible">
|
||||||
<div class="template-editor-form">
|
<div class="template-editor-form">
|
||||||
<div class="compose-text-title">Template Name</div>
|
<div class="compose-text-title">Template Name</div>
|
||||||
<input name="template-name" type="text">
|
<input name="template-name" type="text">
|
||||||
@@ -320,7 +320,7 @@ enabled(){
|
|||||||
|
|
||||||
$(".js-app-content").prepend(html);
|
$(".js-app-content").prepend(html);
|
||||||
|
|
||||||
let ele = $(".templates-modal-wrap").first();
|
let ele = $("#templates-modal-wrap").first();
|
||||||
|
|
||||||
ele.on("click", "li[data-template]", function(e){
|
ele.on("click", "li[data-template]", function(e){
|
||||||
let template = me.config.templates[$(this).attr("data-template")];
|
let template = me.config.templates[$(this).attr("data-template")];
|
||||||
@@ -332,7 +332,7 @@ enabled(){
|
|||||||
|
|
||||||
switch($(this).attr("data-action")){
|
switch($(this).attr("data-action")){
|
||||||
case "edit-template":
|
case "edit-template":
|
||||||
let editor = $(".template-editor");
|
let editor = $("#template-editor");
|
||||||
|
|
||||||
if (editor.hasClass("invisible")){
|
if (editor.hasClass("invisible")){
|
||||||
toggleEditor();
|
toggleEditor();
|
||||||
@@ -372,7 +372,7 @@ enabled(){
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "editor-confirm":
|
case "editor-confirm":
|
||||||
let editor = $(".template-editor");
|
let editor = $("#template-editor");
|
||||||
|
|
||||||
if (me.editingTemplate !== null){
|
if (me.editingTemplate !== null){
|
||||||
delete me.config.templates[me.editingTemplate];
|
delete me.config.templates[me.editingTemplate];
|
||||||
@@ -408,15 +408,15 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
var hideTemplateModal = function(){
|
var hideTemplateModal = function(){
|
||||||
$(".templates-modal-wrap").remove();
|
$("#templates-modal-wrap").remove();
|
||||||
$(".manage-templates-btn").removeClass("active");
|
$(".manage-templates-btn").removeClass("active");
|
||||||
};
|
};
|
||||||
|
|
||||||
var toggleEditor = function(){
|
var toggleEditor = function(){
|
||||||
let editor = $(".template-editor");
|
let editor = $("#template-editor");
|
||||||
$("[name]", editor).val("");
|
$("[name]", editor).val("");
|
||||||
|
|
||||||
if ($("button[data-action='new-template']", ".template-list").add(editor).toggleClass("invisible").hasClass("invisible")){
|
if ($("button[data-action='new-template']", "#template-list").add(editor).toggleClass("invisible").hasClass("invisible")){
|
||||||
me.editingTemplate = null;
|
me.editingTemplate = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -435,7 +435,7 @@ enabled(){
|
|||||||
eles.push("<li>No templates available</li>");
|
eles.push("<li>No templates available</li>");
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".template-list").children("ul").html(eles.join(""));
|
$("#template-list").children("ul").html(eles.join(""));
|
||||||
|
|
||||||
if (save){
|
if (save){
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
@@ -445,7 +445,7 @@ enabled(){
|
|||||||
// event handlers
|
// event handlers
|
||||||
|
|
||||||
this.manageTemplatesButtonClickEvent = function(e){
|
this.manageTemplatesButtonClickEvent = function(e){
|
||||||
if ($(".templates-modal-wrap").length){
|
if ($("#templates-modal-wrap").length){
|
||||||
hideTemplateModal();
|
hideTemplateModal();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -471,7 +471,7 @@ disabled(){
|
|||||||
this.css.remove();
|
this.css.remove();
|
||||||
|
|
||||||
$(".manage-templates-btn").remove();
|
$(".manage-templates-btn").remove();
|
||||||
$(".templates-modal-wrap").remove();
|
$("#templates-modal-wrap").remove();
|
||||||
|
|
||||||
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
||||||
|
|
||||||
|
@@ -1,8 +1,4 @@
|
|||||||
enabled(){
|
enabled(){
|
||||||
this.reloadColumns = () => {
|
|
||||||
Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets());
|
|
||||||
};
|
|
||||||
|
|
||||||
// styles
|
// styles
|
||||||
|
|
||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
@@ -10,17 +6,17 @@ enabled(){
|
|||||||
|
|
||||||
// utility functions
|
// utility functions
|
||||||
|
|
||||||
var hasPoll = function(tweet){
|
const hasPoll = function(tweet){
|
||||||
return tweet.hasPoll && tweet.hasPoll();
|
return tweet.hasPoll && tweet.hasPoll();
|
||||||
};
|
};
|
||||||
|
|
||||||
var renderTweetPoll = function(tweet){
|
const renderTweetPoll = function(tweet){
|
||||||
return `<div class='td-timeline-poll'>${TD.ui.template.render("status/poll", $.extend({}, tweet, {
|
return `<div class='td-timeline-poll'>${TD.ui.template.render("status/poll", $.extend({}, tweet, {
|
||||||
chirp: tweet
|
chirp: tweet
|
||||||
}))}</div>`;
|
}))}</div>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
var renderPollHook = function(tweet, html){
|
const renderPollHook = function(tweet, html){
|
||||||
let ele = null;
|
let ele = null;
|
||||||
|
|
||||||
if (hasPoll(tweet)){
|
if (hasPoll(tweet)){
|
||||||
@@ -67,7 +63,7 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.prevRenderFuncs = funcs;
|
this.prevRenderFuncs = funcs;
|
||||||
this.reloadColumns();
|
window.TDPF_reloadColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled(){
|
disabled(){
|
||||||
@@ -76,5 +72,5 @@ disabled(){
|
|||||||
TD.components.TweetDetailView.prototype._renderChirp = this.prevRenderFuncs.TweetDetailView;
|
TD.components.TweetDetailView.prototype._renderChirp = this.prevRenderFuncs.TweetDetailView;
|
||||||
|
|
||||||
this.css.remove();
|
this.css.remove();
|
||||||
this.reloadColumns();
|
window.TDPF_reloadColumns();
|
||||||
}
|
}
|
@@ -3,6 +3,18 @@ $ErrorActionPreference = "Stop"
|
|||||||
|
|
||||||
Set-Location $dir
|
Set-Location $dir
|
||||||
|
|
||||||
|
function Check-Carriage-Return{
|
||||||
|
Param([Parameter(Mandatory = $True, Position = 1)] $fname)
|
||||||
|
|
||||||
|
$file = @(Get-ChildItem -Include $fname -Recurse)[0]
|
||||||
|
|
||||||
|
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){
|
||||||
|
Throw "$fname cannot contain carriage return"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Verified" $file.FullName.Substring($dir.Length)
|
||||||
|
}
|
||||||
|
|
||||||
function Rewrite-File{
|
function Rewrite-File{
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
Param([Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines, [Parameter(Mandatory = $True, Position = 1)] $file)
|
Param([Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines, [Parameter(Mandatory = $True, Position = 1)] $file)
|
||||||
@@ -11,7 +23,10 @@ function Rewrite-File{
|
|||||||
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Include *.js -Exclude configuration.default.js -Recurse){
|
try{
|
||||||
|
Check-Carriage-Return("emoji-ordering.txt")
|
||||||
|
|
||||||
|
ForEach($file in Get-ChildItem -Filter *.js -Exclude configuration.default.js -Recurse){
|
||||||
$lines = Get-Content -Path $file.FullName
|
$lines = Get-Content -Path $file.FullName
|
||||||
$lines = $lines | % { $_.TrimStart() }
|
$lines = $lines | % { $_.TrimStart() }
|
||||||
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
|
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
|
||||||
@@ -19,7 +34,7 @@ ForEach($file in Get-ChildItem -Include *.js -Exclude configuration.default.js -
|
|||||||
,$lines | Rewrite-File $file
|
,$lines | Rewrite-File $file
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Include *.css -Recurse){
|
ForEach($file in Get-ChildItem -Filter *.css -Recurse){
|
||||||
$lines = Get-Content -Path $file.FullName
|
$lines = Get-Content -Path $file.FullName
|
||||||
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
||||||
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;'
|
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;'
|
||||||
@@ -27,7 +42,12 @@ ForEach($file in Get-ChildItem -Include *.css -Recurse){
|
|||||||
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
|
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Include *.html -Recurse){
|
ForEach($file in Get-ChildItem -Filter *.html -Recurse){
|
||||||
$lines = Get-Content -Path $file.FullName
|
$lines = Get-Content -Path $file.FullName
|
||||||
,($lines | % { $_.TrimStart() }) | Rewrite-File $file
|
,($lines | % { $_.TrimStart() }) | Rewrite-File $file
|
||||||
}
|
}
|
||||||
|
}catch{
|
||||||
|
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
||||||
|
Write-Host $_
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
@@ -14,10 +14,15 @@
|
|||||||
//
|
//
|
||||||
var onAppReady = [];
|
var onAppReady = [];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Variable: DOM HTML element.
|
||||||
|
//
|
||||||
|
const doc = document.documentElement;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Variable: DOM object containing the main app element.
|
// Variable: DOM object containing the main app element.
|
||||||
//
|
//
|
||||||
var app = $(document.body).children(".js-app");
|
const app = $(document.body).children(".js-app");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Constant: Column types mapped to their titles.
|
// Constant: Column types mapped to their titles.
|
||||||
@@ -45,7 +50,7 @@
|
|||||||
//
|
//
|
||||||
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
|
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
|
||||||
//
|
//
|
||||||
var prependToFunction = function(func, extension){
|
const prependToFunction = function(func, extension){
|
||||||
return function(){
|
return function(){
|
||||||
return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
|
return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
|
||||||
};
|
};
|
||||||
@@ -54,7 +59,7 @@
|
|||||||
//
|
//
|
||||||
// Function: Appends code at the end of a function.
|
// Function: Appends code at the end of a function.
|
||||||
//
|
//
|
||||||
var appendToFunction = function(func, extension){
|
const appendToFunction = function(func, extension){
|
||||||
return function(){
|
return function(){
|
||||||
let res = func.apply(this, arguments);
|
let res = func.apply(this, arguments);
|
||||||
extension.apply(this, arguments);
|
extension.apply(this, arguments);
|
||||||
@@ -65,7 +70,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){
|
const ensurePropertyExists = function(obj, ...chain){
|
||||||
for(let 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("."));
|
||||||
@@ -81,7 +86,7 @@
|
|||||||
//
|
//
|
||||||
// Function: Retrieves a property of an element with a specified class.
|
// Function: Retrieves a property of an element with a specified class.
|
||||||
//
|
//
|
||||||
var getClassStyleProperty = function(cls, property){
|
const getClassStyleProperty = function(cls, property){
|
||||||
let column = document.createElement("div");
|
let column = document.createElement("div");
|
||||||
column.classList.add(cls);
|
column.classList.add(cls);
|
||||||
column.style.display = "none";
|
column.style.display = "none";
|
||||||
@@ -96,7 +101,7 @@
|
|||||||
//
|
//
|
||||||
// Function: Event callback for a new tweet.
|
// Function: Event callback for a new tweet.
|
||||||
//
|
//
|
||||||
var onNewTweet = (function(){
|
const onNewTweet = (function(){
|
||||||
let recentMessages = new Set();
|
let recentMessages = new Set();
|
||||||
let recentTweets = new Set();
|
let recentTweets = new Set();
|
||||||
let recentTweetTimer = null;
|
let recentTweetTimer = null;
|
||||||
@@ -123,7 +128,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
let fixMedia = (html, media) => {
|
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 html.find(".js-media a[data-media-entity-id='"+media.mediaId+"']").css("background-image", 'url("'+media.small()+'")').removeClass("is-zoomable");
|
||||||
};
|
};
|
||||||
|
|
||||||
return function(column, tweet){
|
return function(column, tweet){
|
||||||
@@ -214,7 +219,7 @@
|
|||||||
// Function: Shows tweet detail, used in notification context menu.
|
// Function: Shows tweet detail, used in notification context menu.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
var showTweetDetailInternal = function(column, chirp){
|
const showTweetDetailInternal = function(column, chirp){
|
||||||
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
|
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
|
||||||
TD.controller.columnManager.showColumn(column.model.privateState.key);
|
TD.controller.columnManager.showColumn(column.model.privateState.key);
|
||||||
|
|
||||||
@@ -258,49 +263,64 @@
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Retrieves the tags to be put into <head> for notification HTML code.
|
// Block: Hook into settings object to detect when the settings change, and update html attributes and notification layout.
|
||||||
//
|
//
|
||||||
var getNotificationHeadContents = function(){
|
(function(){
|
||||||
let tags = [];
|
const refreshSettings = function(){
|
||||||
|
let fontSizeName = TD.settings.getFontSize();
|
||||||
|
let themeName = TD.settings.getTheme();
|
||||||
|
|
||||||
$(document.head).children("link[rel='stylesheet']:not([title]),link[title='"+TD.settings.getTheme()+"'],meta[charset],meta[http-equiv]").each(function(){
|
let tags = [
|
||||||
|
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
|
||||||
|
];
|
||||||
|
|
||||||
|
$(document.head).children("link[href*='css/font.']:first,link[href*='css/app-"+themeName+".']:first,meta[charset],meta[http-equiv]").each(function(){
|
||||||
tags.push($(this)[0].outerHTML);
|
tags.push($(this)[0].outerHTML);
|
||||||
});
|
});
|
||||||
|
|
||||||
tags.push("<style type='text/css'>");
|
tags.push("<style type='text/css'>");
|
||||||
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color
|
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" !important }"); // set background color
|
||||||
tags.push("a[data-full-url] { word-break: break-all }"); // break long urls
|
tags.push("a[data-full-url] { word-break: break-all !important }"); // break long urls
|
||||||
tags.push(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important }"); // fix cut off badge icon
|
|
||||||
tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
|
tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
|
||||||
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets
|
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets
|
||||||
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications
|
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px !important }"); // tweak alignment of avatar and text in notifications
|
||||||
tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications
|
tags.push(".activity-header .tweet-timestamp { line-height: unset !important }"); // fix timestamp position in notifications
|
||||||
|
|
||||||
|
if (fontSizeName === "smallest"){
|
||||||
|
tags.push(".badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important }"); // fix cut off badge icon
|
||||||
|
}
|
||||||
|
|
||||||
tags.push("</style>");
|
tags.push("</style>");
|
||||||
|
|
||||||
return tags.join("");
|
doc.setAttribute("data-td-font", fontSizeName);
|
||||||
|
doc.setAttribute("data-td-theme", themeName);
|
||||||
|
$TD.loadNotificationLayout(fontSizeName, tags.join(""));
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Block: Hook into settings object to detect when the settings change.
|
|
||||||
//
|
|
||||||
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
|
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
|
||||||
$TD.loadFontSizeClass(name);
|
setTimeout(refreshSettings, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(name){
|
TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(name){
|
||||||
document.documentElement.setAttribute("data-td-theme", name);
|
setTimeout(refreshSettings, 0);
|
||||||
|
|
||||||
setTimeout(function(){
|
|
||||||
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
|
||||||
}, 0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onAppReady.push(function(){
|
onAppReady.push(refreshSettings);
|
||||||
document.documentElement.setAttribute("data-td-theme", TD.settings.getTheme());
|
})();
|
||||||
|
|
||||||
$TD.loadFontSizeClass(TD.settings.getFontSize());
|
//
|
||||||
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
// Block: Fix OS name and add ID to the document for priority CSS selectors.
|
||||||
});
|
//
|
||||||
|
if (ensurePropertyExists(TD, "util", "getOSName")){
|
||||||
|
TD.util.getOSName = function(){
|
||||||
|
return "windows";
|
||||||
|
};
|
||||||
|
|
||||||
|
doc.classList.remove("os-");
|
||||||
|
doc.classList.add("os-windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.id = "tduck";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Enable popup notifications.
|
// Block: Enable popup notifications.
|
||||||
@@ -357,7 +377,7 @@
|
|||||||
mouseenter: function(){
|
mouseenter: function(){
|
||||||
let me = $(this);
|
let me = $(this);
|
||||||
let text = me.text();
|
let text = me.text();
|
||||||
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis
|
return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
tooltipTimer = window.setTimeout(function(){
|
tooltipTimer = window.setTimeout(function(){
|
||||||
@@ -409,7 +429,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
|
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
|
||||||
let prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
|
const prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
|
||||||
|
|
||||||
TD.services.TwitterUser.prototype.fromJSONObject = function(){
|
TD.services.TwitterUser.prototype.fromJSONObject = function(){
|
||||||
let obj = prevFunc.apply(this, arguments);
|
let obj = prevFunc.apply(this, arguments);
|
||||||
@@ -424,7 +444,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
||||||
let prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
||||||
|
|
||||||
TD.services.TwitterMedia.prototype.fromMediaEntity = function(){
|
TD.services.TwitterMedia.prototype.fromMediaEntity = function(){
|
||||||
let obj = prevFunc.apply(this, arguments);
|
let obj = prevFunc.apply(this, arguments);
|
||||||
@@ -479,13 +499,13 @@
|
|||||||
(function(){
|
(function(){
|
||||||
var lastTweet = "";
|
var lastTweet = "";
|
||||||
|
|
||||||
var updateHighlightedColumn = function(ele){
|
const updateHighlightedColumn = function(ele){
|
||||||
highlightedColumnEle = ele;
|
highlightedColumnEle = ele;
|
||||||
highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null;
|
highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null;
|
||||||
return !!highlightedColumnObj;
|
return !!highlightedColumnObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
|
const updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
|
||||||
highlightedTweetEle = ele;
|
highlightedTweetEle = ele;
|
||||||
highlightedTweetObj = obj;
|
highlightedTweetObj = obj;
|
||||||
|
|
||||||
@@ -495,7 +515,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var processMedia = function(chirp){
|
const processMedia = function(chirp){
|
||||||
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -544,7 +564,7 @@
|
|||||||
(function(){
|
(function(){
|
||||||
var selectedTweet;
|
var selectedTweet;
|
||||||
|
|
||||||
var setImportantProperty = function(obj, property, value){
|
const setImportantProperty = function(obj, property, value){
|
||||||
if (obj.length === 1){
|
if (obj.length === 1){
|
||||||
obj[0].style.setProperty(property, value, "important");
|
obj[0].style.setProperty(property, value, "important");
|
||||||
}
|
}
|
||||||
@@ -613,7 +633,7 @@
|
|||||||
// Block: Paste images when tweeting.
|
// Block: Paste images when tweeting.
|
||||||
//
|
//
|
||||||
onAppReady.push(function(){
|
onAppReady.push(function(){
|
||||||
var uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
|
const uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
|
||||||
|
|
||||||
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
|
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
|
||||||
for(let item of e.originalEvent.clipboardData.items){
|
for(let item of e.originalEvent.clipboardData.items){
|
||||||
@@ -631,25 +651,41 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Setup a global function to reorder event priority.
|
||||||
|
//
|
||||||
|
window.TDGF_prioritizeNewestEvent = function(element, event){
|
||||||
|
let events = $._data(element, "events");
|
||||||
|
|
||||||
|
let handlers = events[event];
|
||||||
|
let newHandler = handlers[handlers.length-1];
|
||||||
|
|
||||||
|
for(let index = handlers.length-1; index > 0; index--){
|
||||||
|
handlers[index] = handlers[index-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers[0] = newHandler;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Support for extra mouse buttons.
|
// Block: Support for extra mouse buttons.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
var tryClickSelector = function(selector, parent){
|
const tryClickSelector = function(selector, parent){
|
||||||
return $(selector, parent).click().length;
|
return $(selector, parent).click().length;
|
||||||
};
|
};
|
||||||
|
|
||||||
var tryCloseModal1 = function(){
|
const tryCloseModal1 = function(){
|
||||||
let 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(){
|
const tryCloseModal2 = function(){
|
||||||
let 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(){
|
const tryCloseHighlightedColumn = function(){
|
||||||
if (highlightedColumnEle){
|
if (highlightedColumnEle){
|
||||||
let 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));
|
||||||
@@ -737,14 +773,14 @@
|
|||||||
(function(){
|
(function(){
|
||||||
var holdingShift = false;
|
var holdingShift = false;
|
||||||
|
|
||||||
var updateShiftState = (pressed) => {
|
const updateShiftState = (pressed) => {
|
||||||
if (pressed != holdingShift){
|
if (pressed != holdingShift){
|
||||||
holdingShift = pressed;
|
holdingShift = pressed;
|
||||||
$("button[data-action='clear']").children("span").text(holdingShift ? "Restore" : "Clear");
|
$("button[data-action='clear']").children("span").text(holdingShift ? "Restore" : "Clear");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var resetActiveFocus = () => {
|
const resetActiveFocus = () => {
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -775,7 +811,7 @@
|
|||||||
// Block: Swap shift key functionality for selecting accounts, and refocus the textbox afterwards.
|
// Block: Swap shift key functionality for selecting accounts, and refocus the textbox afterwards.
|
||||||
//
|
//
|
||||||
onAppReady.push(function(){
|
onAppReady.push(function(){
|
||||||
var onAccountClick = function(e){
|
const onAccountClick = function(e){
|
||||||
if ($TDX.switchAccountSelectors){
|
if ($TDX.switchAccountSelectors){
|
||||||
e.shiftKey = !e.shiftKey;
|
e.shiftKey = !e.shiftKey;
|
||||||
}
|
}
|
||||||
@@ -796,25 +832,63 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Make middle click on tweet reply icon open the compose drawer. Only works for non-temporary columns.
|
// Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal.
|
||||||
//
|
//
|
||||||
app.delegate(".js-reply-action", "auxclick", function(e){
|
app.delegate(".tweet-action,.tweet-detail-action", "auxclick", function(e){
|
||||||
if (e.which === 2){
|
return if e.which !== 2;
|
||||||
let column = $(this).closest(".js-column");
|
|
||||||
|
|
||||||
if (column && column.hasClass("column") && $("[data-drawer='compose']").hasClass("is-hidden")){
|
let column = TD.controller.columnManager.get($(this).closest("section.js-column").attr("data-column"));
|
||||||
$(document).trigger("uiDrawerShowDrawer", {
|
return if !column;
|
||||||
drawer: "compose",
|
|
||||||
withAnimation: true
|
let ele = $(this).closest("article");
|
||||||
|
let tweet = column.findChirp(ele.attr("data-tweet-id")) || column.findChirp(ele.attr("data-key"));
|
||||||
|
return if !tweet;
|
||||||
|
|
||||||
|
switch($(this).attr("rel")){
|
||||||
|
case "reply":
|
||||||
|
let main = tweet.getMainTweet();
|
||||||
|
|
||||||
|
$(document).trigger("uiDockedComposeTweet", {
|
||||||
|
type: "reply",
|
||||||
|
from: [ tweet.account.getKey() ],
|
||||||
|
inReplyTo: {
|
||||||
|
id: tweet.id,
|
||||||
|
htmlText: main.htmlText,
|
||||||
|
user: {
|
||||||
|
screenName: main.user.screenName,
|
||||||
|
name: main.user.name,
|
||||||
|
profileImageURL: main.user.profileImageURL
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mentions: tweet.getReplyUsers(),
|
||||||
|
element: ele
|
||||||
});
|
});
|
||||||
|
|
||||||
window.setTimeout(() => $(this).trigger("click"), 1);
|
break;
|
||||||
|
|
||||||
|
case "favorite":
|
||||||
|
$(document).trigger("uiShowFavoriteFromOptions", { tweet });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "retweet":
|
||||||
|
TD.controller.stats.quoteTweet();
|
||||||
|
|
||||||
|
$(document).trigger("uiComposeTweet", {
|
||||||
|
type: "tweet",
|
||||||
|
from: [ tweet.account.getKey() ],
|
||||||
|
quotedTweet: tweet.getMainTweet(),
|
||||||
|
element: ele // triggers reply-account plugin
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -828,7 +902,7 @@
|
|||||||
// Block: Inject custom CSS and layout into the page.
|
// Block: Inject custom CSS and layout into the page.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
var createStyle = function(id, styles){
|
const createStyle = function(id, styles){
|
||||||
let ele = document.createElement("style");
|
let ele = document.createElement("style");
|
||||||
ele.id = id;
|
ele.id = id;
|
||||||
ele.innerText = styles;
|
ele.innerText = styles;
|
||||||
@@ -872,26 +946,34 @@
|
|||||||
// Block: Setup video player hooks.
|
// Block: Setup video player hooks.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
var playVideo = function(url){
|
window.TDGF_playVideo = function(url, username){
|
||||||
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
||||||
$TD.playVideo(null);
|
$TD.playVideo(null, null);
|
||||||
}).appendTo(app);
|
}).appendTo(app);
|
||||||
|
|
||||||
$TD.playVideo(url);
|
$TD.playVideo(url, username || null);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getVideoTweetLink = function(obj){
|
const getGifLink = function(ele){
|
||||||
|
return ele.attr("src") || ele.children("source[video-src]").first().attr("video-src");
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVideoTweetLink = function(obj){
|
||||||
let parent = obj.closest(".js-tweet").first();
|
let parent = obj.closest(".js-tweet").first();
|
||||||
let 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();
|
||||||
return link.attr("href");
|
return link.attr("href");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getUsername = function(tweet){
|
||||||
|
return tweet && (tweet.quotedTweet || tweet).getMainUser().screenName;
|
||||||
|
}
|
||||||
|
|
||||||
app.delegate(".js-gif-play", {
|
app.delegate(".js-gif-play", {
|
||||||
click: function(e){
|
click: function(e){
|
||||||
let src = !e.ctrlKey && $(this).closest(".js-media-gif-container").find("video").attr("src");
|
let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
|
||||||
|
|
||||||
if (src){
|
if (src){
|
||||||
playVideo(src);
|
window.TDGF_playVideo(src, getUsername(highlightedTweetObj));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$TD.openBrowser(getVideoTweetLink($(this)));
|
$TD.openBrowser(getVideoTweetLink($(this)));
|
||||||
@@ -927,7 +1009,7 @@
|
|||||||
let 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 === "twitter"){
|
if (media && media.isVideo && media.service === "twitter"){
|
||||||
playVideo(media.chooseVideoVariant().url);
|
window.TDGF_playVideo(media.chooseVideoVariant().url, getUsername(this.chirp));
|
||||||
cancelModal = true;
|
cancelModal = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1008,6 +1090,58 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Make submitting search queries while holding Ctrl or middle-clicking the search icon open the search externally.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
const openSearchExternally = function(event, input){
|
||||||
|
$TD.openBrowser("https://twitter.com/search/?q="+encodeURIComponent(input.val() || ""));
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
input.val("").blur();
|
||||||
|
app.click(); // unfocus everything
|
||||||
|
};
|
||||||
|
|
||||||
|
$(".js-app-search-input").keydown(function(e){
|
||||||
|
(e.ctrlKey && e.keyCode === 13) && openSearchExternally(e, $(this)); // enter
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".js-perform-search").on("click auxclick", function(e){
|
||||||
|
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $(".js-app-search-input:visible"));
|
||||||
|
}).each(function(){
|
||||||
|
window.TDGF_prioritizeNewestEvent($(this)[0], "click");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("[data-action='show-search']").on("click auxclick", function(e){
|
||||||
|
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Setup global function to refresh all columns.
|
||||||
|
//
|
||||||
|
window.TDGF_reloadColumns = function(){
|
||||||
|
Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets());
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Allow applying ROT13 to input selection.
|
||||||
|
//
|
||||||
|
window.TDGF_applyROT13 = function(){
|
||||||
|
let ele = document.activeElement;
|
||||||
|
return if !ele || !ele.value;
|
||||||
|
|
||||||
|
let selection = ele.value.substring(ele.selectionStart, ele.selectionEnd);
|
||||||
|
return if !selection;
|
||||||
|
|
||||||
|
document.execCommand("insertText", false, selection.replace(/[a-zA-Z]/g, function(chr){
|
||||||
|
let code = chr.charCodeAt(0);
|
||||||
|
let start = code <= 90 ? 65 : 97;
|
||||||
|
return String.fromCharCode(start+(code-start+13)%26);
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Fix DM reply input box not getting focused after opening a conversation.
|
// Block: Fix DM reply input box not getting focused after opening a conversation.
|
||||||
//
|
//
|
||||||
@@ -1023,7 +1157,7 @@
|
|||||||
// Block: Fix DM notifications not showing if the conversation is open.
|
// Block: Fix DM notifications not showing if the conversation is open.
|
||||||
//
|
//
|
||||||
if (ensurePropertyExists(TD, "services", "TwitterConversation", "prototype", "getUnreadChirps")){
|
if (ensurePropertyExists(TD, "services", "TwitterConversation", "prototype", "getUnreadChirps")){
|
||||||
var prevFunc = TD.services.TwitterConversation.prototype.getUnreadChirps;
|
const prevFunc = TD.services.TwitterConversation.prototype.getUnreadChirps;
|
||||||
|
|
||||||
TD.services.TwitterConversation.prototype.getUnreadChirps = function(e){
|
TD.services.TwitterConversation.prototype.getUnreadChirps = function(e){
|
||||||
return (e && e.sortIndex && !e.id && !this.notificationsDisabled)
|
return (e && e.sortIndex && !e.id && !this.notificationsDisabled)
|
||||||
@@ -1165,15 +1299,24 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Disable default TweetDeck update notification, as the app usually reloads automatically every so often.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
let events = $._data(document, "events");
|
||||||
|
delete events["uiSuggestRefreshToggle"];
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Disable TweetDeck metrics.
|
// Block: Disable TweetDeck metrics.
|
||||||
//
|
//
|
||||||
if (ensurePropertyExists(TD, "metrics")){
|
if (ensurePropertyExists(TD, "metrics")){
|
||||||
TD.metrics.inflate = function(){};
|
const noop = function(){};
|
||||||
TD.metrics.inflateMetricTriple = function(){};
|
TD.metrics.inflate = noop;
|
||||||
TD.metrics.log = function(){};
|
TD.metrics.inflateMetricTriple = noop;
|
||||||
TD.metrics.makeKey = function(){};
|
TD.metrics.log = noop;
|
||||||
TD.metrics.send = function(){};
|
TD.metrics.makeKey = noop;
|
||||||
|
TD.metrics.send = noop;
|
||||||
}
|
}
|
||||||
|
|
||||||
onAppReady.push(function(){
|
onAppReady.push(function(){
|
||||||
|
@@ -52,7 +52,7 @@
|
|||||||
return if !url;
|
return if !url;
|
||||||
|
|
||||||
var text = me.textContent;
|
var text = me.textContent;
|
||||||
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis
|
return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
tooltipTimer = window.setTimeout(function(){
|
tooltipTimer = window.setTimeout(function(){
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<div class="js-tweet tweet">
|
<div class="js-tweet tweet">
|
||||||
<header class="tweet-header">
|
<header class="tweet-header">
|
||||||
<time class="tweet-timestamp js-timestamp pull-right txt-mute">
|
<time class="tweet-timestamp js-timestamp pull-right txt-mute">
|
||||||
<a target="_blank" rel="url" href="https://twitter.com/chylexmc" class="txt-small">0s</a>
|
<a target="_blank" rel="url" href="https://twitter.com/chylexmc" class="txt-size-variable--12">0s</a>
|
||||||
</time>
|
</time>
|
||||||
<a target="_blank" rel="user" href="https://twitter.com/chylexmc" class="account-link link-complex block">
|
<a target="_blank" rel="user" href="https://twitter.com/chylexmc" class="account-link link-complex block">
|
||||||
<div class="obj-left item-img tweet-img">
|
<div class="obj-left item-img tweet-img">
|
||||||
|
@@ -87,14 +87,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Setup global function to change plugin state.
|
// Block: Setup a function to change plugin state.
|
||||||
//
|
//
|
||||||
window.TDPF_setPluginState = function(identifier, enable){
|
window.TDPF_setPluginState = function(identifier, enable){
|
||||||
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable);
|
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Setup global function to reload the page.
|
// Block: Setup a function to reload the page.
|
||||||
//
|
//
|
||||||
window.TDPF_requestReload = function(){
|
window.TDPF_requestReload = function(){
|
||||||
if (!isReloading){
|
if (!isReloading){
|
||||||
@@ -102,4 +102,11 @@
|
|||||||
isReloading = true;
|
isReloading = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Setup bridges to global functions.
|
||||||
|
//
|
||||||
|
window.TDPF_playVideo = window.TDGF_playVideo;
|
||||||
|
window.TDPF_reloadColumns = window.TDGF_reloadColumns;
|
||||||
|
window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent;
|
||||||
})();
|
})();
|
||||||
|
@@ -3,23 +3,23 @@
|
|||||||
/***********************/
|
/***********************/
|
||||||
|
|
||||||
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar {
|
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar {
|
||||||
border-radius: 0;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.antiscroll-scrollbar-vertical {
|
.antiscroll-scrollbar-vertical {
|
||||||
margin-top: 0;
|
margin-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.antiscroll-scrollbar-horizontal {
|
.antiscroll-scrollbar-horizontal {
|
||||||
margin-left: 0;
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar {
|
.scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar {
|
.scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar {
|
||||||
height: 8px;
|
height: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-columns-container::-webkit-scrollbar {
|
.app-columns-container::-webkit-scrollbar {
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-columns-container::-webkit-scrollbar-track {
|
.app-columns-container::-webkit-scrollbar-track {
|
||||||
border-left: 0;
|
border-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-columns-container {
|
.app-columns-container {
|
||||||
@@ -38,11 +38,15 @@
|
|||||||
/* Square-ify stuff */
|
/* Square-ify stuff */
|
||||||
/********************/
|
/********************/
|
||||||
|
|
||||||
.btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner {
|
.btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .tooltip-inner {
|
||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient {
|
.media-item, .media-preview, .media-image, .js-media-added .br--4 {
|
||||||
|
border-radius: 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-header:not(.is-condensed) .nav-user-info {
|
.app-header:not(.is-condensed) .nav-user-info {
|
||||||
padding: 0 5px;
|
padding: 0 5px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************/
|
/****************************************/
|
||||||
@@ -92,11 +96,11 @@
|
|||||||
|
|
||||||
.activity-header {
|
.activity-header {
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-header .tweet-timestamp {
|
.activity-header .tweet-timestamp {
|
||||||
line-height: unset;
|
line-height: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-bio.padding-t--5 {
|
.account-bio.padding-t--5 {
|
||||||
@@ -122,17 +126,12 @@ html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
|
|||||||
|
|
||||||
a[data-full-url] {
|
a[data-full-url] {
|
||||||
/* break long urls */
|
/* break long urls */
|
||||||
word-break: break-all;
|
word-break: break-all !important;
|
||||||
}
|
|
||||||
|
|
||||||
.character-count-compose {
|
|
||||||
/* fix strangely wide character count element */
|
|
||||||
width: 40px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot {
|
.is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot {
|
||||||
/* change play icon on mp4s */
|
/* change play icon on mp4s */
|
||||||
color: #9f51cf;
|
color: #9f51cf !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************/
|
/***************************************/
|
||||||
@@ -148,7 +147,7 @@ a[data-full-url] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.js-docked-compose footer {
|
.js-docked-compose footer {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose-content {
|
.compose-content {
|
||||||
@@ -161,17 +160,17 @@ a[data-full-url] {
|
|||||||
|
|
||||||
.account-inline .username {
|
.account-inline .username {
|
||||||
/* move usernames a bit higher */
|
/* move usernames a bit higher */
|
||||||
vertical-align: 10%;
|
vertical-align: 10% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.txt-base-smallest .sprite-verified-mini {
|
html[data-td-font='smallest'] .sprite-verified-mini {
|
||||||
/* fix cut off badge when zoomed in */
|
/* fix cut off badge when zoomed in */
|
||||||
width: 13px !important;
|
width: 13px !important;
|
||||||
height: 13px !important;
|
height: 13px !important;
|
||||||
background-position: -223px -99px !important;
|
background-position: -223px -99px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.txt-base-smallest .badge-verified:before {
|
html[data-td-font='smallest'] .badge-verified:before {
|
||||||
/* fix cut off badge in notifications */
|
/* fix cut off badge in notifications */
|
||||||
width: 13px !important;
|
width: 13px !important;
|
||||||
height: 13px !important;
|
height: 13px !important;
|
||||||
@@ -185,7 +184,7 @@ a[data-full-url] {
|
|||||||
|
|
||||||
.keyboard-shortcut-list {
|
.keyboard-shortcut-list {
|
||||||
/* fix keyboard navigation alignment */
|
/* fix keyboard navigation alignment */
|
||||||
vertical-align: top;
|
vertical-align: top !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-detail-wrapper .js-media-gif-container {
|
.tweet-detail-wrapper .js-media-gif-container {
|
||||||
@@ -195,7 +194,7 @@ a[data-full-url] {
|
|||||||
|
|
||||||
.is-inverted-dark .inline-reply .btn:hover {
|
.is-inverted-dark .inline-reply .btn:hover {
|
||||||
/* Reply buttons in modals are bork */
|
/* Reply buttons in modals are bork */
|
||||||
background-color: transparent;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
@@ -204,10 +203,10 @@ a[data-full-url] {
|
|||||||
|
|
||||||
.column-nav-link .attribution {
|
.column-nav-link .attribution {
|
||||||
/* fix cut off account names */
|
/* fix cut off account names */
|
||||||
position: absolute;
|
position: absolute !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.js-docked-compose .js-drawer-close {
|
#tduck .js-docked-compose .js-drawer-close {
|
||||||
/* fix close drawer button position */
|
/* fix close drawer button position */
|
||||||
margin: 20px 0 0 !important;
|
margin: 20px 0 0 !important;
|
||||||
}
|
}
|
||||||
@@ -220,7 +219,7 @@ a[data-full-url] {
|
|||||||
|
|
||||||
#account-safeguard-checkbox, #inline-account-safeguard-checkbox {
|
#account-safeguard-checkbox, #inline-account-safeguard-checkbox {
|
||||||
/* fix "Ready to Tweet/send" alignment with the checkbox */
|
/* fix "Ready to Tweet/send" alignment with the checkbox */
|
||||||
vertical-align: -15%;
|
vertical-align: -15% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************/
|
/************************************************/
|
||||||
@@ -232,7 +231,7 @@ a[data-full-url] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.js-column-header .column-header-link {
|
.js-column-header .column-header-link {
|
||||||
padding: 0;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.js-column-header .column-header-link .icon {
|
.js-column-header .column-header-link .icon {
|
||||||
@@ -251,9 +250,9 @@ a[data-full-url] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.column:not(.is-options-open) .column-header {
|
.column:not(.is-options-open) .column-header {
|
||||||
border-bottom: none;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-options-open .column-type-icon {
|
.is-options-open .column-type-icon {
|
||||||
bottom: 27px;
|
bottom: 27px !important;
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,23 @@
|
|||||||
/* General */
|
/* General */
|
||||||
|
|
||||||
body:before {
|
body:before {
|
||||||
content: none;
|
content: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
overflow-y: auto;
|
overflow-y: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-styled-v::-webkit-scrollbar {
|
.scroll-styled-v::-webkit-scrollbar {
|
||||||
width: 7px;
|
width: 7px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-styled-v::-webkit-scrollbar-thumb {
|
.scroll-styled-v::-webkit-scrollbar-thumb {
|
||||||
border-radius: 0;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-styled-v::-webkit-scrollbar-track {
|
.scroll-styled-v::-webkit-scrollbar-track {
|
||||||
border-left: 0;
|
border-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Media */
|
/* Media */
|
||||||
|
@@ -46,25 +46,25 @@
|
|||||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tdu-title {
|
#tweetduck-update .tdu-title {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin: 8px 0 2px;
|
margin: 8px 0 2px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tdu-info {
|
#tweetduck-update .tdu-info {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 3px 0;
|
margin: 3px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tdu-showlog {
|
#tweetduck-update .tdu-showlog {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tdu-buttons button {
|
#tweetduck-update .tdu-buttons button {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 7px auto 0;
|
margin: 7px auto 0;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
@@ -81,12 +81,12 @@
|
|||||||
transition: box-shadow 0.2s ease;
|
transition: box-shadow 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tdu-buttons button:hover {
|
#tweetduck-update .tdu-buttons button:hover {
|
||||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.75);
|
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.75);
|
||||||
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.75), 0 -2px 0 rgba(17, 17, 17, 0.33) inset !important;
|
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.75), 0 -2px 0 rgba(17, 17, 17, 0.33) inset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tdu-buttons button.tdu-btn-ignore, .tdu-buttons button.tdu-btn-later {
|
#tweetduck-update .tdu-buttons button.tdu-btn-ignore, .tdu-buttons button.tdu-btn-later {
|
||||||
background-color: #607a8e;
|
background-color: #607a8e;
|
||||||
color: #dfdfdf;
|
color: #dfdfdf;
|
||||||
}
|
}
|
||||||
|
@@ -105,6 +105,9 @@
|
|||||||
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
||||||
<Compile Include="Core\Handling\General\RequestHandlerBase.cs" />
|
<Compile Include="Core\Handling\General\RequestHandlerBase.cs" />
|
||||||
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
|
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
|
||||||
|
<Compile Include="Core\Notification\Example\FormNotificationExample.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Core\Notification\FormNotificationMain.cs">
|
<Compile Include="Core\Notification\FormNotificationMain.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -128,6 +131,10 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Notification\SoundNotification.cs" />
|
<Compile Include="Core\Notification\SoundNotification.cs" />
|
||||||
<Compile Include="Core\Notification\TweetNotification.cs" />
|
<Compile Include="Core\Notification\TweetNotification.cs" />
|
||||||
|
<Compile Include="Core\Other\Analytics\AnalyticsFile.cs" />
|
||||||
|
<Compile Include="Core\Other\Analytics\AnalyticsManager.cs" />
|
||||||
|
<Compile Include="Core\Other\Analytics\AnalyticsReport.cs" />
|
||||||
|
<Compile Include="Core\Other\Analytics\AnalyticsReportGenerator.cs" />
|
||||||
<Compile Include="Core\Other\FormAbout.cs">
|
<Compile Include="Core\Other\FormAbout.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -153,6 +160,12 @@
|
|||||||
<DependentUpon>FormPlugins.cs</DependentUpon>
|
<DependentUpon>FormPlugins.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Other\Management\VideoPlayer.cs" />
|
<Compile Include="Core\Other\Management\VideoPlayer.cs" />
|
||||||
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.Designer.cs">
|
||||||
|
<DependentUpon>DialogSettingsAnalytics.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCSS.cs">
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsCSS.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -190,6 +203,7 @@
|
|||||||
<Compile Include="Core\Other\Settings\TabSettingsTray.Designer.cs">
|
<Compile Include="Core\Other\Settings\TabSettingsTray.Designer.cs">
|
||||||
<DependentUpon>TabSettingsTray.cs</DependentUpon>
|
<DependentUpon>TabSettingsTray.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\TweetDeckBrowser.cs" />
|
||||||
<Compile Include="Core\Utils\StringUtils.cs" />
|
<Compile Include="Core\Utils\StringUtils.cs" />
|
||||||
<Compile Include="Core\Utils\TwitterUtils.cs" />
|
<Compile Include="Core\Utils\TwitterUtils.cs" />
|
||||||
<Compile Include="Data\CombinedFileStream.cs" />
|
<Compile Include="Data\CombinedFileStream.cs" />
|
||||||
@@ -269,10 +283,10 @@
|
|||||||
<Compile Include="Updates\FormUpdateDownload.Designer.cs">
|
<Compile Include="Updates\FormUpdateDownload.Designer.cs">
|
||||||
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
|
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\TrayIcon.cs">
|
<Compile Include="Core\Other\TrayIcon.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\TrayIcon.Designer.cs">
|
<Compile Include="Core\Other\TrayIcon.Designer.cs">
|
||||||
<DependentUpon>TrayIcon.cs</DependentUpon>
|
<DependentUpon>TrayIcon.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Utils\BrowserCache.cs" />
|
<Compile Include="Core\Utils\BrowserCache.cs" />
|
||||||
|
@@ -31,7 +31,7 @@ namespace TweetDuck.Updates{
|
|||||||
timerDownloadCheck.Stop();
|
timerDownloadCheck.Stop();
|
||||||
|
|
||||||
if (FormMessage.Error("Update Has Failed", "Could not download the update: "+(updateInfo.DownloadError?.Message ?? "unknown error")+"\n\nDo you want to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
if (FormMessage.Error("Update Has Failed", "Could not download the update: "+(updateInfo.DownloadError?.Message ?? "unknown error")+"\n\nDo you want to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
||||||
BrowserUtils.OpenExternalBrowserUnsafe(Program.Website);
|
BrowserUtils.OpenExternalBrowser(Program.Website);
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -43,9 +43,9 @@ namespace TweetLib.Audio.Impl{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnNotificationSoundError(string message){
|
private void OnNotificationSoundError(string message){
|
||||||
if (!ignorePlaybackError && PlaybackError != null){
|
if (!ignorePlaybackError){
|
||||||
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
|
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
|
||||||
PlaybackError(this, args);
|
PlaybackError?.Invoke(this, args);
|
||||||
ignorePlaybackError = args.Ignore;
|
ignorePlaybackError = args.Ignore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -102,9 +102,9 @@ namespace TweetLib.Audio.Impl{
|
|||||||
if (wasTryingToPlay){
|
if (wasTryingToPlay){
|
||||||
wasTryingToPlay = false;
|
wasTryingToPlay = false;
|
||||||
|
|
||||||
if (!ignorePlaybackError && PlaybackError != null){
|
if (!ignorePlaybackError){
|
||||||
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
|
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
|
||||||
PlaybackError(this, args);
|
PlaybackError?.Invoke(this, args);
|
||||||
ignorePlaybackError = args.Ignore;
|
ignorePlaybackError = args.Ignore;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.1.0.0")]
|
[assembly: AssemblyVersion("1.1.1.0")]
|
||||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
[assembly: AssemblyFileVersion("1.1.1.0")]
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using TweetLib.Communication.Utils;
|
|
||||||
|
|
||||||
namespace TweetLib.Communication{
|
|
||||||
public static class Comms{
|
|
||||||
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
|
|
||||||
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint RegisterMessage(string name){
|
|
||||||
return NativeMethods.RegisterWindowMessage(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.1.0.0")]
|
[assembly: AssemblyVersion("1.2.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
[assembly: AssemblyFileVersion("1.2.0.0")]
|
||||||
|
@@ -36,9 +36,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="DuplexPipe.cs" />
|
<Compile Include="DuplexPipe.cs" />
|
||||||
<Compile Include="Utils\NativeMethods.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Comms.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace TweetLib.Communication.Utils{
|
|
||||||
static class NativeMethods{
|
|
||||||
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern uint RegisterWindowMessage(string messageName);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.BrowserSubprocess;
|
using CefSharp.BrowserSubprocess;
|
||||||
using TweetLib.Communication;
|
|
||||||
|
|
||||||
namespace TweetDuck.Browser{
|
namespace TweetDuck.Browser{
|
||||||
static class Program{
|
static class Program{
|
||||||
internal const string Version = "1.2.0.0";
|
internal const string Version = "1.2.1.0";
|
||||||
|
|
||||||
private static int Main(string[] args){
|
private static int Main(string[] args){
|
||||||
SubProcess.EnableHighDPISupport();
|
SubProcess.EnableHighDPISupport();
|
||||||
@@ -30,9 +30,17 @@ namespace TweetDuck.Browser{
|
|||||||
base.OnBrowserCreated(wrapper);
|
base.OnBrowserCreated(wrapper);
|
||||||
|
|
||||||
using(Process me = Process.GetCurrentProcess()){
|
using(Process me = Process.GetCurrentProcess()){
|
||||||
Comms.BroadcastMessage(Comms.RegisterMessage("TweetDuckSubProcess"), (uint)me.Id, wrapper.BrowserId);
|
PostMessage(HWND_BROADCAST, RegisterWindowMessage("TweetDuckSubProcess"), new UIntPtr((uint)me.Id), new IntPtr(wrapper.BrowserId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern uint RegisterWindowMessage(string messageName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -34,12 +34,6 @@
|
|||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\lib\TweetLib.Communication\TweetLib.Communication.csproj">
|
|
||||||
<Project>{72473763-4b9d-4fb6-a923-9364b2680f06}</Project>
|
|
||||||
<Name>TweetLib.Communication</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
|
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
|
||||||
|
@@ -3,7 +3,7 @@ using System.Diagnostics.Contracts;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core;
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace UnitTests.Configuration{
|
namespace UnitTests.Configuration{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
@@ -76,7 +76,7 @@ namespace UnitTests.Configuration{
|
|||||||
cfg.Save();
|
cfg.Save();
|
||||||
|
|
||||||
cfg.ZoomLevel = 200;
|
cfg.ZoomLevel = 200;
|
||||||
Assert.IsTrue(cfg.Reload());
|
cfg.Reload();
|
||||||
Assert.AreEqual(123, cfg.ZoomLevel);
|
Assert.AreEqual(123, cfg.ZoomLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ namespace UnitTests.Configuration{
|
|||||||
cfg.Save();
|
cfg.Save();
|
||||||
|
|
||||||
File.Delete("reset");
|
File.Delete("reset");
|
||||||
Assert.IsTrue(cfg.Reload());
|
cfg.Reload();
|
||||||
Assert.AreEqual(100, cfg.ZoomLevel);
|
Assert.AreEqual(100, cfg.ZoomLevel);
|
||||||
Assert.IsTrue(File.Exists("reset"));
|
Assert.IsTrue(File.Exists("reset"));
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Video.Controls;
|
using TweetDuck.Video.Controls;
|
||||||
@@ -107,6 +108,7 @@ namespace TweetDuck.Video{
|
|||||||
Environment.Exit(Program.CODE_MEDIA_ERROR);
|
Environment.Exit(Program.CODE_MEDIA_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HandleProcessCorruptedStateExceptions]
|
||||||
private void timerSync_Tick(object sender, EventArgs e){
|
private void timerSync_Tick(object sender, EventArgs e){
|
||||||
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
|
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
|
||||||
int width = rect.Right-rect.Left+1;
|
int width = rect.Right-rect.Left+1;
|
||||||
@@ -139,9 +141,14 @@ namespace TweetDuck.Video{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (controls.currentPosition > media.duration){ // pausing near the end of the video causes WMP to play beyond the end of the video wtf
|
if (controls.currentPosition > media.duration){ // pausing near the end of the video causes WMP to play beyond the end of the video wtf
|
||||||
|
try{
|
||||||
controls.stop();
|
controls.stop();
|
||||||
controls.currentPosition = 0;
|
controls.currentPosition = 0;
|
||||||
controls.play();
|
controls.play();
|
||||||
|
}catch(AccessViolationException){
|
||||||
|
// something is super retarded here because shit gets disposed between the start of this method and
|
||||||
|
// the controls.play() call even though it runs on the UI thread
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCursorInside && !wasCursorInside){
|
if (isCursorInside && !wasCursorInside){
|
||||||
@@ -255,6 +262,7 @@ namespace TweetDuck.Video{
|
|||||||
Visible = false;
|
Visible = false;
|
||||||
pipe.Write("rip");
|
pipe.Write("rip");
|
||||||
|
|
||||||
|
Player.close();
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user