mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
a63e210b88 | |||
06bd65b7f8 | |||
b6c17eb05e | |||
a3d40fdc2b | |||
c064ef7a30 | |||
762717da1e | |||
b7d3758bea | |||
d20541fd24 | |||
2c2f860f26 | |||
d1db3aa673 | |||
cedc52cdf5 | |||
33f8eafbcf | |||
ad45cf8c72 | |||
f99d035621 | |||
f3072caea8 | |||
1410974292 | |||
44413fa96c | |||
342a4b4067 | |||
4356dde92d | |||
21e64a18d8 | |||
5a305a6740 | |||
44595bad40 | |||
7fc9edc9cb | |||
93e191f522 | |||
8d8355e792 | |||
a5379d290c | |||
caea8d4315 | |||
24224ab4c6 | |||
4dbc02360c | |||
aa7a29af0c | |||
296d0c6199 | |||
812a034e8d | |||
e9de789b79 | |||
cfbc1b9575 | |||
e39e85e4dd | |||
3f0b161cd0 | |||
ebe3868720 | |||
ffd0f5e986 | |||
217535a3ba | |||
7abfbea2da | |||
86ffeaac9a | |||
ab915b7115 | |||
705b5d38cf | |||
fc2acb00b3 | |||
5add8a1d0e | |||
063d3a2637 | |||
f1f90a2ee3 | |||
ed317a4e46 | |||
cca16f3bb1 | |||
aba156cb3b | |||
cd4e4d7095 | |||
8fbb639430 | |||
d5bf8ec558 | |||
b6cff40f1e | |||
833e42f455 | |||
8134843dad | |||
1f92d5e633 | |||
dc51c0ae85 | |||
45c79643d6 | |||
9041bfc627 | |||
0b3b3dd0be | |||
89e92dab59 | |||
8c168c9ad7 | |||
9f63357a92 | |||
d91b4bd1f3 | |||
c0c64f6d62 | |||
1a5d2af779 | |||
f40a33192b | |||
ca4900aff0 | |||
56fc9e2d40 | |||
d2174c0b69 | |||
9f76754ad3 | |||
118ceaec35 | |||
5a57d28a7d | |||
07af99f862 | |||
59fba7fba0 | |||
dd4edc4249 | |||
856226473a |
@@ -6,7 +6,6 @@ namespace TweetDuck.Configuration{
|
|||||||
// public args
|
// public args
|
||||||
public const string ArgDataFolder = "-datafolder";
|
public const string ArgDataFolder = "-datafolder";
|
||||||
public const string ArgLogging = "-log";
|
public const string ArgLogging = "-log";
|
||||||
public const string ArgDebugUpdates = "-debugupdates";
|
|
||||||
|
|
||||||
// internal args
|
// internal args
|
||||||
public const string ArgRestart = "-restart";
|
public const string ArgRestart = "-restart";
|
||||||
|
@@ -42,7 +42,6 @@ namespace TweetDuck.Configuration{
|
|||||||
public WindowState PluginsWindow { get; set; } = new WindowState();
|
public WindowState PluginsWindow { get; set; } = new WindowState();
|
||||||
|
|
||||||
public bool ExpandLinksOnHover { get; set; } = true;
|
public bool ExpandLinksOnHover { get; set; } = true;
|
||||||
public bool SwitchAccountSelectors { get; set; } = true;
|
|
||||||
public bool OpenSearchInFirstColumn { get; set; } = true;
|
public bool OpenSearchInFirstColumn { get; set; } = true;
|
||||||
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
|
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
|
||||||
public bool BestImageQuality { get; set; } = true;
|
public bool BestImageQuality { get; set; } = true;
|
||||||
@@ -51,6 +50,7 @@ namespace TweetDuck.Configuration{
|
|||||||
public bool IgnoreTrackingUrlWarning { get; set; } = false;
|
public bool IgnoreTrackingUrlWarning { get; set; } = false;
|
||||||
public bool EnableSmoothScrolling { get; set; } = true;
|
public bool EnableSmoothScrolling { get; set; } = true;
|
||||||
public string BrowserPath { get; set; } = null;
|
public string BrowserPath { get; set; } = null;
|
||||||
|
public string SearchEngineUrl { get; set; } = null;
|
||||||
private int _zoomLevel = 100;
|
private int _zoomLevel = 100;
|
||||||
private bool _muteNotifications;
|
private bool _muteNotifications;
|
||||||
|
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using TweetDuck.Core.Controls;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
|
||||||
sealed class CallbackBridge{
|
|
||||||
private readonly Control owner;
|
|
||||||
private readonly Action safeCallback;
|
|
||||||
|
|
||||||
public CallbackBridge(Control owner, Action safeCallback){
|
|
||||||
this.owner = owner;
|
|
||||||
this.safeCallback = safeCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Trigger(){
|
|
||||||
owner.InvokeSafe(safeCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -15,7 +15,6 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
|
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
|
||||||
|
|
||||||
if (environment == Environment.Browser){
|
if (environment == Environment.Browser){
|
||||||
build.Append("x.switchAccountSelectors=").Append(Bool(Program.UserConfig.SwitchAccountSelectors));
|
|
||||||
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
|
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
|
||||||
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(Program.UserConfig.KeepLikeFollowDialogsOpen));
|
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(Program.UserConfig.KeepLikeFollowDialogsOpen));
|
||||||
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
||||||
|
@@ -3,7 +3,7 @@ using System.Text;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
@@ -13,20 +13,12 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
class TweetDeckBridge{
|
class TweetDeckBridge{
|
||||||
public static string FontSize { get; private set; }
|
public static string FontSize { get; private set; }
|
||||||
public static string NotificationHeadLayout { get; private set; }
|
public static string NotificationHeadLayout { get; private set; }
|
||||||
|
public static readonly ContextInfo ContextInfo = new ContextInfo();
|
||||||
public static string LastHighlightedTweetUrl = string.Empty;
|
|
||||||
public static string LastHighlightedQuoteUrl = string.Empty;
|
|
||||||
private static string LastHighlightedTweetAuthors = string.Empty;
|
|
||||||
private static string LastHighlightedTweetImages = string.Empty;
|
|
||||||
|
|
||||||
public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
|
|
||||||
public static string[] LastHighlightedTweetImagesArray => LastHighlightedTweetImages.Split(';');
|
|
||||||
|
|
||||||
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(){
|
||||||
FontSize = NotificationHeadLayout = null;
|
FontSize = NotificationHeadLayout = null;
|
||||||
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RestoreSessionData(IFrame frame){
|
public static void RestoreSessionData(IFrame frame){
|
||||||
@@ -72,13 +64,8 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
|
public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||||
form.InvokeAsyncSafe(() => {
|
ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||||
LastHighlightedTweetUrl = tweetUrl;
|
|
||||||
LastHighlightedQuoteUrl = quoteUrl;
|
|
||||||
LastHighlightedTweetAuthors = authors;
|
|
||||||
LastHighlightedTweetImages = imageList;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisplayTooltip(string text){
|
public void DisplayTooltip(string text){
|
||||||
@@ -112,8 +99,8 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
|
|
||||||
// Global
|
// Global
|
||||||
|
|
||||||
public void SetLastRightClickInfo(string type, string link){
|
public void SetLastRightClickInfo(string type, string url){
|
||||||
form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
|
ContextInfo.SetLink(type, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
|
||||||
@@ -130,8 +117,8 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScreenshotTweet(string html, int width, int height){
|
public void ScreenshotTweet(string html, int width){
|
||||||
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
|
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayVideo(string url, string username){
|
public void PlayVideo(string url, string username){
|
||||||
|
@@ -12,8 +12,10 @@ using TweetDuck.Core.Other;
|
|||||||
using TweetDuck.Core.Other.Analytics;
|
using TweetDuck.Core.Other.Analytics;
|
||||||
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.Updates;
|
using TweetDuck.Updates;
|
||||||
|
using TweetDuck.Updates.Events;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
||||||
@@ -37,6 +39,7 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string UpdateInstallerPath { get; private set; }
|
public string UpdateInstallerPath { get; private set; }
|
||||||
|
private bool ignoreUpdateCheckError;
|
||||||
|
|
||||||
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
|
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
|
||||||
|
|
||||||
@@ -58,10 +61,7 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
Text = Program.BrandName;
|
Text = Program.BrandName;
|
||||||
|
|
||||||
this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification));
|
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
||||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
|
||||||
|
|
||||||
this.plugins = new PluginManager(browser, 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.Reload();
|
this.plugins.Reload();
|
||||||
@@ -69,6 +69,11 @@ namespace TweetDuck.Core{
|
|||||||
this.notification = new FormNotificationTweet(this, plugins);
|
this.notification = new FormNotificationTweet(this, plugins);
|
||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
|
|
||||||
|
this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification));
|
||||||
|
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
||||||
|
|
||||||
|
this.plugins.Register(browser, PluginEnvironment.Browser, true);
|
||||||
|
|
||||||
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) => {
|
||||||
@@ -76,6 +81,7 @@ namespace TweetDuck.Core{
|
|||||||
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
||||||
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
|
updates.Dispose();
|
||||||
contextMenu.Dispose();
|
contextMenu.Dispose();
|
||||||
|
|
||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
@@ -92,6 +98,7 @@ namespace TweetDuck.Core{
|
|||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, updaterSettings);
|
this.updates = new UpdateHandler(browser, updaterSettings);
|
||||||
|
this.updates.CheckFinished += updates_CheckFinished;
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||||
|
|
||||||
@@ -229,6 +236,28 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
||||||
|
this.InvokeAsyncSafe(() => {
|
||||||
|
e.Result.Handle(update => {
|
||||||
|
if (!update.IsUpdateDismissed){
|
||||||
|
if (update.IsUpdateNew){
|
||||||
|
browser.ShowUpdateNotification(update.VersionTag, update.ReleaseNotes);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
updates.StartTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, ex => {
|
||||||
|
if (!ignoreUpdateCheckError){
|
||||||
|
Program.Reporter.HandleException("Update Check Error", "An error occurred while checking for updates.", true, ex);
|
||||||
|
updates.StartTimer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ignoreUpdateCheckError = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
FormManager.CloseAllDialogs();
|
FormManager.CloseAllDialogs();
|
||||||
@@ -239,11 +268,19 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
||||||
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
UpdateDownloadStatus status = update.DownloadStatus;
|
||||||
UpdateInstallerPath = update.InstallerPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (status == UpdateDownloadStatus.Done){
|
||||||
|
UpdateInstallerPath = update.InstallerPath;
|
||||||
ForceClose();
|
ForceClose();
|
||||||
|
}
|
||||||
|
else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: "+(update.DownloadError?.Message ?? "unknown error")+"\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
BrowserUtils.OpenExternalBrowser(Program.Website);
|
||||||
|
ForceClose();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Show();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -294,10 +331,19 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadToTweetDeck(){
|
public void ReloadToTweetDeck(){
|
||||||
|
#if DEBUG
|
||||||
|
Resources.ScriptLoader.HotSwap();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ignoreUpdateCheckError = false;
|
||||||
browser.ReloadToTweetDeck();
|
browser.ReloadToTweetDeck();
|
||||||
AnalyticsFile.BrowserReloads.Trigger();
|
AnalyticsFile.BrowserReloads.Trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddSearchColumn(string query){
|
||||||
|
browser.AddSearchColumn(query);
|
||||||
|
}
|
||||||
|
|
||||||
public void TriggerTweetScreenshot(){
|
public void TriggerTweetScreenshot(){
|
||||||
browser.TriggerTweetScreenshot();
|
browser.TriggerTweetScreenshot();
|
||||||
}
|
}
|
||||||
@@ -452,12 +498,12 @@ namespace TweetDuck.Core{
|
|||||||
AnalyticsFile.TweetDetails.Trigger();
|
AnalyticsFile.TweetDetails.Trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetScreenshotReady(string html, int width, int height){
|
public void OnTweetScreenshotReady(string html, int width){
|
||||||
if (notificationScreenshotManager == null){
|
if (notificationScreenshotManager == null){
|
||||||
notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
|
notificationScreenshotManager = new TweetScreenshotManager(this, plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationScreenshotManager.Trigger(html, width, height);
|
notificationScreenshotManager.Trigger(html, width);
|
||||||
AnalyticsFile.TweetScreenshots.Trigger();
|
AnalyticsFile.TweetScreenshots.Trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,15 +3,15 @@ using System.IO;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Bridge;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Other.Analytics;
|
using TweetDuck.Core.Other.Analytics;
|
||||||
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
abstract class ContextMenuBase : IContextMenuHandler{
|
||||||
@@ -19,19 +19,6 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
||||||
|
|
||||||
private static KeyValuePair<string, string> ContextInfo;
|
|
||||||
private static bool IsLink => ContextInfo.Key == "link";
|
|
||||||
private static bool IsImage => ContextInfo.Key == "image";
|
|
||||||
private static bool IsVideo => ContextInfo.Key == "video";
|
|
||||||
|
|
||||||
public static void SetContextInfo(string type, string link){
|
|
||||||
ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetMediaLink(IContextMenuParams parameters){
|
|
||||||
return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -40,10 +27,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505;
|
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505;
|
||||||
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
|
||||||
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
|
||||||
|
private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508;
|
||||||
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||||
|
|
||||||
private string[] lastHighlightedTweetAuthors;
|
protected ContextInfo.LinkInfo LastLink { get; private set; }
|
||||||
private string[] lastHighlightedTweetImageList;
|
protected ContextInfo.ChirpInfo LastChirp { get; private set; }
|
||||||
|
|
||||||
private readonly AnalyticsFile.IProvider analytics;
|
private readonly AnalyticsFile.IProvider analytics;
|
||||||
|
|
||||||
@@ -51,19 +39,28 @@ namespace TweetDuck.Core.Handling{
|
|||||||
this.analytics = analytics;
|
this.analytics = analytics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
private void ResetContextInfo(){
|
||||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
LastLink = default(ContextInfo.LinkInfo);
|
||||||
lastHighlightedTweetAuthors = StringUtils.EmptyArray;
|
LastChirp = default(ContextInfo.ChirpInfo);
|
||||||
lastHighlightedTweetImageList = StringUtils.EmptyArray;
|
TweetDeckBridge.ContextInfo.Reset();
|
||||||
ContextInfo = default(KeyValuePair<string, string>);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
|
|
||||||
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasTweetImage = IsImage;
|
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
bool hasTweetVideo = IsVideo;
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
|
ResetContextInfo();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
LastLink = TweetDeckBridge.ContextInfo.Link;
|
||||||
|
LastChirp = TweetDeckBridge.ContextInfo.Chirp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
||||||
|
model.AddItem(MenuSearchInBrowser, "Search in browser");
|
||||||
|
model.AddSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasTweetImage = LastLink.IsImage;
|
||||||
|
bool hasTweetVideo = LastLink.IsVideo;
|
||||||
|
|
||||||
string TextOpen(string name) => "Open "+name+" in browser";
|
string TextOpen(string name) => "Open "+name+" in browser";
|
||||||
string TextCopy(string name) => "Copy "+name+" address";
|
string TextCopy(string name) => "Copy "+name+" address";
|
||||||
@@ -95,7 +92,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
||||||
model.AddItem(MenuSaveMedia, TextSave("image"));
|
model.AddItem(MenuSaveMedia, TextSave("image"));
|
||||||
|
|
||||||
if (lastHighlightedTweetImageList.Length > 1){
|
if (LastChirp.Images.Length > 1){
|
||||||
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +105,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
switch(commandId){
|
switch(commandId){
|
||||||
case MenuOpenLinkUrl:
|
case MenuOpenLinkUrl:
|
||||||
OpenBrowser(control, IsLink ? ContextInfo.Value : parameters.LinkUrl);
|
OpenBrowser(control, LastLink.GetUrl(parameters, true));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyLinkUrl:
|
case MenuCopyLinkUrl:
|
||||||
SetClipboardText(control, IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
|
SetClipboardText(control, LastLink.GetUrl(parameters, false));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyUsername:
|
case MenuCopyUsername:
|
||||||
@@ -122,35 +119,37 @@ namespace TweetDuck.Core.Handling{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuOpenMediaUrl:
|
case MenuOpenMediaUrl:
|
||||||
OpenBrowser(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
OpenBrowser(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyMediaUrl:
|
case MenuCopyMediaUrl:
|
||||||
SetClipboardText(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
|
SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuViewImage:
|
case MenuViewImage:
|
||||||
string url = GetMediaLink(parameters);
|
void ViewImage(string path){
|
||||||
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
|
string ext = Path.GetExtension(path);
|
||||||
|
|
||||||
void ViewFile(){
|
|
||||||
string ext = Path.GetExtension(file);
|
|
||||||
|
|
||||||
if (TwitterUtils.ValidImageExtensions.Contains(ext)){
|
if (TwitterUtils.ValidImageExtensions.Contains(ext)){
|
||||||
WindowsUtils.OpenAssociatedProgram(file);
|
WindowsUtils.OpenAssociatedProgram(path);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
|
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string url = LastLink.GetMediaSource(parameters);
|
||||||
|
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName());
|
||||||
|
|
||||||
if (File.Exists(file)){
|
if (File.Exists(file)){
|
||||||
ViewFile();
|
ViewImage(file);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger);
|
control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger);
|
||||||
|
|
||||||
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => {
|
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
|
||||||
|
ViewImage(file);
|
||||||
|
}, ex => {
|
||||||
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,20 +157,26 @@ namespace TweetDuck.Core.Handling{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuSaveMedia:
|
case MenuSaveMedia:
|
||||||
if (IsVideo){
|
if (LastLink.IsVideo){
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger);
|
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger);
|
||||||
TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
|
TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault());
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
|
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
|
||||||
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuSaveTweetImages:
|
case MenuSaveTweetImages:
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
|
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
|
||||||
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
|
TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MenuSearchInBrowser:
|
||||||
|
string query = parameters.SelectionText;
|
||||||
|
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalSearch(query));
|
||||||
|
DeselectAll(frame);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuOpenDevTools:
|
case MenuOpenDevTools:
|
||||||
@@ -183,13 +188,17 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
||||||
ContextInfo = default(KeyValuePair<string, string>);
|
ResetContextInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void DeselectAll(IFrame frame){
|
||||||
|
ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect");
|
||||||
|
}
|
||||||
|
|
||||||
protected void OpenBrowser(Control control, string url){
|
protected void OpenBrowser(Control control, string url){
|
||||||
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
|
||||||
}
|
}
|
||||||
@@ -198,6 +207,10 @@ namespace TweetDuck.Core.Handling{
|
|||||||
control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void InsertSelectionSearchItem(IMenuModel model, CefMenuCommand insertCommand, string insertLabel){
|
||||||
|
model.InsertItemAt(model.GetIndexOf(MenuSearchInBrowser)+1, insertCommand, insertLabel);
|
||||||
|
}
|
||||||
|
|
||||||
protected static void AddDebugMenuItems(IMenuModel model){
|
protected static void AddDebugMenuItems(IMenuModel model){
|
||||||
model.AddItem(MenuOpenDevTools, "Open dev tools");
|
model.AddItem(MenuOpenDevTools, "Open dev tools");
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Bridge;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
@@ -18,6 +17,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
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 CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615;
|
||||||
|
private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616;
|
||||||
|
|
||||||
private const string TitleReloadBrowser = "Reload browser";
|
private const string TitleReloadBrowser = "Reload browser";
|
||||||
private const string TitleMuteNotifications = "Mute notifications";
|
private const string TitleMuteNotifications = "Mute notifications";
|
||||||
@@ -27,22 +27,22 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
private string lastHighlightedTweetUrl;
|
|
||||||
private string lastHighlightedQuoteUrl;
|
|
||||||
|
|
||||||
public ContextMenuBrowser(FormBrowser form) : base(form){
|
public ContextMenuBrowser(FormBrowser form) : base(form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
|
||||||
|
bool isSelecting = parameters.TypeFlags.HasFlag(ContextMenuType.Selection);
|
||||||
|
bool isEditing = parameters.TypeFlags.HasFlag(ContextMenuType.Editable);
|
||||||
|
|
||||||
model.Remove(CefMenuCommand.Back);
|
model.Remove(CefMenuCommand.Back);
|
||||||
model.Remove(CefMenuCommand.Forward);
|
model.Remove(CefMenuCommand.Forward);
|
||||||
model.Remove(CefMenuCommand.Print);
|
model.Remove(CefMenuCommand.Print);
|
||||||
model.Remove(CefMenuCommand.ViewSource);
|
model.Remove(CefMenuCommand.ViewSource);
|
||||||
RemoveSeparatorIfLast(model);
|
RemoveSeparatorIfLast(model);
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
|
if (isSelecting){
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
if (isEditing){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
model.AddItem(MenuInputApplyROT13, "Apply ROT13");
|
model.AddItem(MenuInputApplyROT13, "Apply ROT13");
|
||||||
}
|
}
|
||||||
@@ -52,20 +52,16 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||||
|
|
||||||
lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
|
if (isSelecting && !isEditing && TwitterUtils.IsTweetDeckWebsite(frame)){
|
||||||
lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
|
InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column");
|
||||||
|
|
||||||
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
|
||||||
lastHighlightedTweetUrl = string.Empty;
|
|
||||||
lastHighlightedQuoteUrl = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if (!string.IsNullOrEmpty(LastChirp.TweetUrl) && !isSelecting && !isEditing){
|
||||||
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
||||||
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||||
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
|
if (!string.IsNullOrEmpty(LastChirp.QuoteUrl)){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||||
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||||
@@ -74,7 +70,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
if (!isSelecting && !isEditing){
|
||||||
AddSeparator(model);
|
AddSeparator(model);
|
||||||
|
|
||||||
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName);
|
IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName);
|
||||||
@@ -126,11 +122,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenTweetUrl:
|
case MenuOpenTweetUrl:
|
||||||
OpenBrowser(form, lastHighlightedTweetUrl);
|
OpenBrowser(form, LastChirp.TweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
SetClipboardText(form, lastHighlightedTweetUrl);
|
SetClipboardText(form, LastChirp.TweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuScreenshotTweet:
|
case MenuScreenshotTweet:
|
||||||
@@ -138,16 +134,22 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenQuotedTweetUrl:
|
case MenuOpenQuotedTweetUrl:
|
||||||
OpenBrowser(form, lastHighlightedQuoteUrl);
|
OpenBrowser(form, LastChirp.QuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
SetClipboardText(form, lastHighlightedQuoteUrl);
|
SetClipboardText(form, LastChirp.QuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuInputApplyROT13:
|
case MenuInputApplyROT13:
|
||||||
form.InvokeAsyncSafe(form.ApplyROT13);
|
form.InvokeAsyncSafe(form.ApplyROT13);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case MenuSearchInColumn:
|
||||||
|
string query = parameters.SelectionText;
|
||||||
|
form.InvokeAsyncSafe(() => form.AddSearchColumn(query));
|
||||||
|
DeselectAll(frame);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
@@ -28,7 +27,7 @@ namespace TweetDuck.Core.Handling.General{
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
|
bool IJsDialogHandler.OnJSDialog(IWebBrowser browserControl, IBrowser browser, string originUrl, CefJsDialogType dialogType, string messageText, string defaultPromptText, IJsDialogCallback callback, ref bool suppressMessage){
|
||||||
((ChromiumWebBrowser)browserControl).InvokeSafe(() => {
|
browserControl.AsControl().InvokeSafe(() => {
|
||||||
FormMessage form;
|
FormMessage form;
|
||||||
TextBox input = null;
|
TextBox input = null;
|
||||||
|
|
||||||
|
68
Core/Management/ContextInfo.cs
Normal file
68
Core/Management/ContextInfo.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Management{
|
||||||
|
sealed class ContextInfo{
|
||||||
|
public LinkInfo Link { get; private set; }
|
||||||
|
public ChirpInfo Chirp { get; private set; }
|
||||||
|
|
||||||
|
public ContextInfo(){
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLink(string type, string url){
|
||||||
|
Link = string.IsNullOrEmpty(url) ? new LinkInfo() : new LinkInfo(type, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||||
|
Chirp = new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset(){
|
||||||
|
Link = new LinkInfo();
|
||||||
|
Chirp = new ChirpInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data structures
|
||||||
|
|
||||||
|
public struct LinkInfo{
|
||||||
|
public bool IsLink => type == "link";
|
||||||
|
public bool IsImage => type == "image";
|
||||||
|
public bool IsVideo => type == "video";
|
||||||
|
|
||||||
|
public string GetUrl(IContextMenuParams parameters, bool safe){
|
||||||
|
return IsLink ? url : (safe ? parameters.LinkUrl : parameters.UnfilteredLinkUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMediaSource(IContextMenuParams parameters){
|
||||||
|
return IsImage || IsVideo ? url : parameters.SourceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly string type;
|
||||||
|
private readonly string url;
|
||||||
|
|
||||||
|
public LinkInfo(string type, string url){
|
||||||
|
this.type = type;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ChirpInfo{
|
||||||
|
public string TweetUrl { get; }
|
||||||
|
public string QuoteUrl { get; }
|
||||||
|
|
||||||
|
public string[] Authors => chirpAuthors?.Split(';') ?? StringUtils.EmptyArray;
|
||||||
|
public string[] Images => chirpImages?.Split(';') ?? StringUtils.EmptyArray;
|
||||||
|
|
||||||
|
private readonly string chirpAuthors;
|
||||||
|
private readonly string chirpImages;
|
||||||
|
|
||||||
|
public ChirpInfo(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||||
|
this.TweetUrl = tweetUrl;
|
||||||
|
this.QuoteUrl = quoteUrl;
|
||||||
|
this.chirpAuthors = chirpAuthors;
|
||||||
|
this.chirpImages = chirpImages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -23,7 +23,6 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRestarting { get; private set; }
|
public bool IsRestarting { get; private set; }
|
||||||
public Exception LastException { get; private set; }
|
|
||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
@@ -67,7 +66,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
LastException = e;
|
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,8 +99,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception){
|
||||||
LastException = e;
|
|
||||||
items = Items.None;
|
items = Items.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,12 +162,12 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (missingPlugins.Count > 0){
|
if (missingPlugins.Count > 0){
|
||||||
FormMessage.Information("Importing TweetDuck Profile", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
|
FormMessage.Information("Profile Import", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
LastException = e;
|
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing TweetDuck profile.", true, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,15 +197,23 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
private static IEnumerable<PathInfo> EnumerateFilesRelative(string root){
|
||||||
return Directory.Exists(root) ? Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo{
|
if (Directory.Exists(root)){
|
||||||
Full = fullPath,
|
int rootLength = root.Length;
|
||||||
Relative = fullPath.Substring(root.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) // strip leading separator character
|
return Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories).Select(fullPath => new PathInfo(fullPath, rootLength));
|
||||||
}) : Enumerable.Empty<PathInfo>();
|
}
|
||||||
|
else{
|
||||||
|
return Enumerable.Empty<PathInfo>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class PathInfo{
|
private sealed class PathInfo{
|
||||||
public string Full { get; set; }
|
public string Full { get; }
|
||||||
public string Relative { get; set; }
|
public string Relative { get; }
|
||||||
|
|
||||||
|
public PathInfo(string fullPath, int rootLength){
|
||||||
|
this.Full = fullPath;
|
||||||
|
this.Relative = fullPath.Substring(rootLength).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); // strip leading separator character
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,25 +9,13 @@ using TweetLib.Communication;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Management{
|
namespace TweetDuck.Core.Management{
|
||||||
sealed class VideoPlayer : IDisposable{
|
sealed class VideoPlayer : IDisposable{
|
||||||
public bool Running{
|
public bool Running => currentInstance != null && currentInstance.Running;
|
||||||
get{
|
|
||||||
if (currentProcess == null){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentProcess.Refresh();
|
|
||||||
return !currentProcess.HasExited;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler ProcessExited;
|
public event EventHandler ProcessExited;
|
||||||
|
|
||||||
private readonly FormBrowser owner;
|
private readonly FormBrowser owner;
|
||||||
private string lastUrl;
|
|
||||||
private string lastUsername;
|
|
||||||
|
|
||||||
private Process currentProcess;
|
private Instance currentInstance;
|
||||||
private DuplexPipe.Server currentPipe;
|
|
||||||
private bool isClosing;
|
private bool isClosing;
|
||||||
|
|
||||||
public VideoPlayer(FormBrowser owner){
|
public VideoPlayer(FormBrowser owner){
|
||||||
@@ -41,37 +29,42 @@ namespace TweetDuck.Core.Management{
|
|||||||
isClosing = false;
|
isClosing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastUrl = url;
|
|
||||||
lastUsername = username;
|
|
||||||
|
|
||||||
try{
|
try{
|
||||||
currentPipe = DuplexPipe.CreateServer();
|
DuplexPipe.Server pipe = DuplexPipe.CreateServer();
|
||||||
currentPipe.DataIn += currentPipe_DataIn;
|
pipe.DataIn += pipe_DataIn;
|
||||||
|
|
||||||
if ((currentProcess = Process.Start(new ProcessStartInfo{
|
Process process;
|
||||||
|
|
||||||
|
if ((process = Process.Start(new ProcessStartInfo{
|
||||||
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
||||||
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
|
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
})) != null){
|
})) != null){
|
||||||
currentProcess.EnableRaisingEvents = true;
|
currentInstance = new Instance(process, pipe, url, username);
|
||||||
currentProcess.Exited += process_Exited;
|
|
||||||
|
|
||||||
currentProcess.BeginOutputReadLine();
|
process.EnableRaisingEvents = true;
|
||||||
currentProcess.OutputDataReceived += process_OutputDataReceived;
|
process.Exited += process_Exited;
|
||||||
|
|
||||||
|
process.BeginOutputReadLine();
|
||||||
|
process.OutputDataReceived += process_OutputDataReceived;
|
||||||
|
|
||||||
|
pipe.DisposeToken();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pipe.DataIn -= pipe_DataIn;
|
||||||
|
pipe.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPipe.DisposeToken();
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
|
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendKeyEvent(Keys key){
|
public void SendKeyEvent(Keys key){
|
||||||
currentPipe?.Write("key", ((int)key).ToString());
|
currentInstance?.Pipe.Write("key", ((int)key).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
|
||||||
owner.InvokeSafe(() => {
|
owner.InvokeSafe(() => {
|
||||||
switch(e.Key){
|
switch(e.Key){
|
||||||
case "vol":
|
case "vol":
|
||||||
@@ -83,16 +76,16 @@ namespace TweetDuck.Core.Management{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "download":
|
case "download":
|
||||||
|
if (currentInstance != null){
|
||||||
owner.AnalyticsFile.DownloadedVideos.Trigger();
|
owner.AnalyticsFile.DownloadedVideos.Trigger();
|
||||||
TwitterUtils.DownloadVideo(lastUrl, lastUsername);
|
TwitterUtils.DownloadVideo(currentInstance.Url, currentInstance.Username);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "rip":
|
case "rip":
|
||||||
currentPipe.Dispose();
|
currentInstance?.Dispose();
|
||||||
currentPipe = null;
|
currentInstance = null;
|
||||||
|
|
||||||
currentProcess.Dispose();
|
|
||||||
currentProcess = null;
|
|
||||||
|
|
||||||
isClosing = false;
|
isClosing = false;
|
||||||
TriggerProcessExitEventUnsafe();
|
TriggerProcessExitEventUnsafe();
|
||||||
@@ -102,15 +95,15 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Close(){
|
public void Close(){
|
||||||
if (currentProcess != null){
|
if (currentInstance != null){
|
||||||
if (isClosing){
|
if (isClosing){
|
||||||
Destroy();
|
Destroy();
|
||||||
isClosing = false;
|
isClosing = false;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
isClosing = true;
|
isClosing = true;
|
||||||
currentProcess.Exited -= process_Exited;
|
currentInstance.Process.Exited -= process_Exited;
|
||||||
currentPipe.Write("die");
|
currentInstance.Pipe.Write("die");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,26 +116,17 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Destroy(){
|
private void Destroy(){
|
||||||
if (currentProcess != null){
|
if (currentInstance != null){
|
||||||
try{
|
currentInstance.KillAndDispose();
|
||||||
currentProcess.Kill();
|
currentInstance = null;
|
||||||
}catch{
|
|
||||||
// kill me instead then
|
|
||||||
}
|
|
||||||
|
|
||||||
currentProcess.Dispose();
|
|
||||||
currentProcess = null;
|
|
||||||
|
|
||||||
currentPipe.Dispose();
|
|
||||||
currentPipe = null;
|
|
||||||
|
|
||||||
TriggerProcessExitEventUnsafe();
|
TriggerProcessExitEventUnsafe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void owner_FormClosing(object sender, FormClosingEventArgs e){
|
private void owner_FormClosing(object sender, FormClosingEventArgs e){
|
||||||
if (currentProcess != null){
|
if (currentInstance != null){
|
||||||
currentProcess.Exited -= process_Exited;
|
currentInstance.Process.Exited -= process_Exited;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,25 +137,27 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void process_Exited(object sender, EventArgs e){
|
private void process_Exited(object sender, EventArgs e){
|
||||||
int exitCode = currentProcess.ExitCode;
|
if (currentInstance == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
currentProcess.Dispose();
|
int exitCode = currentInstance.Process.ExitCode;
|
||||||
currentProcess = null;
|
string url = currentInstance.Url;
|
||||||
|
|
||||||
currentPipe.Dispose();
|
currentInstance.Dispose();
|
||||||
currentPipe = null;
|
currentInstance = null;
|
||||||
|
|
||||||
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 your 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(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 your 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(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -183,5 +169,42 @@ namespace TweetDuck.Core.Management{
|
|||||||
private void TriggerProcessExitEventUnsafe(){
|
private void TriggerProcessExitEventUnsafe(){
|
||||||
ProcessExited?.Invoke(this, EventArgs.Empty);
|
ProcessExited?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class Instance : IDisposable{
|
||||||
|
public bool Running{
|
||||||
|
get{
|
||||||
|
Process.Refresh();
|
||||||
|
return !Process.HasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Process Process { get; }
|
||||||
|
public DuplexPipe.Server Pipe { get; }
|
||||||
|
|
||||||
|
public string Url { get; }
|
||||||
|
public string Username { get; }
|
||||||
|
|
||||||
|
public Instance(Process process, DuplexPipe.Server pipe, string url, string username){
|
||||||
|
this.Process = process;
|
||||||
|
this.Pipe = pipe;
|
||||||
|
this.Url = url;
|
||||||
|
this.Username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void KillAndDispose(){
|
||||||
|
try{
|
||||||
|
Process.Kill();
|
||||||
|
}catch{
|
||||||
|
// kill me instead then
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
Process.Dispose();
|
||||||
|
Pipe.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ namespace TweetDuck.Core.Notification.Example{
|
|||||||
private readonly TweetNotification exampleNotification;
|
private readonly TweetNotification exampleNotification;
|
||||||
|
|
||||||
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
|
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
|
||||||
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true).Replace("{avatar}", TweetNotification.AppLogo.Url);
|
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true)?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
||||||
|
@@ -135,12 +135,6 @@ namespace TweetDuck.Core.Notification{
|
|||||||
this.browser.ClientSize = ClientSize;
|
this.browser.ClientSize = ClientSize;
|
||||||
this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
|
this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DpiScale = this.GetDPIScale();
|
|
||||||
|
|
||||||
browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
||||||
browser.SetupResourceHandler(TweetNotification.AppLogo);
|
browser.SetupResourceHandler(TweetNotification.AppLogo);
|
||||||
|
|
||||||
@@ -151,6 +145,8 @@ namespace TweetDuck.Core.Notification{
|
|||||||
this.owner.FormClosed -= owner_FormClosed;
|
this.owner.FormClosed -= owner_FormClosed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DpiScale = this.GetDPIScale();
|
||||||
|
|
||||||
// ReSharper disable once VirtualMemberCallInContructor
|
// ReSharper disable once VirtualMemberCallInContructor
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
}
|
}
|
||||||
@@ -200,7 +196,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string GetTweetHTML(TweetNotification tweet){
|
protected virtual string GetTweetHTML(TweetNotification tweet){
|
||||||
return tweet.GenerateHtml(IsCursorOverBrowser ? "td-hover" : string.Empty);
|
return tweet.GenerateHtml(IsCursorOverBrowser ? "td-notification td-hover" : "td-notification");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void LoadTweet(TweetNotification tweet){
|
protected virtual void LoadTweet(TweetNotification tweet){
|
||||||
@@ -221,8 +217,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ShowTweetDetail(){
|
public void ShowTweetDetail(){
|
||||||
|
if (currentNotification != null){
|
||||||
owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl);
|
owner.ShowTweetDetail(currentNotification.ColumnId, currentNotification.ChirpId, currentNotification.TweetUrl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void MoveToVisibleLocation(){
|
public void MoveToVisibleLocation(){
|
||||||
bool needsReactivating = Location == ControlExtensions.InvisibleLocation;
|
bool needsReactivating = Location == ControlExtensions.InvisibleLocation;
|
||||||
|
@@ -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.Interfaces;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
@@ -12,7 +13,7 @@ using TweetDuck.Plugins.Enums;
|
|||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
abstract partial class FormNotificationMain : FormNotificationBase{
|
abstract partial class FormNotificationMain : FormNotificationBase, ITweetDeckBrowser{
|
||||||
private const string NotificationScriptFile = "notification.js";
|
private const string NotificationScriptFile = "notification.js";
|
||||||
|
|
||||||
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
|
||||||
@@ -81,17 +82,39 @@ namespace TweetDuck.Core.Notification{
|
|||||||
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
||||||
|
|
||||||
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||||
|
|
||||||
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this));
|
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this));
|
||||||
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
|
||||||
|
|
||||||
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||||
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||||
|
|
||||||
|
plugins.Register(this, PluginEnvironment.Notification);
|
||||||
|
|
||||||
mouseHookDelegate = MouseHookProc;
|
mouseHookDelegate = MouseHookProc;
|
||||||
Disposed += (sender, args) => StopMouseHook(true);
|
Disposed += (sender, args) => StopMouseHook(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implementation of ITweetDeckBrowser
|
||||||
|
|
||||||
|
bool ITweetDeckBrowser.IsTweetDeckWebsite => IsNotificationVisible;
|
||||||
|
|
||||||
|
void ITweetDeckBrowser.RegisterBridge(string name, object obj){
|
||||||
|
browser.RegisterAsyncJsObject(name, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){
|
||||||
|
browser.FrameLoadEnd += (sender, args) => {
|
||||||
|
IFrame frame = args.Frame;
|
||||||
|
|
||||||
|
if (frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
||||||
|
callback(frame);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){
|
||||||
|
browser.ExecuteScriptAsync(name, args);
|
||||||
|
}
|
||||||
|
|
||||||
// mouse wheel hook
|
// mouse wheel hook
|
||||||
|
|
||||||
private void StartMouseHook(){
|
private void StartMouseHook(){
|
||||||
@@ -166,7 +189,6 @@ namespace TweetDuck.Core.Notification{
|
|||||||
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
|
||||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||||
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
|
||||||
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +254,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
protected override string GetTweetHTML(TweetNotification tweet){
|
protected override string GetTweetHTML(TweetNotification tweet){
|
||||||
string html = base.GetTweetHTML(tweet);
|
string html = base.GetTweetHTML(tweet);
|
||||||
|
|
||||||
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
|
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||||
html = injection.Inject(html);
|
html = injection.Inject(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ using System.Drawing;
|
|||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
@@ -15,37 +15,55 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
protected override bool CanDragWindow => false;
|
protected override bool CanDragWindow => false;
|
||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
|
private readonly int width;
|
||||||
|
|
||||||
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
|
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager, string html, int width) : base(owner, false){
|
||||||
this.plugins = pluginManager;
|
this.plugins = pluginManager;
|
||||||
|
this.width = width;
|
||||||
|
|
||||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
|
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback));
|
||||||
|
|
||||||
browser.LoadingStateChanged += (sender, args) => {
|
browser.LoadingStateChanged += (sender, args) => {
|
||||||
if (!args.IsLoading){
|
if (args.IsLoading){
|
||||||
using(IFrame frame = args.Browser.MainFrame){
|
return;
|
||||||
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, document.getElementsByTagName('iframe').length ? 267 : 67)", "gen:screenshot");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string script = ScriptLoader.LoadResource("screenshot.js", true);
|
||||||
|
|
||||||
|
if (script == null){
|
||||||
|
this.InvokeAsyncSafe(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using(IFrame frame = args.Browser.MainFrame){
|
||||||
|
ScriptLoader.ExecuteScript(frame, script.Replace("{width}", ClientSize.Width.ToString()), "screenshot");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SetScreenshotHeight(1);
|
||||||
|
LoadTweet(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetTweetHTML(TweetNotification tweet){
|
protected override string GetTweetHTML(TweetNotification tweet){
|
||||||
string html = tweet.GenerateHtml("td-screenshot", false);
|
string html = tweet.GenerateHtml("td-screenshot");
|
||||||
|
|
||||||
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
|
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||||
html = injection.Inject(html);
|
html = injection.Inject(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
|
private void SetScreenshotHeight(int height){
|
||||||
LoadTweet(tweet);
|
|
||||||
SetNotificationSize(width, height);
|
SetNotificationSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TakeScreenshot(){
|
public void TakeScreenshot(){
|
||||||
|
if (ClientSize.Height == 0){
|
||||||
|
FormMessage.Error("Screenshot Failed", "Could not detect screenshot size.", FormMessage.OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr context = NativeMethods.GetDC(this.Handle);
|
IntPtr context = NativeMethods.GetDC(this.Handle);
|
||||||
|
|
||||||
if (context == IntPtr.Zero){
|
if (context == IntPtr.Zero){
|
||||||
|
26
Core/Notification/Screenshot/ScreenshotBridge.cs
Normal file
26
Core/Notification/Screenshot/ScreenshotBridge.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Notification.Screenshot{
|
||||||
|
sealed class ScreenshotBridge{
|
||||||
|
private readonly Control owner;
|
||||||
|
|
||||||
|
private readonly Action<int> safeSetHeight;
|
||||||
|
private readonly Action safeTriggerScreenshot;
|
||||||
|
|
||||||
|
public ScreenshotBridge(Control owner, Action<int> safeSetHeight, Action safeTriggerScreenshot){
|
||||||
|
this.owner = owner;
|
||||||
|
this.safeSetHeight = safeSetHeight;
|
||||||
|
this.safeTriggerScreenshot = safeTriggerScreenshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetHeight(int tweetHeight){
|
||||||
|
owner.InvokeSafe(() => safeSetHeight(tweetHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerScreenshot(){
|
||||||
|
owner.InvokeSafe(safeTriggerScreenshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -37,13 +37,12 @@ namespace TweetDuck.Core.Notification.Screenshot{
|
|||||||
screenshot = null;
|
screenshot = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Trigger(string html, int width, int height){
|
public void Trigger(string html, int width){
|
||||||
if (screenshot != null){
|
if (screenshot != null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins);
|
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins, html, width);
|
||||||
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();
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ using TweetDuck.Resources;
|
|||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
sealed class TweetNotification{
|
sealed class TweetNotification{
|
||||||
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 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 static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
|
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty;
|
||||||
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
||||||
|
|
||||||
public static TweetNotification Example(string html, int characters){
|
public static TweetNotification Example(string html, int characters){
|
||||||
@@ -53,18 +53,15 @@ namespace TweetDuck.Core.Notification{
|
|||||||
return 2000+Math.Max(1000, value*characters);
|
return 2000+Math.Max(1000, value*characters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
|
public string GenerateHtml(string bodyClasses){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
build.Append("<!DOCTYPE html>");
|
build.Append("<!DOCTYPE html>");
|
||||||
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
|
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
|
||||||
|
|
||||||
if (enableCustomCSS){
|
|
||||||
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
|
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
|
||||||
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
|
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
build.Append("</head>");
|
build.Append("</head>");
|
||||||
build.Append("<body class='scroll-styled-v");
|
build.Append("<body class='scroll-styled-v");
|
||||||
|
@@ -21,7 +21,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly AnalyticsFile Dummy = new AnalyticsFile(null);
|
public static readonly AnalyticsFile Dummy = new AnalyticsFile();
|
||||||
|
|
||||||
// STATE PROPERTIES
|
// STATE PROPERTIES
|
||||||
|
|
||||||
@@ -67,6 +67,10 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AnalyticsFile(){
|
||||||
|
this.file = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void SetupProperties(){
|
private void SetupProperties(){
|
||||||
foreach(Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter)prop.GetValue(this))){
|
foreach(Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter)prop.GetValue(this))){
|
||||||
counter.SetOwner(this);
|
counter.SetOwner(this);
|
||||||
|
@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
this.saveTimer.Elapsed += saveTimer_Elapsed;
|
this.saveTimer.Elapsed += saveTimer_Elapsed;
|
||||||
|
|
||||||
if (this.File.LastCollectionVersion != Program.VersionTag){
|
if (this.File.LastCollectionVersion != Program.VersionTag){
|
||||||
ScheduleReportIn(TimeSpan.FromHours(12), string.Empty);
|
ScheduleReportIn(TimeSpan.FromHours(8), string.Empty);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
RestartTimer();
|
RestartTimer();
|
||||||
@@ -90,7 +90,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
|
||||||
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes));
|
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes));
|
||||||
|
|
||||||
currentTimer.Interval = Math.Max(minutesTillNext, 1)*60000;
|
currentTimer.Interval = Math.Max(minutesTillNext, 2)*60000;
|
||||||
currentTimer.Start();
|
currentTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
{ "Clear Cache Threshold" , Exact(SysConfig.ClearCacheThreshold) },
|
{ "Clear Cache Threshold" , Exact(SysConfig.ClearCacheThreshold) },
|
||||||
0,
|
0,
|
||||||
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
|
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
|
||||||
{ "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
|
{ "Switch Account Selectors" , Bool(false) }, // TODO remove in next major update
|
||||||
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
|
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
|
||||||
{ "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) },
|
{ "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) },
|
||||||
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
|
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
|
||||||
|
@@ -58,11 +58,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Guide";
|
Text = Program.BrandName+" Guide";
|
||||||
|
|
||||||
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);
|
||||||
}
|
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser(url){
|
this.browser = new ChromiumWebBrowser(url){
|
||||||
MenuHandler = new ContextMenuGuide(owner),
|
MenuHandler = new ContextMenuGuide(owner),
|
||||||
|
10
Core/Other/FormSettings.Designer.cs
generated
10
Core/Other/FormSettings.Designer.cs
generated
@@ -33,7 +33,7 @@
|
|||||||
//
|
//
|
||||||
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnClose.AutoSize = true;
|
this.btnClose.AutoSize = true;
|
||||||
this.btnClose.Location = new System.Drawing.Point(449, 483);
|
this.btnClose.Location = new System.Drawing.Point(449, 504);
|
||||||
this.btnClose.Name = "btnClose";
|
this.btnClose.Name = "btnClose";
|
||||||
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnClose.Size = new System.Drawing.Size(49, 23);
|
this.btnClose.Size = new System.Drawing.Size(49, 23);
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
this.panelContents.Location = new System.Drawing.Point(135, 12);
|
this.panelContents.Location = new System.Drawing.Point(135, 12);
|
||||||
this.panelContents.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3);
|
this.panelContents.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3);
|
||||||
this.panelContents.Name = "panelContents";
|
this.panelContents.Name = "panelContents";
|
||||||
this.panelContents.Size = new System.Drawing.Size(363, 465);
|
this.panelContents.Size = new System.Drawing.Size(363, 486);
|
||||||
this.panelContents.TabIndex = 1;
|
this.panelContents.TabIndex = 1;
|
||||||
//
|
//
|
||||||
// panelButtons
|
// panelButtons
|
||||||
@@ -63,14 +63,14 @@
|
|||||||
this.panelButtons.Location = new System.Drawing.Point(12, 12);
|
this.panelButtons.Location = new System.Drawing.Point(12, 12);
|
||||||
this.panelButtons.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
|
this.panelButtons.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
|
||||||
this.panelButtons.Name = "panelButtons";
|
this.panelButtons.Name = "panelButtons";
|
||||||
this.panelButtons.Size = new System.Drawing.Size(124, 465);
|
this.panelButtons.Size = new System.Drawing.Size(124, 486);
|
||||||
this.panelButtons.TabIndex = 0;
|
this.panelButtons.TabIndex = 0;
|
||||||
//
|
//
|
||||||
// btnManageOptions
|
// btnManageOptions
|
||||||
//
|
//
|
||||||
this.btnManageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.btnManageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.btnManageOptions.AutoSize = true;
|
this.btnManageOptions.AutoSize = true;
|
||||||
this.btnManageOptions.Location = new System.Drawing.Point(12, 483);
|
this.btnManageOptions.Location = new System.Drawing.Point(12, 504);
|
||||||
this.btnManageOptions.Name = "btnManageOptions";
|
this.btnManageOptions.Name = "btnManageOptions";
|
||||||
this.btnManageOptions.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnManageOptions.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnManageOptions.Size = new System.Drawing.Size(101, 23);
|
this.btnManageOptions.Size = new System.Drawing.Size(101, 23);
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(510, 518);
|
this.ClientSize = new System.Drawing.Size(510, 539);
|
||||||
this.Controls.Add(this.btnManageOptions);
|
this.Controls.Add(this.btnManageOptions);
|
||||||
this.Controls.Add(this.panelContents);
|
this.Controls.Add(this.panelContents);
|
||||||
this.Controls.Add(this.panelButtons);
|
this.Controls.Add(this.panelButtons);
|
||||||
|
@@ -36,7 +36,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
|
|
||||||
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
|
||||||
|
|
||||||
AddButton("General", () => new TabSettingsGeneral(this.browser, updates));
|
AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadColumns, updates));
|
||||||
AddButton("Locales", () => new TabSettingsLocales());
|
AddButton("Locales", () => new TabSettingsLocales());
|
||||||
AddButton("System Tray", () => new TabSettingsTray());
|
AddButton("System Tray", () => new TabSettingsTray());
|
||||||
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
|
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
|
||||||
@@ -156,6 +156,10 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void control_MouseLeave(object sender, EventArgs e){
|
private void control_MouseLeave(object sender, EventArgs e){
|
||||||
|
if (sender is ComboBox cb && cb.DroppedDown){
|
||||||
|
return; // prevents comboboxes from closing when MouseLeave event triggers during opening animation
|
||||||
|
}
|
||||||
|
|
||||||
panelContents.Focus();
|
panelContents.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core.Other.Interfaces{
|
||||||
interface ITweetDeckBrowser{
|
interface ITweetDeckBrowser{
|
||||||
|
bool IsTweetDeckWebsite { get; }
|
||||||
|
|
||||||
void RegisterBridge(string name, object obj);
|
void RegisterBridge(string name, object obj);
|
||||||
void OnFrameLoaded(Action<IFrame> callback);
|
void OnFrameLoaded(Action<IFrame> callback);
|
||||||
void ExecuteFunction(string name, params object[] args);
|
void ExecuteFunction(string name, params object[] args);
|
@@ -42,7 +42,9 @@
|
|||||||
//
|
//
|
||||||
// textBoxBrowserCSS
|
// textBoxBrowserCSS
|
||||||
//
|
//
|
||||||
this.textBoxBrowserCSS.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this.textBoxBrowserCSS.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.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.textBoxBrowserCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16);
|
this.textBoxBrowserCSS.Location = new System.Drawing.Point(0, 16);
|
||||||
this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
this.textBoxBrowserCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
||||||
@@ -124,7 +126,9 @@
|
|||||||
//
|
//
|
||||||
// textBoxNotificationCSS
|
// textBoxNotificationCSS
|
||||||
//
|
//
|
||||||
this.textBoxNotificationCSS.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this.textBoxNotificationCSS.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.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.textBoxNotificationCSS.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16);
|
this.textBoxNotificationCSS.Location = new System.Drawing.Point(0, 16);
|
||||||
this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
this.textBoxNotificationCSS.Margin = new System.Windows.Forms.Padding(0, 3, 0, 0);
|
||||||
|
@@ -164,9 +164,6 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
ShouldReloadBrowser = true;
|
ShouldReloadBrowser = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
Program.Reporter.HandleException("Profile Import Error", "An exception happened while importing TweetDuck profile.", true, importManager.LastException);
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
@@ -192,11 +189,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Program.UserConfig.Save();
|
Program.UserConfig.Save();
|
||||||
Program.SystemConfig.Save();
|
Program.SystemConfig.Save();
|
||||||
|
|
||||||
ProfileManager manager = new ProfileManager(file, plugins);
|
new ProfileManager(file, plugins).Export(SelectedItems);
|
||||||
|
|
||||||
if (!manager.Export(SelectedItems)){
|
|
||||||
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, manager.LastException);
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
|
@@ -28,21 +28,22 @@
|
|||||||
this.btnRestart = new System.Windows.Forms.Button();
|
this.btnRestart = new System.Windows.Forms.Button();
|
||||||
this.cbLogging = new System.Windows.Forms.CheckBox();
|
this.cbLogging = new System.Windows.Forms.CheckBox();
|
||||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||||
this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
|
|
||||||
this.tbDataFolder = new System.Windows.Forms.TextBox();
|
this.tbDataFolder = new System.Windows.Forms.TextBox();
|
||||||
this.tbShortcutTarget = new System.Windows.Forms.TextBox();
|
this.tbShortcutTarget = new System.Windows.Forms.TextBox();
|
||||||
this.labelDataFolder = new System.Windows.Forms.Label();
|
this.labelDataFolder = new System.Windows.Forms.Label();
|
||||||
this.labelShortcutTarget = new System.Windows.Forms.Label();
|
this.labelShortcutTarget = new System.Windows.Forms.Label();
|
||||||
|
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
|
||||||
|
this.flowPanel.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnCancel.Location = new System.Drawing.Point(215, 163);
|
this.btnCancel.Location = new System.Drawing.Point(215, 139);
|
||||||
this.btnCancel.Name = "btnCancel";
|
this.btnCancel.Name = "btnCancel";
|
||||||
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||||
this.btnCancel.TabIndex = 9;
|
this.btnCancel.TabIndex = 2;
|
||||||
this.btnCancel.Text = "Cancel";
|
this.btnCancel.Text = "Cancel";
|
||||||
this.btnCancel.UseVisualStyleBackColor = true;
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
@@ -50,11 +51,11 @@
|
|||||||
// btnRestart
|
// btnRestart
|
||||||
//
|
//
|
||||||
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.btnRestart.Location = new System.Drawing.Point(152, 163);
|
this.btnRestart.Location = new System.Drawing.Point(152, 139);
|
||||||
this.btnRestart.Name = "btnRestart";
|
this.btnRestart.Name = "btnRestart";
|
||||||
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
this.btnRestart.Size = new System.Drawing.Size(57, 23);
|
||||||
this.btnRestart.TabIndex = 8;
|
this.btnRestart.TabIndex = 1;
|
||||||
this.btnRestart.Text = "Restart";
|
this.btnRestart.Text = "Restart";
|
||||||
this.btnRestart.UseVisualStyleBackColor = true;
|
this.btnRestart.UseVisualStyleBackColor = true;
|
||||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
// cbLogging
|
// cbLogging
|
||||||
//
|
//
|
||||||
this.cbLogging.AutoSize = true;
|
this.cbLogging.AutoSize = true;
|
||||||
this.cbLogging.Location = new System.Drawing.Point(12, 12);
|
this.cbLogging.Location = new System.Drawing.Point(3, 3);
|
||||||
this.cbLogging.Name = "cbLogging";
|
this.cbLogging.Name = "cbLogging";
|
||||||
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
this.cbLogging.Size = new System.Drawing.Size(64, 17);
|
||||||
this.cbLogging.TabIndex = 0;
|
this.cbLogging.TabIndex = 0;
|
||||||
@@ -70,25 +71,12 @@
|
|||||||
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into TD_Console.txt file in the data folder.");
|
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into TD_Console.txt file in the data folder.");
|
||||||
this.cbLogging.UseVisualStyleBackColor = true;
|
this.cbLogging.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// cbDebugUpdates
|
|
||||||
//
|
|
||||||
this.cbDebugUpdates.AutoSize = true;
|
|
||||||
this.cbDebugUpdates.Location = new System.Drawing.Point(12, 35);
|
|
||||||
this.cbDebugUpdates.Name = "cbDebugUpdates";
|
|
||||||
this.cbDebugUpdates.Size = new System.Drawing.Size(127, 17);
|
|
||||||
this.cbDebugUpdates.TabIndex = 1;
|
|
||||||
this.cbDebugUpdates.Text = "Pre-Release Updates";
|
|
||||||
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
|
|
||||||
this.cbDebugUpdates.UseVisualStyleBackColor = true;
|
|
||||||
//
|
|
||||||
// tbDataFolder
|
// tbDataFolder
|
||||||
//
|
//
|
||||||
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.tbDataFolder.Location = new System.Drawing.Point(3, 51);
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.tbDataFolder.Location = new System.Drawing.Point(15, 83);
|
|
||||||
this.tbDataFolder.Name = "tbDataFolder";
|
this.tbDataFolder.Name = "tbDataFolder";
|
||||||
this.tbDataFolder.Size = new System.Drawing.Size(257, 20);
|
this.tbDataFolder.Size = new System.Drawing.Size(260, 20);
|
||||||
this.tbDataFolder.TabIndex = 5;
|
this.tbDataFolder.TabIndex = 2;
|
||||||
this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder nam" +
|
this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder nam" +
|
||||||
"e that will be created in LocalAppData.");
|
"e that will be created in LocalAppData.");
|
||||||
//
|
//
|
||||||
@@ -97,44 +85,57 @@
|
|||||||
this.tbShortcutTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
this.tbShortcutTarget.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.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
this.tbShortcutTarget.Location = new System.Drawing.Point(15, 134);
|
this.tbShortcutTarget.Location = new System.Drawing.Point(3, 102);
|
||||||
this.tbShortcutTarget.Name = "tbShortcutTarget";
|
this.tbShortcutTarget.Name = "tbShortcutTarget";
|
||||||
this.tbShortcutTarget.ReadOnly = true;
|
this.tbShortcutTarget.ReadOnly = true;
|
||||||
this.tbShortcutTarget.Size = new System.Drawing.Size(257, 20);
|
this.tbShortcutTarget.Size = new System.Drawing.Size(260, 20);
|
||||||
this.tbShortcutTarget.TabIndex = 7;
|
this.tbShortcutTarget.TabIndex = 4;
|
||||||
this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click);
|
this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click);
|
||||||
//
|
//
|
||||||
// labelDataFolder
|
// labelDataFolder
|
||||||
//
|
//
|
||||||
this.labelDataFolder.AutoSize = true;
|
this.labelDataFolder.AutoSize = true;
|
||||||
this.labelDataFolder.Location = new System.Drawing.Point(12, 67);
|
this.labelDataFolder.Location = new System.Drawing.Point(3, 35);
|
||||||
this.labelDataFolder.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelDataFolder.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelDataFolder.Name = "labelDataFolder";
|
this.labelDataFolder.Name = "labelDataFolder";
|
||||||
this.labelDataFolder.Size = new System.Drawing.Size(62, 13);
|
this.labelDataFolder.Size = new System.Drawing.Size(62, 13);
|
||||||
this.labelDataFolder.TabIndex = 4;
|
this.labelDataFolder.TabIndex = 1;
|
||||||
this.labelDataFolder.Text = "Data Folder";
|
this.labelDataFolder.Text = "Data Folder";
|
||||||
//
|
//
|
||||||
// labelShortcutTarget
|
// labelShortcutTarget
|
||||||
//
|
//
|
||||||
this.labelShortcutTarget.AutoSize = true;
|
this.labelShortcutTarget.AutoSize = true;
|
||||||
this.labelShortcutTarget.Location = new System.Drawing.Point(12, 118);
|
this.labelShortcutTarget.Location = new System.Drawing.Point(3, 86);
|
||||||
this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelShortcutTarget.Name = "labelShortcutTarget";
|
this.labelShortcutTarget.Name = "labelShortcutTarget";
|
||||||
this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
|
this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
|
||||||
this.labelShortcutTarget.TabIndex = 6;
|
this.labelShortcutTarget.TabIndex = 3;
|
||||||
this.labelShortcutTarget.Text = "Shortcut Target (click to select)";
|
this.labelShortcutTarget.Text = "Shortcut Target (click to select)";
|
||||||
//
|
//
|
||||||
|
// flowPanel
|
||||||
|
//
|
||||||
|
this.flowPanel.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.flowPanel.Controls.Add(this.cbLogging);
|
||||||
|
this.flowPanel.Controls.Add(this.labelDataFolder);
|
||||||
|
this.flowPanel.Controls.Add(this.tbDataFolder);
|
||||||
|
this.flowPanel.Controls.Add(this.labelShortcutTarget);
|
||||||
|
this.flowPanel.Controls.Add(this.tbShortcutTarget);
|
||||||
|
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||||
|
this.flowPanel.Location = new System.Drawing.Point(9, 9);
|
||||||
|
this.flowPanel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.flowPanel.Name = "flowPanel";
|
||||||
|
this.flowPanel.Size = new System.Drawing.Size(266, 127);
|
||||||
|
this.flowPanel.TabIndex = 0;
|
||||||
|
this.flowPanel.WrapContents = false;
|
||||||
|
//
|
||||||
// DialogSettingsRestart
|
// DialogSettingsRestart
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.ClientSize = new System.Drawing.Size(284, 198);
|
this.ClientSize = new System.Drawing.Size(284, 174);
|
||||||
this.Controls.Add(this.tbShortcutTarget);
|
this.Controls.Add(this.flowPanel);
|
||||||
this.Controls.Add(this.labelShortcutTarget);
|
|
||||||
this.Controls.Add(this.tbDataFolder);
|
|
||||||
this.Controls.Add(this.labelDataFolder);
|
|
||||||
this.Controls.Add(this.cbDebugUpdates);
|
|
||||||
this.Controls.Add(this.cbLogging);
|
|
||||||
this.Controls.Add(this.btnRestart);
|
this.Controls.Add(this.btnRestart);
|
||||||
this.Controls.Add(this.btnCancel);
|
this.Controls.Add(this.btnCancel);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||||
@@ -143,8 +144,9 @@
|
|||||||
this.Name = "DialogSettingsRestart";
|
this.Name = "DialogSettingsRestart";
|
||||||
this.ShowIcon = false;
|
this.ShowIcon = false;
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.flowPanel.ResumeLayout(false);
|
||||||
|
this.flowPanel.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,10 +156,10 @@
|
|||||||
private System.Windows.Forms.Button btnRestart;
|
private System.Windows.Forms.Button btnRestart;
|
||||||
private System.Windows.Forms.CheckBox cbLogging;
|
private System.Windows.Forms.CheckBox cbLogging;
|
||||||
private System.Windows.Forms.ToolTip toolTip;
|
private System.Windows.Forms.ToolTip toolTip;
|
||||||
private System.Windows.Forms.CheckBox cbDebugUpdates;
|
|
||||||
private System.Windows.Forms.Label labelDataFolder;
|
private System.Windows.Forms.Label labelDataFolder;
|
||||||
private System.Windows.Forms.TextBox tbDataFolder;
|
private System.Windows.Forms.TextBox tbDataFolder;
|
||||||
private System.Windows.Forms.TextBox tbShortcutTarget;
|
private System.Windows.Forms.TextBox tbShortcutTarget;
|
||||||
private System.Windows.Forms.Label labelShortcutTarget;
|
private System.Windows.Forms.Label labelShortcutTarget;
|
||||||
|
private System.Windows.Forms.FlowLayoutPanel flowPanel;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,10 +11,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
|
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
|
||||||
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
|
|
||||||
|
|
||||||
cbLogging.CheckedChanged += control_Change;
|
cbLogging.CheckedChanged += control_Change;
|
||||||
cbDebugUpdates.CheckedChanged += control_Change;
|
|
||||||
|
|
||||||
if (Program.IsPortable){
|
if (Program.IsPortable){
|
||||||
tbDataFolder.Text = "Not available in portable version";
|
tbDataFolder.Text = "Not available in portable version";
|
||||||
@@ -37,10 +34,6 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Args.AddFlag(Arguments.ArgLogging);
|
Args.AddFlag(Arguments.ArgLogging);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cbDebugUpdates.Checked){
|
|
||||||
Args.AddFlag(Arguments.ArgDebugUpdates);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
|
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
|
||||||
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
|
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
|
||||||
}
|
}
|
||||||
|
103
Core/Other/Settings/Dialogs/DialogSettingsSearchEngine.Designer.cs
generated
Normal file
103
Core/Other/Settings/Dialogs/DialogSettingsSearchEngine.Designer.cs
generated
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
namespace TweetDuck.Core.Other.Settings.Dialogs {
|
||||||
|
partial class DialogSettingsSearchEngine {
|
||||||
|
/// <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.textBoxUrl = new System.Windows.Forms.TextBox();
|
||||||
|
this.btnCancel = new System.Windows.Forms.Button();
|
||||||
|
this.btnApply = new System.Windows.Forms.Button();
|
||||||
|
this.labelInfo = new System.Windows.Forms.Label();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// textBoxUrl
|
||||||
|
//
|
||||||
|
this.textBoxUrl.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.textBoxUrl.Location = new System.Drawing.Point(12, 28);
|
||||||
|
this.textBoxUrl.Name = "textBoxUrl";
|
||||||
|
this.textBoxUrl.Size = new System.Drawing.Size(310, 20);
|
||||||
|
this.textBoxUrl.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// btnCancel
|
||||||
|
//
|
||||||
|
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnCancel.Location = new System.Drawing.Point(204, 56);
|
||||||
|
this.btnCancel.Name = "btnCancel";
|
||||||
|
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnCancel.Size = new System.Drawing.Size(56, 23);
|
||||||
|
this.btnCancel.TabIndex = 3;
|
||||||
|
this.btnCancel.Text = "Cancel";
|
||||||
|
this.btnCancel.UseVisualStyleBackColor = true;
|
||||||
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
|
//
|
||||||
|
// btnApply
|
||||||
|
//
|
||||||
|
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.btnApply.Location = new System.Drawing.Point(266, 56);
|
||||||
|
this.btnApply.Name = "btnApply";
|
||||||
|
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||||
|
this.btnApply.Size = new System.Drawing.Size(56, 23);
|
||||||
|
this.btnApply.TabIndex = 2;
|
||||||
|
this.btnApply.Text = "Apply";
|
||||||
|
this.btnApply.UseVisualStyleBackColor = true;
|
||||||
|
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
|
||||||
|
//
|
||||||
|
// labelInfo
|
||||||
|
//
|
||||||
|
this.labelInfo.AutoSize = true;
|
||||||
|
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(264, 13);
|
||||||
|
this.labelInfo.TabIndex = 0;
|
||||||
|
this.labelInfo.Text = "The search query will be added at the end of the URL.";
|
||||||
|
//
|
||||||
|
// DialogSettingsSearchEngine
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(334, 91);
|
||||||
|
this.Controls.Add(this.labelInfo);
|
||||||
|
this.Controls.Add(this.btnApply);
|
||||||
|
this.Controls.Add(this.btnCancel);
|
||||||
|
this.Controls.Add(this.textBoxUrl);
|
||||||
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.Name = "DialogSettingsSearchEngine";
|
||||||
|
this.ShowIcon = false;
|
||||||
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
this.PerformLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.TextBox textBoxUrl;
|
||||||
|
private System.Windows.Forms.Button btnCancel;
|
||||||
|
private System.Windows.Forms.Button btnApply;
|
||||||
|
private System.Windows.Forms.Label labelInfo;
|
||||||
|
}
|
||||||
|
}
|
27
Core/Other/Settings/Dialogs/DialogSettingsSearchEngine.cs
Normal file
27
Core/Other/Settings/Dialogs/DialogSettingsSearchEngine.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other.Settings.Dialogs{
|
||||||
|
sealed partial class DialogSettingsSearchEngine : Form{
|
||||||
|
public string Url => textBoxUrl.Text;
|
||||||
|
|
||||||
|
public DialogSettingsSearchEngine(){
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Text = Program.BrandName+" Options - Custom Search Engine";
|
||||||
|
|
||||||
|
textBoxUrl.Text = Program.UserConfig.SearchEngineUrl ?? "";
|
||||||
|
textBoxUrl.Select(textBoxUrl.Text.Length, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void btnCancel_Click(object sender, EventArgs e){
|
||||||
|
DialogResult = DialogResult.Cancel;
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
96
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -29,7 +29,6 @@
|
|||||||
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
|
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
|
||||||
this.btnCheckUpdates = new System.Windows.Forms.Button();
|
this.btnCheckUpdates = new System.Windows.Forms.Button();
|
||||||
this.labelZoomValue = new System.Windows.Forms.Label();
|
this.labelZoomValue = new System.Windows.Forms.Label();
|
||||||
this.checkSwitchAccountSelectors = new System.Windows.Forms.CheckBox();
|
|
||||||
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
|
this.checkBestImageQuality = new System.Windows.Forms.CheckBox();
|
||||||
this.checkOpenSearchInFirstColumn = new System.Windows.Forms.CheckBox();
|
this.checkOpenSearchInFirstColumn = new System.Windows.Forms.CheckBox();
|
||||||
this.trackBarZoom = new System.Windows.Forms.TrackBar();
|
this.trackBarZoom = new System.Windows.Forms.TrackBar();
|
||||||
@@ -45,6 +44,8 @@
|
|||||||
this.checkSmoothScrolling = new System.Windows.Forms.CheckBox();
|
this.checkSmoothScrolling = new System.Windows.Forms.CheckBox();
|
||||||
this.labelBrowserPath = new System.Windows.Forms.Label();
|
this.labelBrowserPath = new System.Windows.Forms.Label();
|
||||||
this.comboBoxBrowserPath = new System.Windows.Forms.ComboBox();
|
this.comboBoxBrowserPath = new System.Windows.Forms.ComboBox();
|
||||||
|
this.labelSearchEngine = new System.Windows.Forms.Label();
|
||||||
|
this.comboBoxSearchEngine = new System.Windows.Forms.ComboBox();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
|
||||||
this.panelZoom.SuspendLayout();
|
this.panelZoom.SuspendLayout();
|
||||||
this.flowPanel.SuspendLayout();
|
this.flowPanel.SuspendLayout();
|
||||||
@@ -64,21 +65,21 @@
|
|||||||
// checkUpdateNotifications
|
// checkUpdateNotifications
|
||||||
//
|
//
|
||||||
this.checkUpdateNotifications.AutoSize = true;
|
this.checkUpdateNotifications.AutoSize = true;
|
||||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 386);
|
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 415);
|
||||||
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
|
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
|
||||||
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
||||||
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
|
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
|
||||||
this.checkUpdateNotifications.TabIndex = 14;
|
this.checkUpdateNotifications.TabIndex = 15;
|
||||||
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
||||||
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// btnCheckUpdates
|
// btnCheckUpdates
|
||||||
//
|
//
|
||||||
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 409);
|
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 438);
|
||||||
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
||||||
this.btnCheckUpdates.Size = new System.Drawing.Size(144, 23);
|
this.btnCheckUpdates.Size = new System.Drawing.Size(144, 23);
|
||||||
this.btnCheckUpdates.TabIndex = 15;
|
this.btnCheckUpdates.TabIndex = 16;
|
||||||
this.btnCheckUpdates.Text = "Check Updates Now";
|
this.btnCheckUpdates.Text = "Check Updates Now";
|
||||||
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -93,36 +94,25 @@
|
|||||||
this.labelZoomValue.Text = "100%";
|
this.labelZoomValue.Text = "100%";
|
||||||
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
|
||||||
//
|
//
|
||||||
// checkSwitchAccountSelectors
|
|
||||||
//
|
|
||||||
this.checkSwitchAccountSelectors.AutoSize = true;
|
|
||||||
this.checkSwitchAccountSelectors.Location = new System.Drawing.Point(6, 49);
|
|
||||||
this.checkSwitchAccountSelectors.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
|
||||||
this.checkSwitchAccountSelectors.Name = "checkSwitchAccountSelectors";
|
|
||||||
this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17);
|
|
||||||
this.checkSwitchAccountSelectors.TabIndex = 2;
|
|
||||||
this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
|
|
||||||
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
|
|
||||||
//
|
|
||||||
// checkBestImageQuality
|
// checkBestImageQuality
|
||||||
//
|
//
|
||||||
this.checkBestImageQuality.AutoSize = true;
|
this.checkBestImageQuality.AutoSize = true;
|
||||||
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 118);
|
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 95);
|
||||||
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
||||||
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
|
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
|
||||||
this.checkBestImageQuality.TabIndex = 5;
|
this.checkBestImageQuality.TabIndex = 4;
|
||||||
this.checkBestImageQuality.Text = "Best Image Quality";
|
this.checkBestImageQuality.Text = "Best Image Quality";
|
||||||
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// checkOpenSearchInFirstColumn
|
// checkOpenSearchInFirstColumn
|
||||||
//
|
//
|
||||||
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
||||||
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 72);
|
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 49);
|
||||||
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
||||||
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
|
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
|
||||||
this.checkOpenSearchInFirstColumn.TabIndex = 3;
|
this.checkOpenSearchInFirstColumn.TabIndex = 2;
|
||||||
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
||||||
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -144,11 +134,11 @@
|
|||||||
// labelZoom
|
// labelZoom
|
||||||
//
|
//
|
||||||
this.labelZoom.AutoSize = true;
|
this.labelZoom.AutoSize = true;
|
||||||
this.labelZoom.Location = new System.Drawing.Point(3, 291);
|
this.labelZoom.Location = new System.Drawing.Point(3, 320);
|
||||||
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 = 11;
|
this.labelZoom.TabIndex = 12;
|
||||||
this.labelZoom.Text = "Zoom";
|
this.labelZoom.Text = "Zoom";
|
||||||
//
|
//
|
||||||
// zoomUpdateTimer
|
// zoomUpdateTimer
|
||||||
@@ -172,20 +162,20 @@
|
|||||||
this.panelZoom.Anchor = System.Windows.Forms.AnchorStyles.Top;
|
this.panelZoom.Anchor = System.Windows.Forms.AnchorStyles.Top;
|
||||||
this.panelZoom.Controls.Add(this.trackBarZoom);
|
this.panelZoom.Controls.Add(this.trackBarZoom);
|
||||||
this.panelZoom.Controls.Add(this.labelZoomValue);
|
this.panelZoom.Controls.Add(this.labelZoomValue);
|
||||||
this.panelZoom.Location = new System.Drawing.Point(0, 304);
|
this.panelZoom.Location = new System.Drawing.Point(0, 333);
|
||||||
this.panelZoom.Margin = new System.Windows.Forms.Padding(0);
|
this.panelZoom.Margin = new System.Windows.Forms.Padding(0);
|
||||||
this.panelZoom.Name = "panelZoom";
|
this.panelZoom.Name = "panelZoom";
|
||||||
this.panelZoom.Size = new System.Drawing.Size(322, 36);
|
this.panelZoom.Size = new System.Drawing.Size(322, 36);
|
||||||
this.panelZoom.TabIndex = 12;
|
this.panelZoom.TabIndex = 13;
|
||||||
//
|
//
|
||||||
// checkAnimatedAvatars
|
// checkAnimatedAvatars
|
||||||
//
|
//
|
||||||
this.checkAnimatedAvatars.AutoSize = true;
|
this.checkAnimatedAvatars.AutoSize = true;
|
||||||
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 141);
|
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 118);
|
||||||
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
||||||
this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
|
this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
|
||||||
this.checkAnimatedAvatars.TabIndex = 6;
|
this.checkAnimatedAvatars.TabIndex = 5;
|
||||||
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
||||||
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -193,11 +183,11 @@
|
|||||||
//
|
//
|
||||||
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(0, 360);
|
this.labelUpdates.Location = new System.Drawing.Point(0, 389);
|
||||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
|
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 20, 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 = 13;
|
this.labelUpdates.TabIndex = 14;
|
||||||
this.labelUpdates.Text = "Updates";
|
this.labelUpdates.Text = "Updates";
|
||||||
//
|
//
|
||||||
// flowPanel
|
// flowPanel
|
||||||
@@ -207,7 +197,6 @@
|
|||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.flowPanel.Controls.Add(this.labelUI);
|
this.flowPanel.Controls.Add(this.labelUI);
|
||||||
this.flowPanel.Controls.Add(this.checkExpandLinks);
|
this.flowPanel.Controls.Add(this.checkExpandLinks);
|
||||||
this.flowPanel.Controls.Add(this.checkSwitchAccountSelectors);
|
|
||||||
this.flowPanel.Controls.Add(this.checkOpenSearchInFirstColumn);
|
this.flowPanel.Controls.Add(this.checkOpenSearchInFirstColumn);
|
||||||
this.flowPanel.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
|
this.flowPanel.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
|
||||||
this.flowPanel.Controls.Add(this.checkBestImageQuality);
|
this.flowPanel.Controls.Add(this.checkBestImageQuality);
|
||||||
@@ -216,6 +205,8 @@
|
|||||||
this.flowPanel.Controls.Add(this.checkSmoothScrolling);
|
this.flowPanel.Controls.Add(this.checkSmoothScrolling);
|
||||||
this.flowPanel.Controls.Add(this.labelBrowserPath);
|
this.flowPanel.Controls.Add(this.labelBrowserPath);
|
||||||
this.flowPanel.Controls.Add(this.comboBoxBrowserPath);
|
this.flowPanel.Controls.Add(this.comboBoxBrowserPath);
|
||||||
|
this.flowPanel.Controls.Add(this.labelSearchEngine);
|
||||||
|
this.flowPanel.Controls.Add(this.comboBoxSearchEngine);
|
||||||
this.flowPanel.Controls.Add(this.labelZoom);
|
this.flowPanel.Controls.Add(this.labelZoom);
|
||||||
this.flowPanel.Controls.Add(this.panelZoom);
|
this.flowPanel.Controls.Add(this.panelZoom);
|
||||||
this.flowPanel.Controls.Add(this.labelUpdates);
|
this.flowPanel.Controls.Add(this.labelUpdates);
|
||||||
@@ -224,18 +215,18 @@
|
|||||||
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
|
||||||
this.flowPanel.Location = new System.Drawing.Point(9, 9);
|
this.flowPanel.Location = new System.Drawing.Point(9, 9);
|
||||||
this.flowPanel.Name = "flowPanel";
|
this.flowPanel.Name = "flowPanel";
|
||||||
this.flowPanel.Size = new System.Drawing.Size(322, 438);
|
this.flowPanel.Size = new System.Drawing.Size(322, 462);
|
||||||
this.flowPanel.TabIndex = 0;
|
this.flowPanel.TabIndex = 0;
|
||||||
this.flowPanel.WrapContents = false;
|
this.flowPanel.WrapContents = false;
|
||||||
//
|
//
|
||||||
// checkKeepLikeFollowDialogsOpen
|
// checkKeepLikeFollowDialogsOpen
|
||||||
//
|
//
|
||||||
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
|
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
|
||||||
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 95);
|
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 72);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
|
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
|
||||||
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(176, 17);
|
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(176, 17);
|
||||||
this.checkKeepLikeFollowDialogsOpen.TabIndex = 4;
|
this.checkKeepLikeFollowDialogsOpen.TabIndex = 3;
|
||||||
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
|
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
|
||||||
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
|
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -243,43 +234,63 @@
|
|||||||
//
|
//
|
||||||
this.labelBrowserSettings.AutoSize = true;
|
this.labelBrowserSettings.AutoSize = true;
|
||||||
this.labelBrowserSettings.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelBrowserSettings.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||||
this.labelBrowserSettings.Location = new System.Drawing.Point(0, 181);
|
this.labelBrowserSettings.Location = new System.Drawing.Point(0, 158);
|
||||||
this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
|
this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
|
||||||
this.labelBrowserSettings.Name = "labelBrowserSettings";
|
this.labelBrowserSettings.Name = "labelBrowserSettings";
|
||||||
this.labelBrowserSettings.Size = new System.Drawing.Size(130, 20);
|
this.labelBrowserSettings.Size = new System.Drawing.Size(130, 20);
|
||||||
this.labelBrowserSettings.TabIndex = 7;
|
this.labelBrowserSettings.TabIndex = 6;
|
||||||
this.labelBrowserSettings.Text = "Browser Settings";
|
this.labelBrowserSettings.Text = "Browser Settings";
|
||||||
//
|
//
|
||||||
// checkSmoothScrolling
|
// checkSmoothScrolling
|
||||||
//
|
//
|
||||||
this.checkSmoothScrolling.AutoSize = true;
|
this.checkSmoothScrolling.AutoSize = true;
|
||||||
this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 207);
|
this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 184);
|
||||||
this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
|
this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
|
||||||
this.checkSmoothScrolling.Name = "checkSmoothScrolling";
|
this.checkSmoothScrolling.Name = "checkSmoothScrolling";
|
||||||
this.checkSmoothScrolling.Size = new System.Drawing.Size(105, 17);
|
this.checkSmoothScrolling.Size = new System.Drawing.Size(105, 17);
|
||||||
this.checkSmoothScrolling.TabIndex = 8;
|
this.checkSmoothScrolling.TabIndex = 7;
|
||||||
this.checkSmoothScrolling.Text = "Smooth Scrolling";
|
this.checkSmoothScrolling.Text = "Smooth Scrolling";
|
||||||
this.checkSmoothScrolling.UseVisualStyleBackColor = true;
|
this.checkSmoothScrolling.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
// labelBrowserPath
|
// labelBrowserPath
|
||||||
//
|
//
|
||||||
this.labelBrowserPath.AutoSize = true;
|
this.labelBrowserPath.AutoSize = true;
|
||||||
this.labelBrowserPath.Location = new System.Drawing.Point(3, 239);
|
this.labelBrowserPath.Location = new System.Drawing.Point(3, 216);
|
||||||
this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelBrowserPath.Name = "labelBrowserPath";
|
this.labelBrowserPath.Name = "labelBrowserPath";
|
||||||
this.labelBrowserPath.Size = new System.Drawing.Size(95, 13);
|
this.labelBrowserPath.Size = new System.Drawing.Size(95, 13);
|
||||||
this.labelBrowserPath.TabIndex = 9;
|
this.labelBrowserPath.TabIndex = 8;
|
||||||
this.labelBrowserPath.Text = "Open Links With...";
|
this.labelBrowserPath.Text = "Open Links With...";
|
||||||
//
|
//
|
||||||
// comboBoxBrowserPath
|
// comboBoxBrowserPath
|
||||||
//
|
//
|
||||||
this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
this.comboBoxBrowserPath.FormattingEnabled = true;
|
this.comboBoxBrowserPath.FormattingEnabled = true;
|
||||||
this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 255);
|
this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 232);
|
||||||
this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
this.comboBoxBrowserPath.Name = "comboBoxBrowserPath";
|
this.comboBoxBrowserPath.Name = "comboBoxBrowserPath";
|
||||||
this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 21);
|
this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 21);
|
||||||
this.comboBoxBrowserPath.TabIndex = 10;
|
this.comboBoxBrowserPath.TabIndex = 9;
|
||||||
|
//
|
||||||
|
// labelSearchEngine
|
||||||
|
//
|
||||||
|
this.labelSearchEngine.AutoSize = true;
|
||||||
|
this.labelSearchEngine.Location = new System.Drawing.Point(3, 268);
|
||||||
|
this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
|
this.labelSearchEngine.Name = "labelSearchEngine";
|
||||||
|
this.labelSearchEngine.Size = new System.Drawing.Size(77, 13);
|
||||||
|
this.labelSearchEngine.TabIndex = 10;
|
||||||
|
this.labelSearchEngine.Text = "Search Engine";
|
||||||
|
//
|
||||||
|
// comboBoxSearchEngine
|
||||||
|
//
|
||||||
|
this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
|
this.comboBoxSearchEngine.FormattingEnabled = true;
|
||||||
|
this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 284);
|
||||||
|
this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
|
this.comboBoxSearchEngine.Name = "comboBoxSearchEngine";
|
||||||
|
this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 21);
|
||||||
|
this.comboBoxSearchEngine.TabIndex = 11;
|
||||||
//
|
//
|
||||||
// TabSettingsGeneral
|
// TabSettingsGeneral
|
||||||
//
|
//
|
||||||
@@ -287,7 +298,7 @@
|
|||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.Controls.Add(this.flowPanel);
|
this.Controls.Add(this.flowPanel);
|
||||||
this.Name = "TabSettingsGeneral";
|
this.Name = "TabSettingsGeneral";
|
||||||
this.Size = new System.Drawing.Size(340, 456);
|
this.Size = new System.Drawing.Size(340, 480);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
|
||||||
this.panelZoom.ResumeLayout(false);
|
this.panelZoom.ResumeLayout(false);
|
||||||
this.flowPanel.ResumeLayout(false);
|
this.flowPanel.ResumeLayout(false);
|
||||||
@@ -306,7 +317,6 @@
|
|||||||
private System.Windows.Forms.Label labelZoomValue;
|
private System.Windows.Forms.Label labelZoomValue;
|
||||||
private System.Windows.Forms.TrackBar trackBarZoom;
|
private System.Windows.Forms.TrackBar trackBarZoom;
|
||||||
private System.Windows.Forms.Timer zoomUpdateTimer;
|
private System.Windows.Forms.Timer zoomUpdateTimer;
|
||||||
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
|
|
||||||
private System.Windows.Forms.Label labelUI;
|
private System.Windows.Forms.Label labelUI;
|
||||||
private System.Windows.Forms.Panel panelZoom;
|
private System.Windows.Forms.Panel panelZoom;
|
||||||
private System.Windows.Forms.Label labelUpdates;
|
private System.Windows.Forms.Label labelUpdates;
|
||||||
@@ -319,5 +329,7 @@
|
|||||||
private System.Windows.Forms.ComboBox comboBoxBrowserPath;
|
private System.Windows.Forms.ComboBox comboBoxBrowserPath;
|
||||||
private System.Windows.Forms.Label labelBrowserSettings;
|
private System.Windows.Forms.Label labelBrowserSettings;
|
||||||
private System.Windows.Forms.CheckBox checkSmoothScrolling;
|
private System.Windows.Forms.CheckBox checkSmoothScrolling;
|
||||||
|
private System.Windows.Forms.Label labelSearchEngine;
|
||||||
|
private System.Windows.Forms.ComboBox comboBoxSearchEngine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,29 +4,34 @@ using System.Linq;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
using TweetDuck.Updates.Events;
|
||||||
|
|
||||||
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 Action reloadColumns;
|
||||||
|
|
||||||
private readonly UpdateHandler updates;
|
private readonly UpdateHandler updates;
|
||||||
private int updateCheckEventId = -1;
|
private int updateCheckEventId = -1;
|
||||||
|
|
||||||
private readonly int browserListIndexDefault;
|
private readonly int browserListIndexDefault;
|
||||||
private readonly int browserListIndexCustom;
|
private readonly int browserListIndexCustom;
|
||||||
|
|
||||||
public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){
|
private readonly int searchEngineIndexDefault;
|
||||||
|
private readonly int searchEngineIndexCustom;
|
||||||
|
|
||||||
|
public TabSettingsGeneral(Action reloadColumns, UpdateHandler updates){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.browser = browser;
|
this.reloadColumns = reloadColumns;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
||||||
toolTip.SetToolTip(checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
|
|
||||||
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
||||||
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
|
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
|
||||||
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
||||||
@@ -41,7 +46,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
toolTip.SetToolTip(btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
|
toolTip.SetToolTip(btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
|
||||||
|
|
||||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||||
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
|
|
||||||
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
||||||
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
|
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
|
||||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
@@ -57,6 +61,14 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
browserListIndexCustom = comboBoxBrowserPath.Items.Add("(custom program...)");
|
browserListIndexCustom = comboBoxBrowserPath.Items.Add("(custom program...)");
|
||||||
UpdateBrowserPathSelection();
|
UpdateBrowserPathSelection();
|
||||||
|
|
||||||
|
comboBoxSearchEngine.Items.Add(new SearchEngine("DuckDuckGo", "https://duckduckgo.com/?q="));
|
||||||
|
comboBoxSearchEngine.Items.Add(new SearchEngine("Google", "https://www.google.com/search?q="));
|
||||||
|
comboBoxSearchEngine.Items.Add(new SearchEngine("Bing", "https://www.bing.com/search?q="));
|
||||||
|
comboBoxSearchEngine.Items.Add(new SearchEngine("Yahoo", "https://search.yahoo.com/search?p="));
|
||||||
|
searchEngineIndexDefault = comboBoxSearchEngine.Items.Add("(no engine set)");
|
||||||
|
searchEngineIndexCustom = comboBoxSearchEngine.Items.Add("(custom url...)");
|
||||||
|
UpdateSearchEngineSelection();
|
||||||
|
|
||||||
trackBarZoom.SetValueSafe(Config.ZoomLevel);
|
trackBarZoom.SetValueSafe(Config.ZoomLevel);
|
||||||
labelZoomValue.Text = trackBarZoom.Value+"%";
|
labelZoomValue.Text = trackBarZoom.Value+"%";
|
||||||
|
|
||||||
@@ -65,7 +77,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
||||||
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
|
|
||||||
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
||||||
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
|
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
|
||||||
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||||
@@ -73,6 +84,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
|
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
|
||||||
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged;
|
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged;
|
||||||
|
comboBoxSearchEngine.SelectedIndexChanged += comboBoxSearchEngine_SelectedIndexChanged;
|
||||||
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
|
||||||
|
|
||||||
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
|
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
|
||||||
@@ -87,10 +99,6 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSwitchAccountSelectors_CheckedChanged(object sender, EventArgs e){
|
|
||||||
Config.SwitchAccountSelectors = checkSwitchAccountSelectors.Checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
||||||
}
|
}
|
||||||
@@ -105,7 +113,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e){
|
private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
|
Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
|
||||||
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => browser.ReloadColumns());
|
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => reloadColumns());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e){
|
private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e){
|
||||||
@@ -141,18 +149,50 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
Config.BrowserPath = dialog.FileName;
|
Config.BrowserPath = dialog.FileName;
|
||||||
}
|
}
|
||||||
else{
|
}
|
||||||
|
|
||||||
comboBoxBrowserPath.SelectedIndexChanged -= comboBoxBrowserPath_SelectedIndexChanged;
|
comboBoxBrowserPath.SelectedIndexChanged -= comboBoxBrowserPath_SelectedIndexChanged;
|
||||||
UpdateBrowserPathSelection();
|
UpdateBrowserPathSelection();
|
||||||
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged;
|
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
else{
|
||||||
Config.BrowserPath = (comboBoxBrowserPath.SelectedItem as WindowsUtils.Browser)?.Path; // default browser item is a string and casts to null
|
Config.BrowserPath = (comboBoxBrowserPath.SelectedItem as WindowsUtils.Browser)?.Path; // default browser item is a string and casts to null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void comboBoxSearchEngine_SelectedIndexChanged(object sender, EventArgs e){
|
||||||
|
if (comboBoxSearchEngine.SelectedIndex == searchEngineIndexCustom){
|
||||||
|
using(DialogSettingsSearchEngine dialog = new DialogSettingsSearchEngine()){
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
Config.SearchEngineUrl = dialog.Url.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comboBoxSearchEngine.SelectedIndexChanged -= comboBoxSearchEngine_SelectedIndexChanged;
|
||||||
|
UpdateSearchEngineSelection();
|
||||||
|
comboBoxSearchEngine.SelectedIndexChanged += comboBoxSearchEngine_SelectedIndexChanged;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Config.SearchEngineUrl = (comboBoxSearchEngine.SelectedItem as SearchEngine)?.Url; // default search engine item is a string and casts to null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSearchEngineSelection(){
|
||||||
|
if (string.IsNullOrEmpty(Config.SearchEngineUrl)){
|
||||||
|
comboBoxSearchEngine.SelectedIndex = searchEngineIndexDefault;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
SearchEngine engineInfo = comboBoxSearchEngine.Items.OfType<SearchEngine>().FirstOrDefault(engine => engine.Url == Config.SearchEngineUrl);
|
||||||
|
|
||||||
|
if (engineInfo == null){
|
||||||
|
comboBoxSearchEngine.SelectedIndex = searchEngineIndexCustom;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
comboBoxSearchEngine.SelectedItem = engineInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void trackBarZoom_ValueChanged(object sender, EventArgs e){
|
private void trackBarZoom_ValueChanged(object sender, EventArgs e){
|
||||||
if (trackBarZoom.AlignValueToTick()){
|
if (trackBarZoom.AlignValueToTick()){
|
||||||
zoomUpdateTimer.Stop();
|
zoomUpdateTimer.Stop();
|
||||||
@@ -170,16 +210,25 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
btnCheckUpdates.Enabled = false;
|
btnCheckUpdates.Enabled = false;
|
||||||
updateCheckEventId = updates.Check(true);
|
updateCheckEventId = updates.Check(true);
|
||||||
|
|
||||||
|
if (updateCheckEventId == UpdateHandler.CheckCodeNotOnTweetDeck){
|
||||||
|
FormMessage.Error("Update Check", "Updates can only be checked once TweetDeck is fully loaded.", FormMessage.OK);
|
||||||
|
btnCheckUpdates.Enabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updates_CheckFinished(object sender, UpdateEventArgs e){
|
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
if (e.EventId == updateCheckEventId){
|
if (e.EventId == updateCheckEventId){
|
||||||
btnCheckUpdates.Enabled = true;
|
btnCheckUpdates.Enabled = true;
|
||||||
|
|
||||||
if (!e.IsUpdateAvailable){
|
e.Result.Handle(update => {
|
||||||
|
if (!update.IsUpdateNew){
|
||||||
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
||||||
}
|
}
|
||||||
|
}, ex => {
|
||||||
|
Program.Reporter.HandleException("Update Check Error", "An error occurred while checking for updates.", true, ex);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -188,5 +237,19 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.ZoomLevel = trackBarZoom.Value;
|
Config.ZoomLevel = trackBarZoom.Value;
|
||||||
zoomUpdateTimer.Stop();
|
zoomUpdateTimer.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class SearchEngine{
|
||||||
|
private string Name { get; }
|
||||||
|
public string Url { get; }
|
||||||
|
|
||||||
|
public SearchEngine(string name, string url){
|
||||||
|
Name = name;
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => Name.GetHashCode();
|
||||||
|
public override bool Equals(object obj) => obj is SearchEngine other && Name == other.Name;
|
||||||
|
public override string ToString() => Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,12 +42,12 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void comboBoxSpellCheckLanguage_SelectedValueChanged(object sender, EventArgs e){
|
private void comboBoxSpellCheckLanguage_SelectedValueChanged(object sender, EventArgs e){
|
||||||
Config.SpellCheckLanguage = (comboBoxSpellCheckLanguage.SelectedItem as LocaleUtils.Item)?.Code;
|
Config.SpellCheckLanguage = (comboBoxSpellCheckLanguage.SelectedItem as LocaleUtils.Item)?.Code ?? "en-US";
|
||||||
PromptRestart();
|
PromptRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void comboBoxTranslationTarget_SelectedValueChanged(object sender, EventArgs e){
|
private void comboBoxTranslationTarget_SelectedValueChanged(object sender, EventArgs e){
|
||||||
Config.TranslationTarget = (comboBoxTranslationTarget.SelectedItem as LocaleUtils.Item)?.Code;
|
Config.TranslationTarget = (comboBoxTranslationTarget.SelectedItem as LocaleUtils.Item)?.Code ?? "en";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
@@ -8,6 +9,7 @@ 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.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
using TweetDuck.Core.Other.Interfaces;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
@@ -22,6 +24,10 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
public bool IsTweetDeckWebsite{
|
public bool IsTweetDeckWebsite{
|
||||||
get{
|
get{
|
||||||
|
if (!Ready){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
using(IFrame frame = browser.GetBrowser().MainFrame){
|
using(IFrame frame = browser.GetBrowser().MainFrame){
|
||||||
return TwitterUtils.IsTweetDeckWebsite(frame);
|
return TwitterUtils.IsTweetDeckWebsite(frame);
|
||||||
}
|
}
|
||||||
@@ -43,10 +49,6 @@ namespace TweetDuck.Core{
|
|||||||
RequestHandler = new RequestHandlerBrowser()
|
RequestHandler = new RequestHandlerBrowser()
|
||||||
};
|
};
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
this.browser.FrameLoadStart += browser_FrameLoadStart;
|
this.browser.FrameLoadStart += browser_FrameLoadStart;
|
||||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
@@ -122,14 +124,18 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
||||||
if (e.Frame.IsMain){
|
IFrame frame = e.Frame;
|
||||||
|
|
||||||
|
if (frame.IsMain){
|
||||||
if (Program.UserConfig.ZoomLevel != 100){
|
if (Program.UserConfig.ZoomLevel != 100){
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
|
if (TwitterUtils.IsTwitterWebsite(frame)){
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
|
ScriptLoader.ExecuteFile(frame, "twitter.js", browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +143,11 @@ namespace TweetDuck.Core{
|
|||||||
IFrame frame = e.Frame;
|
IFrame frame = e.Frame;
|
||||||
|
|
||||||
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
|
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
|
||||||
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
|
|
||||||
|
|
||||||
UpdateProperties();
|
UpdateProperties();
|
||||||
TweetDeckBridge.RestoreSessionData(frame);
|
TweetDeckBridge.RestoreSessionData(frame);
|
||||||
ScriptLoader.ExecuteFile(frame, "code.js");
|
ScriptLoader.ExecuteFile(frame, "code.js", browser);
|
||||||
|
ScriptLoader.ExecuteFile(frame, "update.js", browser);
|
||||||
|
|
||||||
InjectBrowserCSS();
|
InjectBrowserCSS();
|
||||||
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
|
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
|
||||||
UserConfig_SoundNotificationInfoChanged(null, EventArgs.Empty);
|
UserConfig_SoundNotificationInfoChanged(null, EventArgs.Empty);
|
||||||
@@ -149,7 +155,7 @@ namespace TweetDuck.Core{
|
|||||||
TweetDeckBridge.ResetStaticProperties();
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
|
|
||||||
if (Program.UserConfig.FirstRun){
|
if (Program.UserConfig.FirstRun){
|
||||||
ScriptLoader.ExecuteFile(frame, "introduction.js");
|
ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +215,7 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void InjectBrowserCSS(){
|
public void InjectBrowserCSS(){
|
||||||
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
|
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css", false, browser)?.TrimEnd() ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReinjectCustomCSS(string css){
|
public void ReinjectCustomCSS(string css){
|
||||||
@@ -224,6 +230,10 @@ namespace TweetDuck.Core{
|
|||||||
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddSearchColumn(string query){
|
||||||
|
browser.ExecuteScriptAsync("TDGF_performSearch", query);
|
||||||
|
}
|
||||||
|
|
||||||
public void TriggerTweetScreenshot(){
|
public void TriggerTweetScreenshot(){
|
||||||
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
|
||||||
}
|
}
|
||||||
@@ -239,5 +249,9 @@ namespace TweetDuck.Core{
|
|||||||
public void ApplyROT13(){
|
public void ApplyROT13(){
|
||||||
browser.ExecuteScriptAsync("TDGF_applyROT13()");
|
browser.ExecuteScriptAsync("TDGF_applyROT13()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ShowUpdateNotification(string versionTag, string releaseNotes){
|
||||||
|
browser.ExecuteScriptAsync("TDUF_displayNotification", versionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(releaseNotes)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -127,6 +127,33 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void OpenExternalSearch(string query){
|
||||||
|
if (string.IsNullOrWhiteSpace(query))return;
|
||||||
|
|
||||||
|
string searchUrl = Program.UserConfig.SearchEngineUrl;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(searchUrl)){
|
||||||
|
if (FormMessage.Question("Search Options", "You have not configured a default search engine yet, would you like to do it now?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
bool wereSettingsOpen = FormManager.TryFind<FormSettings>() != null;
|
||||||
|
|
||||||
|
FormManager.TryFind<FormBrowser>()?.OpenSettings();
|
||||||
|
if (wereSettingsOpen)return;
|
||||||
|
|
||||||
|
FormSettings settings = FormManager.TryFind<FormSettings>();
|
||||||
|
if (settings == null)return;
|
||||||
|
|
||||||
|
settings.FormClosed += (sender, args) => {
|
||||||
|
if (args.CloseReason == CloseReason.UserClosing && Program.UserConfig.SearchEngineUrl != searchUrl){
|
||||||
|
OpenExternalSearch(query);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
OpenExternalBrowser(searchUrl+Uri.EscapeUriString(query));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -137,6 +164,8 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static WebClient CreateWebClient(){
|
public static WebClient CreateWebClient(){
|
||||||
|
WindowsUtils.EnsureTLS12();
|
||||||
|
|
||||||
WebClient client = new WebClient{ Proxy = null };
|
WebClient client = new WebClient{ Proxy = null };
|
||||||
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
|
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
|
||||||
return client;
|
return client;
|
||||||
@@ -172,11 +201,5 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public static void SetZoomLevel(IBrowser browser, int percentage){
|
public static void SetZoomLevel(IBrowser browser, int percentage){
|
||||||
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
|
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
public static void HandleConsoleMessage(object sender, ConsoleMessageEventArgs e){
|
|
||||||
Debug.WriteLine("[Console] {0} ({1}:{2})", e.Message, e.Source, e.Line);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,11 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
||||||
|
|
||||||
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
||||||
public const string BackgroundColorOverride = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body,body::before{background:#1c6399!important}'";
|
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important}';h.appendChild(e);},1)";
|
||||||
|
|
||||||
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng"));
|
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng"));
|
||||||
|
|
||||||
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$)([^/]+)/?$", RegexOptions.Compiled), false);
|
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)/?$", RegexOptions.Compiled), false);
|
||||||
public static Regex RegexAccount => RegexAccountLazy.Value;
|
public static Regex RegexAccount => RegexAccountLazy.Value;
|
||||||
|
|
||||||
public static readonly string[] DictionaryWords = {
|
public static readonly string[] DictionaryWords = {
|
||||||
@@ -41,8 +41,8 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static string ExtractMediaBaseLink(string url){
|
private static string ExtractMediaBaseLink(string url){
|
||||||
int dot = url.LastIndexOf('/');
|
int slash = url.LastIndexOf('/');
|
||||||
return dot == -1 ? url : StringUtils.ExtractBefore(url, ':', dot);
|
return slash == -1 ? url : StringUtils.ExtractBefore(url, ':', slash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetMediaLink(string url, ImageQuality quality){
|
public static string GetMediaLink(string url, ImageQuality quality){
|
||||||
@@ -91,7 +91,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
AutoUpgradeEnabled = true,
|
AutoUpgradeEnabled = true,
|
||||||
OverwritePrompt = urls.Length == 1,
|
OverwritePrompt = urls.Length == 1,
|
||||||
Title = "Save Image",
|
Title = "Save Image",
|
||||||
FileName = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{ext}",
|
FileName = $"{string.Join(" ", fileNameParts.Where(part => !string.IsNullOrEmpty(part)))}{ext}",
|
||||||
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -17,6 +18,8 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public static int CurrentProcessID { get; }
|
public static int CurrentProcessID { get; }
|
||||||
public static bool ShouldAvoidToolWindow { get; }
|
public static bool ShouldAvoidToolWindow { get; }
|
||||||
|
|
||||||
|
private static bool HasMicrosoftBeenBroughtTo2008Yet;
|
||||||
|
|
||||||
static WindowsUtils(){
|
static WindowsUtils(){
|
||||||
using(Process me = Process.GetCurrentProcess()){
|
using(Process me = Process.GetCurrentProcess()){
|
||||||
CurrentProcessID = me.Id;
|
CurrentProcessID = me.Id;
|
||||||
@@ -26,6 +29,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EnsureTLS12(){
|
||||||
|
if (!HasMicrosoftBeenBroughtTo2008Yet){
|
||||||
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||||
|
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
|
||||||
|
HasMicrosoftBeenBroughtTo2008Yet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void CreateDirectoryForFile(string file){
|
public static void CreateDirectoryForFile(string file){
|
||||||
string dir = Path.GetDirectoryName(file);
|
string dir = Path.GetDirectoryName(file);
|
||||||
|
|
||||||
|
36
Data/Result.cs
Normal file
36
Data/Result.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Data{
|
||||||
|
sealed class Result<T>{
|
||||||
|
public bool HasValue => exception == null;
|
||||||
|
|
||||||
|
public T Value => HasValue ? value : throw new InvalidOperationException("Requested value from a failed result.");
|
||||||
|
public Exception Exception => exception ?? throw new InvalidOperationException("Requested exception from a successful result.");
|
||||||
|
|
||||||
|
private readonly T value;
|
||||||
|
private readonly Exception exception;
|
||||||
|
|
||||||
|
public Result(T value){
|
||||||
|
this.value = value;
|
||||||
|
this.exception = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result(Exception exception){
|
||||||
|
this.value = default(T);
|
||||||
|
this.exception = exception ?? throw new ArgumentNullException(nameof(exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(Action<T> onSuccess, Action<Exception> onException){
|
||||||
|
if (HasValue){
|
||||||
|
onSuccess(value);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
onException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<R> Select<R>(Func<T, R> map){
|
||||||
|
return HasValue ? new Result<R>(map(value)) : new Result<R>(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -62,8 +62,7 @@ namespace TweetDuck.Data{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(K1 outerKey, K2 innerKey){
|
public bool Contains(K1 outerKey, K2 innerKey){
|
||||||
Dictionary<K2, V> innerDict;
|
return dict.TryGetValue(outerKey, out Dictionary<K2, V> innerDict) && innerDict.ContainsKey(innerKey);
|
||||||
return dict.TryGetValue(outerKey, out innerDict) && innerDict.ContainsKey(innerKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count(){
|
public int Count(){
|
||||||
|
@@ -36,7 +36,7 @@ namespace TweetDuck.Plugins.Controls{
|
|||||||
labelDescription.Visible = false;
|
labelDescription.Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
panelDescription_Resize(panelDescription, null);
|
panelDescription_Resize(panelDescription, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void panelDescription_Resize(object sender, EventArgs e){
|
private void panelDescription_Resize(object sender, EventArgs e){
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins.Enums{
|
namespace TweetDuck.Plugins.Enums{
|
||||||
[Flags]
|
[Flags]
|
||||||
@@ -45,5 +48,50 @@ namespace TweetDuck.Plugins.Enums{
|
|||||||
default: return string.Empty;
|
default: return string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IReadOnlyDictionary<PluginEnvironment, T> Map<T>(T forNone, T forBrowser, T forNotification){
|
||||||
|
return new PluginEnvironmentDictionary<T>(forNone, forBrowser, forNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "MemberHidesStaticFromOuterClass")]
|
||||||
|
private sealed class PluginEnvironmentDictionary<T> : IReadOnlyDictionary<PluginEnvironment, T>{
|
||||||
|
private const int TotalKeys = 3;
|
||||||
|
|
||||||
|
public IEnumerable<PluginEnvironment> Keys => Enum.GetValues(typeof(PluginEnvironment)).Cast<PluginEnvironment>();
|
||||||
|
public IEnumerable<T> Values => data;
|
||||||
|
public int Count => TotalKeys;
|
||||||
|
|
||||||
|
public T this[PluginEnvironment key] => data[(int)key];
|
||||||
|
|
||||||
|
private readonly T[] data;
|
||||||
|
|
||||||
|
public PluginEnvironmentDictionary(T forNone, T forBrowser, T forNotification){
|
||||||
|
this.data = new T[TotalKeys];
|
||||||
|
this.data[(int)PluginEnvironment.None] = forNone;
|
||||||
|
this.data[(int)PluginEnvironment.Browser] = forBrowser;
|
||||||
|
this.data[(int)PluginEnvironment.Notification] = forNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(PluginEnvironment key){
|
||||||
|
return key >= 0 && (int)key < TotalKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(PluginEnvironment key, out T value){
|
||||||
|
if (ContainsKey(key)){
|
||||||
|
value = this[key];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
value = default(T);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<PluginEnvironment, T>> GetEnumerator(){
|
||||||
|
return Keys.Select(key => new KeyValuePair<PluginEnvironment, T>(key, this[key])).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,28 +1,26 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class Plugin{
|
sealed class Plugin{
|
||||||
private const string VersionWildcard = "*";
|
private const string VersionWildcard = "*";
|
||||||
|
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
||||||
|
|
||||||
public string Identifier { get; }
|
public string Identifier { get; }
|
||||||
public PluginGroup Group { get; }
|
public PluginGroup Group { get; }
|
||||||
public PluginEnvironment Environments { get; }
|
public PluginEnvironment Environments { get; }
|
||||||
|
|
||||||
public string Name => metadata["NAME"];
|
public string Name { get; }
|
||||||
public string Description => metadata["DESCRIPTION"];
|
public string Description { get; }
|
||||||
public string Author => metadata["AUTHOR"];
|
public string Author { get; }
|
||||||
public string Version => metadata["VERSION"];
|
public string Version { get; }
|
||||||
public string Website => metadata["WEBSITE"];
|
public string Website { get; }
|
||||||
public string ConfigFile => metadata["CONFIGFILE"];
|
public string ConfigFile { get; }
|
||||||
public string ConfigDefault => metadata["CONFIGDEFAULT"];
|
public string ConfigDefault { get; }
|
||||||
public string RequiredVersion => metadata["REQUIRES"];
|
public Version RequiredVersion { get; }
|
||||||
|
|
||||||
public bool CanRun { get; private set; }
|
public bool CanRun { get; }
|
||||||
|
|
||||||
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;
|
||||||
@@ -42,41 +40,25 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
private readonly string pathRoot;
|
private readonly string pathRoot;
|
||||||
private readonly string pathData;
|
private readonly string pathData;
|
||||||
private readonly Dictionary<string, string> metadata = new Dictionary<string, string>(8){
|
|
||||||
{ "NAME", "" },
|
|
||||||
{ "DESCRIPTION", "" },
|
|
||||||
{ "AUTHOR", "(anonymous)" },
|
|
||||||
{ "VERSION", "(unknown)" },
|
|
||||||
{ "WEBSITE", "" },
|
|
||||||
{ "CONFIGFILE", "" },
|
|
||||||
{ "CONFIGDEFAULT", "" },
|
|
||||||
{ "REQUIRES", VersionWildcard }
|
|
||||||
};
|
|
||||||
|
|
||||||
private Plugin(string path, string name, PluginGroup group, PluginEnvironment environments){
|
private Plugin(PluginGroup group, string identifier, string pathRoot, string pathData, Builder builder){
|
||||||
this.pathRoot = path;
|
this.pathRoot = pathRoot;
|
||||||
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
|
this.pathData = pathData;
|
||||||
|
|
||||||
this.Identifier = group.GetIdentifierPrefix()+name;
|
|
||||||
this.Group = group;
|
this.Group = group;
|
||||||
this.Environments = environments;
|
this.Identifier = identifier;
|
||||||
}
|
this.Environments = builder.Environments;
|
||||||
|
|
||||||
private void OnMetadataLoaded(){
|
this.Name = builder.Name;
|
||||||
CanRun = CheckRequiredVersion(RequiredVersion);
|
this.Description = builder.Description;
|
||||||
|
this.Author = builder.Author;
|
||||||
|
this.Version = builder.Version;
|
||||||
|
this.Website = builder.Website;
|
||||||
|
this.ConfigFile = builder.ConfigFile;
|
||||||
|
this.ConfigDefault = builder.ConfigDefault;
|
||||||
|
this.RequiredVersion = builder.RequiredVersion;
|
||||||
|
|
||||||
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
|
this.CanRun = AppVersion >= RequiredVersion;
|
||||||
|
|
||||||
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
|
||||||
string dataFolder = GetPluginFolder(PluginFolder.Data);
|
|
||||||
|
|
||||||
try{
|
|
||||||
Directory.CreateDirectory(dataFolder);
|
|
||||||
File.Copy(defaultConfigPath, configPath, false);
|
|
||||||
}catch(Exception e){
|
|
||||||
throw new IOException("Could not generate a configuration file for '"+Identifier+"' plugin: "+e.Message, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetScriptPath(PluginEnvironment environment){
|
public string GetScriptPath(PluginEnvironment environment){
|
||||||
@@ -132,78 +114,94 @@ namespace TweetDuck.Plugins{
|
|||||||
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static
|
// Builder
|
||||||
|
|
||||||
private static readonly Version AppVersion = new Version(Program.VersionTag);
|
public sealed class Builder{
|
||||||
private static readonly string[] EndTag = { "[END]" };
|
private static readonly Version DefaultRequiredVersion = new Version(0, 0, 0, 0);
|
||||||
|
|
||||||
public static Plugin CreateFromFolder(string path, PluginGroup group){
|
public string Name { get; private set; }
|
||||||
Plugin plugin = new Plugin(path, Path.GetFileName(path), group, LoadEnvironments(path));
|
public string Description { get; private set; } = string.Empty;
|
||||||
LoadMetadata(path, plugin);
|
public string Author { get; private set; } = "(anonymous)";
|
||||||
return plugin;
|
public string Version { get; private set; } = "(unknown)";
|
||||||
|
public string Website { get; private set; } = string.Empty;
|
||||||
|
public string ConfigFile { get; private set; } = string.Empty;
|
||||||
|
public string ConfigDefault { get; private set; } = string.Empty;
|
||||||
|
public Version RequiredVersion { get; private set; } = DefaultRequiredVersion;
|
||||||
|
|
||||||
|
public PluginEnvironment Environments { get; private set; } = PluginEnvironment.None;
|
||||||
|
|
||||||
|
private readonly PluginGroup group;
|
||||||
|
private readonly string pathRoot;
|
||||||
|
private readonly string pathData;
|
||||||
|
private readonly string identifier;
|
||||||
|
|
||||||
|
public Builder(PluginGroup group, string name, string pathRoot, string pathData){
|
||||||
|
this.group = group;
|
||||||
|
this.pathRoot = pathRoot;
|
||||||
|
this.pathData = pathData;
|
||||||
|
this.identifier = group.GetIdentifierPrefix()+name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PluginEnvironment LoadEnvironments(string path){
|
public void SetFromTag(string tag, string value){
|
||||||
PluginEnvironment environments = PluginEnvironment.None;
|
switch(tag){
|
||||||
|
case "NAME": this.Name = value; break;
|
||||||
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
case "DESCRIPTION": this.Description = value; break;
|
||||||
environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
|
case "AUTHOR": this.Author = value; break;
|
||||||
}
|
case "VERSION": this.Version = value; break;
|
||||||
|
case "WEBSITE": this.Website = value; break;
|
||||||
if (environments == PluginEnvironment.None){
|
case "CONFIGFILE": this.ConfigFile = value; break;
|
||||||
throw new ArgumentException("Plugin has no script files");
|
case "CONFIGDEFAULT": this.ConfigDefault = value; break;
|
||||||
}
|
case "REQUIRES": SetRequiredVersion(value); break;
|
||||||
|
default: throw new FormatException("Invalid metadata tag: "+tag);
|
||||||
return environments;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LoadMetadata(string path, Plugin plugin){
|
|
||||||
string metaFile = Path.Combine(path, ".meta");
|
|
||||||
|
|
||||||
if (!File.Exists(metaFile)){
|
|
||||||
throw new ArgumentException("Missing .meta file");
|
|
||||||
}
|
|
||||||
|
|
||||||
string currentTag = null, currentContents = string.Empty;
|
|
||||||
|
|
||||||
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 (currentTag != null){
|
|
||||||
plugin.metadata[currentTag] = currentContents;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
|
||||||
currentContents = string.Empty;
|
|
||||||
|
|
||||||
if (line.Equals(EndTag[0])){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!plugin.metadata.ContainsKey(currentTag)){
|
|
||||||
throw new FormatException("Invalid metadata tag: "+currentTag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentTag != null){
|
|
||||||
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
|
public void AddEnvironment(PluginEnvironment environment){
|
||||||
|
this.Environments |= environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRequiredVersion(string versionStr){
|
||||||
|
if (System.Version.TryParse(versionStr, out Version version)){
|
||||||
|
this.RequiredVersion = version;
|
||||||
|
}
|
||||||
|
else if (versionStr == VersionWildcard){
|
||||||
|
this.RequiredVersion = DefaultRequiredVersion;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
throw new FormatException("Missing metadata tag before value: "+line);
|
throw new FormatException("Plugin contains invalid minimum version: "+versionStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Plugin BuildAndSetup(){
|
||||||
|
Plugin plugin = new Plugin(group, identifier, pathRoot, pathData, this);
|
||||||
|
|
||||||
if (plugin.Name.Length == 0){
|
if (plugin.Name.Length == 0){
|
||||||
throw new FormatException("Plugin is missing a name in the .meta file");
|
throw new InvalidOperationException("Plugin is missing a name in the .meta file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
|
if (plugin.Environments == PluginEnvironment.None){
|
||||||
throw new FormatException("Plugin contains invalid version: "+plugin.RequiredVersion);
|
throw new InvalidOperationException("Plugin has no script files");
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.OnMetadataLoaded();
|
// setup
|
||||||
|
|
||||||
|
string configPath = plugin.ConfigPath, defaultConfigPath = plugin.DefaultConfigPath;
|
||||||
|
|
||||||
|
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
|
||||||
|
string dataFolder = plugin.GetPluginFolder(PluginFolder.Data);
|
||||||
|
|
||||||
|
try{
|
||||||
|
Directory.CreateDirectory(dataFolder);
|
||||||
|
File.Copy(defaultConfigPath, configPath, false);
|
||||||
|
}catch(Exception e){
|
||||||
|
throw new IOException($"Could not generate a configuration file for '{plugin.Identifier}' plugin: {e.Message}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool CheckRequiredVersion(string requires){
|
// done
|
||||||
return requires == VersionWildcard || AppVersion >= new Version(requires);
|
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
Plugins/PluginLoader.cs
Normal file
56
Plugins/PluginLoader.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
|
namespace TweetDuck.Plugins{
|
||||||
|
static class PluginLoader{
|
||||||
|
private static readonly string[] EndTag = { "[END]" };
|
||||||
|
|
||||||
|
public static Plugin FromFolder(string path, PluginGroup group){
|
||||||
|
string name = Path.GetFileName(path);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(name)){
|
||||||
|
throw new ArgumentException("Could not extract directory name from path: "+path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin.Builder builder = new Plugin.Builder(group, name, path, Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name));
|
||||||
|
|
||||||
|
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
|
||||||
|
builder.AddEnvironment(PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
string metaFile = Path.Combine(path, ".meta");
|
||||||
|
|
||||||
|
if (!File.Exists(metaFile)){
|
||||||
|
throw new ArgumentException("Plugin is missing a .meta file");
|
||||||
|
}
|
||||||
|
|
||||||
|
string currentTag = null, currentContents = string.Empty;
|
||||||
|
|
||||||
|
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 (currentTag != null){
|
||||||
|
builder.SetFromTag(currentTag, currentContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTag = line.Substring(1, line.Length-2).ToUpper();
|
||||||
|
currentContents = string.Empty;
|
||||||
|
|
||||||
|
if (line.Equals(EndTag[0])){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (currentTag != null){
|
||||||
|
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new FormatException("Missing metadata tag before value: "+line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.BuildAndSetup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -4,59 +4,68 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using TweetDuck.Core;
|
using TweetDuck.Core.Other.Interfaces;
|
||||||
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
sealed class PluginManager{
|
sealed class PluginManager{
|
||||||
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
|
private static IReadOnlyDictionary<PluginEnvironment, string> LoadSetupScripts(){
|
||||||
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
|
return PluginEnvironmentExtensions.Map(
|
||||||
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
|
ScriptLoader.LoadResource("plugins.js"),
|
||||||
{ PluginEnvironment.Notification, ScriptLoader.LoadResource("plugins.notification.js") }
|
ScriptLoader.LoadResource("plugins.browser.js"),
|
||||||
};
|
ScriptLoader.LoadResource("plugins.notification.js")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<PluginEnvironment, string> PluginSetupScripts = LoadSetupScripts();
|
||||||
|
|
||||||
public string PathOfficialPlugins => Path.Combine(rootPath, "official");
|
public string PathOfficialPlugins => Path.Combine(rootPath, "official");
|
||||||
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
public string PathCustomPlugins => Path.Combine(rootPath, "user");
|
||||||
|
|
||||||
public IEnumerable<Plugin> Plugins => plugins;
|
public IEnumerable<Plugin> Plugins => plugins;
|
||||||
|
public IEnumerable<InjectedHTML> NotificationInjections => bridge.NotificationInjections;
|
||||||
|
|
||||||
public PluginConfig Config { get; }
|
public PluginConfig Config { get; }
|
||||||
public PluginBridge Bridge { get; }
|
|
||||||
|
|
||||||
public event EventHandler<PluginErrorEventArgs> Reloaded;
|
public event EventHandler<PluginErrorEventArgs> Reloaded;
|
||||||
public event EventHandler<PluginErrorEventArgs> Executed;
|
public event EventHandler<PluginErrorEventArgs> Executed;
|
||||||
|
|
||||||
private readonly ITweetDeckBrowser browser;
|
|
||||||
private readonly string rootPath;
|
private readonly string rootPath;
|
||||||
private readonly string configPath;
|
private readonly string configPath;
|
||||||
|
private readonly PluginBridge bridge;
|
||||||
|
|
||||||
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
||||||
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();
|
||||||
|
|
||||||
public PluginManager(ITweetDeckBrowser browser, string rootPath, string configPath){
|
private ITweetDeckBrowser mainBrowser;
|
||||||
this.browser = browser;
|
|
||||||
|
public PluginManager(string rootPath, string configPath){
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
this.configPath = configPath;
|
this.configPath = configPath;
|
||||||
|
|
||||||
this.Config = new PluginConfig();
|
this.Config = new PluginConfig();
|
||||||
this.Bridge = new PluginBridge(this);
|
this.bridge = new PluginBridge(this);
|
||||||
|
|
||||||
this.browser.OnFrameLoaded(OnFrameLoaded);
|
|
||||||
this.browser.RegisterBridge("$TDP", Bridge);
|
|
||||||
|
|
||||||
Config.Load(configPath);
|
Config.Load(configPath);
|
||||||
Config.PluginChangedState += Config_PluginChangedState;
|
Config.PluginChangedState += Config_PluginChangedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
public void Register(ITweetDeckBrowser browser, PluginEnvironment environment, bool asMainBrowser = false){
|
||||||
browser.ExecuteFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
browser.OnFrameLoaded(frame => ExecutePlugins(frame, environment));
|
||||||
Config.Save(configPath);
|
browser.RegisterBridge("$TDP", bridge);
|
||||||
|
|
||||||
|
if (asMainBrowser){
|
||||||
|
mainBrowser = browser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFrameLoaded(IFrame frame){
|
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
ExecutePlugins(frame, PluginEnvironment.Browser);
|
mainBrowser?.ExecuteFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||||
|
Config.Save(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPluginInstalled(string identifier){
|
public bool IsPluginInstalled(string identifier){
|
||||||
@@ -68,12 +77,12 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPluginConfigurable(Plugin plugin){
|
public bool IsPluginConfigurable(Plugin plugin){
|
||||||
return plugin.HasConfig || Bridge.WithConfigureFunction.Contains(plugin);
|
return plugin.HasConfig || bridge.WithConfigureFunction.Contains(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConfigurePlugin(Plugin plugin){
|
public void ConfigurePlugin(Plugin plugin){
|
||||||
if (Bridge.WithConfigureFunction.Contains(plugin)){
|
if (bridge.WithConfigureFunction.Contains(plugin)){
|
||||||
browser.ExecuteFunction("TDPF_configurePlugin", plugin);
|
mainBrowser?.ExecuteFunction("TDPF_configurePlugin", plugin);
|
||||||
}
|
}
|
||||||
else if (plugin.HasConfig){
|
else if (plugin.HasConfig){
|
||||||
if (File.Exists(plugin.ConfigPath)){
|
if (File.Exists(plugin.ConfigPath)){
|
||||||
@@ -127,7 +136,7 @@ namespace TweetDuck.Plugins{
|
|||||||
Plugin plugin;
|
Plugin plugin;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
plugin = Plugin.CreateFromFolder(fullDir, group);
|
plugin = PluginLoader.FromFolder(fullDir, group);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
|
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
|
||||||
continue;
|
continue;
|
||||||
@@ -143,7 +152,7 @@ namespace TweetDuck.Plugins{
|
|||||||
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
private void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
||||||
if (!HasAnyPlugin(environment)){
|
if (!HasAnyPlugin(environment)){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ namespace TweetDuck{
|
|||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string VersionTag = "1.13";
|
public const string VersionTag = "1.13.4";
|
||||||
|
|
||||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||||
|
|
||||||
@@ -151,10 +151,9 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||||
|
|
||||||
UpdaterSettings updaterSettings = new UpdaterSettings{
|
UpdaterSettings updaterSettings = new UpdaterSettings(InstallerPath){
|
||||||
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
||||||
DismissedUpdate = UserConfig.DismissedUpdate,
|
DismissedUpdate = UserConfig.DismissedUpdate
|
||||||
InstallerDownloadFolder = InstallerPath
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FormBrowser mainForm = new FormBrowser(updaterSettings);
|
FormBrowser mainForm = new FormBrowser(updaterSettings);
|
||||||
|
@@ -12,10 +12,9 @@ The program was built using Visual Studio 2017. Before opening the solution, ple
|
|||||||
* **Desktop development with C++**
|
* **Desktop development with C++**
|
||||||
* VC++ 2017 v141 toolset
|
* VC++ 2017 v141 toolset
|
||||||
|
|
||||||
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**:
|
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running this command in the **Package Manager Console**:
|
||||||
```
|
```
|
||||||
PM> Install-Package CefSharp.WinForms -Version 63.0.0-pre01 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
|
PM> Install-Package CefSharp.WinForms -Version 64.0.0-CI2508 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
|
||||||
PM> Install-Package Microsoft.VC120.CRT.JetBrains
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
|
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
|
||||||
|
BIN
Resources/Design/app_logo.afdesign
Normal file
BIN
Resources/Design/app_logo.afdesign
Normal file
Binary file not shown.
BIN
Resources/Design/video_buttons.afdesign
Normal file
BIN
Resources/Design/video_buttons.afdesign
Normal file
Binary file not shown.
@@ -9,7 +9,7 @@ Clear columns
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.1.1
|
1.2
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -7,12 +7,17 @@ constructor(){
|
|||||||
enabled(){
|
enabled(){
|
||||||
// prepare variables and functions
|
// prepare variables and functions
|
||||||
var clearColumn = (columnName) => {
|
var clearColumn = (columnName) => {
|
||||||
TD.controller.columnManager.get(columnName).clear();
|
let col = TD.controller.columnManager.get(columnName);
|
||||||
|
return if !col.isClearable();
|
||||||
|
|
||||||
|
col.clear();
|
||||||
TD.controller.stats.columnActionClick("clear");
|
TD.controller.stats.columnActionClick("clear");
|
||||||
};
|
};
|
||||||
|
|
||||||
var resetColumn = (columnName) => {
|
var resetColumn = (columnName) => {
|
||||||
var col = TD.controller.columnManager.get(columnName);
|
let col = TD.controller.columnManager.get(columnName);
|
||||||
|
return if !col.isClearable();
|
||||||
|
|
||||||
col.model.setClearedTimestamp(0);
|
col.model.setClearedTimestamp(0);
|
||||||
col.reloadTweets();
|
col.reloadTweets();
|
||||||
};
|
};
|
||||||
@@ -38,7 +43,7 @@ enabled(){
|
|||||||
$(document).off("mousemove", this.eventKeyUp);
|
$(document).off("mousemove", this.eventKeyUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#clear-columns-btn-all").text(pressed ? "Restore columns" : "Clear columns");
|
$("#clear-columns-btn-all-1,#clear-columns-btn-all-2").text(pressed ? "Restore columns" : "Clear columns");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,7 +86,7 @@ enabled(){
|
|||||||
// add column buttons and keyboard shortcut info to UI
|
// add column buttons and keyboard shortcut info to UI
|
||||||
replaceMustache("column/column_header.mustache", "</header>", [
|
replaceMustache("column/column_header.mustache", "</header>", [
|
||||||
'{{^isTemporary}}',
|
'{{^isTemporary}}',
|
||||||
'<a class="column-header-link" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">',
|
'<a class="column-header-link td-clear-column-shortcut" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">',
|
||||||
'<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></i>',
|
'<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></i>',
|
||||||
'</a>',
|
'</a>',
|
||||||
'{{/isTemporary}}',
|
'{{/isTemporary}}',
|
||||||
@@ -98,11 +103,15 @@ enabled(){
|
|||||||
|
|
||||||
// load custom style
|
// load custom style
|
||||||
var css = window.TDPF_createCustomStyle(this);
|
var css = window.TDPF_createCustomStyle(this);
|
||||||
|
css.insert(".js-app-add-column.is-hidden + #clear-columns-btn-all-parent-1 { display: none; }");
|
||||||
|
css.insert(".column-navigator-overflow #clear-columns-btn-all-parent-2 { display: none; }");
|
||||||
css.insert(".column-title { margin-right: 60px !important; }");
|
css.insert(".column-title { margin-right: 60px !important; }");
|
||||||
css.insert(".column-type-message .column-title { margin-right: 115px !important; }");
|
|
||||||
css.insert(".mark-all-read-link { right: 59px !important; }");
|
css.insert(".mark-all-read-link { right: 59px !important; }");
|
||||||
css.insert(".open-compose-dm-link { right: 90px !important; }");
|
css.insert(".open-compose-dm-link { right: 90px !important; }");
|
||||||
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }");
|
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }");
|
||||||
|
css.insert("[data-td-icon='icon-message'] .column-title { margin-right: 115px !important; }");
|
||||||
|
css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }");
|
||||||
|
css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }");
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
@@ -113,18 +122,22 @@ ready(){
|
|||||||
$(document).on("keyup", this.eventKeyUp);
|
$(document).on("keyup", this.eventKeyUp);
|
||||||
|
|
||||||
// add clear all button
|
// add clear all button
|
||||||
$("nav.app-navigator").first().append([
|
const generateButton = (idParent, idButton) => {
|
||||||
'<a id="clear-columns-btn-all-parent" class="js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">',
|
return `
|
||||||
'<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>',
|
<a id="${idParent}" class="js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
|
||||||
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed txt-size--16">Clear columns</div>',
|
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
|
||||||
'</a></nav>'
|
<div id="${idButton}" class="nbfc padding-ts hide-condensed txt-size--16 app-nav-link-text">Clear columns</div>
|
||||||
].join(""));
|
</a>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
$(".js-app-add-column").first().after(generateButton("clear-columns-btn-all-parent-1", "clear-columns-btn-all-1"));
|
||||||
|
$(".js-column-nav-list").first().append(generateButton("clear-columns-btn-all-parent-2", "clear-columns-btn-all-2"));
|
||||||
|
|
||||||
// setup tooltip handling
|
// setup tooltip handling
|
||||||
var tooltipEvents = $._data($(".js-header-action")[0]).events;
|
var tooltipEvents = $._data($(".js-header-action")[0]).events;
|
||||||
|
|
||||||
if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length){
|
if (tooltipEvents.mouseover && tooltipEvents.mouseover.length && tooltipEvents.mouseout && tooltipEvents.mouseout.length){
|
||||||
$("#clear-columns-btn-all-parent").on("mouseover", tooltipEvents.mouseover[0].handler).on("mouseout", tooltipEvents.mouseout[0].handler);
|
$("#clear-columns-btn-all-parent-1,#clear-columns-btn-all-parent-2").on("mouseover", tooltipEvents.mouseover[0].handler).on("mouseout", tooltipEvents.mouseout[0].handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ Edit layout & design
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.2.3
|
1.2.5
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -23,6 +23,8 @@ enabled(){
|
|||||||
|
|
||||||
this.firstTimeLoad = null;
|
this.firstTimeLoad = null;
|
||||||
|
|
||||||
|
var me = this;
|
||||||
|
|
||||||
// modal dialog loading
|
// modal dialog loading
|
||||||
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
|
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
|
||||||
this.htmlModal = contents;
|
this.htmlModal = contents;
|
||||||
@@ -137,8 +139,6 @@ enabled(){
|
|||||||
};
|
};
|
||||||
|
|
||||||
// modal dialog setup
|
// modal dialog setup
|
||||||
var me = this;
|
|
||||||
|
|
||||||
var updateKey = function(key, value){
|
var updateKey = function(key, value){
|
||||||
me.config[key] = value;
|
me.config[key] = value;
|
||||||
|
|
||||||
@@ -238,7 +238,12 @@ enabled(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
// THEMES
|
// THEMES
|
||||||
let selectedTheme = me.config.themeOverride || TD.settings.getTheme();
|
let selectedTheme = TD.settings.getTheme();
|
||||||
|
|
||||||
|
if (selectedTheme === "dark" && me.config.themeOverride === "black"){
|
||||||
|
selectedTheme = me.config.themeOverride;
|
||||||
|
}
|
||||||
|
|
||||||
modal.find("[data-td-theme='"+selectedTheme+"']").prop("checked", true);
|
modal.find("[data-td-theme='"+selectedTheme+"']").prop("checked", true);
|
||||||
|
|
||||||
modal.find("[data-td-theme]").change(function(){
|
modal.find("[data-td-theme]").change(function(){
|
||||||
@@ -379,22 +384,26 @@ enabled(){
|
|||||||
this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
|
this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
|
||||||
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
|
||||||
|
|
||||||
|
let currentTheme = TD.settings.getTheme();
|
||||||
|
|
||||||
|
if (currentTheme === "dark" && this.config.themeOverride){
|
||||||
|
currentTheme = this.config.themeOverride;
|
||||||
|
}
|
||||||
|
|
||||||
let notificationScrollbarColor = null;
|
let notificationScrollbarColor = null;
|
||||||
|
|
||||||
if (this.config.themeColorTweaks){
|
if (this.config.themeColorTweaks){
|
||||||
switch(TD.settings.getTheme()){
|
switch(currentTheme){
|
||||||
case "dark":
|
case "black":
|
||||||
if (this.config.themeOverride === "black"){
|
|
||||||
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
|
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
|
||||||
this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
|
this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
|
||||||
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
|
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
|
||||||
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
|
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
|
||||||
notificationScrollbarColor = "666";
|
notificationScrollbarColor = "666";
|
||||||
}
|
break;
|
||||||
else{
|
|
||||||
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
case "dark":
|
||||||
|
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "light":
|
case "light":
|
||||||
@@ -539,10 +548,11 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
|
|||||||
document.head.appendChild(this.icons);
|
document.head.appendChild(this.icons);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.themeOverride === "black"){
|
if (currentTheme === "black"){
|
||||||
$TDP.readFileRoot(this.$token, "theme.black.css").then(contents => {
|
$TDP.readFileRoot(this.$token, "theme.black.css").then(contents => {
|
||||||
if (this.theme){
|
if (this.theme){
|
||||||
this.theme.element.innerHTML = contents;
|
this.theme.element.innerHTML = contents;
|
||||||
|
TD.settings.setTheme("dark"); // forces refresh of notification head tag
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -598,9 +608,9 @@ ${this.config.revertIcons ? `
|
|||||||
#tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important}
|
#tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important}
|
||||||
` : ``}
|
` : ``}
|
||||||
|
|
||||||
${this.config.themeOverride === "black" ? `
|
${currentTheme === "black" ? `
|
||||||
html.dark a, html.dark a:hover, html.dark a:focus, html.dark a:active { color: #8bd }
|
html.dark a, html.dark a:hover, html.dark a:focus, html.dark a:active { color: #8bd }
|
||||||
.btn-neutral-positive { color: #8bd !important }
|
#tduck-show-thread, .other-replies-link { color: #8bd !important }
|
||||||
.quoted-tweet { border-color: #292f33 !important }
|
.quoted-tweet { border-color: #292f33 !important }
|
||||||
` : ``}
|
` : ``}
|
||||||
|
|
||||||
|
@@ -92,63 +92,31 @@ html.dark .btn[disabled],html.dark .btn[disabled]:hover,html.dark .btn[disabled]
|
|||||||
html.dark .btn-on-dark:focus{box-shadow:0 0 0 1px #292F33,0 0 0 3px #71C9F8}
|
html.dark .btn-on-dark:focus{box-shadow:0 0 0 1px #292F33,0 0 0 3px #71C9F8}
|
||||||
html.dark .mdl-content .btn-on-dark:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
|
html.dark .mdl-content .btn-on-dark:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
|
||||||
html.dark .is-inverted-dark .btn:hover,html.dark .is-inverted-dark .btn:focus,html.dark .is-inverted-dark .btn:active,html.dark .is-inverted-dark .btn.is-selected{background-color:#F2F9FF}
|
html.dark .is-inverted-dark .btn:hover,html.dark .is-inverted-dark .btn:focus,html.dark .is-inverted-dark .btn:active,html.dark .is-inverted-dark .btn.is-selected{background-color:#F2F9FF}
|
||||||
html.dark .is-inverted-dark .btn-positive:hover,html.dark .is-inverted-dark .btn-positive-alt:hover,html.dark .is-inverted-dark .btn-fav.s-favorited:hover,html.dark .is-inverted-dark .s-following .follow-btn:hover,html.dark .s-following .is-inverted-dark .follow-btn:hover{background-color:#005FD1}
|
html.dark .is-inverted-dark .btn-fav.s-favorited:hover,html.dark .is-inverted-dark .s-following .follow-btn:hover,html.dark .s-following .is-inverted-dark .follow-btn:hover{background-color:#005FD1}
|
||||||
html.dark .is-inverted-dark .btn-positive:focus,html.dark .is-inverted-dark .btn-positive-alt:focus,html.dark .is-inverted-dark .btn-fav.s-favorited:focus,html.dark .is-inverted-dark .s-following .follow-btn:focus,html.dark .s-following .is-inverted-dark .follow-btn:focus{background-color:#005FD1}
|
html.dark .is-inverted-dark .btn-fav.s-favorited:focus,html.dark .is-inverted-dark .s-following .follow-btn:focus,html.dark .s-following .is-inverted-dark .follow-btn:focus{background-color:#005FD1}
|
||||||
html.dark .is-inverted-dark .btn-positive:active,html.dark .is-inverted-dark .btn-positive-alt:active,html.dark .is-inverted-dark .btn-fav.s-favorited:active,html.dark .is-inverted-dark .s-following .follow-btn:active,html.dark .s-following .is-inverted-dark .follow-btn:active,html.dark .is-inverted-dark .btn-positive.is-selected,html.dark .is-inverted-dark .is-selected.btn-positive-alt,html.dark .is-inverted-dark .is-selected.btn-fav.s-favorited,html.dark .is-inverted-dark .s-following .is-selected.follow-btn,html.dark .s-following .is-inverted-dark .is-selected.follow-btn{background-color:#005FD1}
|
html.dark .is-inverted-dark .btn-fav.s-favorited:active,html.dark .is-inverted-dark .s-following .follow-btn:active,html.dark .s-following .is-inverted-dark .follow-btn:active,html.dark .is-inverted-dark .is-selected.btn-fav.s-favorited,html.dark .is-inverted-dark .s-following .is-selected.follow-btn,html.dark .s-following .is-inverted-dark .is-selected.follow-btn{background-color:#005FD1}
|
||||||
html.dark .is-inverted-dark .btn-positive-alt:hover,html.dark .is-inverted-dark .btn-fav.s-favorited:hover,html.dark .is-inverted-dark .s-following .follow-btn:hover,html.dark .s-following .is-inverted-dark .follow-btn:hover{background-color:#A01744}
|
html.dark .is-inverted-dark .btn-fav.s-favorited:hover,html.dark .is-inverted-dark .s-following .follow-btn:hover,html.dark .s-following .is-inverted-dark .follow-btn:hover{background-color:#A01744}
|
||||||
html.dark .is-inverted-dark .btn-positive-alt:focus,html.dark .is-inverted-dark .btn-fav.s-favorited:focus,html.dark .is-inverted-dark .s-following .follow-btn:focus,html.dark .s-following .is-inverted-dark .follow-btn:focus{background-color:#A01744}
|
html.dark .is-inverted-dark .btn-fav.s-favorited:focus,html.dark .is-inverted-dark .s-following .follow-btn:focus,html.dark .s-following .is-inverted-dark .follow-btn:focus{background-color:#A01744}
|
||||||
html.dark .is-inverted-dark .btn-positive-alt:active,html.dark .is-inverted-dark .btn-fav.s-favorited:active,html.dark .is-inverted-dark .s-following .follow-btn:active,html.dark .s-following .is-inverted-dark .follow-btn:active,html.dark .is-inverted-dark .btn-positive-alt.is-selected,html.dark .is-inverted-dark .is-selected.btn-fav.s-favorited,html.dark .is-inverted-dark .s-following .is-selected.follow-btn,html.dark .s-following .is-inverted-dark .is-selected.follow-btn{background-color:#A01744}
|
html.dark .is-inverted-dark .btn-fav.s-favorited:active,html.dark .is-inverted-dark .s-following .follow-btn:active,html.dark .s-following .is-inverted-dark .follow-btn:active,html.dark .is-inverted-dark .is-selected.btn-fav.s-favorited,html.dark .is-inverted-dark .s-following .is-selected.follow-btn,html.dark .s-following .is-inverted-dark .is-selected.follow-btn{background-color:#A01744}
|
||||||
html.dark .is-inverted-dark .btn-negative:hover{background-color:#A01744}
|
html.dark .btn-fav.s-favorited,html.dark .s-following .follow-btn{color:#fff;background-color:#1DA1F2;border:1px solid #1DA1F2}
|
||||||
html.dark .is-inverted-dark .btn-negative:focus{background-color:#A01744}
|
html.dark .btn-fav.s-favorited:hover,html.dark .s-following .follow-btn:hover{color:#fff;background-color:#005FD1;border:1px solid #005FD1}
|
||||||
html.dark .is-inverted-dark .btn-negative:active,html.dark .is-inverted-dark .btn-negative.is-selected{background-color:#A01744}
|
html.dark .btn-fav.s-favorited:focus,html.dark .s-following .follow-btn:focus{color:#fff;background-color:#005FD1;border:1px solid #005FD1;box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
|
||||||
html.dark .is-inverted-dark .btn-tertiary:hover{background-color:#F5F8FA}
|
html.dark .btn-fav.s-favorited:active,html.dark .s-following .follow-btn:active,html.dark .is-selected.btn-fav.s-favorited,html.dark .s-following .is-selected.follow-btn{color:#fff;background-color:#005FD1;border:1px solid #005FD1}
|
||||||
html.dark .is-inverted-dark .btn-tertiary:focus{background-color:#F5F8FA}
|
html.dark [disabled].btn-fav.s-favorited,html.dark .s-following [disabled].follow-btn,html.dark [disabled].btn-fav.s-favorited:hover,html.dark .s-following [disabled].follow-btn:hover,html.dark [disabled].btn-fav.s-favorited:active,html.dark .s-following [disabled].follow-btn:active,html.dark .is-disabled.btn-fav.s-favorited,html.dark .s-following .is-disabled.follow-btn,html.dark .is-disabled.btn-fav.s-favorited:hover,html.dark .s-following .is-disabled.follow-btn:hover,html.dark .is-disabled.btn-fav.s-favorited:focus,html.dark .s-following .is-disabled.follow-btn:focus,html.dark .is-disabled.btn-fav.s-favorited:active,html.dark .s-following .is-disabled.follow-btn:active{color:#fff;background-color:#1DA1F2;border:1px solid #1DA1F2}
|
||||||
html.dark .is-inverted-dark .btn-tertiary:active,html.dark .is-inverted-dark .btn-tertiary.is-selected{background-color:#F5F8FA}
|
html.dark .btn-fav.s-favorited:hover,html.dark .s-following .follow-btn:hover{color:#fff;background-color:#A01744;border:#A01744}
|
||||||
html.dark .btn-positive,html.dark .btn-positive-alt,html.dark .btn-fav.s-favorited,html.dark .s-following .follow-btn{color:#fff;background-color:#1DA1F2;border:1px solid #1DA1F2}
|
html.dark .btn-fav.s-favorited:active,html.dark .s-following .follow-btn:active,html.dark .is-selected.btn-fav.s-favorited,html.dark .s-following .is-selected.follow-btn{color:#fff;background-color:#A01744;border:#A01744}
|
||||||
html.dark .btn-positive:hover,html.dark .btn-positive-alt:hover,html.dark .btn-fav.s-favorited:hover,html.dark .s-following .follow-btn:hover{color:#fff;background-color:#005FD1;border:1px solid #005FD1}
|
|
||||||
html.dark .btn-positive:focus,html.dark .btn-positive-alt:focus,html.dark .btn-fav.s-favorited:focus,html.dark .s-following .follow-btn:focus{color:#fff;background-color:#005FD1;border:1px solid #005FD1;box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
|
|
||||||
html.dark .btn-positive:active,html.dark .btn-positive-alt:active,html.dark .btn-fav.s-favorited:active,html.dark .s-following .follow-btn:active,html.dark .btn-positive.is-selected,html.dark .is-selected.btn-positive-alt,html.dark .is-selected.btn-fav.s-favorited,html.dark .s-following .is-selected.follow-btn{color:#fff;background-color:#005FD1;border:1px solid #005FD1}
|
|
||||||
html.dark .btn-positive[disabled],html.dark [disabled].btn-positive-alt,html.dark [disabled].btn-fav.s-favorited,html.dark .s-following [disabled].follow-btn,html.dark .btn-positive[disabled]:hover,html.dark [disabled].btn-positive-alt:hover,html.dark [disabled].btn-fav.s-favorited:hover,html.dark .s-following [disabled].follow-btn:hover,html.dark .btn-positive[disabled]:active,html.dark [disabled].btn-positive-alt:active,html.dark [disabled].btn-fav.s-favorited:active,html.dark .s-following [disabled].follow-btn:active,html.dark .btn-positive.is-disabled,html.dark .is-disabled.btn-positive-alt,html.dark .is-disabled.btn-fav.s-favorited,html.dark .s-following .is-disabled.follow-btn,html.dark .btn-positive.is-disabled:hover,html.dark .is-disabled.btn-positive-alt:hover,html.dark .is-disabled.btn-fav.s-favorited:hover,html.dark .s-following .is-disabled.follow-btn:hover,html.dark .btn-positive.is-disabled:focus,html.dark .is-disabled.btn-positive-alt:focus,html.dark .is-disabled.btn-fav.s-favorited:focus,html.dark .s-following .is-disabled.follow-btn:focus,html.dark .btn-positive.is-disabled:active,html.dark .is-disabled.btn-positive-alt:active,html.dark .is-disabled.btn-fav.s-favorited:active,html.dark .s-following .is-disabled.follow-btn:active{color:#fff;background-color:#1DA1F2;border:1px solid #1DA1F2}
|
|
||||||
html.dark .btn-compose{color:#fff;background-color:#2b7bb9}
|
|
||||||
html.dark .btn-compose:hover{color:#fff;background-color:#2b7bb9}
|
|
||||||
html.dark .btn-compose:focus{color:#fff;background-color:#2b7bb9}
|
|
||||||
html.dark .btn-compose:active,html.dark .btn-compose.is-selected{color:#fff;background-color:#2b7bb9}
|
|
||||||
html.dark .btn-positive-alt:hover,html.dark .btn-fav.s-favorited:hover,html.dark .s-following .follow-btn:hover{color:#fff;background-color:#A01744;border:#A01744}
|
|
||||||
html.dark .btn-positive-alt:active,html.dark .btn-fav.s-favorited:active,html.dark .s-following .follow-btn:active,html.dark .btn-positive-alt.is-selected,html.dark .is-selected.btn-fav.s-favorited,html.dark .s-following .is-selected.follow-btn{color:#fff;background-color:#A01744;border:#A01744}
|
|
||||||
html.dark .btn-negative{border-color:#E0245E;color:#fff;background-color:#E0245E}
|
|
||||||
html.dark .btn-negative:hover{color:#fff;background-color:#A01744;border-color:#A01744}
|
|
||||||
html.dark .btn-negative:focus{color:#fff;background-color:#A01744;border-color:#A01744;box-shadow:0 0 0 1px #fff,0 0 0 3px #F6809A}
|
|
||||||
html.dark .btn-negative:active,html.dark .btn-negative.is-selected{color:#fff;background-color:#A01744;border-color:#A01744}
|
|
||||||
html.dark .btn-negative[disabled],html.dark .btn-negative[disabled]:hover,html.dark .btn-negative[disabled]:active,html.dark .btn-negative.is-disabled,html.dark .btn-negative.is-disabled:hover,html.dark .btn-negative.is-disabled:focus,html.dark .btn-negative.is-disabled:active{border-color:#E0245E;color:#fff;background-color:#E0245E}
|
|
||||||
html.dark .btn-tertiary{border-color:#657786;color:#657786}
|
|
||||||
html.dark .btn-tertiary:hover{color:#657786;background-color:#F5F8FA;border-color:#657786}
|
|
||||||
html.dark .btn-tertiary:focus{color:#657786;background-color:#F5F8FA;border-color:#657786;box-shadow:0 0 0 1px #fff,0 0 0 3px #CCD6DD}
|
|
||||||
html.dark .btn-tertiary:active,html.dark .btn-tertiary.is-selected{color:#657786;background-color:#F5F8FA;border-color:#657786}
|
|
||||||
html.dark .btn-tertiary[disabled],html.dark .btn-tertiary[disabled]:hover,html.dark .btn-tertiary[disabled]:active,html.dark .btn-tertiary.is-disabled,html.dark .btn-tertiary.is-disabled:hover,html.dark .btn-tertiary.is-disabled:focus,html.dark .btn-tertiary.is-disabled:active{color:#AAB8C2;border-color:#e1e8ed;background-color:#eaeaea}
|
|
||||||
html.dark .btn-on-blue{color:#fff;background-color:#66757f}
|
html.dark .btn-on-blue{color:#fff;background-color:#66757f}
|
||||||
html.dark .btn-on-blue:hover{color:#fff;background-color:#66757f}
|
html.dark .btn-on-blue:hover{color:#fff;background-color:#66757f}
|
||||||
html.dark .btn-on-blue:focus{color:#fff;background-color:#66757f;box-shadow:0 0 2px 3px #50a5e6}
|
html.dark .btn-on-blue:focus{color:#fff;background-color:#66757f;box-shadow:0 0 2px 3px #50a5e6}
|
||||||
html.dark .btn-on-blue:active,html.dark .btn-on-blue.is-selected{color:#fff;background-color:#434c51}
|
html.dark .btn-on-blue:active,html.dark .btn-on-blue.is-selected{color:#fff;background-color:#434c51}
|
||||||
html.dark .btn-on-blue[disabled],html.dark .btn-on-blue[disabled]:hover,html.dark .btn-on-blue[disabled]:active,html.dark .btn-on-blue.is-disabled,html.dark .btn-on-blue.is-disabled:hover,html.dark .btn-on-blue.is-disabled:focus,html.dark .btn-on-blue.is-disabled:active{color:#fff;background-color:#66757f}
|
html.dark .btn-on-blue[disabled],html.dark .btn-on-blue[disabled]:hover,html.dark .btn-on-blue[disabled]:active,html.dark .btn-on-blue.is-disabled,html.dark .btn-on-blue.is-disabled:hover,html.dark .btn-on-blue.is-disabled:focus,html.dark .btn-on-blue.is-disabled:active{color:#fff;background-color:#66757f}
|
||||||
html.dark .btn-neutral-negative{color:#d29b9a}
|
|
||||||
html.dark .btn-neutral-negative:hover,html.dark .btn-neutral-negative:focus{color:#d29b9a}
|
|
||||||
html.dark .btn-neutral-negative[disabled],html.dark .btn-neutral-negative[disabled]:hover,html.dark .btn-neutral-negative[disabled]:active,html.dark .btn-neutral-negative.is-disabled,html.dark .btn-neutral-negative.is-disabled:hover,html.dark .btn-neutral-negative.is-disabled:focus,html.dark .btn-neutral-negative.is-disabled:active{color:#d29b9a}
|
|
||||||
html.dark .btn-neutral-positive{color:#8bd}
|
|
||||||
html.dark .btn-neutral-positive:hover,html.dark .btn-neutral-positive:focus{color:#8bd}
|
|
||||||
html.dark .btn-neutral-positive[disabled],html.dark .btn-neutral-positive[disabled]:hover,html.dark .btn-neutral-positive[disabled]:active,html.dark .btn-neutral-positive.is-disabled,html.dark .btn-neutral-positive.is-disabled:hover,html.dark .btn-neutral-positive.is-disabled:focus,html.dark .btn-neutral-positive.is-disabled:active{color:#8bd}
|
|
||||||
html.dark .btn-options-tray{color:#e1e8ed}
|
html.dark .btn-options-tray{color:#e1e8ed}
|
||||||
html.dark .btn-options-tray:hover,html.dark .btn-options-tray:focus{color:#8bd}
|
html.dark .btn-options-tray:hover,html.dark .btn-options-tray:focus{color:#8bd}
|
||||||
html.dark .btn-options-tray[disabled],html.dark .btn-options-tray[disabled]:hover,html.dark .btn-options-tray[disabled]:active,html.dark .btn-options-tray.is-disabled,html.dark .btn-options-tray.is-disabled:hover,html.dark .btn-options-tray.is-disabled:focus,html.dark .btn-options-tray.is-disabled:active{color:#8bd}
|
html.dark .btn-options-tray[disabled],html.dark .btn-options-tray[disabled]:hover,html.dark .btn-options-tray[disabled]:active,html.dark .btn-options-tray.is-disabled,html.dark .btn-options-tray.is-disabled:hover,html.dark .btn-options-tray.is-disabled:focus,html.dark .btn-options-tray.is-disabled:active{color:#8bd}
|
||||||
html.dark .btn-bg-positive{background-color:rgba(102,117,127,0.5)}
|
html.dark .btn-bg-positive{background-color:rgba(102,117,127,0.5)}
|
||||||
html.dark .btn-bg-positive:hover,html.dark .btn-bg-positive:focus{background-color:rgba(102,117,127,0.5)}
|
html.dark .btn-bg-positive:hover,html.dark .btn-bg-positive:focus{background-color:rgba(102,117,127,0.5)}
|
||||||
html.dark .btn-bg-positive[disabled],html.dark .btn-bg-positive[disabled]:hover,html.dark .btn-bg-positive[disabled]:active,html.dark .btn-bg-positive.is-disabled,html.dark .btn-bg-positive.is-disabled:hover,html.dark .btn-bg-positive.is-disabled:focus,html.dark .btn-bg-positive.is-disabled:active{background-color:rgba(102,117,127,0.5)}
|
html.dark .btn-bg-positive[disabled],html.dark .btn-bg-positive[disabled]:hover,html.dark .btn-bg-positive[disabled]:active,html.dark .btn-bg-positive.is-disabled,html.dark .btn-bg-positive.is-disabled:hover,html.dark .btn-bg-positive.is-disabled:focus,html.dark .btn-bg-positive.is-disabled:active{background-color:rgba(102,117,127,0.5)}
|
||||||
html.dark .btn-bg-negative{background-color:#5d5457}
|
|
||||||
html.dark .btn-bg-negative:hover,html.dark .btn-bg-negative:focus{background-color:#5d5457}
|
|
||||||
html.dark .btn-bg-negative[disabled],html.dark .btn-bg-negative[disabled]:hover,html.dark .btn-bg-negative[disabled]:active,html.dark .btn-bg-negative.is-disabled,html.dark .btn-bg-negative.is-disabled:hover,html.dark .btn-bg-negative.is-disabled:focus,html.dark .btn-bg-negative.is-disabled:active{background-color:#5d5457}
|
|
||||||
html.dark .btn-bg-white{background-color:#fff;color:#55acee}
|
|
||||||
html.dark .btn-bg-white:hover,html.dark .btn-bg-white:focus{background-color:#fff;color:#55acee}
|
|
||||||
html.dark .follow-btn .icon,html.dark .follow-btn .Icon{color:#1DA1F2}
|
html.dark .follow-btn .icon,html.dark .follow-btn .Icon{color:#1DA1F2}
|
||||||
html.dark .input-group-button{border:1px solid #e1e8ed}
|
|
||||||
html.dark .account-profile-header{background-color:#1DA1F2}
|
html.dark .account-profile-header{background-color:#1DA1F2}
|
||||||
html.dark .account-settings-bt{border-top:1px solid #e1e8ed}
|
html.dark .account-settings-bt{border-top:1px solid #e1e8ed}
|
||||||
html.dark .account-settings-bb{border-bottom:1px solid #e1e8ed}
|
html.dark .account-settings-bb{border-bottom:1px solid #e1e8ed}
|
||||||
@@ -290,7 +258,7 @@ html.dark .app-nav-tab{color:#8899A6}
|
|||||||
html.dark .app-nav-tab:hover{color:#fff}
|
html.dark .app-nav-tab:hover{color:#fff}
|
||||||
html.dark .app-nav-tab.is-selected{color:#292f33}
|
html.dark .app-nav-tab.is-selected{color:#292f33}
|
||||||
html.dark .app-nav-tab.is-selected:hover{color:#292f33}
|
html.dark .app-nav-tab.is-selected:hover{color:#292f33}
|
||||||
html.dark .attach-compose-buttons .tweet-button{background-color:#485865}
|
html.dark .attach-compose-buttons .tweet-button{background-color:#485865!important}
|
||||||
html.dark .with-nav-border-t:before{border-top:1px solid #777}
|
html.dark .with-nav-border-t:before{border-top:1px solid #777}
|
||||||
html.dark .app-search-input,html.dark .app-search-fake{background-color:#14171A;color:#aab8c2;box-shadow:inset 0 1px 1px rgba(17,17,17,0.5)}
|
html.dark .app-search-input,html.dark .app-search-fake{background-color:#14171A;color:#aab8c2;box-shadow:inset 0 1px 1px rgba(17,17,17,0.5)}
|
||||||
html.dark .app-search-input::-webkit-input-placeholder{color:#aab8c2}
|
html.dark .app-search-input::-webkit-input-placeholder{color:#aab8c2}
|
||||||
@@ -440,12 +408,6 @@ html.dark .char-count:disabled{color:#777}
|
|||||||
html.dark .over-char-count:disabled{color:#be1931}
|
html.dark .over-char-count:disabled{color:#be1931}
|
||||||
html.dark .cmp-replyto{background-color:#eaeaea;border-top:1px solid #ddd}
|
html.dark .cmp-replyto{background-color:#eaeaea;border-top:1px solid #ddd}
|
||||||
html.dark .s-link-added.s-photo-added p:last-child{border-top:1px solid #ddd}
|
html.dark .s-link-added.s-photo-added p:last-child{border-top:1px solid #ddd}
|
||||||
html.dark .accs li{background:#eaeaea;border:1px solid #e1e8ed}
|
|
||||||
html.dark .accs li:hover{background:#e1e8ed}
|
|
||||||
html.dark .accs .icon,html.dark .accs .Icon{color:#999}
|
|
||||||
html.dark .accs .acc-selected{background-color:#55acee;border:1px solid #e1e8ed}
|
|
||||||
html.dark .accs .acc-selected i{color:#fff}
|
|
||||||
html.dark .accs .acc-selected:hover{border-color:#e1e8ed;background-color:#50a5e6}
|
|
||||||
html.dark .inline-reply{background-color:#485865;color:#fff}
|
html.dark .inline-reply{background-color:#485865;color:#fff}
|
||||||
html.dark .inline-reply .btn-neutral,html.dark .inline-reply .character-count{color:#fff}
|
html.dark .inline-reply .btn-neutral,html.dark .inline-reply .character-count{color:#fff}
|
||||||
html.dark .reply-triangle{border-color:transparent transparent #485865}
|
html.dark .reply-triangle{border-color:transparent transparent #485865}
|
||||||
@@ -849,3 +811,4 @@ html.dark .DrawerModal{color:#14171A}
|
|||||||
/* fixes */
|
/* fixes */
|
||||||
html.dark .app-search-fake{border-color:transparent}
|
html.dark .app-search-fake{border-color:transparent}
|
||||||
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(80%)brightness(93%)}
|
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(80%)brightness(93%)}
|
||||||
|
html.dark .tweet>.color-twitter-blue{color:#8bd!important}
|
||||||
|
@@ -9,7 +9,7 @@ Emoji keyboard
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.4.2
|
1.4.4
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -32,6 +32,7 @@ enabled(){
|
|||||||
|
|
||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }");
|
this.css.insert(".emoji-keyboard { position: absolute; width: 15.35em; background-color: white; border-radius: 1px; font-size: 24px; z-index: 9999 }");
|
||||||
|
this.css.insert(".emoji-keyboard-popup-btn .icon { vertical-align: -4px !important }");
|
||||||
|
|
||||||
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
this.css.insert(".emoji-keyboard-list { height: 10.14em; padding: 0.1em; box-sizing: border-box; overflow-y: auto }");
|
||||||
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
this.css.insert(".emoji-keyboard-list .separator { height: 26px }");
|
||||||
@@ -49,7 +50,7 @@ enabled(){
|
|||||||
|
|
||||||
// layout
|
// layout
|
||||||
|
|
||||||
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--9 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
var buttonHTML = '<button class="needsclick btn btn-on-blue txt-left padding-v--6 padding-h--8 emoji-keyboard-popup-btn"><i class="icon icon-heart"></i></button>';
|
||||||
|
|
||||||
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="cf margin-t--12 margin-b--30">', '<div class="cf margin-t--12 margin-b--30">'+buttonHTML);
|
||||||
|
@@ -59,7 +59,19 @@ Remove unsupported emoji:
|
|||||||
> remove copyright
|
> remove copyright
|
||||||
> remove registered trademark
|
> remove registered trademark
|
||||||
> remove trademark
|
> remove trademark
|
||||||
0023 FE0F 20E3;keycap
|
> remove keycap #
|
||||||
|
002A FE0F 20E3;keycap *
|
||||||
|
> remove keycap 0
|
||||||
|
> remove keycap 1
|
||||||
|
> remove keycap 2
|
||||||
|
> remove keycap 3
|
||||||
|
> remove keycap 4
|
||||||
|
> remove keycap 5
|
||||||
|
> remove keycap 6
|
||||||
|
> remove keycap 7
|
||||||
|
> remove keycap 8
|
||||||
|
> remove keycap 9
|
||||||
|
1F51F;keycap 10
|
||||||
|
|
||||||
1F441;eye
|
1F441;eye
|
||||||
> remove eye in speech bubble
|
> remove eye in speech bubble
|
||||||
|
@@ -1354,18 +1354,7 @@
|
|||||||
2755;white exclamation mark
|
2755;white exclamation mark
|
||||||
2757;exclamation mark
|
2757;exclamation mark
|
||||||
3030;wavy dash
|
3030;wavy dash
|
||||||
0023 FE0F 20E3;keycap #
|
|
||||||
002A FE0F 20E3;keycap *
|
002A FE0F 20E3;keycap *
|
||||||
0030 FE0F 20E3;keycap 0
|
|
||||||
0031 FE0F 20E3;keycap 1
|
|
||||||
0032 FE0F 20E3;keycap 2
|
|
||||||
0033 FE0F 20E3;keycap 3
|
|
||||||
0034 FE0F 20E3;keycap 4
|
|
||||||
0035 FE0F 20E3;keycap 5
|
|
||||||
0036 FE0F 20E3;keycap 6
|
|
||||||
0037 FE0F 20E3;keycap 7
|
|
||||||
0038 FE0F 20E3;keycap 8
|
|
||||||
0039 FE0F 20E3;keycap 9
|
|
||||||
1F51F;keycap 10
|
1F51F;keycap 10
|
||||||
1F4AF;hundred points
|
1F4AF;hundred points
|
||||||
1F520;input latin uppercase
|
1F520;input latin uppercase
|
||||||
|
@@ -8,7 +8,7 @@ Custom reply account
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.2.4
|
1.3
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -12,10 +12,16 @@ enabled(){
|
|||||||
|
|
||||||
if (configuration.useAdvancedSelector){
|
if (configuration.useAdvancedSelector){
|
||||||
if (configuration.customSelector){
|
if (configuration.customSelector){
|
||||||
if (configuration.customSelector.toString().startsWith("function (column){")){
|
let customSelectorDef = configuration.customSelector.toString();
|
||||||
|
|
||||||
|
if (customSelectorDef.startsWith("function (column){")){
|
||||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector needs to be updated due to TweetDeck changes, please read the default configuration file for the updated guide");
|
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector needs to be updated due to TweetDeck changes, please read the default configuration file for the updated guide");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (customSelectorDef.startsWith("function (type,")){
|
||||||
|
$TD.alert("warning", "Plugin reply-account has invalid configuration: the type parameter is no longer present due to TweetDeck changes, please read the default configuration file for the updated guide");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var section = data.element.closest("section.js-column");
|
var section = data.element.closest("section.js-column");
|
||||||
|
|
||||||
@@ -35,7 +41,7 @@ enabled(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column, section.hasClass("column-temp"));
|
query = configuration.customSelector(columnTitle, columnAccount, column, section.hasClass("column-temp"));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
|
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
|
||||||
return;
|
return;
|
||||||
|
@@ -30,14 +30,19 @@
|
|||||||
* https://tweetduck.chylex.com/guide/#dev-tools
|
* https://tweetduck.chylex.com/guide/#dev-tools
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* The 'type' parameter is TweetDeck column type. Here is the full list of column types, note that some are
|
* In order to check the column type, use the 'column.isOfType' function. It is recommended to always put it
|
||||||
* unused and have misleading names (for example, Home columns are 'col_timeline' instead of 'col_home'):
|
* last in an 'if' statement, because it is much more demanding than checking the title/account.
|
||||||
* col_timeline, col_interactions, col_mentions, col_followers, col_search, col_list,
|
*
|
||||||
* col_customtimeline, col_messages, col_usertweets, col_favorites, col_activity,
|
* Here is the full list of column types, note that some are unused and have misleading names.
|
||||||
* col_dataminr, col_home, col_me, col_inbox, col_scheduled, col_unknown
|
* (for example, Home columns are 'col_timeline' instead of 'col_home')
|
||||||
|
*
|
||||||
|
* col_activity, col_customtimeline, col_dataminr, col_favorites, col_followers, col_home,
|
||||||
|
* col_inbox, col_interactions, col_list, col_livevideo, col_me, col_mentions,
|
||||||
|
* col_messages, col_scheduled, col_search, col_timeline, col_usertweets, col_unknown
|
||||||
*
|
*
|
||||||
* If you want to see your current column types, run this in your browser console:
|
* If you want to see your current column types, run this in your browser console:
|
||||||
* TD.controller.columnManager.getAllOrdered().map(obj => obj.getColumnType());
|
*
|
||||||
|
* (c=>c.columnManager.getAllOrdered().map(o=>Object.keys(c.stats.columnNamespaces).find(t=>o.isOfType(t))).map(t=>t==""+void 0?"col_unknown":t))(TD.controller)
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* The 'title' parameter is the column title. Some are fixed (such as 'Home' or 'Notifications'),
|
* The 'title' parameter is the column title. Some are fixed (such as 'Home' or 'Notifications'),
|
||||||
@@ -61,16 +66,16 @@
|
|||||||
|
|
||||||
useAdvancedSelector: false,
|
useAdvancedSelector: false,
|
||||||
|
|
||||||
customSelector: function(type, title, account, column, isTemporary){
|
customSelector: function(title, account, column, isTemporary){
|
||||||
console.info(arguments); // Prints all arguments into the console
|
console.info(arguments); // Prints all arguments into the console
|
||||||
|
|
||||||
if (type === "col_search" && title === "TweetDuck"){
|
if (title === "TweetDuck" && column.isOfType("col_search")){
|
||||||
// This is a search column that looks for 'TweetDuck' in the tweets,
|
// This is a search column that looks for 'TweetDuck' in the tweets,
|
||||||
// search columns are normally linked to the preferred account
|
// search columns are normally linked to the preferred account
|
||||||
// so this forces the @TryTweetDuck account to be used instead
|
// so this forces the @TryTweetDuck account to be used instead
|
||||||
return "@TryTweetDuck";
|
return "@TryTweetDuck";
|
||||||
}
|
}
|
||||||
else if (type === "col_timeline" && account === "@chylexcz"){
|
else if (account === "@chylexcz" && column.isOfType("col_timeline")){
|
||||||
// This is a Home column of my test account @chylexcz,
|
// This is a Home column of my test account @chylexcz,
|
||||||
// but I want to reply to tweets from my official account
|
// but I want to reply to tweets from my official account
|
||||||
return "@chylexmc";
|
return "@chylexmc";
|
||||||
|
@@ -8,7 +8,7 @@ Templates
|
|||||||
chylex
|
chylex
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
1.0.3
|
1.0.4
|
||||||
|
|
||||||
[website]
|
[website]
|
||||||
https://tweetduck.chylex.com
|
https://tweetduck.chylex.com
|
||||||
|
@@ -34,7 +34,7 @@ enabled(){
|
|||||||
|
|
||||||
// button
|
// button
|
||||||
|
|
||||||
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--9"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
|
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
|
||||||
|
|
||||||
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="js-tweet-type-button">', buttonHTML+'<div class="js-tweet-type-button">');
|
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="js-tweet-type-button">', buttonHTML+'<div class="js-tweet-type-button">');
|
||||||
|
@@ -1,32 +1,77 @@
|
|||||||
Param([Parameter(Mandatory = $True, Position = 1)][string] $dir)
|
Param(
|
||||||
|
[Parameter(Mandatory = $True, Position = 1)][string] $targetDir,
|
||||||
|
[Parameter(Mandatory = $True, Position = 2)][string] $projectDir,
|
||||||
|
[Parameter(Position = 3)][string] $version = ""
|
||||||
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
Set-Location $dir
|
try{
|
||||||
|
Write-Host "------------------------------"
|
||||||
|
|
||||||
function Check-Carriage-Return{
|
if ($version.Equals("")){
|
||||||
Param([Parameter(Mandatory = $True, Position = 1)] $fname)
|
$version = (Get-Item (Join-Path $targetDir "TweetDuck.exe")).VersionInfo.FileVersion
|
||||||
|
}
|
||||||
|
|
||||||
$file = @(Get-ChildItem -Include $fname -Recurse)[0]
|
Write-Host "TweetDuck version" $version
|
||||||
|
|
||||||
|
Write-Host "------------------------------"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
|
||||||
|
if (Test-Path (Join-Path $targetDir "locales")){
|
||||||
|
Remove-Item -Path (Join-Path $targetDir "locales\*.pak") -Exclude "en-US.pak"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy resources
|
||||||
|
|
||||||
|
Copy-Item (Join-Path $projectDir "bld\Resources\CEFSHARP-LICENSE.txt") -Destination $targetDir -Force
|
||||||
|
Copy-Item (Join-Path $projectDir "LICENSE.md") -Destination (Join-Path $targetDir "LICENSE.txt") -Force
|
||||||
|
|
||||||
|
New-Item -ItemType directory -Path $targetDir -Name "scripts" | Out-Null
|
||||||
|
New-Item -ItemType directory -Path $targetDir -Name "plugins" | Out-Null
|
||||||
|
New-Item -ItemType directory -Path $targetDir -Name "plugins\official" | Out-Null
|
||||||
|
New-Item -ItemType directory -Path $targetDir -Name "plugins\user" | Out-Null
|
||||||
|
|
||||||
|
Copy-Item (Join-Path $projectDir "Resources\Scripts\*") -Destination (Join-Path $targetDir "scripts") -Recurse
|
||||||
|
Copy-Item (Join-Path $projectDir "Resources\Plugins\*") -Exclude ".debug", "emoji-instructions.txt" -Destination (Join-Path $targetDir "plugins\official") -Recurse
|
||||||
|
|
||||||
|
# Post processing
|
||||||
|
|
||||||
|
function Check-Carriage-Return{
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory = $True, Position = 1)] $fname
|
||||||
|
)
|
||||||
|
|
||||||
|
$file = @(Get-ChildItem -Path $targetDir -Include $fname -Recurse)[0]
|
||||||
|
|
||||||
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){
|
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){
|
||||||
Throw "$fname cannot contain carriage return"
|
Throw "$fname cannot contain carriage return"
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "Verified" $file.FullName.Substring($dir.Length)
|
Write-Host "Verified" $file.FullName.Substring($targetDir.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
|
||||||
|
)
|
||||||
|
|
||||||
|
$relativePath = $file.FullName.Substring($targetDir.Length)
|
||||||
|
|
||||||
|
if ($relativePath.StartsWith("scripts\")){
|
||||||
|
$lines = (,("#" + $version) + $lines)
|
||||||
|
}
|
||||||
|
|
||||||
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName
|
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName
|
||||||
Write-Host "Processed" $file.FullName.Substring($dir.Length)
|
Write-Host "Processed" $relativePath
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
|
||||||
Check-Carriage-Return("emoji-ordering.txt")
|
Check-Carriage-Return("emoji-ordering.txt")
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Filter *.js -Exclude configuration.default.js -Recurse){
|
ForEach($file in Get-ChildItem -Path $targetDir -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'
|
||||||
@@ -34,7 +79,7 @@ try{
|
|||||||
,$lines | Rewrite-File $file
|
,$lines | Rewrite-File $file
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Filter *.css -Recurse){
|
ForEach($file in Get-ChildItem -Path $targetDir -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;'
|
||||||
@@ -42,10 +87,12 @@ try{
|
|||||||
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
|
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach($file in Get-ChildItem -Filter *.html -Recurse){
|
ForEach($file in Get-ChildItem -Path $targetDir -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Host "------------------------------"
|
||||||
}catch{
|
}catch{
|
||||||
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
||||||
Write-Host $_
|
Write-Host $_
|
||||||
|
@@ -2,36 +2,155 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
#if DEBUG
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using TweetDuck.Core;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace TweetDuck.Resources{
|
namespace TweetDuck.Resources{
|
||||||
static class ScriptLoader{
|
static class ScriptLoader{
|
||||||
private const string UrlPrefix = "td:";
|
public static string LoadResource(string name, bool silent = false, Control sync = null){
|
||||||
|
|
||||||
public static string LoadResource(string name, bool silent = false){
|
|
||||||
try{
|
try{
|
||||||
return File.ReadAllText(Path.Combine(Program.ScriptPath, name), Encoding.UTF8);
|
string path = Program.ScriptPath;
|
||||||
}catch(Exception ex){
|
|
||||||
if (!silent){
|
#if DEBUG
|
||||||
FormMessage.Error("TweetDuck Has Failed :(", "Unfortunately, TweetDuck could not load the "+name+" file. The program will continue running with limited functionality.\n\n"+ex.Message, FormMessage.OK);
|
if (Directory.Exists(HotSwapTargetDir)){
|
||||||
|
path = Path.Combine(HotSwapTargetDir, "scripts");
|
||||||
|
Debug.WriteLine("Hot swap active, redirecting "+name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
string contents = File.ReadAllText(Path.Combine(path, name), Encoding.UTF8);
|
||||||
|
int separator;
|
||||||
|
|
||||||
|
// first line can be either:
|
||||||
|
// #<version>\r\n
|
||||||
|
// #<version>\n
|
||||||
|
|
||||||
|
if (contents[0] != '#'){
|
||||||
|
ShowLoadError(silent, sync, $"File {name} appears to be corrupted, please try reinstalling the app.");
|
||||||
|
separator = 0;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
separator = contents.IndexOf('\n');
|
||||||
|
string fileVersion = contents.Substring(1, separator-1).TrimEnd();
|
||||||
|
|
||||||
|
if (fileVersion != Program.VersionTag){
|
||||||
|
ShowLoadError(silent, sync, $"File {name} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return contents.Substring(separator).TrimStart();
|
||||||
|
}catch(Exception ex){
|
||||||
|
ShowLoadError(silent, sync, $"Could not load {name}. The program will continue running with limited functionality.\n\n{ex.Message}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteFile(IFrame frame, string file){
|
public static bool ExecuteFile(IFrame frame, string file, Control sync = null){
|
||||||
ExecuteScript(frame, LoadResource(file), GetRootIdentifier(file));
|
string script = LoadResource(file, sync == null, sync);
|
||||||
|
ExecuteScript(frame, script, GetRootIdentifier(file));
|
||||||
|
return script != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ExecuteScript(IFrame frame, string script, string identifier){
|
public static void ExecuteScript(IFrame frame, string script, string identifier){
|
||||||
if (script != null){
|
if (script != null){
|
||||||
frame.ExecuteJavaScriptAsync(script, UrlPrefix+identifier, 1);
|
frame.ExecuteJavaScriptAsync(script, "td:"+identifier, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetRootIdentifier(string file){
|
public static string GetRootIdentifier(string file){
|
||||||
return "root:"+Path.GetFileNameWithoutExtension(file);
|
return "root:"+Path.GetFileNameWithoutExtension(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ShowLoadError(bool silent, Control sync, string message){
|
||||||
|
if (silent){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync == null){
|
||||||
|
FormMessage.Error("Resource Error", message, FormMessage.OK);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sync.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
|
||||||
|
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
|
||||||
|
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "Resources", "PostBuild.ps1");
|
||||||
|
|
||||||
|
static ScriptLoader(){
|
||||||
|
if (File.Exists(HotSwapRebuildScript)){
|
||||||
|
Debug.WriteLine("Activating resource hot swap");
|
||||||
|
|
||||||
|
ResetHotSwap();
|
||||||
|
Application.ApplicationExit += (sender, args) => ResetHotSwap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HotSwap(){
|
||||||
|
if (!File.Exists(HotSwapRebuildScript)){
|
||||||
|
Debug.WriteLine("Failed resource hot swap, missing rebuild script: "+HotSwapRebuildScript);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetHotSwap();
|
||||||
|
Directory.CreateDirectory(HotSwapTargetDir);
|
||||||
|
|
||||||
|
using(Process process = Process.Start(new ProcessStartInfo{
|
||||||
|
FileName = "powershell",
|
||||||
|
Arguments = $"-ExecutionPolicy Unrestricted -File \"{HotSwapRebuildScript}\" \"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"{Program.VersionTag}\"",
|
||||||
|
WindowStyle = ProcessWindowStyle.Hidden
|
||||||
|
})){
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
if (!process.WaitForExit(8000)){
|
||||||
|
Debug.WriteLine("Failed resource hot swap, script did not finish in time");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (process.ExitCode != 0){
|
||||||
|
Debug.WriteLine("Failed resource hot swap, script exited with code "+process.ExitCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force update plugin manager setup scripts
|
||||||
|
|
||||||
|
string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins");
|
||||||
|
|
||||||
|
const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||||
|
const BindingFlags flagsStatic = BindingFlags.Static | BindingFlags.NonPublic;
|
||||||
|
|
||||||
|
Type typePluginManager = typeof(PluginManager);
|
||||||
|
Type typeFormBrowser = typeof(FormBrowser);
|
||||||
|
|
||||||
|
// ReSharper disable PossibleNullReferenceException
|
||||||
|
object pluginSetupScripts = typePluginManager.GetMethod("LoadSetupScripts", flagsStatic).Invoke(null, new object[0]);
|
||||||
|
typePluginManager.GetField("PluginSetupScripts", flagsStatic).SetValue(null, pluginSetupScripts);
|
||||||
|
|
||||||
|
object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>());
|
||||||
|
typePluginManager.GetField("rootPath", flagsInstance).SetValue(instPluginManager, newPluginRoot);
|
||||||
|
|
||||||
|
Debug.WriteLine("Reloading hot swapped plugins...");
|
||||||
|
((PluginManager)instPluginManager).Reload();
|
||||||
|
// ReSharper restore PossibleNullReferenceException
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResetHotSwap(){
|
||||||
|
try{
|
||||||
|
Directory.Delete(HotSwapTargetDir, true);
|
||||||
|
}catch(DirectoryNotFoundException){}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FixPathSlash(string path){
|
||||||
|
return path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)+'\\';
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -98,6 +98,13 @@
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Retrieves column name
|
||||||
|
const getColumnName = function(column){
|
||||||
|
let cached = column._tduck_type || (column._tduck_type = Object.keys(columnTypes).find(type => column.isOfType(type)));
|
||||||
|
return columnTypes[cached] || "";
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Event callback for a new tweet.
|
// Function: Event callback for a new tweet.
|
||||||
//
|
//
|
||||||
@@ -199,7 +206,7 @@
|
|||||||
html.find(".js-user-actions-menu").parent().remove();
|
html.find(".js-user-actions-menu").parent().remove();
|
||||||
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
|
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
|
||||||
}
|
}
|
||||||
else if (type.startsWith("favorite") || type.startsWith("retweet")){
|
else if ((type.startsWith("favorite") || type.startsWith("retweet")) && tweet.isAboutYou()){
|
||||||
html.children().first().addClass("td-notification-padded");
|
html.children().first().addClass("td-notification-padded");
|
||||||
}
|
}
|
||||||
else if (type.includes("list_member")){
|
else if (type.includes("list_member")){
|
||||||
@@ -220,7 +227,7 @@
|
|||||||
let tweetUrl = source ? source.getChirpURL() : "";
|
let tweetUrl = source ? source.getChirpURL() : "";
|
||||||
let quoteUrl = source && source.quotedTweet ? source.quotedTweet.getChirpURL() : "";
|
let quoteUrl = source && source.quotedTweet ? source.quotedTweet.getChirpURL() : "";
|
||||||
|
|
||||||
$TD.onTweetPopup(column.model.privateState.apiid, chirpId, columnTypes[column.getColumnType()] || "", html.html(), duration, tweetUrl, quoteUrl);
|
$TD.onTweetPopup(column.model.privateState.apiid, chirpId, getColumnName(column), html.html(), duration, tweetUrl, quoteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column.model.getHasSound()){
|
if (column.model.getHasSound()){
|
||||||
@@ -284,6 +291,8 @@
|
|||||||
let fontSizeName = TD.settings.getFontSize();
|
let fontSizeName = TD.settings.getFontSize();
|
||||||
let themeName = TD.settings.getTheme();
|
let themeName = TD.settings.getTheme();
|
||||||
|
|
||||||
|
let columnBackground = getClassStyleProperty("column", "background-color");
|
||||||
|
|
||||||
let tags = [
|
let tags = [
|
||||||
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
|
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
|
||||||
];
|
];
|
||||||
@@ -292,21 +301,7 @@
|
|||||||
tags.push($(this)[0].outerHTML);
|
tags.push($(this)[0].outerHTML);
|
||||||
});
|
});
|
||||||
|
|
||||||
tags.push("<style type='text/css'>");
|
tags.push("<style type='text/css'>body { background: "+columnBackground+" !important }</style>");
|
||||||
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" !important }"); // set background color
|
|
||||||
tags.push("a[data-full-url] { word-break: break-all !important }"); // break long urls
|
|
||||||
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(".activity-header.has-source-avatar { margin-bottom: 4px !important }"); // tweak distance between avatar and text
|
|
||||||
tags.push(".activity-header .tweet-timestamp { line-height: unset !important }"); // fix timestamp position
|
|
||||||
tags.push(".activity-header .icon-user-filled { vertical-align: sub !important; }"); // fix follow icon position
|
|
||||||
tags.push("#tduck-show-thread { display: inline-block !important; cursor: pointer }");
|
|
||||||
|
|
||||||
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>");
|
|
||||||
|
|
||||||
doc.setAttribute("data-td-font", fontSizeName);
|
doc.setAttribute("data-td-font", fontSizeName);
|
||||||
doc.setAttribute("data-td-theme", themeName);
|
doc.setAttribute("data-td-theme", themeName);
|
||||||
@@ -540,7 +535,8 @@
|
|||||||
let me = $(this)[0];
|
let me = $(this)[0];
|
||||||
|
|
||||||
if (me.classList.contains("js-media-image-link") && highlightedTweetObj){
|
if (me.classList.contains("js-media-image-link") && highlightedTweetObj){
|
||||||
let media = (highlightedTweetObj.quotedTweet || highlightedTweetObj).getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
let tweet = highlightedTweetObj.hasMedia() ? highlightedTweetObj : highlightedTweetObj.quotedTweet;
|
||||||
|
let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
||||||
|
|
||||||
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
||||||
$TD.setLastRightClickInfo("video", media.chooseVideoVariant().url);
|
$TD.setLastRightClickInfo("video", media.chooseVideoVariant().url);
|
||||||
@@ -592,22 +588,15 @@
|
|||||||
// Block: Update highlighted column and tweet for context menu and other functionality.
|
// Block: Update highlighted column and tweet for context menu and other functionality.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
var lastTweet = "";
|
|
||||||
|
|
||||||
const 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
|
const updateHighlightedTweet = function(ele, obj){
|
||||||
highlightedTweetEle = ele;
|
highlightedTweetEle = ele;
|
||||||
highlightedTweetObj = obj;
|
highlightedTweetObj = obj;
|
||||||
|
|
||||||
if (lastTweet !== tweetUrl){
|
|
||||||
$TD.setLastHighlightedTweet(tweetUrl, quoteUrl, authors, imageList);
|
|
||||||
lastTweet = tweetUrl;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const processMedia = function(chirp){
|
const processMedia = function(chirp){
|
||||||
@@ -623,6 +612,19 @@
|
|||||||
|
|
||||||
mouseleave: function(){
|
mouseleave: function(){
|
||||||
updateHighlightedColumn(null);
|
updateHighlightedColumn(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
contextmenu: function(){
|
||||||
|
let tweet = highlightedTweetObj;
|
||||||
|
|
||||||
|
if (tweet && tweet.chirpType === TD.services.ChirpBase.TWEET){
|
||||||
|
let tweetUrl = tweet.getChirpURL();
|
||||||
|
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
||||||
|
let chirpAuthors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
||||||
|
let chirpImages = tweet.hasImage() ? processMedia(tweet) : tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : "";
|
||||||
|
|
||||||
|
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -634,21 +636,11 @@
|
|||||||
let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
|
let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
|
||||||
return if !tweet;
|
return if !tweet;
|
||||||
|
|
||||||
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
updateHighlightedTweet(me, tweet);
|
||||||
let tweetUrl = tweet.getChirpURL();
|
|
||||||
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
|
||||||
let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
|
||||||
let imageList = tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : tweet.hasImage() ? processMedia(tweet) : "";
|
|
||||||
|
|
||||||
updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
updateHighlightedTweet(me, tweet, "", "", "", "");
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mouseleave: function(){
|
mouseleave: function(){
|
||||||
updateHighlightedTweet(null, null, "", "", "", "");
|
updateHighlightedTweet(null, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
@@ -657,85 +649,52 @@
|
|||||||
// Block: Screenshot tweet to clipboard.
|
// Block: Screenshot tweet to clipboard.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
var selectedTweet;
|
|
||||||
|
|
||||||
const setImportantProperty = function(obj, property, value){
|
|
||||||
if (obj.length === 1){
|
|
||||||
obj[0].style.setProperty(property, value, "important");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
app.delegate("article.js-stream-item", "contextmenu", function(){
|
|
||||||
selectedTweet = $(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.TDGF_triggerScreenshot = function(){
|
window.TDGF_triggerScreenshot = function(){
|
||||||
if (selectedTweet){
|
return if !highlightedTweetObj || !highlightedColumnObj;
|
||||||
let tweetWidth = Math.floor(selectedTweet.width());
|
|
||||||
let parent = selectedTweet.parent();
|
|
||||||
|
|
||||||
let isDetail = parent.hasClass("js-tweet-detail");
|
let chirp = highlightedColumnObj.findChirp(highlightedTweetEle.attr("data-key")) || highlightedTweetObj;
|
||||||
let isReply = !isDetail && (parent.hasClass("js-replies-to") || parent.hasClass("js-replies-before"));
|
|
||||||
|
|
||||||
selectedTweet = selectedTweet.clone();
|
let columnWidth = highlightedColumnEle.width();
|
||||||
|
|
||||||
let container = selectedTweet.children().first();
|
let html = $(chirp.render({
|
||||||
container.addClass($(document.documentElement).attr("class"));
|
withFooter: false,
|
||||||
container.addClass($(document.body).attr("class"));
|
withTweetActions: false,
|
||||||
container.css("padding-bottom", "0");
|
isInConvo: false,
|
||||||
|
isFavorite: false,
|
||||||
|
isRetweeted: false, // keeps retweet mark above tweet
|
||||||
|
isPossiblySensitive: false,
|
||||||
|
mediaPreviewSize: highlightedColumnObj.getMediaPreviewSize()
|
||||||
|
}));
|
||||||
|
|
||||||
setImportantProperty(selectedTweet.find(".js-tweet-text"), "margin-bottom", "8px");
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||||
setImportantProperty(selectedTweet.find(".js-quote-detail"), "margin-bottom", "10px");
|
html.find(".td-screenshot-remove").remove();
|
||||||
setImportantProperty(selectedTweet.find(".js-poll-link").next(), "margin-bottom", "8px");
|
|
||||||
|
|
||||||
if (isDetail){
|
html.find("p.link-complex-target").filter(function(){
|
||||||
if (selectedTweet.find("[class*='media-grid-']").length > 0){
|
|
||||||
setImportantProperty(selectedTweet.find(".js-tweet-media"), "margin-bottom", "10px");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
setImportantProperty(selectedTweet.find(".js-tweet-media"), "margin-bottom", "6px");
|
|
||||||
}
|
|
||||||
|
|
||||||
setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "4px");
|
|
||||||
selectedTweet.find(".js-translate-call-to-action").first().remove();
|
|
||||||
selectedTweet.find(".js-tweet").first().nextAll().remove();
|
|
||||||
selectedTweet.find("footer").last().prevUntil(":not(.txt-mute)").addBack().remove(); // footer, date, location
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
setImportantProperty(selectedTweet.find(".js-media-preview-container").filter(function(){
|
|
||||||
return $(this).closest(".js-quote-detail").length === 0;
|
|
||||||
}), "margin-bottom", "10px");
|
|
||||||
|
|
||||||
selectedTweet.find("footer").last().remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isReply){
|
|
||||||
selectedTweet.find(".is-conversation").removeClass("is-conversation");
|
|
||||||
selectedTweet.find(".thread").remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedTweet.find("p.link-complex-target").filter(function(){
|
|
||||||
return $(this).text() === "Show this thread";
|
return $(this).text() === "Show this thread";
|
||||||
}).first().remove();
|
}).first().remove();
|
||||||
|
|
||||||
selectedTweet.find(".js-poll-link").remove();
|
html.addClass($(document.documentElement).attr("class"));
|
||||||
selectedTweet.find(".td-screenshot-remove").remove();
|
html.addClass($(document.body).attr("class"));
|
||||||
|
|
||||||
let testTweet = selectedTweet.clone().css({
|
html.css("background-color", getClassStyleProperty("column", "background-color"));
|
||||||
position: "absolute",
|
html.css("border", "none");
|
||||||
left: "-999px",
|
|
||||||
width: tweetWidth+"px"
|
|
||||||
}).appendTo(document.body);
|
|
||||||
|
|
||||||
let testTweetAvatar = testTweet.find(".tweet-avatar").first();
|
for(let selector of [ ".js-quote-detail", ".js-media-preview-container", ".js-media" ]){
|
||||||
let avatarBottom = testTweetAvatar.offset().top+testTweetAvatar.height();
|
let ele = html.find(selector);
|
||||||
|
|
||||||
let realHeight = Math.floor(Math.max(testTweet.height(), avatarBottom+10));
|
if (ele.length){
|
||||||
testTweet.remove();
|
ele[0].style.setProperty("margin-bottom", "2px", "important");
|
||||||
|
break;
|
||||||
selectedTweet.find(".js-stream-item-content").first().css("height", "100vh");
|
|
||||||
$TD.screenshotTweet(selectedTweet.html(), tweetWidth, realHeight);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let type = chirp.getChirpType();
|
||||||
|
|
||||||
|
if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){
|
||||||
|
html.addClass("td-notification-padded");
|
||||||
|
}
|
||||||
|
|
||||||
|
$TD.screenshotTweet(html[0].outerHTML, columnWidth);
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -926,26 +885,15 @@
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Swap shift key functionality for selecting accounts, and refocus the textbox afterwards.
|
// Block: Refocus the textbox after switching accounts.
|
||||||
//
|
//
|
||||||
onAppReady.push(function(){
|
onAppReady.push(function(){
|
||||||
const onAccountClick = function(e){
|
const refocusInput = function(){
|
||||||
if ($TDX.switchAccountSelectors){
|
|
||||||
e.shiftKey = !e.shiftKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(".js-compose-text", ".js-docked-compose").focus();
|
$(".js-compose-text", ".js-docked-compose").focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", onAccountClick);
|
$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", function(e){
|
||||||
|
setTimeout(refocusInput, 0);
|
||||||
return if !ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts");
|
|
||||||
|
|
||||||
TD.components.AccountSelector.prototype.refreshPostingAccounts = appendToFunction(TD.components.AccountSelector.prototype.refreshPostingAccounts, function(){
|
|
||||||
if (!this.$node.attr("td-account-selector-hook")){
|
|
||||||
this.$node.attr("td-account-selector-hook", "1");
|
|
||||||
this.$node.delegate(".js-account-item", "click", onAccountClick);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1203,9 +1151,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!("tweetduck" in data)){
|
||||||
$(".js-app-search-input").val("");
|
$(".js-app-search-input").val("");
|
||||||
$(".js-perform-search").blur();
|
$(".js-perform-search").blur();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Setup global function to add a search column with the specified query.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
let context = $._data(document, "events")["uiSearchInputSubmit"][0].handler.context;
|
||||||
|
|
||||||
|
window.TDGF_performSearch = function(query){
|
||||||
|
context.performSearch({ query, tweetduck: true });
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Reorder search results to move accounts above hashtags.
|
||||||
|
//
|
||||||
|
onAppReady.push(function(){
|
||||||
|
let container = $(".js-search-in-popover");
|
||||||
|
let hashtags = $(".js-typeahead-topic-list", container);
|
||||||
|
|
||||||
|
$(".js-typeahead-user-list", container).insertBefore(hashtags);
|
||||||
|
hashtags.addClass("list-divider");
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -1345,6 +1317,11 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix columns missing any identifiable attributes to allow individual styles.
|
||||||
|
//
|
||||||
|
TD.mustaches["column.mustache"] = TD.mustaches["column.mustache"].replace("{{columnclass}}\"", "{{columnclass}}\" data-td-icon=\"{{columniconclass}}\"");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Remove column mouse wheel handler, which allows smooth scrolling inside columns, and horizontally scrolling column container when holding Shift.
|
// Block: Remove column mouse wheel handler, which allows smooth scrolling inside columns, and horizontally scrolling column container when holding Shift.
|
||||||
//
|
//
|
||||||
@@ -1404,6 +1381,8 @@
|
|||||||
window.gc && window.gc();
|
window.gc && window.gc();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window.TD_SESSION && window.TD_SESSION.gc){
|
if (window.TD_SESSION && window.TD_SESSION.gc){
|
||||||
|
@@ -98,7 +98,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`).appendTo(".js-app");
|
</div>`).appendTo(".js-app"); /* TODO btn-positive is removed, check all files again */
|
||||||
|
|
||||||
let tdUser = null;
|
let tdUser = null;
|
||||||
let loadTweetDuckUser = (onSuccess, onError) => {
|
let loadTweetDuckUser = (onSuccess, onError) => {
|
||||||
|
@@ -14,9 +14,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Hook into links to bypass default open function and t.co.
|
// Block: Hook into links to bypass default open function and t.co, and handle skipping notification when opening links.
|
||||||
//
|
//
|
||||||
addEventListener(links, "click", function(e){
|
(function(){
|
||||||
|
const onLinkClick = function(e){
|
||||||
|
if (e.button === 0 || e.button === 1){
|
||||||
let ele = e.currentTarget;
|
let ele = e.currentTarget;
|
||||||
|
|
||||||
$TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
|
$TD.openBrowser(ele.hasAttribute("data-full-url") ? ele.getAttribute("data-full-url") : ele.getAttribute("href"));
|
||||||
@@ -29,7 +31,12 @@
|
|||||||
$TD.loadNextNotification();
|
$TD.loadNextNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addEventListener(links, "click", onLinkClick);
|
||||||
|
addEventListener(links, "auxclick", onLinkClick);
|
||||||
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Allow bypassing of t.co in context menus.
|
// Block: Allow bypassing of t.co in context menus.
|
||||||
|
15
Resources/Scripts/screenshot.js
Normal file
15
Resources/Scripts/screenshot.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
(function($TD){
|
||||||
|
let ele = document.getElementsByTagName("article")[0];
|
||||||
|
ele.style.width = "{width}px";
|
||||||
|
|
||||||
|
ele.style.position = "absolute";
|
||||||
|
let contentHeight = ele.offsetHeight;
|
||||||
|
ele.style.position = "static";
|
||||||
|
|
||||||
|
let avatar = ele.querySelector(".tweet-avatar");
|
||||||
|
let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
|
||||||
|
|
||||||
|
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => {
|
||||||
|
setTimeout($TD.triggerScreenshot, document.getElementsByTagName("iframe").length ? 267 : 67);
|
||||||
|
});
|
||||||
|
})($TD_NotificationScreenshot);
|
@@ -38,7 +38,7 @@
|
|||||||
/* Square-ify stuff */
|
/* Square-ify stuff */
|
||||||
/********************/
|
/********************/
|
||||||
|
|
||||||
.btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
|
button, .btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
|
||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,14 +46,60 @@
|
|||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-compose, .app-search-fake, .app-search-input, .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, .detail-view-inline-text {
|
.tweet-button, .app-search-fake, .app-search-input, .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, .detail-view-inline-text {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select, .prf-header, .accs li, .accs img {
|
.dropdown-menu, .list-item-last, .quoted-tweet, input, textarea, select, .prf-header {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************************/
|
||||||
|
/* Revert various design decisions */
|
||||||
|
/***********************************/
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tweet-button:not(:hover) {
|
||||||
|
background-color: #2B7BB9 !important;
|
||||||
|
border-color: #2B7BB9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tduck .js-compose-scroller button.full-width {
|
||||||
|
padding: 7px 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tduck .js-compose-scroller button.full-width > i {
|
||||||
|
font-size: 20px !important;
|
||||||
|
vertical-align: -5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-show-drawer {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-show-drawer .icon-compose {
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-show-drawer span::before {
|
||||||
|
content: "New ";
|
||||||
|
}
|
||||||
|
|
||||||
|
#tduck .app-header .search-input-control input {
|
||||||
|
font-size: 13px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tduck .app-header .search-input-perform-search {
|
||||||
|
right: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-account-safeguard-checkbox, .js-account-safeguard-checkbox label {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************/
|
/***********************/
|
||||||
/* Hide TweetDeck logo */
|
/* Hide TweetDeck logo */
|
||||||
/***********************/
|
/***********************/
|
||||||
@@ -118,19 +164,19 @@ html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
|
|||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-item[data-key^="favorite"] .item-img, .stream-item[data-key^="retweet"] .item-img {
|
.stream-item[data-key^="favorite"] .has-source-avatar .item-img, .stream-item[data-key^="retweet"] .has-source-avatar .item-img {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 21px;
|
left: 21px;
|
||||||
top: 48px;
|
top: 48px;
|
||||||
width: 0 !important;
|
width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-item[data-key^="favorite"] .activity-header > .nbfc, .stream-item[data-key^="retweet"] .activity-header > .nbfc {
|
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc, .stream-item[data-key^="retweet"] .has-source-avatar > .nbfc {
|
||||||
margin-left: 46px;
|
margin-left: 46px;
|
||||||
line-height: unset !important;
|
line-height: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-item[data-key^="favorite"] .activity-header > .nbfc > .avatar, .stream-item[data-key^="retweet"] .activity-header > .nbfc > .avatar {
|
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc > .avatar, .stream-item[data-key^="retweet"] .has-source-avatar > .nbfc > .avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-left: -34px;
|
margin-left: -34px;
|
||||||
}
|
}
|
||||||
@@ -149,6 +195,11 @@ a[data-full-url] {
|
|||||||
color: #9f51cf !important;
|
color: #9f51cf !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.js-typeahead-user-list.is-hidden + .js-typeahead-topic-list {
|
||||||
|
/* hide custom search result divider when not needed */
|
||||||
|
border-top: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************/
|
/***************************************/
|
||||||
/* Replace 'Stay open' with a pin icon */
|
/* Replace 'Stay open' with a pin icon */
|
||||||
/***************************************/
|
/***************************************/
|
||||||
@@ -183,6 +234,12 @@ a[data-full-url] {
|
|||||||
vertical-align: -10% !important;
|
vertical-align: -10% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tduck .nav-user-info .hide-condensed {
|
||||||
|
/* move login account info */
|
||||||
|
margin: 0px !important;
|
||||||
|
padding-left: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
html[data-td-font='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;
|
||||||
@@ -204,11 +261,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
background-position: -223px -97px !important;
|
background-position: -223px -97px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.accs-header {
|
|
||||||
/* fix retweet account selector heading */
|
|
||||||
padding-left: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keyboard-shortcut-list {
|
.keyboard-shortcut-list {
|
||||||
/* fix keyboard navigation alignment */
|
/* fix keyboard navigation alignment */
|
||||||
vertical-align: top !important;
|
vertical-align: top !important;
|
||||||
@@ -219,17 +271,21 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-reply .btn-square, .rpl-actions .btn-square {
|
|
||||||
/* remove effects from buttons under reply input... this keeps happening for some stupid reason */
|
|
||||||
background: transparent !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.js-add-to-customtimeline-input {
|
.js-add-to-customtimeline-input {
|
||||||
/* the custom timeline input shadow is behaving super weird when focused */
|
/* the custom timeline input shadow is behaving super weird when focused */
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tduck .js-follow-button .icon-follow {
|
||||||
|
/* follow icon is too small which makes it aliased and misaligned */
|
||||||
|
font-size: 17px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-follow-button .following-text, .js-follow-button .unfollow-text {
|
||||||
|
/* unfollow button has a border for some reason */
|
||||||
|
border-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
|
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
@@ -294,18 +350,18 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
|||||||
/* Fix cut off usernames in Messages column */
|
/* Fix cut off usernames in Messages column */
|
||||||
/********************************************/
|
/********************************************/
|
||||||
|
|
||||||
.column-type-message.is-shifted-1 .column-title-container {
|
[data-td-icon="icon-message"].is-shifted-1 .column-title-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tduck .column-type-message.is-shifted-1 .column-title-items {
|
#tduck [data-td-icon="icon-message"].is-shifted-1 .column-title-items {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-left: 4px !important;
|
margin-left: 4px !important;
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-type-message.is-shifted-1 .username {
|
[data-td-icon="icon-message"].is-shifted-1 .username {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,18 +2,22 @@
|
|||||||
/* General */
|
/* General */
|
||||||
/***********/
|
/***********/
|
||||||
|
|
||||||
body:before {
|
body.td-notification {
|
||||||
content: none !important;
|
overflow-y: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body::before {
|
||||||
overflow-y: auto !important;
|
content: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************/
|
||||||
|
/* Redesign scrollbars */
|
||||||
|
/***********************/
|
||||||
|
|
||||||
.scroll-styled-v::-webkit-scrollbar {
|
.scroll-styled-v::-webkit-scrollbar {
|
||||||
width: 7px !important;
|
width: 7px !important;
|
||||||
}
|
}
|
||||||
@@ -26,9 +30,41 @@ body {
|
|||||||
border-left: 0 !important;
|
border-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************/
|
/********************/
|
||||||
/* Usernames */
|
/* Square-ify stuff */
|
||||||
/*************/
|
/********************/
|
||||||
|
|
||||||
|
.media-item, .media-preview {
|
||||||
|
border-radius: 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quoted-tweet {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************/
|
||||||
|
/* Tweaks for features */
|
||||||
|
/***********************/
|
||||||
|
|
||||||
|
a[data-full-url] {
|
||||||
|
word-break: break-all !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tduck-show-thread {
|
||||||
|
display: inline-block !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************/
|
||||||
|
/* Fix general visual issues or annoyances */
|
||||||
|
/*******************************************/
|
||||||
|
|
||||||
|
html[data-td-font='smallest'] .badge-verified:before {
|
||||||
|
/* fix cut off badge icon */
|
||||||
|
width: 13px !important;
|
||||||
|
height: 13px !important;
|
||||||
|
background-position: -223px -98px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.account-inline .username {
|
.account-inline .username {
|
||||||
vertical-align: 10% !important;
|
vertical-align: 10% !important;
|
||||||
@@ -38,9 +74,21 @@ body {
|
|||||||
vertical-align: -10% !important;
|
vertical-align: -10% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************/
|
/****************************************/
|
||||||
/* Favorite & retweet notifications */
|
/* Tweak notification layout and design */
|
||||||
/************************************/
|
/****************************************/
|
||||||
|
|
||||||
|
.activity-header.has-source-avatar {
|
||||||
|
margin-bottom: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-header .tweet-timestamp {
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-header .icon-user-filled {
|
||||||
|
vertical-align: sub !important;
|
||||||
|
}
|
||||||
|
|
||||||
.td-notification-padded .item-img {
|
.td-notification-padded .item-img {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -63,13 +111,13 @@ body {
|
|||||||
/* Media */
|
/* Media */
|
||||||
/*********/
|
/*********/
|
||||||
|
|
||||||
.media-size-medium {
|
.td-notification .media-size-medium {
|
||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
height: calc(100vh - 20px) !important;
|
height: calc(100vh - 20px) !important;
|
||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.js-quote-detail .media-size-medium {
|
.td-notification .js-quote-detail .media-size-medium {
|
||||||
height: calc(100vh - 28px) !important;
|
height: calc(100vh - 28px) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
//
|
//
|
||||||
var injectCSS = function(){
|
var injectCSS = function(){
|
||||||
if (!document.head){
|
if (!document.head){
|
||||||
setTimeout(injectCSS, 25);
|
setTimeout(injectCSS, 5);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
style.sheet.insertRule(rule, 0);
|
style.sheet.insertRule(rule, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
addRule("body { overflow: hidden !important; background-color: #1c6399 !important; }"); // remove scrollbar and change background
|
addRule("body { overflow: hidden !important; }"); // remove scrollbar
|
||||||
addRule(".page-canvas { box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important; }"); // change page box shadow
|
addRule(".page-canvas { box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important; }"); // change page box shadow
|
||||||
addRule(".topbar { display: none !important; }"); // hide top bar
|
addRule(".topbar { display: none !important; }"); // hide top bar
|
||||||
|
|
||||||
|
@@ -1,33 +1,9 @@
|
|||||||
(function($, $TDU){
|
(function($, $TDU){
|
||||||
//
|
|
||||||
// Variable: Current timeout ID for update checking.
|
|
||||||
//
|
|
||||||
var updateCheckTimeoutID;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Update exe file name.
|
|
||||||
//
|
|
||||||
const updateFileName = "TweetDuck.Update.exe";
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Url that returns JSON data about latest version.
|
|
||||||
//
|
|
||||||
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Url that returns JSON data about all versions, including prereleases.
|
|
||||||
//
|
|
||||||
const updateCheckUrlAll = "https://api.github.com/repos/chylex/TweetDuck/releases";
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Fallback url in case the update installer file is missing.
|
|
||||||
//
|
|
||||||
const updateDownloadFallback = "https://tweetduck.chylex.com";
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Creates the update notification element. Removes the old one if already exists.
|
// Function: Creates the update notification element. Removes the old one if already exists.
|
||||||
//
|
//
|
||||||
var displayNotification = function(version, download, changelog){
|
var displayNotification = function(version, changelog){
|
||||||
|
|
||||||
// styles
|
// styles
|
||||||
var css = $("#tweetduck-update-css");
|
var css = $("#tweetduck-update-css");
|
||||||
|
|
||||||
@@ -167,7 +143,7 @@
|
|||||||
<div id='tweetduck-changelog'>
|
<div id='tweetduck-changelog'>
|
||||||
<div id='tweetduck-changelog-box'>
|
<div id='tweetduck-changelog-box'>
|
||||||
<h2>TweetDuck Update ${version}</h2>
|
<h2>TweetDuck Update ${version}</h2>
|
||||||
${changelog}
|
${markdown(atob(changelog))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).appendTo(document.body).css("display", "none");
|
`).appendTo(document.body).css("display", "none");
|
||||||
@@ -219,17 +195,11 @@
|
|||||||
|
|
||||||
buttonDiv.children(".tdu-btn-download").click(function(){
|
buttonDiv.children(".tdu-btn-download").click(function(){
|
||||||
hide();
|
hide();
|
||||||
|
|
||||||
if (download){
|
|
||||||
$TDU.onUpdateAccepted();
|
$TDU.onUpdateAccepted();
|
||||||
}
|
|
||||||
else{
|
|
||||||
$TDU.openBrowser(updateDownloadFallback);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonDiv.children(".tdu-btn-later").click(function(){
|
buttonDiv.children(".tdu-btn-later").click(function(){
|
||||||
clearTimeout(updateCheckTimeoutID);
|
$TDU.onUpdateDelayed();
|
||||||
slide();
|
slide();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,15 +215,6 @@
|
|||||||
return ele;
|
return ele;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Function: Returns milliseconds until the start of the next hour, with an extra offset in seconds that can skip an hour if the clock would roll over too soon.
|
|
||||||
//
|
|
||||||
var getTimeUntilNextHour = function(extra){
|
|
||||||
var now = new Date();
|
|
||||||
var offset = new Date(+now+extra*1000);
|
|
||||||
return new Date(offset.getFullYear(), offset.getMonth(), offset.getDate(), offset.getHours()+1, 0, 0)-now;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Ghetto-converts markdown to HTML.
|
// Function: Ghetto-converts markdown to HTML.
|
||||||
//
|
//
|
||||||
@@ -274,34 +235,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Runs an update check and updates all DOM elements appropriately.
|
// Block: Check updates on startup.
|
||||||
//
|
//
|
||||||
var runUpdateCheck = function(eventID, versionTag, dismissedVersionTag, allowPre){
|
$(document).one("TD.ready", function(){
|
||||||
clearTimeout(updateCheckTimeoutID);
|
$TDU.triggerUpdateCheck();
|
||||||
updateCheckTimeoutID = setTimeout($TDU.triggerUpdateCheck, getTimeUntilNextHour(60*30)); // 30 minute offset
|
|
||||||
|
|
||||||
$.getJSON(allowPre ? updateCheckUrlAll : updateCheckUrlLatest, function(response){
|
|
||||||
var release = allowPre ? response[0] : response;
|
|
||||||
|
|
||||||
var tagName = release.tag_name;
|
|
||||||
var hasUpdate = tagName !== versionTag && tagName !== dismissedVersionTag && release.assets.length > 0;
|
|
||||||
|
|
||||||
if (hasUpdate){
|
|
||||||
var obj = release.assets.find(asset => asset.name === updateFileName) || { browser_download_url: "" };
|
|
||||||
displayNotification(tagName, obj.browser_download_url, markdown(release.body));
|
|
||||||
|
|
||||||
if (eventID){ // ignore undefined and 0
|
|
||||||
$TDU.onUpdateCheckFinished(eventID, tagName, obj.browser_download_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (eventID){ // ignore undefined and 0
|
|
||||||
$TDU.onUpdateCheckFinished(eventID, null, null);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Setup global functions.
|
// Block: Setup global functions.
|
||||||
//
|
//
|
||||||
window.TDUF_runUpdateCheck = runUpdateCheck;
|
window.TDUF_displayNotification = displayNotification;
|
||||||
})($, $TDU);
|
})($, $TDU);
|
||||||
|
26
Resources/Utilities/CompareStylesheets.cs
Normal file
26
Resources/Utilities/CompareStylesheets.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
HashSet<string> ReadSelectors(string file){
|
||||||
|
return new HashSet<string>(
|
||||||
|
File.ReadAllLines(file)
|
||||||
|
.Where(line => line.Contains('{'))
|
||||||
|
.Select(line => line.Substring(0, line.IndexOf('{')).Trim())
|
||||||
|
.SelectMany(lines => lines.Split(new char[]{ ',', ' ' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<string> ExtractClasses(HashSet<string> selectors){
|
||||||
|
return new HashSet<string>(
|
||||||
|
selectors.SelectMany(selector => Regex.Matches(selector, @"\.[a-zA-Z0-9_-]+").Cast<Match>().Select(match => match.Value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintAll(IEnumerable<string> data){
|
||||||
|
foreach(string line in data){
|
||||||
|
Print(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintMissing(HashSet<string> all, HashSet<string> subset){
|
||||||
|
PrintAll(subset.Where(ele => !all.Contains(ele)));
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props')" />
|
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" />
|
||||||
<Import Project="packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props')" />
|
<Import Project="packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props')" />
|
||||||
<Import Project="packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" />
|
<Import Project="packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" />
|
||||||
<Import Project="packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" />
|
<Import Project="packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" />
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
@@ -62,12 +62,14 @@
|
|||||||
<DefineConstants>
|
<DefineConstants>
|
||||||
</DefineConstants>
|
</DefineConstants>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Web.Extensions" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -107,7 +109,8 @@
|
|||||||
<Compile Include="Core\Handling\RequestHandlerBase.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBase.cs" />
|
||||||
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
||||||
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
|
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
|
||||||
<Compile Include="Core\ITweetDeckBrowser.cs" />
|
<Compile Include="Core\Other\Interfaces\ITweetDeckBrowser.cs" />
|
||||||
|
<Compile Include="Core\Management\ContextInfo.cs" />
|
||||||
<Compile Include="Core\Notification\Example\FormNotificationExample.cs">
|
<Compile Include="Core\Notification\Example\FormNotificationExample.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -169,6 +172,12 @@
|
|||||||
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.Designer.cs">
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.Designer.cs">
|
||||||
<DependentUpon>DialogSettingsAnalytics.cs</DependentUpon>
|
<DependentUpon>DialogSettingsAnalytics.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsSearchEngine.cs">
|
||||||
|
<SubType>Form</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsSearchEngine.Designer.cs">
|
||||||
|
<DependentUpon>DialogSettingsSearchEngine.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>
|
||||||
@@ -244,13 +253,14 @@
|
|||||||
<Compile Include="Core\Other\Settings\TabSettingsNotifications.Designer.cs">
|
<Compile Include="Core\Other\Settings\TabSettingsNotifications.Designer.cs">
|
||||||
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Bridge\CallbackBridge.cs" />
|
<Compile Include="Core\Notification\Screenshot\ScreenshotBridge.cs" />
|
||||||
<Compile Include="Data\CommandLineArgs.cs" />
|
<Compile Include="Data\CommandLineArgs.cs" />
|
||||||
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
<Compile Include="Core\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
|
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
|
||||||
<Compile Include="Data\ResourceLink.cs" />
|
<Compile Include="Data\ResourceLink.cs" />
|
||||||
|
<Compile Include="Data\Result.cs" />
|
||||||
<Compile Include="Data\Serialization\FileSerializer.cs" />
|
<Compile Include="Data\Serialization\FileSerializer.cs" />
|
||||||
<Compile Include="Data\InjectedHTML.cs" />
|
<Compile Include="Data\InjectedHTML.cs" />
|
||||||
<Compile Include="Data\Serialization\ITypeConverter.cs" />
|
<Compile Include="Data\Serialization\ITypeConverter.cs" />
|
||||||
@@ -282,6 +292,7 @@
|
|||||||
<Compile Include="Plugins\Enums\PluginEnvironment.cs" />
|
<Compile Include="Plugins\Enums\PluginEnvironment.cs" />
|
||||||
<Compile Include="Plugins\Enums\PluginGroup.cs" />
|
<Compile Include="Plugins\Enums\PluginGroup.cs" />
|
||||||
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
|
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
|
||||||
|
<Compile Include="Plugins\PluginLoader.cs" />
|
||||||
<Compile Include="Plugins\PluginManager.cs" />
|
<Compile Include="Plugins\PluginManager.cs" />
|
||||||
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
<Compile Include="Plugins\PluginScriptGenerator.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
@@ -290,6 +301,7 @@
|
|||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Reporter.cs" />
|
<Compile Include="Reporter.cs" />
|
||||||
|
<Compile Include="Updates\Events\UpdateCheckEventArgs.cs" />
|
||||||
<Compile Include="Updates\FormUpdateDownload.cs">
|
<Compile Include="Updates\FormUpdateDownload.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -305,13 +317,14 @@
|
|||||||
<Compile Include="Core\Management\BrowserCache.cs" />
|
<Compile Include="Core\Management\BrowserCache.cs" />
|
||||||
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
||||||
<Compile Include="Core\Utils\NativeMethods.cs" />
|
<Compile Include="Core\Utils\NativeMethods.cs" />
|
||||||
|
<Compile Include="Updates\UpdateCheckClient.cs" />
|
||||||
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
||||||
<Compile Include="Updates\UpdateHandler.cs" />
|
<Compile Include="Updates\UpdateHandler.cs" />
|
||||||
<Compile Include="Updates\UpdateInfo.cs" />
|
<Compile Include="Updates\UpdateInfo.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Resources\ScriptLoader.cs" />
|
<Compile Include="Resources\ScriptLoader.cs" />
|
||||||
<Compile Include="Updates\UpdateEventArgs.cs" />
|
<Compile Include="Updates\Events\UpdateEventArgs.cs" />
|
||||||
<Compile Include="Updates\UpdaterSettings.cs" />
|
<Compile Include="Updates\UpdaterSettings.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -394,39 +407,20 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>xcopy "$(ProjectDir)LICENSE.md" "$(TargetDir)" /Y
|
<PostBuildEvent>rmdir "$(ProjectDir)bin\Debug"
|
||||||
del "$(TargetDir)LICENSE.txt"
|
|
||||||
ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
|
|
||||||
xcopy "$(ProjectDir)bld\Resources\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y
|
|
||||||
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y
|
|
||||||
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y
|
|
||||||
|
|
||||||
rmdir "$(TargetDir)scripts" /S /Q
|
|
||||||
mkdir "$(TargetDir)scripts"
|
|
||||||
xcopy "$(ProjectDir)Resources\Scripts\*" "$(TargetDir)scripts\" /E /Y
|
|
||||||
|
|
||||||
rmdir "$(TargetDir)plugins" /S /Q
|
|
||||||
mkdir "$(TargetDir)plugins"
|
|
||||||
mkdir "$(TargetDir)plugins\official"
|
|
||||||
mkdir "$(TargetDir)plugins\user"
|
|
||||||
xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y
|
|
||||||
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
|
||||||
del "$(TargetDir)plugins\official\emoji-keyboard\emoji-instructions.txt"
|
|
||||||
|
|
||||||
rmdir "$(ProjectDir)bin\Debug"
|
|
||||||
rmdir "$(ProjectDir)bin\Release"
|
rmdir "$(ProjectDir)bin\Release"
|
||||||
|
|
||||||
attrib +R "$(TargetDir)locales\en-US.pak"
|
rmdir "$(TargetDir)scripts" /S /Q
|
||||||
del /Q /A:-R "$(TargetDir)locales\*.pak"
|
rmdir "$(TargetDir)plugins" /S /Q
|
||||||
attrib -R "$(TargetDir)locales\en-US.pak"
|
|
||||||
|
powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild.ps1" "$(TargetDir)\" "$(ProjectDir)\"
|
||||||
|
|
||||||
if $(ConfigurationName) == Debug (
|
if $(ConfigurationName) == Debug (
|
||||||
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
rmdir "$(TargetDir)plugins\official\.debug" /S /Q
|
||||||
mkdir "$(TargetDir)plugins\user\.debug"
|
mkdir "$(TargetDir)plugins\user\.debug"
|
||||||
xcopy "$(ProjectDir)Resources\Plugins\.debug\*" "$(TargetDir)plugins\user\.debug\" /E /Y
|
xcopy "$(ProjectDir)Resources\Plugins\.debug\*" "$(TargetDir)plugins\user\.debug\" /E /Y
|
||||||
)
|
)
|
||||||
|
</PostBuildEvent>
|
||||||
powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild.ps1" "$(TargetDir)\"</PostBuildEvent>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">
|
<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">
|
||||||
<Exec Command="del "$(TargetDir)*.pdb"" />
|
<Exec Command="del "$(TargetDir)*.pdb"" />
|
||||||
@@ -444,11 +438,11 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Error Condition="!Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props'))" />
|
<Error Condition="!Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props'))" />
|
||||||
<Error Condition="!Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props'))" />
|
<Error Condition="!Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets')" />
|
<Import Project="packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" />
|
||||||
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets')" />
|
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets')" />
|
||||||
</Project>
|
</Project>
|
14
Updates/Events/UpdateCheckEventArgs.cs
Normal file
14
Updates/Events/UpdateCheckEventArgs.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
|
||||||
|
namespace TweetDuck.Updates.Events{
|
||||||
|
sealed class UpdateCheckEventArgs : EventArgs{
|
||||||
|
public int EventId { get; }
|
||||||
|
public Result<UpdateInfo> Result { get; }
|
||||||
|
|
||||||
|
public UpdateCheckEventArgs(int eventId, Result<UpdateInfo> result){
|
||||||
|
this.EventId = eventId;
|
||||||
|
this.Result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Updates/Events/UpdateEventArgs.cs
Normal file
11
Updates/Events/UpdateEventArgs.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TweetDuck.Updates.Events{
|
||||||
|
sealed class UpdateEventArgs : EventArgs{
|
||||||
|
public UpdateInfo UpdateInfo { get; }
|
||||||
|
|
||||||
|
public UpdateEventArgs(UpdateInfo updateInfo){
|
||||||
|
this.UpdateInfo = updateInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Other;
|
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed partial class FormUpdateDownload : Form{
|
sealed partial class FormUpdateDownload : Form{
|
||||||
@@ -22,21 +20,11 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void timerDownloadCheck_Tick(object sender, EventArgs e){
|
private void timerDownloadCheck_Tick(object sender, EventArgs e){
|
||||||
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
|
if (updateInfo.DownloadStatus.IsFinished()){
|
||||||
timerDownloadCheck.Stop();
|
timerDownloadCheck.Stop();
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
else if (updateInfo.DownloadStatus == UpdateDownloadStatus.Failed){
|
|
||||||
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)){
|
|
||||||
BrowserUtils.OpenExternalBrowser(Program.Website);
|
|
||||||
DialogResult = DialogResult.OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
Updates/UpdateCheckClient.cs
Normal file
63
Updates/UpdateCheckClient.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web.Script.Serialization;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
|
||||||
|
|
||||||
|
namespace TweetDuck.Updates{
|
||||||
|
sealed class UpdateCheckClient{
|
||||||
|
private const string ApiLatestRelease = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
||||||
|
private const string UpdaterAssetName = "TweetDuck.Update.exe";
|
||||||
|
|
||||||
|
private readonly UpdaterSettings settings;
|
||||||
|
|
||||||
|
public UpdateCheckClient(UpdaterSettings settings){
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UpdateInfo> Check(){
|
||||||
|
TaskCompletionSource<UpdateInfo> result = new TaskCompletionSource<UpdateInfo>();
|
||||||
|
|
||||||
|
WebClient client = BrowserUtils.CreateWebClient();
|
||||||
|
client.Headers[HttpRequestHeader.Accept] = "application/vnd.github.v3+json";
|
||||||
|
|
||||||
|
client.DownloadStringTaskAsync(ApiLatestRelease).ContinueWith(task => {
|
||||||
|
if (task.IsCanceled){
|
||||||
|
result.SetCanceled();
|
||||||
|
}
|
||||||
|
else if (task.IsFaulted){
|
||||||
|
result.SetException(task.Exception.InnerException);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
result.SetResult(ParseFromJson(task.Result));
|
||||||
|
}catch(Exception e){
|
||||||
|
result.SetException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdateInfo ParseFromJson(string json){
|
||||||
|
bool IsUpdaterAsset(JsonObject obj){
|
||||||
|
return UpdaterAssetName == (string)obj["name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
string AssetDownloadUrl(JsonObject obj){
|
||||||
|
return (string)obj["browser_download_url"];
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject root = (JsonObject)new JavaScriptSerializer().DeserializeObject(json);
|
||||||
|
|
||||||
|
string versionTag = (string)root["tag_name"];
|
||||||
|
string releaseNotes = (string)root["body"];
|
||||||
|
string downloadUrl = ((Array)root["assets"]).Cast<JsonObject>().Where(IsUpdaterAsset).Select(AssetDownloadUrl).FirstOrDefault();
|
||||||
|
|
||||||
|
return new UpdateInfo(settings, versionTag, releaseNotes, downloadUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,15 @@
|
|||||||
public enum UpdateDownloadStatus{
|
public enum UpdateDownloadStatus{
|
||||||
None = 0,
|
None = 0,
|
||||||
InProgress,
|
InProgress,
|
||||||
Done,
|
Canceled,
|
||||||
Failed
|
AssetMissing,
|
||||||
|
Failed,
|
||||||
|
Done
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UpdateDownloadStatusExtensions{
|
||||||
|
public static bool IsFinished(this UpdateDownloadStatus status){
|
||||||
|
return status == UpdateDownloadStatus.AssetMissing || status == UpdateDownloadStatus.Done || status == UpdateDownloadStatus.Failed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
|
||||||
sealed class UpdateEventArgs : EventArgs{
|
|
||||||
public int EventId { get; }
|
|
||||||
public UpdateInfo UpdateInfo { get; }
|
|
||||||
|
|
||||||
public bool IsUpdateAvailable => UpdateInfo != null;
|
|
||||||
|
|
||||||
public UpdateEventArgs(int eventId, UpdateInfo updateInfo){
|
|
||||||
this.EventId = eventId;
|
|
||||||
this.UpdateInfo = updateInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateEventArgs(UpdateInfo updateInfo){
|
|
||||||
this.EventId = updateInfo.EventId;
|
|
||||||
this.UpdateInfo = updateInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,52 +1,96 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Other.Interfaces;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Data;
|
||||||
|
using TweetDuck.Updates.Events;
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateHandler{
|
sealed class UpdateHandler : IDisposable{
|
||||||
private readonly ITweetDeckBrowser browser;
|
public const int CheckCodeUpdatesDisabled = -1;
|
||||||
|
public const int CheckCodeNotOnTweetDeck = -2;
|
||||||
|
|
||||||
private readonly UpdaterSettings settings;
|
private readonly UpdaterSettings settings;
|
||||||
|
private readonly UpdateCheckClient client;
|
||||||
|
private readonly ITweetDeckBrowser browser;
|
||||||
|
private readonly Timer timer;
|
||||||
|
|
||||||
public event EventHandler<UpdateEventArgs> UpdateAccepted;
|
public event EventHandler<UpdateEventArgs> UpdateAccepted;
|
||||||
|
public event EventHandler<UpdateEventArgs> UpdateDelayed;
|
||||||
public event EventHandler<UpdateEventArgs> UpdateDismissed;
|
public event EventHandler<UpdateEventArgs> UpdateDismissed;
|
||||||
public event EventHandler<UpdateEventArgs> CheckFinished;
|
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
||||||
|
|
||||||
private int lastEventId;
|
private ushort lastEventId;
|
||||||
private UpdateInfo lastUpdateInfo;
|
private UpdateInfo lastUpdateInfo;
|
||||||
|
|
||||||
public UpdateHandler(ITweetDeckBrowser browser, UpdaterSettings settings){
|
public UpdateHandler(ITweetDeckBrowser browser, UpdaterSettings settings){
|
||||||
this.browser = browser;
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.client = new UpdateCheckClient(settings);
|
||||||
|
|
||||||
browser.OnFrameLoaded(OnFrameLoaded);
|
this.browser = browser;
|
||||||
browser.RegisterBridge("$TDU", new Bridge(this));
|
this.browser.RegisterBridge("$TDU", new Bridge(this));
|
||||||
|
|
||||||
|
this.timer = new Timer();
|
||||||
|
this.timer.Tick += timer_Tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFrameLoaded(IFrame frame){
|
public void Dispose(){
|
||||||
ScriptLoader.ExecuteFile(frame, "update.js");
|
timer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timer_Tick(object sender, EventArgs e){
|
||||||
|
timer.Stop();
|
||||||
Check(false);
|
Check(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StartTimer(){
|
||||||
|
if (timer.Enabled){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Stop();
|
||||||
|
|
||||||
|
if (Program.UserConfig.EnableUpdateCheck){
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
|
||||||
|
|
||||||
|
if (nextHour.TotalMinutes < 15){
|
||||||
|
nextHour = nextHour.Add(TimeSpan.FromHours(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Interval = (int)Math.Ceiling(nextHour.TotalMilliseconds);
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int Check(bool force){
|
public int Check(bool force){
|
||||||
if (Program.UserConfig.EnableUpdateCheck || force){
|
if (Program.UserConfig.EnableUpdateCheck || force){
|
||||||
if (force){
|
if (force){
|
||||||
settings.DismissedUpdate = null;
|
settings.DismissedUpdate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
browser.ExecuteFunction("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases);
|
if (!browser.IsTweetDeckWebsite){
|
||||||
return lastEventId;
|
return CheckCodeNotOnTweetDeck;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
int nextEventId = unchecked(++lastEventId);
|
||||||
|
Task<UpdateInfo> checkTask = client.Check();
|
||||||
|
|
||||||
|
checkTask.ContinueWith(task => HandleUpdateCheckSuccessful(nextEventId, task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||||
|
checkTask.ContinueWith(task => HandleUpdateCheckFailed(nextEventId, task.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
|
||||||
|
|
||||||
|
return nextEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){
|
return CheckCodeUpdatesDisabled;
|
||||||
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
|
}
|
||||||
onSuccess(updateInfo);
|
|
||||||
|
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onFinished){
|
||||||
|
UpdateDownloadStatus status = updateInfo.DownloadStatus;
|
||||||
|
|
||||||
|
if (status == UpdateDownloadStatus.Done || status == UpdateDownloadStatus.AssetMissing){
|
||||||
|
onFinished(updateInfo);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
FormUpdateDownload downloadForm = new FormUpdateDownload(updateInfo);
|
FormUpdateDownload downloadForm = new FormUpdateDownload(updateInfo);
|
||||||
@@ -57,14 +101,12 @@ namespace TweetDuck.Updates{
|
|||||||
};
|
};
|
||||||
|
|
||||||
downloadForm.FormClosed += (sender, args) => {
|
downloadForm.FormClosed += (sender, args) => {
|
||||||
downloadForm.Dispose();
|
if (downloadForm.DialogResult != DialogResult.OK){
|
||||||
|
updateInfo.CancelDownload();
|
||||||
|
}
|
||||||
|
|
||||||
if (downloadForm.DialogResult == DialogResult.OK){ // success or manual download
|
downloadForm.Dispose();
|
||||||
onSuccess(updateInfo);
|
onFinished(updateInfo);
|
||||||
}
|
|
||||||
else{
|
|
||||||
ownerForm.Show();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
downloadForm.Show();
|
downloadForm.Show();
|
||||||
@@ -78,17 +120,39 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerUpdateAcceptedEvent(UpdateEventArgs args){
|
private void HandleUpdateCheckSuccessful(int eventId, UpdateInfo info){
|
||||||
UpdateAccepted?.Invoke(this, args);
|
if (info.IsUpdateNew && !info.IsUpdateDismissed){
|
||||||
|
CleanupDownload();
|
||||||
|
lastUpdateInfo = info;
|
||||||
|
lastUpdateInfo.BeginSilentDownload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerUpdateDismissedEvent(UpdateEventArgs args){
|
CheckFinished?.Invoke(this, new UpdateCheckEventArgs(eventId, new Result<UpdateInfo>(info)));
|
||||||
settings.DismissedUpdate = args.UpdateInfo.VersionTag;
|
|
||||||
UpdateDismissed?.Invoke(this, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerCheckFinishedEvent(UpdateEventArgs args){
|
private void HandleUpdateCheckFailed(int eventId, Exception exception){
|
||||||
CheckFinished?.Invoke(this, args);
|
CheckFinished?.Invoke(this, new UpdateCheckEventArgs(eventId, new Result<UpdateInfo>(exception)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUpdateAcceptedEvent(){
|
||||||
|
if (lastUpdateInfo != null){
|
||||||
|
UpdateAccepted?.Invoke(this, new UpdateEventArgs(lastUpdateInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUpdateDelayedEvent(){
|
||||||
|
if (lastUpdateInfo != null){
|
||||||
|
UpdateDelayed?.Invoke(this, new UpdateEventArgs(lastUpdateInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUpdateDismissedEvent(){
|
||||||
|
if (lastUpdateInfo != null){
|
||||||
|
settings.DismissedUpdate = lastUpdateInfo.VersionTag;
|
||||||
|
UpdateDismissed?.Invoke(this, new UpdateEventArgs(lastUpdateInfo));
|
||||||
|
|
||||||
|
CleanupDownload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Bridge{
|
public sealed class Bridge{
|
||||||
@@ -102,31 +166,16 @@ namespace TweetDuck.Updates{
|
|||||||
owner.Check(false);
|
owner.Check(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateCheckFinished(int eventId, string versionTag, string downloadUrl){
|
|
||||||
if (versionTag != null && (owner.lastUpdateInfo == null || owner.lastUpdateInfo.VersionTag != versionTag)){
|
|
||||||
owner.CleanupDownload();
|
|
||||||
owner.lastUpdateInfo = new UpdateInfo(owner.settings, eventId, versionTag, downloadUrl);
|
|
||||||
owner.lastUpdateInfo.BeginSilentDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
owner.TriggerCheckFinishedEvent(new UpdateEventArgs(eventId, owner.lastUpdateInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnUpdateAccepted(){
|
public void OnUpdateAccepted(){
|
||||||
if (owner.lastUpdateInfo != null){
|
owner.TriggerUpdateAcceptedEvent();
|
||||||
owner.TriggerUpdateAcceptedEvent(new UpdateEventArgs(owner.lastUpdateInfo));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnUpdateDelayed(){
|
||||||
|
owner.TriggerUpdateDelayedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateDismissed(){
|
public void OnUpdateDismissed(){
|
||||||
if (owner.lastUpdateInfo != null){
|
owner.TriggerUpdateDismissedEvent();
|
||||||
owner.TriggerUpdateDismissedEvent(new UpdateEventArgs(owner.lastUpdateInfo));
|
|
||||||
owner.CleanupDownload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenBrowser(string url){
|
|
||||||
BrowserUtils.OpenExternalBrowser(url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,40 +5,43 @@ using TweetDuck.Core.Utils;
|
|||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateInfo{
|
sealed class UpdateInfo{
|
||||||
public int EventId { get; }
|
|
||||||
public string VersionTag { get; }
|
public string VersionTag { get; }
|
||||||
|
public string ReleaseNotes { get; }
|
||||||
public string InstallerPath { get; }
|
public string InstallerPath { get; }
|
||||||
|
|
||||||
|
public bool IsUpdateNew => VersionTag != Program.VersionTag;
|
||||||
|
public bool IsUpdateDismissed => VersionTag == settings.DismissedUpdate;
|
||||||
|
|
||||||
public UpdateDownloadStatus DownloadStatus { get; private set; }
|
public UpdateDownloadStatus DownloadStatus { get; private set; }
|
||||||
public Exception DownloadError { get; private set; }
|
public Exception DownloadError { get; private set; }
|
||||||
|
|
||||||
private readonly string installerFolder;
|
private readonly UpdaterSettings settings;
|
||||||
private readonly string downloadUrl;
|
private readonly string downloadUrl;
|
||||||
private WebClient currentDownload;
|
private WebClient currentDownload;
|
||||||
|
|
||||||
public UpdateInfo(UpdaterSettings settings, int eventId, string versionTag, string downloadUrl){
|
public UpdateInfo(UpdaterSettings settings, string versionTag, string releaseNotes, string downloadUrl){
|
||||||
this.installerFolder = settings.InstallerDownloadFolder;
|
this.settings = settings;
|
||||||
this.downloadUrl = downloadUrl;
|
this.downloadUrl = downloadUrl;
|
||||||
|
|
||||||
this.EventId = eventId;
|
|
||||||
this.VersionTag = versionTag;
|
this.VersionTag = versionTag;
|
||||||
this.InstallerPath = Path.Combine(installerFolder, "TweetDuck."+versionTag+".exe");
|
this.ReleaseNotes = releaseNotes;
|
||||||
|
this.InstallerPath = Path.Combine(settings.InstallerDownloadFolder, "TweetDuck."+versionTag+".exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginSilentDownload(){
|
public void BeginSilentDownload(){
|
||||||
if (DownloadStatus == UpdateDownloadStatus.None || DownloadStatus == UpdateDownloadStatus.Failed){
|
if (DownloadStatus == UpdateDownloadStatus.None || DownloadStatus == UpdateDownloadStatus.Failed){
|
||||||
DownloadStatus = UpdateDownloadStatus.InProgress;
|
DownloadStatus = UpdateDownloadStatus.InProgress;
|
||||||
|
|
||||||
try{
|
if (string.IsNullOrEmpty(downloadUrl)){
|
||||||
Directory.CreateDirectory(installerFolder);
|
DownloadError = new InvalidDataException("Missing installer asset.");
|
||||||
}catch(Exception e){
|
DownloadStatus = UpdateDownloadStatus.AssetMissing;
|
||||||
DownloadError = e;
|
|
||||||
DownloadStatus = UpdateDownloadStatus.Failed;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(downloadUrl)){
|
try{
|
||||||
DownloadError = new UriFormatException("Could not determine URL of the update installer");
|
Directory.CreateDirectory(settings.InstallerDownloadFolder);
|
||||||
|
}catch(Exception e){
|
||||||
|
DownloadError = e;
|
||||||
DownloadStatus = UpdateDownloadStatus.Failed;
|
DownloadStatus = UpdateDownloadStatus.Failed;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -68,5 +71,18 @@ namespace TweetDuck.Updates{
|
|||||||
// rip
|
// rip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CancelDownload(){
|
||||||
|
DeleteInstaller();
|
||||||
|
DownloadStatus = UpdateDownloadStatus.Canceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj){
|
||||||
|
return obj is UpdateInfo info && VersionTag == info.VersionTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode(){
|
||||||
|
return VersionTag.GetHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdaterSettings{
|
sealed class UpdaterSettings{
|
||||||
public bool AllowPreReleases { get; set; }
|
public string InstallerDownloadFolder { get; }
|
||||||
|
|
||||||
public string DismissedUpdate { get; set; }
|
public string DismissedUpdate { get; set; }
|
||||||
public string InstallerDownloadFolder { get; set; }
|
|
||||||
|
public UpdaterSettings(string installerDownloadFolder){
|
||||||
|
this.InstallerDownloadFolder = installerDownloadFolder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
bld/UPDATE ONLY.bat
Normal file
1
bld/UPDATE ONLY.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
start "" /B "ISCC.exe" /Q "gen_upd.iss"
|
106
bld/gen_full.iss
106
bld/gen_full.iss
@@ -4,9 +4,11 @@
|
|||||||
#define MyAppName "TweetDuck"
|
#define MyAppName "TweetDuck"
|
||||||
#define MyAppPublisher "chylex"
|
#define MyAppPublisher "chylex"
|
||||||
#define MyAppURL "https://tweetduck.chylex.com"
|
#define MyAppURL "https://tweetduck.chylex.com"
|
||||||
|
#define MyAppShortURL "https://td.chylex.com"
|
||||||
#define MyAppExeName "TweetDuck.exe"
|
#define MyAppExeName "TweetDuck.exe"
|
||||||
|
|
||||||
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
||||||
|
#define VCRedistLink "releases/download/1.13/vc_redist.x86.exe"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||||
@@ -30,6 +32,8 @@ SolidCompression=yes
|
|||||||
InternalCompressLevel=max
|
InternalCompressLevel=max
|
||||||
MinVersion=0,6.1
|
MinVersion=0,6.1
|
||||||
|
|
||||||
|
#include <idp.iss>
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
|
||||||
@@ -56,36 +60,44 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
|
|||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
var UpdatePath: String;
|
var UpdatePath: String;
|
||||||
|
var ForceRedistPrompt: String;
|
||||||
|
|
||||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||||
|
function TDIsVCMissing: Boolean; forward;
|
||||||
|
procedure TDInstallVCRedist; forward;
|
||||||
|
|
||||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
||||||
function InitializeSetup: Boolean;
|
function InitializeSetup: Boolean;
|
||||||
begin
|
begin
|
||||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||||
|
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
|
||||||
|
|
||||||
if TDGetNetFrameworkVersion() >= 379893 then
|
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if (TDIsVCMissing() or (ForceRedistPrompt = '1')) and (MsgBox('Microsoft Visual C++ 2015 appears to be missing, would you like to automatically install it?', mbConfirmation, MB_YESNO) = IDYES) then
|
||||||
|
begin
|
||||||
|
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe'));
|
||||||
|
end;
|
||||||
|
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Set the installation path if updating. }
|
{ Set the installation path if updating, and prepare download plugin if there are any files to download. }
|
||||||
procedure InitializeWizard();
|
procedure InitializeWizard();
|
||||||
begin
|
begin
|
||||||
if (UpdatePath <> '') then
|
if (UpdatePath <> '') then
|
||||||
begin
|
begin
|
||||||
WizardForm.DirEdit.Text := UpdatePath;
|
WizardForm.DirEdit.Text := UpdatePath;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if (idpFilesCount <> 0) then
|
||||||
|
begin
|
||||||
|
idpDownloadAfter(wpReady);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Skip the install path selection page if running from an update installer. }
|
{ Skip the install path selection page if running from an update installer. }
|
||||||
@@ -94,15 +106,21 @@ begin
|
|||||||
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Check for an old TweetDeck profile and show a warning before installation. }
|
{ Check the desktop icon task if not updating. }
|
||||||
|
procedure CurPageChanged(CurPageID: Integer);
|
||||||
|
begin
|
||||||
|
if CurPageID = wpSelectTasks then
|
||||||
|
begin
|
||||||
|
WizardForm.TasksList.Checked[WizardForm.TasksList.Items.Count-1] := (UpdatePath = '');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Install VC++ if downloaded. }
|
||||||
procedure CurStepChanged(CurStep: TSetupStep);
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
begin
|
begin
|
||||||
if CurStep = ssInstall then
|
if CurStep = ssInstall then
|
||||||
begin
|
begin
|
||||||
if DirExists(ExpandConstant('{localappdata}\twitter\TweetDeck')) then
|
TDInstallVCRedist();
|
||||||
begin
|
|
||||||
MsgBox('Detected a profile from an old TweetDeck installation, you may uninstall the old client to free up some space.', mbInformation, MB_OK)
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -145,3 +163,65 @@ begin
|
|||||||
|
|
||||||
Result := 0;
|
Result := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Check if Visual C++ 2015 or 2017 is installed. }
|
||||||
|
function TDIsVCMissing: Boolean;
|
||||||
|
var Keys: TArrayOfString;
|
||||||
|
var Index: Integer;
|
||||||
|
var Key: String;
|
||||||
|
var DisplayName: String;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if RegGetSubkeyNames(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies', Keys) then
|
||||||
|
begin
|
||||||
|
for Index := 0 to GetArrayLength(Keys)-1 do
|
||||||
|
begin
|
||||||
|
Key := Keys[Index];
|
||||||
|
|
||||||
|
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then
|
||||||
|
begin
|
||||||
|
if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Run the Visual C++ installer if downloaded. }
|
||||||
|
procedure TDInstallVCRedist;
|
||||||
|
var InstallFile: String;
|
||||||
|
var ResultCode: Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
InstallFile := ExpandConstant('{tmp}\{#MyAppName}.VC.exe')
|
||||||
|
|
||||||
|
if FileExists(InstallFile) then
|
||||||
|
begin
|
||||||
|
WizardForm.ProgressGauge.Style := npbstMarquee;
|
||||||
|
|
||||||
|
try
|
||||||
|
if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
|
||||||
|
begin
|
||||||
|
if ResultCode <> 0 then
|
||||||
|
begin
|
||||||
|
DeleteFile(InstallFile);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
MsgBox('Could not run the Visual C++ installer, please visit https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
|
||||||
|
|
||||||
|
DeleteFile(InstallFile);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
WizardForm.ProgressGauge.Style := npbstNormal;
|
||||||
|
DeleteFile(InstallFile);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
105
bld/gen_port.iss
105
bld/gen_port.iss
@@ -4,9 +4,11 @@
|
|||||||
#define MyAppName "TweetDuck"
|
#define MyAppName "TweetDuck"
|
||||||
#define MyAppPublisher "chylex"
|
#define MyAppPublisher "chylex"
|
||||||
#define MyAppURL "https://tweetduck.chylex.com"
|
#define MyAppURL "https://tweetduck.chylex.com"
|
||||||
|
#define MyAppShortURL "https://td.chylex.com"
|
||||||
#define MyAppExeName "TweetDuck.exe"
|
#define MyAppExeName "TweetDuck.exe"
|
||||||
|
|
||||||
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
||||||
|
#define VCRedistLink "releases/download/1.13/vc_redist.x86.exe"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||||
@@ -30,6 +32,8 @@ SolidCompression=yes
|
|||||||
InternalCompressLevel=max
|
InternalCompressLevel=max
|
||||||
MinVersion=0,6.1
|
MinVersion=0,6.1
|
||||||
|
|
||||||
|
#include <idp.iss>
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
|
||||||
@@ -42,36 +46,44 @@ Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChang
|
|||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
var UpdatePath: String;
|
var UpdatePath: String;
|
||||||
|
var ForceRedistPrompt: String;
|
||||||
|
|
||||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||||
|
function TDIsVCMissing: Boolean; forward;
|
||||||
|
procedure TDInstallVCRedist; forward;
|
||||||
|
|
||||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
||||||
function InitializeSetup: Boolean;
|
function InitializeSetup: Boolean;
|
||||||
begin
|
begin
|
||||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||||
|
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
|
||||||
|
|
||||||
if TDGetNetFrameworkVersion() >= 379893 then
|
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if (TDIsVCMissing() or (ForceRedistPrompt = '1')) and (MsgBox('Microsoft Visual C++ 2015 appears to be missing, would you like to automatically install it?', mbConfirmation, MB_YESNO) = IDYES) then
|
||||||
|
begin
|
||||||
|
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe'));
|
||||||
|
end;
|
||||||
|
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Set the installation path if updating. }
|
{ Set the installation path if updating, and prepare download plugin if there are any files to download. }
|
||||||
procedure InitializeWizard();
|
procedure InitializeWizard();
|
||||||
begin
|
begin
|
||||||
if (UpdatePath <> '') then
|
if (UpdatePath <> '') then
|
||||||
begin
|
begin
|
||||||
WizardForm.DirEdit.Text := UpdatePath;
|
WizardForm.DirEdit.Text := UpdatePath;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if (idpFilesCount <> 0) then
|
||||||
|
begin
|
||||||
|
idpDownloadAfter(wpReady);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Skip the install path selection page if running from an update installer. }
|
{ Skip the install path selection page if running from an update installer. }
|
||||||
@@ -80,6 +92,24 @@ begin
|
|||||||
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Install VC++ if downloaded, and create a 'makeportable' file for portable installs. }
|
||||||
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
|
begin
|
||||||
|
if CurStep = ssInstall then
|
||||||
|
begin
|
||||||
|
TDInstallVCRedist();
|
||||||
|
end else if CurStep = ssPostInstall then
|
||||||
|
begin
|
||||||
|
while not SaveStringToFile(ExpandConstant('{app}\makeportable'), '', False) do
|
||||||
|
begin
|
||||||
|
if MsgBox('Could not create a ''makeportable'' file in the installation folder. If the file is not present, the installation will not be fully portable.', mbCriticalError, MB_RETRYCANCEL) <> IDRETRY then
|
||||||
|
begin
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{ Return DWORD value containing the build version of .NET Framework. }
|
{ Return DWORD value containing the build version of .NET Framework. }
|
||||||
function TDGetNetFrameworkVersion: Cardinal;
|
function TDGetNetFrameworkVersion: Cardinal;
|
||||||
var FrameworkVersion: Cardinal;
|
var FrameworkVersion: Cardinal;
|
||||||
@@ -94,17 +124,64 @@ begin
|
|||||||
Result := 0;
|
Result := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Create a 'makeportable' file if running in portable mode. }
|
{ Check if Visual C++ 2015 or 2017 is installed. }
|
||||||
procedure CurStepChanged(CurStep: TSetupStep);
|
function TDIsVCMissing: Boolean;
|
||||||
|
var Keys: TArrayOfString;
|
||||||
|
var Index: Integer;
|
||||||
|
var Key: String;
|
||||||
|
var DisplayName: String;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if CurStep = ssPostInstall then
|
if RegGetSubkeyNames(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies', Keys) then
|
||||||
begin
|
begin
|
||||||
while not SaveStringToFile(ExpandConstant('{app}\makeportable'), '', False) do
|
for Index := 0 to GetArrayLength(Keys)-1 do
|
||||||
begin
|
begin
|
||||||
if MsgBox('Could not create a ''makeportable'' file in the installation folder. If the file is not present, the installation will not be fully portable.', mbCriticalError, MB_RETRYCANCEL) <> IDRETRY then
|
Key := Keys[Index];
|
||||||
|
|
||||||
|
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then
|
||||||
begin
|
begin
|
||||||
break;
|
if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Run the Visual C++ installer if downloaded. }
|
||||||
|
procedure TDInstallVCRedist;
|
||||||
|
var InstallFile: String;
|
||||||
|
var ResultCode: Integer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
InstallFile := ExpandConstant('{tmp}\{#MyAppName}.VC.exe')
|
||||||
|
|
||||||
|
if FileExists(InstallFile) then
|
||||||
|
begin
|
||||||
|
WizardForm.ProgressGauge.Style := npbstMarquee;
|
||||||
|
|
||||||
|
try
|
||||||
|
if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
|
||||||
|
begin
|
||||||
|
if ResultCode <> 0 then
|
||||||
|
begin
|
||||||
|
DeleteFile(InstallFile);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
MsgBox('Could not run the Visual C++ installer, please visit https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
|
||||||
|
|
||||||
|
DeleteFile(InstallFile);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
WizardForm.ProgressGauge.Style := npbstNormal;
|
||||||
|
DeleteFile(InstallFile);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#define MyAppName "TweetDuck"
|
#define MyAppName "TweetDuck"
|
||||||
#define MyAppPublisher "chylex"
|
#define MyAppPublisher "chylex"
|
||||||
#define MyAppURL "https://tweetduck.chylex.com"
|
#define MyAppURL "https://tweetduck.chylex.com"
|
||||||
|
#define MyAppShortURL "https://td.chylex.com"
|
||||||
#define MyAppExeName "TweetDuck.exe"
|
#define MyAppExeName "TweetDuck.exe"
|
||||||
|
|
||||||
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
|
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
|
||||||
@@ -59,6 +60,8 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
|
|||||||
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
|
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
|
||||||
|
|
||||||
[InstallDelete]
|
[InstallDelete]
|
||||||
|
Type: files; Name: "{app}\msvcp120.dll"
|
||||||
|
Type: files; Name: "{app}\msvcr120.dll"
|
||||||
Type: files; Name: "{app}\TweetLib.Audio.dll"
|
Type: files; Name: "{app}\TweetLib.Audio.dll"
|
||||||
Type: filesandordirs; Name: "{app}\scripts"
|
Type: filesandordirs; Name: "{app}\scripts"
|
||||||
Type: filesandordirs; Name: "{app}\plugins\official"
|
Type: filesandordirs; Name: "{app}\plugins\official"
|
||||||
@@ -146,13 +149,7 @@ begin
|
|||||||
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
|
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if TDGetNetFrameworkVersion() >= 379893 then
|
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
Exit;
|
Exit;
|
||||||
@@ -273,8 +270,15 @@ end;
|
|||||||
{ Return whether the version of the installed libcef.dll library matches internal one. }
|
{ Return whether the version of the installed libcef.dll library matches internal one. }
|
||||||
function TDIsMatchingCEFVersion: Boolean;
|
function TDIsMatchingCEFVersion: Boolean;
|
||||||
var CEFVersion: String;
|
var CEFVersion: String;
|
||||||
|
var TmpTDVersion: String;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
if GetVersionNumbersString(UpdatePath+'TweetDuck.exe', TmpTDVersion) and (CompareStr(TmpTDVersion, '1.13.0.0') = 0) then
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
Result := (GetVersionNumbersString(UpdatePath+'libcef.dll', CEFVersion) and (CompareStr(CEFVersion, '{#CefVersion}') = 0))
|
Result := (GetVersionNumbersString(UpdatePath+'libcef.dll', CEFVersion) and (CompareStr(CEFVersion, '{#CefVersion}') = 0))
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@@ -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>
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
<packages>
|
<packages>
|
||||||
<package id="cef.redist.x64" version="3.3282.1731" targetFramework="net452" xmlns="" />
|
<package id="cef.redist.x64" version="3.3282.1731" targetFramework="net452" xmlns="" />
|
||||||
<package id="cef.redist.x86" version="3.3282.1731" targetFramework="net452" xmlns="" />
|
<package id="cef.redist.x86" version="3.3282.1731" targetFramework="net452" xmlns="" />
|
||||||
<package id="CefSharp.Common" version="64.0.0-CI2497" targetFramework="net452" xmlns="" />
|
<package id="CefSharp.Common" version="64.0.0-CI2508" targetFramework="net452" xmlns="" />
|
||||||
<package id="CefSharp.WinForms" version="64.0.0-CI2497" targetFramework="net452" xmlns="" />
|
<package id="CefSharp.WinForms" version="64.0.0-CI2508" targetFramework="net452" xmlns="" />
|
||||||
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" />
|
|
||||||
</packages>
|
</packages>
|
@@ -15,18 +15,20 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=63.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=64.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\CefSharp.Common.64.0.0-CI2497\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
<HintPath>..\packages\CefSharp.Common.64.0.0-CI2508\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -21,7 +21,19 @@ namespace UnitTests.Core{
|
|||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/"));
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/"));
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc/status"));
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://twitter.com/chylexmc/status"));
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://nottwitter.com/chylexmc"));
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("http://nottwitter.com/chylexmc"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/chylexmc?"));
|
||||||
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("www.twitter.com/chylexmc"));
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("www.twitter.com/chylexmc"));
|
||||||
|
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/signup"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/tos"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/privacy"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search?query"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search-home"));
|
||||||
|
Assert.IsFalse(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/search-advanced"));
|
||||||
|
|
||||||
|
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/tosser"));
|
||||||
|
Assert.IsTrue(TwitterUtils.RegexAccount.IsMatch("https://twitter.com/searching"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
@@ -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>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
<OutputPath>bin\x86\Release\</OutputPath>
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||||
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
||||||
|
Reference in New Issue
Block a user