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

Compare commits

..

29 Commits
1.13 ... 1.13.2

Author SHA1 Message Date
aba156cb3b Fix typo from refactoring breaking context menu for some links 2018-03-02 06:00:53 +01:00
cd4e4d7095 Fix hashtags and search links being recognized as account links 2018-03-02 05:59:20 +01:00
8fbb639430 Refactor & optimize context menu, send last tweet info only on right-click 2018-03-02 05:24:45 +01:00
d5bf8ec558 Fix missing image/video context menu for tweets that have both media and a quote 2018-03-02 00:15:28 +01:00
b6cff40f1e Warn when checking updates outside TweetDeck & fix visual and unlikely issues 2018-02-28 11:03:55 +01:00
833e42f455 Add a IsTweetDeckWebsite bool to ITweetDeckBrowser 2018-02-28 07:24:40 +01:00
8134843dad Fix background color & twitter.com hooks not applying quickly enough sometimes 2018-02-28 02:34:29 +01:00
1f92d5e633 Remove 'Shift Selects Multiple Accounts' option & fix refocusing after switching account 2018-02-28 01:43:44 +01:00
dc51c0ae85 Remove unnecessary console logging in debug builds 2018-02-26 17:45:56 +01:00
45c79643d6 Why the fuck is TLS 1.2 disabled by default in .NET on some computers 2018-02-25 23:15:00 +01:00
9041bfc627 Tweak search input font size and icon position 2018-02-21 22:17:51 +01:00
0b3b3dd0be Fix a crash when downloading tweet images with no username
Happens when someone accidentally or through dev tools gets to
twitter.com and tries downloading an image.
2018-02-21 19:48:00 +01:00
89e92dab59 Fix middle-clicking on links in desktop notifications not skipping them or stripping t.co 2018-02-19 17:31:28 +01:00
8c168c9ad7 Fix emoji keyboard button size & tweak composer button layout 2018-02-17 12:21:26 +01:00
9f63357a92 Pre-check desktop icon option in installer when not updating 2018-02-17 10:52:19 +01:00
d91b4bd1f3 Release 1.13.1 2018-02-14 18:43:02 +01:00
c0c64f6d62 Remove old TweetDeck installation check from the installer & tweak formatting 2018-02-14 17:45:19 +01:00
1a5d2af779 Decrease post-update analytics report to 8h and increase startup delay to 2m 2018-02-14 17:10:24 +01:00
f40a33192b Revert and fix various changes from recent TweetDeck update 2018-02-14 15:39:27 +01:00
ca4900aff0 Fix 'Manage templates' button after a recent TweetDeck update 2018-02-14 15:17:58 +01:00
56fc9e2d40 Fix black theme issues (mismatched detection & rare bug with wrong notification background color) 2018-02-13 16:51:40 +01:00
d2174c0b69 Fix misaligned avatars in activity columns 2018-02-13 15:59:21 +01:00
9f76754ad3 Force full install from 1.13 to 1.13.0.1 2018-02-13 13:37:30 +01:00
118ceaec35 Release 1.13.0.1 2018-02-13 13:27:52 +01:00
5a57d28a7d Fix crash by checking and downloading for VC++ 2015 in the installer
Closes #205
2018-02-13 13:17:40 +01:00
07af99f862 Fix wrong background color in tweet screenshots when using black theme 2018-02-13 12:42:25 +01:00
59fba7fba0 Fix a hidden crash that prevented desktop notifications from showing 2018-02-13 12:37:11 +01:00
dd4edc4249 Update CefSharp to latest 64 and remove VC++ 2012 2018-02-13 11:28:48 +01:00
856226473a Update README.md 2018-02-13 05:52:03 +01:00
43 changed files with 620 additions and 322 deletions

View File

@@ -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;

View File

@@ -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));

View File

