diff --git a/Core/Bridge/TweetDeckBridge.cs b/Core/Bridge/TweetDeckBridge.cs index 48b95893..4dd3115f 100644 --- a/Core/Bridge/TweetDeckBridge.cs +++ b/Core/Bridge/TweetDeckBridge.cs @@ -3,7 +3,7 @@ using System.Windows.Forms; using CefSharp; using TweetDuck.Core.Controls; -using TweetDuck.Core.Handling; +using TweetDuck.Core.Management; using TweetDuck.Core.Notification; using TweetDuck.Core.Other; using TweetDuck.Core.Utils; @@ -13,20 +13,12 @@ namespace TweetDuck.Core.Bridge{ class TweetDeckBridge{ public static string FontSize { get; private set; } public static string NotificationHeadLayout { get; private set; } - - 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(';'); + public static readonly ContextInfo ContextInfo = new ContextInfo(); private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2); public static void ResetStaticProperties(){ FontSize = NotificationHeadLayout = null; - LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty; } public static void RestoreSessionData(IFrame frame){ @@ -72,13 +64,8 @@ public void LoadNotificationLayout(string fontSize, string headLayout){ }); } - public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){ - form.InvokeAsyncSafe(() => { - LastHighlightedTweetUrl = tweetUrl; - LastHighlightedQuoteUrl = quoteUrl; - LastHighlightedTweetAuthors = authors; - LastHighlightedTweetImages = imageList; - }); + public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ + ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages); } public void DisplayTooltip(string text){ @@ -112,8 +99,8 @@ public void ShowTweetDetail(){ // Global - public void SetLastRightClickInfo(string type, string link){ - form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link)); + public void SetLastRightClickInfo(string type, string url){ + ContextInfo.SetLink(type, url); } public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){ diff --git a/Core/Handling/ContextMenuBase.cs b/Core/Handling/ContextMenuBase.cs index f2ec36f5..46ecc44b 100644 --- a/Core/Handling/ContextMenuBase.cs +++ b/Core/Handling/ContextMenuBase.cs @@ -3,11 +3,10 @@ using System.Text.RegularExpressions; using System.Windows.Forms; using CefSharp; -using TweetDuck.Core.Bridge; using TweetDuck.Core.Controls; using TweetDuck.Core.Utils; -using System.Collections.Generic; using System.Linq; +using TweetDuck.Core.Bridge; using TweetDuck.Core.Management; using TweetDuck.Core.Notification; using TweetDuck.Core.Other; @@ -18,19 +17,6 @@ abstract class ContextMenuBase : IContextMenuHandler{ public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak")); 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 MenuCopyLinkUrl = (CefMenuCommand)26501; @@ -42,8 +28,8 @@ private static string GetMediaLink(IContextMenuParams parameters){ private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599; - private string[] lastHighlightedTweetAuthors; - private string[] lastHighlightedTweetImageList; + protected ContextInfo.LinkInfo LastLink { get; private set; } + protected ContextInfo.ChirpInfo LastChirp { get; private set; } private readonly AnalyticsFile.IProvider analytics; @@ -51,19 +37,23 @@ protected ContextMenuBase(AnalyticsFile.IProvider 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){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ - lastHighlightedTweetAuthors = StringUtils.EmptyArray; - lastHighlightedTweetImageList = StringUtils.EmptyArray; - ContextInfo = default(KeyValuePair<string, string>); + ResetContextInfo(); } else{ - lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray; - lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray; + LastLink = TweetDeckBridge.ContextInfo.Link; + LastChirp = TweetDeckBridge.ContextInfo.Chirp; } - bool hasTweetImage = IsImage; - bool hasTweetVideo = IsVideo; + bool hasTweetImage = LastLink.IsImage; + bool hasTweetVideo = LastLink.IsVideo; string TextOpen(string name) => "Open "+name+" in browser"; string TextCopy(string name) => "Copy "+name+" address"; @@ -95,7 +85,7 @@ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser bro model.AddItem(MenuCopyMediaUrl, TextCopy("image")); model.AddItem(MenuSaveMedia, TextSave("image")); - if (lastHighlightedTweetImageList.Length > 1){ + if (LastChirp.Images.Length > 1){ model.AddItem(MenuSaveTweetImages, TextSave("all images")); } @@ -108,11 +98,11 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br switch(commandId){ case MenuOpenLinkUrl: - OpenBrowser(control, IsLink ? ContextInfo.Value : parameters.LinkUrl); + OpenBrowser(control, LastLink.GetUrl(parameters, true)); break; case MenuCopyLinkUrl: - SetClipboardText(control, IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl); + SetClipboardText(control, LastLink.GetUrl(parameters, false)); break; case MenuCopyUsername: @@ -122,35 +112,37 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br break; case MenuOpenMediaUrl: - OpenBrowser(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality)); + OpenBrowser(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality)); break; case MenuCopyMediaUrl: - SetClipboardText(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality)); + SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality)); break; case MenuViewImage: - string url = GetMediaLink(parameters); - string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url)); - - void ViewFile(){ - string ext = Path.GetExtension(file); + void ViewImage(string path){ + string ext = Path.GetExtension(path); if (TwitterUtils.ValidImageExtensions.Contains(ext)){ - WindowsUtils.OpenAssociatedProgram(file); + WindowsUtils.OpenAssociatedProgram(path); } else{ 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)){ - ViewFile(); + ViewImage(file); } else{ 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); }); } @@ -158,20 +150,20 @@ void ViewFile(){ break; case MenuSaveMedia: - if (IsVideo){ + if (LastLink.IsVideo){ control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger); - TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault()); + TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault()); } else{ control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger); - TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality); + TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality); } break; case MenuSaveTweetImages: control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger); - TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality); + TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality); break; case MenuOpenDevTools: @@ -183,7 +175,7 @@ void ViewFile(){ } 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){ diff --git a/Core/Handling/ContextMenuBrowser.cs b/Core/Handling/ContextMenuBrowser.cs index 3f00c9c6..126ccccc 100644 --- a/Core/Handling/ContextMenuBrowser.cs +++ b/Core/Handling/ContextMenuBrowser.cs @@ -1,8 +1,6 @@ using CefSharp; using System.Windows.Forms; -using TweetDuck.Core.Bridge; using TweetDuck.Core.Controls; -using TweetDuck.Core.Utils; namespace TweetDuck.Core.Handling{ sealed class ContextMenuBrowser : ContextMenuBase{ @@ -26,10 +24,7 @@ sealed class ContextMenuBrowser : ContextMenuBase{ private const string TitleAboutProgram = "About "+Program.BrandName; private readonly FormBrowser form; - - private string lastHighlightedTweetUrl; - private string lastHighlightedQuoteUrl; - + public ContextMenuBrowser(FormBrowser form) : base(form){ this.form = form; } @@ -52,20 +47,12 @@ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser br base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); - lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl; - 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){ + if (!string.IsNullOrEmpty(LastChirp.TweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){ model.AddItem(MenuOpenTweetUrl, "Open tweet in browser"); model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard"); - if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){ + if (!string.IsNullOrEmpty(LastChirp.QuoteUrl)){ model.AddSeparator(); model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser"); model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); @@ -126,11 +113,11 @@ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser b return true; case MenuOpenTweetUrl: - OpenBrowser(form, lastHighlightedTweetUrl); + OpenBrowser(form, LastChirp.TweetUrl); return true; case MenuCopyTweetUrl: - SetClipboardText(form, lastHighlightedTweetUrl); + SetClipboardText(form, LastChirp.TweetUrl); return true; case MenuScreenshotTweet: @@ -138,11 +125,11 @@ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser b return true; case MenuOpenQuotedTweetUrl: - OpenBrowser(form, lastHighlightedQuoteUrl); + OpenBrowser(form, LastChirp.QuoteUrl); return true; case MenuCopyQuotedTweetUrl: - SetClipboardText(form, lastHighlightedQuoteUrl); + SetClipboardText(form, LastChirp.QuoteUrl); return true; case MenuInputApplyROT13: diff --git a/Core/Management/ContextInfo.cs b/Core/Management/ContextInfo.cs new file mode 100644 index 00000000..643f42d3 --- /dev/null +++ b/Core/Management/ContextInfo.cs @@ -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(type) ? 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; + } + } + } +} diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js index 011380c6..b64fe951 100644 --- a/Resources/Scripts/code.js +++ b/Resources/Scripts/code.js @@ -595,22 +595,15 @@ // Block: Update highlighted column and tweet for context menu and other functionality. // (function(){ - var lastTweet = ""; - const updateHighlightedColumn = function(ele){ highlightedColumnEle = ele; highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null; return !!highlightedColumnObj; }; - const updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){ + const updateHighlightedTweet = function(ele, obj){ highlightedTweetEle = ele; highlightedTweetObj = obj; - - if (lastTweet !== tweetUrl){ - $TD.setLastHighlightedTweet(tweetUrl, quoteUrl, authors, imageList); - lastTweet = tweetUrl; - } }; const processMedia = function(chirp){ @@ -626,6 +619,19 @@ mouseleave: function(){ 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); + } } }); @@ -637,21 +643,11 @@ let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key")); return if !tweet; - if (tweet.chirpType === TD.services.ChirpBase.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.hasImage() ? processMedia(tweet) : tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : ""; - - updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList); - } - else{ - updateHighlightedTweet(me, tweet, "", "", "", ""); - } + updateHighlightedTweet(me, tweet); }, mouseleave: function(){ - updateHighlightedTweet(null, null, "", "", "", ""); + updateHighlightedTweet(null, null); } }); })(); diff --git a/TweetDuck.csproj b/TweetDuck.csproj index 2927b1b4..82d0a630 100644 --- a/TweetDuck.csproj +++ b/TweetDuck.csproj @@ -108,6 +108,7 @@ <Compile Include="Core\Handling\RequestHandlerBrowser.cs" /> <Compile Include="Core\Handling\ResourceHandlerNotification.cs" /> <Compile Include="Core\ITweetDeckBrowser.cs" /> + <Compile Include="Core\Management\ContextInfo.cs" /> <Compile Include="Core\Notification\Example\FormNotificationExample.cs"> <SubType>Form</SubType> </Compile>