@@ -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){

View File

@@ -12,6 +12,7 @@ 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;
@@ -38,6 +39,7 @@ namespace TweetDuck.Core{
public string UpdateInstallerPath { get; private set; } public string UpdateInstallerPath { get; private set; }
public PluginManager PluginManager => plugins;
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy; public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
private readonly TweetDeckBrowser browser; private readonly TweetDeckBrowser browser;
@@ -58,10 +60,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 +68,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) => {

View File

@@ -3,11 +3,10 @@ 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;
@@ -19,19 +18,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;
@@ -42,8 +28,8 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
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 +37,23 @@ namespace TweetDuck.Core.Handling{
this.analytics = analytics; this.analytics = analytics;
} }
private void ResetContextInfo(){
LastLink = default(ContextInfo.LinkInfo);
LastChirp = default(ContextInfo.ChirpInfo);
TweetDeckBridge.ContextInfo.Reset();
}
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweetAuthors = StringUtils.EmptyArray; ResetContextInfo();
lastHighlightedTweetImageList = StringUtils.EmptyArray;
ContextInfo = default(KeyValuePair<string, string>);
} }
else{ else{
lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray; LastLink = TweetDeckBridge.ContextInfo.Link;
lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray; LastChirp = TweetDeckBridge.ContextInfo.Chirp;
} }
bool hasTweetImage = IsImage; bool hasTweetImage = LastLink.IsImage;
bool hasTweetVideo = IsVideo; 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 +85,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 +98,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 +112,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));
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 +150,20 @@ 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; break;
case MenuOpenDevTools: case MenuOpenDevTools:
@@ -183,7 +175,7 @@ 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){

View File

@@ -1,8 +1,6 @@
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;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class ContextMenuBrowser : ContextMenuBase{ sealed class ContextMenuBrowser : ContextMenuBase{
@@ -27,9 +25,6 @@ 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;
} }
@@ -52,20 +47,12 @@ namespace TweetDuck.Core.Handling{
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl; if (!string.IsNullOrEmpty(LastChirp.TweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweetUrl = string.Empty;
lastHighlightedQuoteUrl = string.Empty;
}
if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
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");
@@ -126,11 +113,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,11 +125,11 @@ 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:

View File

@@ -3,6 +3,8 @@ using CefSharp;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
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);

View 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 = new LinkInfo(string.IsNullOrEmpty(url) ? null : 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;
}
}
}
}

View File

@@ -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();
} }

View File

@@ -12,7 +12,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 +81,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 +188,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 +253,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);
} }

View File

@@ -33,7 +33,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
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", false);
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){ foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.Inject(html); html = injection.Inject(html);
} }

View File

@@ -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();
} }

View File

@@ -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) },

View File

@@ -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();
@@ -64,7 +63,7 @@
// 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, 363);
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);
@@ -74,7 +73,7 @@
// //
// btnCheckUpdates // btnCheckUpdates
// //
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 409); this.btnCheckUpdates.Location = new System.Drawing.Point(5, 386);
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);
@@ -93,21 +92,10 @@
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);
@@ -118,7 +106,7 @@
// 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);
@@ -144,7 +132,7 @@
// 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, 268);
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);
@@ -172,7 +160,7 @@
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, 281);
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);
@@ -181,7 +169,7 @@
// 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);
@@ -193,7 +181,7 @@
// //
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, 337);
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);
@@ -207,7 +195,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);
@@ -224,14 +211,14 @@
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, 415);
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);
@@ -243,7 +230,7 @@
// //
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);
@@ -253,7 +240,7 @@
// 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);
@@ -264,7 +251,7 @@
// 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);
@@ -275,7 +262,7 @@
// //
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);
@@ -287,7 +274,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, 433);
((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 +293,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;

View File

@@ -26,7 +26,6 @@ namespace TweetDuck.Core.Other.Settings{
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 +40,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;
@@ -65,7 +63,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;
@@ -87,10 +84,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;
} }
@@ -170,6 +163,11 @@ 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, UpdateEventArgs e){

View File

@@ -22,6 +22,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 +47,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 +122,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");
} }
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
} }
} }
@@ -137,8 +141,6 @@ 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");

View File

@@ -137,6 +137,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 +174,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
} }
} }

View File

@@ -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){

View File

@@ -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);

View File

@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using TweetDuck.Core; using TweetDuck.Core;
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;
@@ -21,42 +22,46 @@ namespace TweetDuck.Plugins{
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 +73,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)){
@@ -143,7 +148,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;
} }

View File

@@ -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.1";
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");

View File

@@ -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.

View File

@@ -8,7 +8,7 @@ Edit layout & design
chylex chylex
[version] [version]
1.2.3 1.2.4
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -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 { color: #8bd !important }
.quoted-tweet { border-color: #292f33 !important } .quoted-tweet { border-color: #292f33 !important }
` : ``} ` : ``}

View File

@@ -290,7 +290,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}
@@ -849,3 +849,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}

View File

@@ -9,7 +9,7 @@ Emoji keyboard
chylex chylex
[version] [version]
1.4.2 1.4.3
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -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);

View File

@@ -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

View File

@@ -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">');

View File

@@ -199,7 +199,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")){
@@ -294,6 +294,8 @@
tags.push("<style type='text/css'>"); tags.push("<style type='text/css'>");
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" !important }"); // set background color tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" !important }"); // set background color
tags.push("body::before { content: none !important }"); // remove background gradient
tags.push(".column { background: transparent !important }"); // remove background color from columns
tags.push("a[data-full-url] { word-break: break-all !important }"); // break long urls 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(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets
@@ -540,7 +542,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 +595,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 +619,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 +643,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);
} }
}); });
})(); })();
@@ -926,26 +925,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);
}
}); });
}); });

View File

@@ -14,22 +14,29 @@
}; };
// //
// 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(){
let ele = e.currentTarget; const onLinkClick = function(e){
if (e.button === 0 || e.button === 1){
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"));
e.preventDefault(); e.preventDefault();
if ($TDX.skipOnLinkClick){ if ($TDX.skipOnLinkClick){
let parentClasses = ele.parentNode.classList; let parentClasses = ele.parentNode.classList;
if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){ if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
$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.

View File

@@ -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,7 +46,7 @@
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;
} }
@@ -54,6 +54,52 @@
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;
} }
@@ -183,6 +229,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;
@@ -230,6 +282,16 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
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 */
/***************************************************************/ /***************************************************************/

View File

@@ -2,18 +2,10 @@
/* General */ /* General */
/***********/ /***********/
body:before {
content: none !important;
}
body { body {
overflow-y: auto !important; overflow-y: auto !important;
} }
.column {
background: transparent !important;
}
.scroll-styled-v::-webkit-scrollbar { .scroll-styled-v::-webkit-scrollbar {
width: 7px !important; width: 7px !important;
} }

View File

@@ -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

View File

@@ -300,6 +300,13 @@
}); });
}; };
//
// Block: Check updates on startup.
//
$(document).one("TD.ready", function(){
$TDU.triggerUpdateCheck();
});
// //
// Block: Setup global functions. // Block: Setup global functions.
// //

View File

@@ -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')" />
@@ -108,6 +108,7 @@
<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\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>
@@ -398,8 +399,6 @@
del "$(TargetDir)LICENSE.txt" del "$(TargetDir)LICENSE.txt"
ren "$(TargetDir)LICENSE.md" "LICENSE.txt" ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
xcopy "$(ProjectDir)bld\Resources\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y 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 rmdir "$(TargetDir)scripts" /S /Q
mkdir "$(TargetDir)scripts" mkdir "$(TargetDir)scripts"
@@ -444,11 +443,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>

View File

@@ -8,6 +8,9 @@ using TweetDuck.Resources;
namespace TweetDuck.Updates{ namespace TweetDuck.Updates{
sealed class UpdateHandler{ sealed class UpdateHandler{
public const int CheckCodeUpdatesDisabled = -1;
public const int CheckCodeNotOnTweetDeck = -2;
private readonly ITweetDeckBrowser browser; private readonly ITweetDeckBrowser browser;
private readonly UpdaterSettings settings; private readonly UpdaterSettings settings;
@@ -15,7 +18,7 @@ namespace TweetDuck.Updates{
public event EventHandler<UpdateEventArgs> UpdateDismissed; public event EventHandler<UpdateEventArgs> UpdateDismissed;
public event EventHandler<UpdateEventArgs> CheckFinished; public event EventHandler<UpdateEventArgs> 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){
@@ -28,7 +31,6 @@ namespace TweetDuck.Updates{
private void OnFrameLoaded(IFrame frame){ private void OnFrameLoaded(IFrame frame){
ScriptLoader.ExecuteFile(frame, "update.js"); ScriptLoader.ExecuteFile(frame, "update.js");
Check(false);
} }
public int Check(bool force){ public int Check(bool force){
@@ -37,11 +39,15 @@ namespace TweetDuck.Updates{
settings.DismissedUpdate = null; settings.DismissedUpdate = null;
} }
browser.ExecuteFunction("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases); if (!browser.IsTweetDeckWebsite){
return CheckCodeNotOnTweetDeck;
}
browser.ExecuteFunction("TDUF_runUpdateCheck", (int)unchecked(++lastEventId), Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases);
return lastEventId; return lastEventId;
} }
return 0; return CheckCodeUpdatesDisabled;
} }
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){ public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -2,7 +2,7 @@
<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="" /> <package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" />
</packages> </packages>

View File

@@ -24,9 +24,9 @@
<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>

View File

@@ -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]