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

Compare commits

..

55 Commits

Author SHA1 Message Date
27c55718c2 Release 1.14.2.1 2018-06-04 13:01:44 +02:00
421ff0654b Temporarily work around buggy notification scrolling 2018-06-04 12:35:34 +02:00
ed947458f9 Update fallback HTML in desktop notifications 2018-06-04 12:09:02 +02:00
9cdb20ba84 Fix several broken column types in 'Add column' modal 2018-06-04 11:02:52 +02:00
d8774b735f Release 1.14.2 2018-06-04 08:59:32 +02:00
adcb42695f Update CEF so I don't have to workaround wrong dev tools version
Closes #220
2018-06-01 22:32:44 +02:00
dd77b5bcbb Revert smooth scrolling fix and rewrite horizontal scrolling fix to avoid column jumping 2018-06-01 21:48:26 +02:00
d2445be155 Fix missing GIF previews in tweet screenshots 2018-05-31 03:07:51 +02:00
10254c8af7 Fix tweet screenshots with Aero disabled by making the window visible
Closes #223
2018-05-31 02:58:25 +02:00
d7e830badf Slightly increase default notification height for larger font sizes 2018-05-31 01:11:17 +02:00
b445a3a9b8 Fix broken reply-account advanced selector arguments after a TweetDeck update 2018-05-31 00:52:08 +02:00
97f42ead66 Make it easier to debug screenshots 2018-05-30 19:50:11 +02:00
03730fafb9 ...because I can't be bothered 2018-05-29 15:34:02 +02:00
0be9465dca Fix column icons being hidden by title with clear-columns or edit-design features on 2018-05-29 15:23:41 +02:00
d7f1df4995 Release 1.14.1 2018-05-16 13:07:47 +02:00
3cb0f90706 Prevent an unlikely crash when showing an error message in context menu 2018-05-16 13:05:59 +02:00
a3e9b15a8a Add 'Apply ROT13' to non-editable selections to allow decoding tweets 2018-05-16 12:40:49 +02:00
00bfa68a57 Fix UI issues after recent TweetDeck updates 2018-05-16 07:48:47 +02:00
c311e24f08 Make it easier to update devtools file after updating CEF 2018-05-15 12:42:57 +02:00
1cdd4e4455 Update CefSharp to 66 (early) to fix memory leak 2018-05-15 10:10:03 +02:00
8078c0081a Add a script to fix browser project references after updating CEF 2018-05-15 10:04:08 +02:00
a867e1fc40 Optimize speed and memory usage of build process for app & installers 2018-05-08 20:20:02 +02:00
61da36ac1c Update README.md 2018-05-08 19:37:06 +02:00
720ca2a018 Update README.md 2018-05-08 19:36:08 +02:00
b39c593552 Update README.md 2018-05-08 06:28:07 +02:00
c808952a45 Update README.md 2018-05-08 00:03:08 +02:00
b468d7a766 Update README.md 2018-05-07 20:03:50 +02:00
28578f60be Include plugin files in the project & update README 2018-05-07 19:35:08 +02:00
92a39e2527 Push debug configuration start arguments (-datafolder TweetDuckDebug) 2018-05-07 18:11:56 +02:00
1bce5e4342 Release 1.14 2018-05-07 17:28:34 +02:00
68f586e104 Fix wrong info in the analytics dialog 2018-05-07 15:42:41 +02:00
d27a66202e Remove "Show this thread" in quoted tweets from notifications and screenshots 2018-05-07 03:01:24 +02:00
07de2f450c Fix broken notification scrolling in CEF 65 properly 2018-05-07 02:43:40 +02:00
3c03726634 Remove old and no longer necessary code from update installer 2018-05-06 22:47:58 +02:00
6fb2643063 Slightly increase installer compression level 2018-05-06 22:24:55 +02:00
5eef6c8196 Kill stubborn app processes when installing updates 2018-05-06 21:18:23 +02:00
829c332e13 Add a global function for mustache injection & fix broken clear-columns mustache 2018-05-05 11:47:19 +02:00
47eec14bca Fix freshly broken GIF previews in notifications 2018-05-04 21:07:33 +02:00
e7ee1d6be7 Revert "Fix broken notification scrolling in CEF 65"
This reverts commit 1029ea5840.
2018-05-04 14:00:41 +02:00
e41b5e5ff7 Replace generated license files with just one and include CEF license 2018-05-04 13:30:54 +02:00
ba1bacd08c Fix minor formatting and method modifier issues 2018-05-04 13:04:23 +02:00
1029ea5840 Fix broken notification scrolling in CEF 65 2018-05-04 11:41:20 +02:00
339eaf0195 Fix button appearance in introduction dialog and some plugins 2018-05-03 16:02:44 +02:00
aa1e1549d8 Fix Twitter's broken Cancel button when logging out 2018-05-03 15:04:09 +02:00
1f8ae9ef80 Update CefSharp to 65 (pre01) and fix blank example notification
* Update CefSharp to 65 (pre01)

* Fix blank example notification on first load
2018-05-03 14:05:56 +02:00
65165de060 Release 1.13.6 2018-05-02 16:27:23 +02:00
485836d2ce Replace about:blank in FormGuide with a dummy page 2018-05-02 15:21:23 +02:00
64c07c14d9 Revert "Update to CefSharp 65 (early) (#215)"
This reverts commit b6a599f8a6.
2018-05-02 15:19:01 +02:00
b6313c2b72 Update CefSharp to 65 (pre01) 2018-05-02 00:05:08 +02:00
58124b5821 Force Chrome UA on TweetDeck and remove -chromeagent argument 2018-05-01 19:31:39 +02:00
b6a599f8a6 Update to CefSharp 65 (early) (#215) 2018-05-01 19:20:13 +02:00
19a6bc0dbd Improve performance of PostBuild.ps1 2018-05-01 16:37:06 +02:00
8cb81d44ee Fix update installer changing uninstaller name and remove /MERGETASKS parameter 2018-05-01 14:36:51 +02:00
22d0a372d8 Add dev tools to the installer as an optional component 2018-05-01 14:30:56 +02:00
988fae75c3 Add a command line argument to use Chrome user agent 2018-04-30 21:02:15 +02:00
50 changed files with 712 additions and 459 deletions

View File

@@ -6,6 +6,8 @@ namespace TweetDuck.Configuration{
// public args // public args
public const string ArgDataFolder = "-datafolder"; public const string ArgDataFolder = "-datafolder";
public const string ArgLogging = "-log"; public const string ArgLogging = "-log";
public const string ArgIgnoreGDPR = "-nogdpr";
public const string ArgNotificationScrollWA = "-nscrollwa";
// internal args // internal args
public const string ArgRestart = "-restart"; public const string ArgRestart = "-restart";

View File

@@ -28,6 +28,7 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506; private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508; private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508;
private const CefMenuCommand MenuReadApplyROT13 = (CefMenuCommand)26509;
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599; private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
protected ContextInfo.LinkInfo LastLink { get; private set; } protected ContextInfo.LinkInfo LastLink { get; private set; }
@@ -57,6 +58,8 @@ namespace TweetDuck.Core.Handling{
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){ if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
model.AddItem(MenuSearchInBrowser, "Search in browser"); model.AddItem(MenuSearchInBrowser, "Search in browser");
model.AddSeparator(); model.AddSeparator();
model.AddItem(MenuReadApplyROT13, "Apply ROT13");
model.AddSeparator();
} }
bool hasTweetImage = LastLink.IsImage; bool hasTweetImage = LastLink.IsImage;
@@ -126,7 +129,7 @@ namespace TweetDuck.Core.Handling{
SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality)); SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
break; break;
case MenuViewImage: case MenuViewImage: {
void ViewImage(string path){ void ViewImage(string path){
string ext = Path.GetExtension(path); string ext = Path.GetExtension(path);
@@ -141,11 +144,12 @@ namespace TweetDuck.Core.Handling{
string url = LastLink.GetMediaSource(parameters); string url = LastLink.GetMediaSource(parameters);
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName()); string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName());
control.InvokeAsyncSafe(() => {
if (File.Exists(file)){ if (File.Exists(file)){
ViewImage(file); ViewImage(file);
} }
else{ else{
control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger); analytics.AnalyticsFile.ViewedImages.Trigger();
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => { BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
ViewImage(file); ViewImage(file);
@@ -153,25 +157,46 @@ namespace TweetDuck.Core.Handling{
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);
}); });
} }
});
break; break;
}
case MenuSaveMedia: case MenuSaveMedia: {
if (LastLink.IsVideo){ bool isVideo = LastLink.IsVideo;
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger); string url = LastLink.GetMediaSource(parameters);
TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault()); string username = LastChirp.Authors.LastOrDefault();
control.InvokeAsyncSafe(() => {
if (isVideo){
TwitterUtils.DownloadVideo(url, username);
analytics.AnalyticsFile.DownloadedVideos.Trigger();
} }
else{ else{
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger); TwitterUtils.DownloadImage(url, username, ImageQuality);
TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality); analytics.AnalyticsFile.DownloadedImages.Trigger();
}
});
break;
} }
break; case MenuSaveTweetImages: {
string[] urls = LastChirp.Images;
string username = LastChirp.Authors.LastOrDefault();
control.InvokeAsyncSafe(() => {
TwitterUtils.DownloadImages(urls, username, ImageQuality);
analytics.AnalyticsFile.DownloadedImages.Trigger();
});
case MenuSaveTweetImages:
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality);
break; break;
}
case MenuReadApplyROT13:
string selection = parameters.SelectionText;
control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK));
return true;
case MenuSearchInBrowser: case MenuSearchInBrowser:
string query = parameters.SelectionText; string query = parameters.SelectionText;
@@ -195,15 +220,15 @@ namespace TweetDuck.Core.Handling{
return false; return false;
} }
protected void DeselectAll(IFrame frame){ protected static void DeselectAll(IFrame frame){
ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect"); ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect");
} }
protected void OpenBrowser(Control control, string url){ protected static void OpenBrowser(Control control, string url){
control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url));
} }
protected void SetClipboardText(Control control, string text){ protected static void SetClipboardText(Control control, string text){
control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText)); control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
} }

View File

@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612; private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613; private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614; private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
private const CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615; private const CefMenuCommand MenuWriteApplyROT13 = (CefMenuCommand)26615;
private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616; private const CefMenuCommand MenuSearchInColumn = (CefMenuCommand)26616;
private const string TitleReloadBrowser = "Reload browser"; private const string TitleReloadBrowser = "Reload browser";
@@ -44,7 +44,7 @@ namespace TweetDuck.Core.Handling{
if (isSelecting){ if (isSelecting){
if (isEditing){ if (isEditing){
model.AddSeparator(); model.AddSeparator();
model.AddItem(MenuInputApplyROT13, "Apply ROT13"); model.AddItem(MenuWriteApplyROT13, "Apply ROT13");
} }
model.AddSeparator(); model.AddSeparator();
@@ -141,7 +141,7 @@ namespace TweetDuck.Core.Handling{
SetClipboardText(form, LastChirp.QuoteUrl); SetClipboardText(form, LastChirp.QuoteUrl);
return true; return true;
case MenuInputApplyROT13: case MenuWriteApplyROT13:
form.InvokeAsyncSafe(form.ApplyROT13); form.InvokeAsyncSafe(form.ApplyROT13);
return true; return true;

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using CefSharp; using CefSharp;
using CefSharp.Enums;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class DragHandlerBrowser : IDragHandler{ sealed class DragHandlerBrowser : IDragHandler{

View File

@@ -1,4 +1,6 @@
using System.Windows.Forms; using System;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Resources; using TweetDuck.Resources;
@@ -21,9 +23,13 @@ namespace TweetDuck.Core.Notification.Example{
} }
} }
public event EventHandler Ready;
private readonly TweetNotification exampleNotification; private readonly TweetNotification exampleNotification;
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){ public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
browser.LoadingStateChanged += browser_LoadingStateChanged;
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true)?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty; string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true)?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty;
#if DEBUG #if DEBUG
@@ -33,6 +39,13 @@ namespace TweetDuck.Core.Notification.Example{
exampleNotification = TweetNotification.Example(exampleTweetHTML, 176); exampleNotification = TweetNotification.Example(exampleTweetHTML, 176);
} }
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
Ready?.Invoke(this, EventArgs.Empty);
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
public override void HideNotification(){ public override void HideNotification(){
Location = ControlExtensions.InvisibleLocation; Location = ControlExtensions.InvisibleLocation;
} }

View File

@@ -1,6 +1,4 @@
using CefSharp; using CefSharp.WinForms;
using CefSharp.WinForms;
using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
@@ -65,7 +63,7 @@ namespace TweetDuck.Core.Notification{
} }
} }
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation; protected bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
protected virtual bool CanDragWindow => true; protected virtual bool CanDragWindow => true;
public new Point Location{ public new Point Location{
@@ -109,15 +107,13 @@ namespace TweetDuck.Core.Notification{
public string CurrentQuoteUrl => currentNotification?.QuoteUrl; public string CurrentQuoteUrl => currentNotification?.QuoteUrl;
public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId); public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId);
public bool IsPaused => pauseCounter > 0;
protected bool IsPaused => pauseCounter > 0;
protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position)); protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position));
public bool FreezeTimer { get; set; } public bool FreezeTimer { get; set; }
public bool ContextMenuOpen { get; set; } public bool ContextMenuOpen { get; set; }
public event EventHandler Initialized;
protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){ protected FormNotificationBase(FormBrowser owner, bool enableContextMenu){
InitializeComponent(); InitializeComponent();
@@ -133,7 +129,6 @@ namespace TweetDuck.Core.Notification{
this.browser.Dock = DockStyle.None; this.browser.Dock = DockStyle.None;
this.browser.ClientSize = ClientSize; this.browser.ClientSize = ClientSize;
this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
browser.SetupResourceHandler(TweetNotification.AppLogo); browser.SetupResourceHandler(TweetNotification.AppLogo);
@@ -165,12 +160,6 @@ namespace TweetDuck.Core.Notification{
Close(); Close();
} }
private void browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
if (e.IsBrowserInitialized){
Initialized?.Invoke(this, EventArgs.Empty);
}
}
// notification methods // notification methods
public virtual void HideNotification(){ public virtual void HideNotification(){

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
@@ -65,7 +66,7 @@ namespace TweetDuck.Core.Notification{
get{ get{
switch(Program.UserConfig.NotificationSize){ switch(Program.UserConfig.NotificationSize){
default: default:
return BrowserUtils.Scale(122, SizeScale*(1.0+0.075*FontSizeLevel)); return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel));
case TweetNotification.Size.Custom: case TweetNotification.Size.Custom:
return Program.UserConfig.CustomNotificationSize.Height; return Program.UserConfig.CustomNotificationSize.Height;
@@ -136,7 +137,14 @@ namespace TweetDuck.Core.Notification{
int eventType = wParam.ToInt32(); int eventType = wParam.ToInt32();
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){ if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
if (Arguments.HasFlag(Arguments.ArgNotificationScrollWA)){
int delta = BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01);
browser.ExecuteScriptAsync("window.scrollBy", 0, -Math.Round(delta/0.72));
}
else{
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None); browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
}
return NativeMethods.HOOK_HANDLED; return NativeMethods.HOOK_HANDLED;
} }
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){ else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){

View File

@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
} }
using(IFrame frame = args.Browser.MainFrame){ using(IFrame frame = args.Browser.MainFrame){
ScriptLoader.ExecuteScript(frame, script.Replace("{width}", BrowserUtils.Scale(width, DpiScale).ToString()), "screenshot"); ScriptLoader.ExecuteScript(frame, script.Replace("{width}", BrowserUtils.Scale(width, DpiScale).ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot");
} }
}; };
@@ -69,6 +69,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
} }
} }
if (!WindowsUtils.IsAeroEnabled){
MoveToVisibleLocation(); // TODO make this look nicer I guess
}
IntPtr context = NativeMethods.GetDC(this.Handle); IntPtr context = NativeMethods.GetDC(this.Handle);
if (context == IntPtr.Zero){ if (context == IntPtr.Zero){

View File

@@ -27,6 +27,10 @@ namespace TweetDuck.Core.Notification.Screenshot{
#if GEN_SCREENSHOT_FRAMES #if GEN_SCREENSHOT_FRAMES
private readonly Timer debugger; private readonly Timer debugger;
private int frameCounter; private int frameCounter;
public const int WaitFrames = 60;
#else
public const int WaitFrames = 5;
#endif #endif
private FormNotificationScreenshotable screenshot; private FormNotificationScreenshotable screenshot;

View File

@@ -7,7 +7,7 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed class TweetNotification{ sealed class TweetNotification{
private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>"; private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>";
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty; private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css") ?? string.Empty;
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png")); public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
@@ -70,7 +70,7 @@ namespace TweetDuck.Core.Notification{
build.Append(' ').Append(bodyClasses); build.Append(' ').Append(bodyClasses);
} }
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;height:auto!important;overflow:initial!important;'>"); build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;min-height:100vh!important;height:auto!important;overflow:initial!important;'>");
build.Append(html); build.Append(html);
build.Append("</div></body>"); build.Append("</div></body>");
build.Append("</html>"); build.Append("</html>");

View File

@@ -42,7 +42,6 @@ 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(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

@@ -8,6 +8,7 @@ using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TweetDuck.Data;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
@@ -15,6 +16,8 @@ namespace TweetDuck.Core.Other{
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/"; private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?"; private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandler.FromString(""));
public static bool CheckGuideUrl(string url, out string hash){ public static bool CheckGuideUrl(string url, out string hash){
if (!url.Contains("//tweetduck.chylex.com/guide")){ if (!url.Contains("//tweetduck.chylex.com/guide")){
hash = null; hash = null;
@@ -53,6 +56,7 @@ namespace TweetDuck.Core.Other{
} }
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
private string nextUrl;
private FormGuide(string url, FormBrowser owner){ private FormGuide(string url, FormBrowser owner){
InitializeComponent(); InitializeComponent();
@@ -75,6 +79,9 @@ namespace TweetDuck.Core.Other{
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb(); browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
browser.Dock = DockStyle.None; browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation; browser.Location = ControlExtensions.InvisibleLocation;
browser.SetupResourceHandler(DummyPage);
Controls.Add(browser); Controls.Add(browser);
Disposed += (sender, args) => { Disposed += (sender, args) => {
@@ -86,15 +93,19 @@ namespace TweetDuck.Core.Other{
} }
private void Reload(string url){ private void Reload(string url){
nextUrl = url;
browser.LoadingStateChanged += browser_LoadingStateChanged; browser.LoadingStateChanged += browser_LoadingStateChanged;
browser.Dock = DockStyle.None; browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation; browser.Location = ControlExtensions.InvisibleLocation;
browser.Load("about:blank"); browser.Load(DummyPage.Url);
browser.Load(url);
} }
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading && browser.Address != "about:blank"){ if (!e.IsLoading){
if (browser.Address == DummyPage.Url){
browser.Load(nextUrl);
}
else{
this.InvokeAsyncSafe(() => { this.InvokeAsyncSafe(() => {
browser.Location = Point.Empty; browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill; browser.Dock = DockStyle.Fill;
@@ -103,13 +114,13 @@ namespace TweetDuck.Core.Other{
browser.LoadingStateChanged -= browser_LoadingStateChanged; browser.LoadingStateChanged -= browser_LoadingStateChanged;
} }
} }
}
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){ private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel); BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
} }
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
// idiot chromium
ScriptLoader.ExecuteScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links"); ScriptLoader.ExecuteScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links");
} }

View File

@@ -63,10 +63,10 @@
this.labelInfo.Location = new System.Drawing.Point(12, 9); this.labelInfo.Location = new System.Drawing.Point(12, 9);
this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
this.labelInfo.Name = "labelInfo"; this.labelInfo.Name = "labelInfo";
this.labelInfo.Size = new System.Drawing.Size(431, 30); this.labelInfo.Size = new System.Drawing.Size(434, 30);
this.labelInfo.TabIndex = 0; this.labelInfo.TabIndex = 0;
this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly twice a week.\r" + this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly every 14 days." +
"\nSome numbers in the report were made imprecise on purpose."; "\r\nSome numbers in the report were made imprecise on purpose.";
// //
// DialogSettingsAnalytics // DialogSettingsAnalytics
// //

View File

@@ -41,7 +41,7 @@
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true; this.btnCancel.AutoSize = true;
this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.btnCancel.Location = new System.Drawing.Point(214, 148); this.btnCancel.Location = new System.Drawing.Point(215, 146);
this.btnCancel.Name = "btnCancel"; this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); this.btnCancel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCancel.Size = new System.Drawing.Size(57, 25); this.btnCancel.Size = new System.Drawing.Size(57, 25);
@@ -55,7 +55,7 @@
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnRestart.AutoSize = true; this.btnRestart.AutoSize = true;
this.btnRestart.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.btnRestart.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.btnRestart.Location = new System.Drawing.Point(151, 148); this.btnRestart.Location = new System.Drawing.Point(152, 146);
this.btnRestart.Name = "btnRestart"; this.btnRestart.Name = "btnRestart";
this.btnRestart.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0); this.btnRestart.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnRestart.Size = new System.Drawing.Size(57, 25); this.btnRestart.Size = new System.Drawing.Size(57, 25);
@@ -74,7 +74,7 @@
this.cbLogging.Size = new System.Drawing.Size(70, 19); this.cbLogging.Size = new System.Drawing.Size(70, 19);
this.cbLogging.TabIndex = 0; this.cbLogging.TabIndex = 0;
this.cbLogging.Text = "Logging"; this.cbLogging.Text = "Logging";
this.toolTip.SetToolTip(this.cbLogging, "Logging JavaScript output into TD_Console.txt file in the data folder."); this.toolTip.SetToolTip(this.cbLogging, "Logs JavaScript output into TD_Console.txt file in the data folder.");
this.cbLogging.UseVisualStyleBackColor = true; this.cbLogging.UseVisualStyleBackColor = true;
// //
// tbDataFolder // tbDataFolder
@@ -136,7 +136,7 @@
this.flowPanel.Location = new System.Drawing.Point(9, 9); this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Margin = new System.Windows.Forms.Padding(0); this.flowPanel.Margin = new System.Windows.Forms.Padding(0);
this.flowPanel.Name = "flowPanel"; this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(266, 138); this.flowPanel.Size = new System.Drawing.Size(266, 136);
this.flowPanel.TabIndex = 0; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
@@ -144,7 +144,7 @@
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 185); this.ClientSize = new System.Drawing.Size(284, 183);
this.Controls.Add(this.flowPanel); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.btnRestart); this.Controls.Add(this.btnRestart);
this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnCancel);

View File

@@ -15,7 +15,7 @@ namespace TweetDuck.Core.Other.Settings{
this.notification = notification; this.notification = notification;
this.notification.Initialized += (sender, args) => { this.notification.Ready += (sender, args) => {
this.InvokeAsyncSafe(() => { this.InvokeAsyncSafe(() => {
this.notification.ShowExampleNotification(true); this.notification.ShowExampleNotification(true);
this.notification.Move += notification_Move; this.notification.Move += notification_Move;

View File

@@ -4,6 +4,7 @@ using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp; using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
@@ -154,6 +155,10 @@ namespace TweetDuck.Core{
TweetDeckBridge.ResetStaticProperties(); TweetDeckBridge.ResetStaticProperties();
if (Arguments.HasFlag(Arguments.ArgIgnoreGDPR)){
ScriptLoader.ExecuteScript(frame, @"TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
}
if (Program.UserConfig.FirstRun){ if (Program.UserConfig.FirstRun){
ScriptLoader.ExecuteFile(frame, "introduction.js", browser); ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
} }

View File

@@ -11,7 +11,8 @@ using TweetDuck.Data;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion; public static string UserAgentVanilla => Program.BrandName+" "+Application.ProductVersion;
public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"+Cef.ChromiumVersion+" Safari/537.36";
public static void SetupCefArgs(IDictionary<string, string> args){ public static void SetupCefArgs(IDictionary<string, string> args){
if (!Program.SystemConfig.HardwareAcceleration){ if (!Program.SystemConfig.HardwareAcceleration){
@@ -167,7 +168,7 @@ namespace TweetDuck.Core.Utils{
WindowsUtils.EnsureTLS12(); WindowsUtils.EnsureTLS12();
WebClient client = new WebClient{ Proxy = null }; WebClient client = new WebClient{ Proxy = null };
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent; client.Headers[HttpRequestHeader.UserAgent] = UserAgentVanilla;
return client; return client;
} }

View File

@@ -71,6 +71,9 @@ namespace TweetDuck.Core.Utils{
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop); private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(out bool enabled);
[DllImport("user32.dll")] [DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow); public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

View File

@@ -19,6 +19,14 @@ namespace TweetDuck.Core.Utils{
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper(); return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
} }
public static string ConvertRot13(string str){
return Regex.Replace(str, @"[a-zA-Z]", match => {
int code = match.Value[0];
int start = code <= 90 ? 65 : 97;
return ((char)(start+(code-start+13)%26)).ToString();
});
}
public static int CountOccurrences(string source, string substring){ public static int CountOccurrences(string source, string substring){
int count = 0, index = 0; int count = 0, index = 0;

View File

@@ -15,10 +15,12 @@ namespace TweetDuck.Core.Utils{
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false); private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false); private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
private static readonly bool IsWindows8OrNewer;
private static bool HasMicrosoftBeenBroughtTo2008Yet;
public static int CurrentProcessID { get; } public static int CurrentProcessID { get; }
public static bool ShouldAvoidToolWindow { get; } public static bool ShouldAvoidToolWindow { get; }
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
private static bool HasMicrosoftBeenBroughtTo2008Yet;
static WindowsUtils(){ static WindowsUtils(){
using(Process me = Process.GetCurrentProcess()){ using(Process me = Process.GetCurrentProcess()){
@@ -26,7 +28,9 @@ namespace TweetDuck.Core.Utils{
} }
Version ver = Environment.OSVersion.Version; Version ver = Environment.OSVersion.Version;
ShouldAvoidToolWindow = ver.Major == 6 && ver.Minor == 2; // windows 8/10 IsWindows8OrNewer = ver.Major == 6 && ver.Minor == 2; // windows 8/10
ShouldAvoidToolWindow = IsWindows8OrNewer;
} }
public static void EnsureTLS12(){ public static void EnsureTLS12(){

View File

@@ -19,7 +19,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.5"; public const string VersionTag = "1.14.2.1";
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory; public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable")); public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
@@ -133,7 +133,7 @@ namespace TweetDuck{
CefSharpSettings.LegacyJavascriptBindingEnabled = true; CefSharpSettings.LegacyJavascriptBindingEnabled = true;
CefSettings settings = new CefSettings{ CefSettings settings = new CefSettings{
UserAgent = BrowserUtils.HeaderUserAgent, UserAgent = BrowserUtils.UserAgentChrome,
BrowserSubprocessPath = BrandName+".Browser.exe", BrowserSubprocessPath = BrandName+".Browser.exe",
CachePath = StoragePath, CachePath = StoragePath,
UserDataPath = CefDataPath, UserDataPath = CefDataPath,
@@ -157,7 +157,7 @@ namespace TweetDuck{
ExitCleanup(); ExitCleanup();
// ProgramPath has a trailing backslash // ProgramPath has a trailing backslash
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : ""); string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath); bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){ if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){

View File

@@ -12,50 +12,44 @@ 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 this command in the **Package Manager Console**: After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
``` ```
PM> Install-Package CefSharp.WinForms -Version 64.0.0-CI2508 -Source https://www.myget.org/F/cefsharp/api/v3/index.json PM> Install-Package CefSharp.WinForms -Version 66.0.0-CI2629 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
``` ```
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.
### Debug ### Debug
It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution. The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure your **Configuration** is set to `Active (Debug)` (or just `Debug`), and insert this into the **Command line arguments** field: While debugging, opening the main menu and clicking **Reload browser** automatically rebuilds all resources in the `Resources/Scripts` and `Resources/Plugins` folders. This allows editing HTML/CSS/JS files and applying the changes without restarting the program, but it will cause a short delay between browser reloads.
```
-datafolder TweetDuckDebug
```
### Build ### Release
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section. Open **Batch Build**, tick all `Release` configurations with `x86` platform except for the `UnitTest` project, and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation. After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck. The `Release` configuration omits debug symbols and other unnecessary files. You can modify this behavior by opening `TweetDuck.csproj`, and editing the `<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">` section.
If you decide to publicly release a custom version, please make it clear that it is not an official release of TweetDuck. There are many references to the official website and this repository, especially in the update system, so search for `chylex.com` and `github.com` in all files and replace them appropriately.
### Troubleshooting ### Troubleshooting
There are a few quirks in the build process that may catch you off guard: #### Error: The command (...) exited with code 1
- This indicates a failed post-build event, open the **Output** tab for logs
- **Plugin files are not updated automatically** - Determine if there was an IO error from the `rmdir` commands, or whether the error was in the **PostBuild.ps1** script (`Encountered an error while running PostBuild.ps1 on line <xyz>`)
- Since official plugins (`Resources/Plugins`) are not included in the project, Visual Studio will not automatically detect changes in the files
- To ensure plugins are updated when testing the app, click **Rebuild Solution** before clicking **Start**
- **Error: The command (...) exited with code 1**
- If the post-build event fails, open the **Output** tab and look for the cause
- Determine if there was an IO error while copying files or modifying folders, or whether the final .ps1 script failed (`Encountered an error while running PostBuild.ps1 on line xyz`)
- Some files are checked for invalid characters: - Some files are checked for invalid characters:
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor - `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
### Installers ### Installers
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin). TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place. Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that despite the name, this will only package the files, you still need to run the [build](#build) in Visual Studio! Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that this will only package the files, you still need to run the [release build](#release) in Visual Studio!
After the window closes, three installers will be generated inside the `bld/Output` folder: After the window closes, three installers will be generated inside the `bld/Output` folder:
* **TweetDuck.exe** * **TweetDuck.exe**
@@ -67,8 +61,8 @@ After the window closes, three installers will be generated inside the `bld/Outp
* This is a portable installer that does not need administrator privileges * This is a portable installer that does not need administrator privileges
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode * It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
Note: There is a small chance you will see a resource error when running `RUN BUILD.bat`. If that happens, close the console window (which will terminate all Inno Setup processes and leave corrupted installer files in the output folder), and run it again. #### Notes
### Code Notes > There is a small chance running `RUN BUILD.bat` immediately shows a resource error. If that happens, close the console window (which terminates all Inno Setup processes and leaves corrupted installers in the output folder), and run it again.
There are many references to the official TweetDuck website and this repository in the code and installers, so if you plan to release your own version, make sure to search for `tweetduck.chylex.com` and `github.com` in the whole repository and replace them appropriately. > Running `RUN BUILD.bat` uses about 400 MB of RAM due to high compression. You can lower this to about 140 MB by opening `gen_full.iss` and `gen_port.iss`, and changing `LZMADictionarySize=15360` to `LZMADictionarySize=4096`.

View File

@@ -21,10 +21,6 @@ enabled(){
Object.keys(TD.controller.columnManager.getAll()).forEach(func); Object.keys(TD.controller.columnManager.getAll()).forEach(func);
}; };
var replaceMustache = (key, search, replace) => {
TD.mustaches[key] = TD.mustaches[key].replace(search, replace);
};
var wasShiftPressed = false; var wasShiftPressed = false;
var updateShiftState = (pressed) => { var updateShiftState = (pressed) => {
@@ -86,33 +82,29 @@ enabled(){
</a>`; </a>`;
// add column buttons and keyboard shortcut info to UI // add column buttons and keyboard shortcut info to UI
replaceMustache("column/column_header.mustache", "</header>", ` window.TDPF_injectMustache("column/column_header.mustache", "prepend", "<a data-testid=\"optionsToggle\"", `
{{^isTemporary}} <a class="js-action-header-button column-header-link" href="#" data-action="td-clearcolumns-dosingle">
<a class="column-header-link td-clear-column-shortcut" href="#" data-action="td-clearcolumns-dosingle" style="right:34px">
<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></i> <i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)"></i>
</a> </a>`);
{{/isTemporary}}
</header>`);
replaceMustache("keyboard_shortcut_list.mustache", "</dl> <dl", ` window.TDPF_injectMustache("keyboard_shortcut_list.mustache", "prepend", "</dl> <dl", `
<dd class="keyboard-shortcut-definition" style="white-space:nowrap"> <dd class="keyboard-shortcut-definition" style="white-space:nowrap">
<span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 19 <span class="text-like-keyboard-key">1</span> … <span class="text-like-keyboard-key">9</span> + <span class="text-like-keyboard-key">Del</span> Clear column 19
</dd><dd class="keyboard-shortcut-definition"> </dd><dd class="keyboard-shortcut-definition">
<span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all columns <span class="text-like-keyboard-key">Alt</span> + <span class="text-like-keyboard-key">Del</span> Clear all columns
</dd></dl><dl`); </dd>`);
replaceMustache("menus/column_nav_menu.mustache", "{{_i}}Add column{{/i}}</div> </a> </div>", `{{_i}}Add column{{/i}}</div></a>${this.btnClearAllHTML}</div>`) window.TDPF_injectMustache("menus/column_nav_menu.mustache", "replace", "{{_i}}Add column{{/i}}</div> </a> </div>", `{{_i}}Add column{{/i}}</div></a>${this.btnClearAllHTML}</div>`)
// load custom style // load custom style
var css = window.TDPF_createCustomStyle(this); var css = window.TDPF_createCustomStyle(this);
css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }"); css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
css.insert(".column-header-links { min-width: 51px !important; }");
css.insert("[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }"); css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
css.insert(".column-navigator-overflow { bottom: 224px !important; }"); css.insert(".column-navigator-overflow { bottom: 224px !important; }");
css.insert(".column-title { margin-right: 60px !important; }"); css.insert("[data-action='td-clearcolumns-dosingle'] { padding: 3px 0 !important; }");
css.insert(".mark-all-read-link { right: 59px !important; }"); css.insert("[data-action='clear'].btn-options-tray { display: none !important; }");
css.insert(".open-compose-dm-link { right: 90px !important; }");
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }");
css.insert("[data-td-icon='icon-message'] .column-title { margin-right: 115px !important; }");
css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }"); css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }");
css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }"); css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }");
} }

View File

@@ -539,8 +539,8 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important } #tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important } #tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
.column-header .column-type-icon { bottom: 26px !important } .column-type-icon { margin-top: -1px !important }
.is-options-open .column-type-icon { bottom: 25px !important } .inline-reply .pull-left .Button--link { margin-top: 3px !important }
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; } .tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; } .tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }

View File

@@ -151,8 +151,7 @@
<!-- END --> <!-- END -->
</div> </div>
<footer class="padding-vxl txt-center"> <footer class="padding-vxl txt-center">
<button class="js-dismiss btn btn-positive pull-right"> <button class="js-dismiss Button--primary pull-right">
<i class="icon icon-check icon-small padding-rs"></i>
<span class="label">Done</span> <span class="label">Done</span>
</button> </button>
</footer> </footer>

View File

@@ -54,7 +54,7 @@ enabled(){
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>'; 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); window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
var maybeDockedComposePanel = $(".js-docked-compose"); var maybeDockedComposePanel = $(".js-docked-compose");

View File

@@ -23,25 +23,34 @@ enabled(){
return; return;
} }
var section = data.element.closest("section.js-column"); let section = data.element.closest("section.js-column");
let column = TD.controller.columnManager.get(section.attr("data-column"));
var column = TD.controller.columnManager.get(section.attr("data-column")); let feeds = column.getFeeds();
var header = $(".column-title", section); let accountText = "";
var title = header.children(".column-head-title");
var columnTitle, columnAccount; if (feeds.length === 1){
let metadata = feeds[0].getMetadata();
let id = metadata.ownerId || metadata.id;
if (title.length){ if (id){
columnTitle = title.text(); accountText = TD.cache.names.getScreenName(id);
columnAccount = header.children(".attribution").text();
} }
else{ else{
columnTitle = header.children(".column-title-edit-box").val(); let account = TD.storage.accountController.get(feeds[0].getAccountKey());
columnAccount = "";
if (account){
accountText = "@"+account.getUsername();
}
}
} }
let header = $(".column-header-title", section);
let title = header.children(".column-heading");
let titleText = title.length ? title.text() : header.children(".column-title-edit-box").val();
try{ try{
query = configuration.customSelector(columnTitle, columnAccount, column, section.hasClass("column-temp")); query = configuration.customSelector(titleText, accountText, column, section.hasClass("column-temp"));
}catch(e){ }catch(e){
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message); $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
return; return;

View File

@@ -37,7 +37,7 @@ enabled(){
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>'; 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">'); window.TDPF_injectMustache("compose/docked_compose.mustache", "prepend", '<div class="js-tweet-type-button">', buttonHTML);
var dockedComposePanel = $(".js-docked-compose"); var dockedComposePanel = $(".js-docked-compose");
@@ -267,8 +267,8 @@ enabled(){
<ul></ul> <ul></ul>
<div class="templates-modal-bottom"> <div class="templates-modal-bottom">
<button data-action="close" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button> <button data-action="close" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
<button data-action="new-template" class="btn btn-positive"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button> <button data-action="new-template" class="Button--primary"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
</div> </div>
</div> </div>
@@ -300,8 +300,8 @@ enabled(){
</div> </div>
<div class="templates-modal-bottom"> <div class="templates-modal-bottom">
<button data-action="editor-cancel" class="btn"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button> <button data-action="editor-cancel" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
<button data-action="editor-confirm" class="btn btn-positive" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button> <button data-action="editor-confirm" class="Button--primary" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -27,90 +27,99 @@ try{
# Copy resources # Copy resources
Copy-Item (Join-Path $projectDir "bld\Resources\CEFSHARP-LICENSE.txt") -Destination $targetDir -Force Copy-Item (Join-Path $projectDir "bld\Resources\LICENSES.txt") -Destination $targetDir -Force
Copy-Item (Join-Path $projectDir "LICENSE.md") -Destination (Join-Path $targetDir "LICENSE.txt") -Force
New-Item -ItemType directory -Path $targetDir -Name "scripts" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "scripts" | Out-Null
New-Item -ItemType directory -Path $targetDir -Name "plugins" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "plugins" | Out-Null
New-Item -ItemType directory -Path $targetDir -Name "plugins\official" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "plugins\official" | Out-Null
New-Item -ItemType directory -Path $targetDir -Name "plugins\user" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "plugins\user" | Out-Null
Copy-Item (Join-Path $projectDir "Resources\Scripts\*") -Destination (Join-Path $targetDir "scripts") -Recurse Copy-Item (Join-Path $projectDir "Resources\Scripts\*") -Recurse -Destination (Join-Path $targetDir "scripts")
Copy-Item (Join-Path $projectDir "Resources\Plugins\*") -Exclude ".debug", "emoji-instructions.txt" -Destination (Join-Path $targetDir "plugins\official") -Recurse Copy-Item (Join-Path $projectDir "Resources\Plugins\*") -Recurse -Destination (Join-Path $targetDir "plugins\official") -Exclude ".debug", "emoji-instructions.txt"
if ($configuration -eq "Debug"){ if ($configuration -eq "Debug"){
New-Item -ItemType directory -Path $targetDir -Name "plugins\user\.debug" | Out-Null New-Item -ItemType directory -Path $targetDir -Name "plugins\user\.debug" | Out-Null
Copy-Item (Join-Path $projectDir "Resources\Plugins\.debug\*") -Destination (Join-Path $targetDir "plugins\user\.debug") -Recurse Copy-Item (Join-Path $projectDir "Resources\Plugins\.debug\*") -Recurse -Destination (Join-Path $targetDir "plugins\user\.debug")
} }
# Helper functions # Helper functions
function Check-Carriage-Return{ function Remove-Empty-Lines{
Param( Param([Parameter(Mandatory = $True, Position = 1)] $lines)
[Parameter(Mandatory = $True, Position = 1)] $fname
)
$file = @(Get-ChildItem -Path $targetDir -Include $fname -Recurse)[0] ForEach($line in $lines){
if ($line -ne ''){
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){ $line
Throw "$fname cannot contain carriage return" }
}
} }
Write-Host "Verified" $file.FullName.Substring($targetDir.Length) function Check-Carriage-Return{
Param([Parameter(Mandatory = $True, Position = 1)] $file)
if (!(Test-Path $file)){
Throw "$file does not exist"
}
if ((Get-Content -Path $file -Raw).Contains("`r")){
Throw "$file cannot contain carriage return"
}
Write-Host "Verified" $file.Substring($targetDir.Length)
} }
function Rewrite-File{ function Rewrite-File{
[CmdletBinding()] Param([Parameter(Mandatory = $True, Position = 1)] $file,
Param( [Parameter(Mandatory = $True, Position = 2)] $lines)
[Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines,
[Parameter(Mandatory = $True, Position = 1)] $file
)
$lines = Remove-Empty-Lines($lines)
$relativePath = $file.FullName.Substring($targetDir.Length) $relativePath = $file.FullName.Substring($targetDir.Length)
if ($relativePath.StartsWith("scripts\")){ if ($relativePath.StartsWith("scripts\")){
$lines = (,("#" + $version) + $lines) $lines = (,("#" + $version) + $lines)
} }
$lines | Where { $_ -ne '' } | Set-Content -Path $file.FullName [IO.File]::WriteAllLines($file.FullName, $lines)
Write-Host "Processed" $relativePath Write-Host "Processed" $relativePath
} }
# Post processing # Post processing
Check-Carriage-Return("emoji-ordering.txt") Check-Carriage-Return(Join-Path $targetDir "plugins\official\emoji-keyboard\emoji-ordering.txt")
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){ ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
$lines = Get-Content -Path $file.FullName $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines | % { $_.TrimStart() } $lines = $lines | % { $_.TrimStart() }
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1' $lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
$lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;' $lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
,$lines | Rewrite-File $file Rewrite-File $file $lines
} }
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){ ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
$lines = Get-Content -Path $file.FullName $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines -Replace '\s*/\*.*?\*/', '' $lines = $lines -Replace '\s*/\*.*?\*/', ''
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;' $lines = $lines -Replace '^(\S.*) {$', '$1{'
$lines = $lines -Replace '^(\S.*?) {$', '$1{' $lines = $lines -Replace '^\s+(.+?):\s*(.+?)(?:\s*(!important))?;$', '$1:$2$3;'
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file $lines = @((Remove-Empty-Lines($lines)) -Join ' ')
Rewrite-File $file $lines
} }
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){ ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){
$lines = Get-Content -Path $file.FullName $lines = [IO.File]::ReadLines($file.FullName)
,($lines | % { $_.TrimStart() }) | Rewrite-File $file $lines = $lines | % { $_.TrimStart() }
Rewrite-File $file $lines
} }
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.meta" -Recurse){ ForEach($file in Get-ChildItem -Path (Join-Path $targetDir "plugins") -Filter "*.meta" -Recurse){
$lines = Get-Content -Path $file.FullName $lines = [IO.File]::ReadLines($file.FullName)
$lines = $lines -Replace '\{version\}', $version $lines = $lines -Replace '\{version\}', $version
,$lines | Rewrite-File $file Rewrite-File $file $lines
} }
Write-Host "-------------------" Write-Host "------------------"
$sw.Stop() $sw.Stop()
Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms" Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms"
Write-Host ------------------- Write-Host "------------------"
}catch{ }catch{
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
Write-Host $_ Write-Host $_

View File

@@ -0,0 +1,18 @@
$ErrorActionPreference = "Stop"
$MainProj = "..\TweetDuck.csproj"
$BrowserProj = "..\subprocess\TweetDuck.Browser.csproj"
$Match = Select-String -Path $MainProj '<Import Project="packages\\cef\.redist\.x86\.(.*?)\\'
$Version = $Match.Matches[0].Groups[1].Value
Copy-Item "..\packages\cef.redist.x86.${Version}\CEF\devtools_resources.pak" -Destination "..\bld\Resources\" -Force
$Match = Select-String -Path $MainProj '<Import Project="packages\\CefSharp\.Common\.(.*?)\\'
$Version = $Match.Matches[0].Groups[1].Value
$Contents = [IO.File]::ReadAllText($BrowserProj)
$Contents = $Contents -Replace '(?<=<HintPath>\.\.\\packages\\CefSharp\.Common\.)(.*?)(?=\\)', $Version
$Contents = $Contents -Replace '(?<=<Reference Include="CefSharp\.BrowserSubprocess\.Core, Version=)(\d+)', $Version.Split(".")[0]
[IO.File]::WriteAllText($BrowserProj, $Contents)

View File

@@ -146,7 +146,7 @@
}; };
let fixMedia = (html, media) => { let fixMedia = (html, media) => {
return html.find(".js-media a[data-media-entity-id='"+media.mediaId+"']").css("background-image", 'url("'+media.small()+'")').removeClass("is-zoomable"); return html.find("a[data-media-entity-id='"+media.mediaId+"'], .media-item").first().removeClass("is-zoomable").css("background-image", 'url("'+media.small()+'")');
}; };
return function(column, tweet){ return function(column, tweet){
@@ -181,13 +181,15 @@
if (previews){ if (previews){
html.find(".reverse-image-search").remove(); html.find(".reverse-image-search").remove();
let container = html.find(".js-media");
for(let media of tweet.getMedia()){ for(let media of tweet.getMedia()){
fixMedia(html, media); fixMedia(container, media);
} }
if (tweet.quotedTweet){ if (tweet.quotedTweet){
for(let media of tweet.quotedTweet.getMedia()){ for(let media of tweet.quotedTweet.getMedia()){
fixMedia(html, media).addClass("media-size-medium"); fixMedia(container, media).addClass("media-size-medium");
} }
} }
} }
@@ -211,6 +213,12 @@
} }
}); });
if (tweet.quotedTweet){
html.find("p.txt-mute").filter(function(){
return $(this).text() === "Show this thread";
}).first().remove();
}
let type = tweet.getChirpType(); let type = tweet.getChirpType();
if (type === "follow"){ if (type === "follow"){
@@ -680,9 +688,9 @@
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
html.find(".td-screenshot-remove").remove(); html.find(".td-screenshot-remove").remove();
html.find("p.link-complex-target").filter(function(){ html.find("p.link-complex-target,p.txt-mute").filter(function(){
return $(this).text() === "Show this thread"; return $(this).text() === "Show this thread";
}).first().remove(); }).remove();
html.addClass($(document.documentElement).attr("class")); html.addClass($(document.documentElement).attr("class"));
html.addClass($(document.body).attr("class")); html.addClass($(document.body).attr("class"));
@@ -699,6 +707,12 @@
} }
} }
let gif = html.find(".js-media-gif-container");
if (gif.length){
gif.css("background-image", 'url("'+chirp.getMedia()[0].small()+'")');
}
let type = chirp.getChirpType(); let type = chirp.getChirpType();
if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){ if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){
@@ -1001,10 +1015,40 @@
}; };
})(); })();
//
// Block: Setup global function to inject custom HTML into mustache templates.
//
window.TDGF_injectMustache = function(name, operation, search, custom){
let replacement;
switch(operation){
case "replace": replacement = custom; break;
case "append": replacement = search+custom; break;
case "prepend": replacement = custom+search; break;
default: throw "Invalid mustache injection operation. Only 'replace', 'append', 'prepend' are supported.";
}
let prev = TD.mustaches[name];
if (!prev){
$TD.crashDebug("Mustache injection is referencing an invalid mustache: "+name);
return false;
}
TD.mustaches[name] = prev.replace(search, replacement);
if (prev === TD.mustaches[name]){
$TD.crashDebug("Mustache injection had no effect: "+name);
return false;
}
return true;
};
// //
// Block: Let's make retweets lowercase again. // Block: Let's make retweets lowercase again.
// //
TD.mustaches["status/tweet_single.mustache"] = TD.mustaches["status/tweet_single.mustache"].replace("{{_i}} Retweeted{{/i}}", "{{_i}} retweeted{{/i}}"); window.TDGF_injectMustache("status/tweet_single.mustache", "replace", "{{_i}} Retweeted{{/i}}", "{{_i}} retweeted{{/i}}");
if (ensurePropertyExists(TD, "services", "TwitterActionRetweet", "prototype", "generateText")){ if (ensurePropertyExists(TD, "services", "TwitterActionRetweet", "prototype", "generateText")){
TD.services.TwitterActionRetweet.prototype.generateText = appendToFunction(TD.services.TwitterActionRetweet.prototype.generateText, function(){ TD.services.TwitterActionRetweet.prototype.generateText = appendToFunction(TD.services.TwitterActionRetweet.prototype.generateText, function(){
@@ -1073,7 +1117,8 @@
} }
}); });
TD.mustaches["status/media_thumb.mustache"] = TD.mustaches["status/media_thumb.mustache"].replace("is-gif", "is-gif is-paused"); window.TDGF_injectMustache("status/media_thumb.mustache", "append", "is-gif", " is-paused");
TD.mustaches["media/native_video.mustache"] = '<div class="js-media-gif-container media-item nbfc is-video" style="background-image:url({{imageSrc}})"><video class="js-media-gif media-item-gif full-width block {{#isPossiblySensitive}}is-invisible{{/isPossiblySensitive}}" loop src="{{videoUrl}}"></video><a class="js-gif-play pin-all is-actionable">{{> media/video_overlay}}</a></div>'; TD.mustaches["media/native_video.mustache"] = '<div class="js-media-gif-container media-item nbfc is-video" style="background-image:url({{imageSrc}})"><video class="js-media-gif media-item-gif full-width block {{#isPossiblySensitive}}is-invisible{{/isPossiblySensitive}}" loop src="{{videoUrl}}"></video><a class="js-gif-play pin-all is-actionable">{{> media/video_overlay}}</a></div>';
if (!ensurePropertyExists(TD, "components", "MediaGallery", "prototype", "_loadTweet") || if (!ensurePropertyExists(TD, "components", "MediaGallery", "prototype", "_loadTweet") ||
@@ -1329,11 +1374,20 @@
} }
// //
// Block: Remove column mouse wheel handler, which allows smooth scrolling inside columns, and horizontally scrolling column container when holding Shift. // Block: Fix broken horizontal scrolling of column container when holding Shift. TODO Fix broken smooth scrolling.
// //
if (ensurePropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){ if (ensurePropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){
TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(e){ TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(column){
$(".js-column[data-column='"+e.model.getKey()+"']").off("mousewheel onmousewheel"); let ele = $(".js-column[data-column='"+column.model.getKey()+"']");
return if !ele.length;
ele.off("onmousewheel").on("mousewheel", ".scroll-v", function(e){
if (e.shiftKey){
e.stopImmediatePropagation();
}
});
window.TDGF_prioritizeNewestEvent(ele[0], "mousewheel");
}); });
} }

View File

@@ -93,12 +93,12 @@
<label for="td-anonymous-data">Send anonymous usage data</label> <label for="td-anonymous-data">Send anonymous usage data</label>
<label>&nbsp;(<a href="https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data" rel="nofollow">learn more</a>)</label> <label>&nbsp;(<a href="https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data" rel="nofollow">learn more</a>)</label>
</div> </div>
<button class="btn btn-positive" data-guide><span class="label">Show Guide</span></button> <button class="Button--primary" data-guide><span class="label">Show Guide</span></button>
<button class="btn btn-positive"><span class="label">Close</span</button> <button class="Button--secondary"><span class="label">Close</span</button>
</footer> </footer>
</div> </div>
</div> </div>
</div>`).appendTo(".js-app"); /* TODO btn-positive is removed, check all files again */ </div>`).appendTo(".js-app");
let tdUser = null; let tdUser = null;
let loadTweetDuckUser = (onSuccess, onError) => { let loadTweetDuckUser = (onSuccess, onError) => {

View File

@@ -129,11 +129,10 @@
// Block: Setup a skip button. // Block: Setup a skip button.
// //
if (!document.body.hasAttribute("td-example-notification")){ if (!document.body.hasAttribute("td-example-notification")){
document.body.insertAdjacentHTML("afterbegin", ` document.body.children[0].insertAdjacentHTML("beforeend", `
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600"> <svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547"> <path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
</svg> </svg>`);
`);
document.getElementById("td-skip").addEventListener("click", function(){ document.getElementById("td-skip").addEventListener("click", function(){
$TD.loadNextNotification(); $TD.loadNextNotification();
@@ -150,4 +149,9 @@
document.body.addEventListener("mouseleave", function(){ document.body.addEventListener("mouseleave", function(){
document.body.classList.remove("td-hover"); document.body.classList.remove("td-hover");
}); });
//
// Block: Force a reset of scroll position on every load.
//
history.scrollRestoration = "manual";
})($TD, $TDX); })($TD, $TDX);

View File

@@ -121,4 +121,5 @@
window.TDPF_playVideo = window.TDGF_playVideo; window.TDPF_playVideo = window.TDGF_playVideo;
window.TDPF_reloadColumns = window.TDGF_reloadColumns; window.TDPF_reloadColumns = window.TDGF_reloadColumns;
window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent; window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent;
window.TDPF_injectMustache = window.TDGF_injectMustache;
})(); })();

View File

@@ -10,7 +10,7 @@
let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0; let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => { $TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom+9))).then(() => {
let framesLeft = 5; // basic render is done in 1 frame, large media take longer let framesLeft = {frames}; // basic render is done in 1 frame, large media take longer
let onNextFrame = function(){ let onNextFrame = function(){
if (--framesLeft < 0){ if (--framesLeft < 0){

View File

@@ -150,6 +150,7 @@ button {
.activity-header .icon-user-filled { .activity-header .icon-user-filled {
vertical-align: sub !important; vertical-align: sub !important;
margin-right: 4px !important;
} }
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
@@ -220,6 +221,29 @@ a[data-full-url] {
bottom: 0 !important; bottom: 0 !important;
} }
/**********************************************************/
/* Prevent column icons from being hidden by column title */
/**********************************************************/
.column-header-title {
overflow: hidden !important;
}
.column-heading {
max-width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.column-header-links {
max-width: 100% !important;
}
[data-td-icon="icon-message"] .column-header-links {
min-width: 86px;
}
/*******************************************/ /*******************************************/
/* Fix general visual issues or annoyances */ /* Fix general visual issues or annoyances */
/*******************************************/ /*******************************************/
@@ -234,6 +258,31 @@ a[data-full-url] {
vertical-align: -10% !important; vertical-align: -10% !important;
} }
.column-title {
/* fix alignment of everything in column headers */
padding-top: 1px !important;
}
.column-title .attribution {
/* fix alignment of usernames in column headers */
margin: 0 0 0 6px !important;
}
.app-navigator .tooltip {
/* fix tooltips in navigation */
display: none !important;
}
.column-header-links .icon {
/* fix tooltips in column headers */
height: calc(1em + 8px) !important;
}
.js-column-options .btn-options-tray {
/* fix underline on buttons in column options */
text-decoration: none !important;
}
#tduck .nav-user-info .hide-condensed { #tduck .nav-user-info .hide-condensed {
/* move login account info */ /* move login account info */
margin: 0px !important; margin: 0px !important;
@@ -290,9 +339,9 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
/* Fix glaring visual issues that twitter hasn't fixed yet smh */ /* Fix glaring visual issues that twitter hasn't fixed yet smh */
/***************************************************************/ /***************************************************************/
.column-nav-link .attribution { .js-column-nav-list .attribution {
/* fix cut off account names */ /* fix cut off account names */
position: absolute !important; line-height: 1.1 !important;
} }
#tduck .js-docked-compose .js-drawer-close { #tduck .js-docked-compose .js-drawer-close {
@@ -311,25 +360,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
vertical-align: -15% !important; vertical-align: -15% !important;
} }
/************************************************/
/* Fix tooltips in navigation and column header */
/************************************************/
.app-navigator .tooltip {
display: none !important;
}
.js-column-header .column-header-link {
padding: 0 !important;
}
.js-column-header .column-header-link .icon {
width: calc(1em + 8px);
height: 100%;
padding: 9px 4px;
box-sizing: border-box;
}
/*******************************************/ /*******************************************/
/* Fix one pixel space below column header */ /* Fix one pixel space below column header */
/*******************************************/ /*******************************************/
@@ -342,10 +372,6 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
border-bottom: none !important; border-bottom: none !important;
} }
.is-options-open .column-type-icon {
bottom: 27px !important;
}
/********************************************/ /********************************************/
/* Fix cut off usernames in Messages column */ /* Fix cut off usernames in Messages column */
/********************************************/ /********************************************/

View File

@@ -2,7 +2,9 @@
/* General */ /* General */
/***********/ /***********/
body.td-notification { html, body {
height: auto !important;
overflow-x: hidden !important;
overflow-y: auto !important; overflow-y: auto !important;
} }
@@ -88,6 +90,7 @@ html[data-td-font='smallest'] .badge-verified:before {
.activity-header .icon-user-filled { .activity-header .icon-user-filled {
vertical-align: sub !important; vertical-align: sub !important;
margin-right: 4px !important;
} }
.td-notification-padded .item-img { .td-notification-padded .item-img {
@@ -136,7 +139,7 @@ html[data-td-font='smallest'] .badge-verified:before {
#td-skip { #td-skip {
position: fixed; position: fixed;
left: 30px; left: 29px;
bottom: 10px; bottom: 10px;
z-index: 1000; z-index: 1000;
cursor: pointer; cursor: pointer;

View File

@@ -60,4 +60,16 @@
} }
}); });
} }
//
// Block: Fix broken Cancel button on logout page.
//
else if (location.pathname === "/logout"){
document.addEventListener("DOMContentLoaded", function(){
let cancel = document.querySelector(".buttons .cancel");
if (cancel && cancel.tagName === "A"){
cancel.href = "https://tweetdeck.twitter.com/";
}
});
}
})(); })();

View File

@@ -1,9 +1,9 @@
<?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-CI2508\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" /> <Import Project="packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.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\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.66.0.0-CI2629\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.3359.1772\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3359.1772\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.3359.1772\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3359.1772\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')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -35,6 +35,9 @@
<UseApplicationTrust>false</UseApplicationTrust> <UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled> <BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<StartArguments>-datafolder TweetDuckDebug</StartArguments>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath> <OutputPath>bin\x86\Debug\</OutputPath>
@@ -365,30 +368,45 @@
</ContentWithTargetPath> </ContentWithTargetPath>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Resources\avatar.png" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Resources\icon-small.ico" /> <None Include="Resources\icon-small.ico" />
<None Include="Resources\icon-tray-new.ico" /> <None Include="Resources\icon-tray-new.ico" />
<None Include="Resources\icon-tray.ico" /> <None Include="Resources\icon-tray.ico" />
<None Include="Resources\PostBuild.ps1" />
<None Include="Resources\spinner.apng" /> <None Include="Resources\spinner.apng" />
</ItemGroup> <None Include="Resources\PostBuild.ps1" />
<ItemGroup> <None Include="Resources\Plugins\.debug\.meta" />
<Folder Include="Resources\Plugins\" /> <None Include="Resources\Plugins\.debug\browser.js" />
</ItemGroup> <None Include="Resources\Plugins\clear-columns\.meta" />
<ItemGroup> <None Include="Resources\Plugins\clear-columns\browser.js" />
<Content Include="Resources\avatar.png" /> <None Include="Resources\Plugins\edit-design\.meta" />
<Content Include="Resources\Scripts\code.js" /> <None Include="Resources\Plugins\edit-design\browser.js" />
<Content Include="Resources\Scripts\introduction.js" /> <None Include="Resources\Plugins\edit-design\modal.html" />
<Content Include="Resources\Scripts\notification.js" /> <None Include="Resources\Plugins\edit-design\theme.black.css" />
<Content Include="Resources\Scripts\pages\error.html" /> <None Include="Resources\Plugins\emoji-keyboard\.meta" />
<Content Include="Resources\Scripts\pages\example.html" /> <None Include="Resources\Plugins\emoji-keyboard\browser.js" />
<Content Include="Resources\Scripts\plugins.browser.js" /> <None Include="Resources\Plugins\emoji-keyboard\emoji-instructions.txt" />
<Content Include="Resources\Scripts\plugins.js" /> <None Include="Resources\Plugins\emoji-keyboard\emoji-ordering.txt" />
<Content Include="Resources\Scripts\plugins.notification.js" /> <None Include="Resources\Plugins\reply-account\.meta" />
<Content Include="Resources\Scripts\styles\browser.css" /> <None Include="Resources\Plugins\reply-account\browser.js" />
<Content Include="Resources\Scripts\styles\notification.css" /> <None Include="Resources\Plugins\reply-account\configuration.default.js" />
<Content Include="Resources\Scripts\twitter.js" /> <None Include="Resources\Plugins\templates\.meta" />
<Content Include="Resources\Scripts\update.js" /> <None Include="Resources\Plugins\templates\browser.js" />
<None Include="Resources\Plugins\timeline-polls\.meta" />
<None Include="Resources\Plugins\timeline-polls\browser.js" />
<None Include="Resources\Scripts\code.js" />
<None Include="Resources\Scripts\introduction.js" />
<None Include="Resources\Scripts\notification.js" />
<None Include="Resources\Scripts\pages\error.html" />
<None Include="Resources\Scripts\pages\example.html" />
<None Include="Resources\Scripts\plugins.browser.js" />
<None Include="Resources\Scripts\plugins.js" />
<None Include="Resources\Scripts\plugins.notification.js" />
<None Include="Resources\Scripts\screenshot.js" />
<None Include="Resources\Scripts\styles\browser.css" />
<None Include="Resources\Scripts\styles\notification.css" />
<None Include="Resources\Scripts\twitter.js" />
<None Include="Resources\Scripts\update.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj"> <ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
@@ -419,7 +437,6 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
<Exec Command="del &quot;$(TargetDir)*.pdb&quot;" /> <Exec Command="del &quot;$(TargetDir)*.pdb&quot;" />
<Exec Command="del &quot;$(TargetDir)*.xml&quot;" /> <Exec Command="del &quot;$(TargetDir)*.xml&quot;" />
<Delete Files="$(TargetDir)CefSharp.BrowserSubprocess.exe" /> <Delete Files="$(TargetDir)CefSharp.BrowserSubprocess.exe" />
<Delete Files="$(TargetDir)devtools_resources.pak" />
<Delete Files="$(TargetDir)widevinecdmadapter.dll" /> <Delete Files="$(TargetDir)widevinecdmadapter.dll" />
</Target> </Target>
<PropertyGroup> <PropertyGroup>
@@ -429,13 +446,11 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</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.3359.1772\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3359.1772\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.3359.1772\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3359.1772\build\cef.redist.x86.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.66.0.0-CI2629\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.props'))" />
<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.Common.66.0.0-CI2629\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.targets'))" />
<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.66.0.0-CI2629\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.66.0.0-CI2629\build\CefSharp.WinForms.props'))" />
<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-CI2508\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" /> <Import Project="packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.66.0.0-CI2629\build\CefSharp.Common.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

@@ -22,7 +22,7 @@ namespace TweetDuck.Updates{
this.VersionTag = versionTag; this.VersionTag = versionTag;
this.ReleaseNotes = releaseNotes; this.ReleaseNotes = releaseNotes;
this.InstallerPath = Path.Combine(installerFolder, $"TweetDuck.{versionTag}.exe");; this.InstallerPath = Path.Combine(installerFolder, $"TweetDuck.{versionTag}.exe");
} }
public void BeginSilentDownload(){ public void BeginSilentDownload(){

View File

@@ -1,30 +0,0 @@
// Copyright <20> 2010-2016 The CefSharp Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the name CefSharp nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,9 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,98 @@
===============================================================================
TweetDuck
===============================================================================
The MIT License (MIT)
Copyright (c) 2016
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
===============================================================================
CefSharp
===============================================================================
// Copyright © 2010-2017 The CefSharp Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the name CefSharp nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
Chromium Embedded Framework
===============================================================================
// Copyright (c) 2008-2014 Marshall A. Greenblatt. Portions Copyright (c)
// 2006-2009 Google Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

View File

@@ -24,12 +24,15 @@ DefaultGroupName={#MyAppName}
OutputBaseFilename={#MyAppName} OutputBaseFilename={#MyAppName}
VersionInfoVersion={#MyAppVersion} VersionInfoVersion={#MyAppVersion}
SetupIconFile=.\Resources\icon.ico SetupIconFile=.\Resources\icon.ico
CloseApplicationsFilter=*.exe,*.dll,*.pak
RestartApplications=False
Uninstallable=TDIsUninstallable Uninstallable=TDIsUninstallable
UninstallDisplayName={#MyAppName} UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName} UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma Compression=lzma2/ultra
LZMADictionarySize=15360
SolidCompression=yes SolidCompression=yes
InternalCompressLevel=max InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
#include <idp.iss> #include <idp.iss>
@@ -38,11 +41,13 @@ MinVersion=0,6.1
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks] [Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalTasks}"; Flags: unchecked
Name: "devtools"; Description: "{cm:TaskDevTools}"; GroupDescription: "{cm:AdditionalTasks}"; Flags: unchecked
[Files] [Files]
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,CefSharp.BrowserSubprocess.exe,devtools_resources.pak,widevinecdmadapter.dll" Source: "..\bin\x86\Release\devtools_resources.pak"; DestDir: "{app}"; Flags: ignoreversion; Tasks: devtools
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "devtools_resources.pak"
[Icons] [Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
@@ -58,9 +63,14 @@ Type: filesandordirs; Name: "{app}\scripts"
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache" Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache" Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
[CustomMessages]
AdditionalTasks=Additional shortcuts and components:
TaskDevTools=Install dev tools
[Code] [Code]
var UpdatePath: String; var UpdatePath: String;
var ForceRedistPrompt: String; var ForceRedistPrompt: String;
var VisitedTasksPage: Boolean;
function TDGetNetFrameworkVersion: Cardinal; forward; function TDGetNetFrameworkVersion: Cardinal; forward;
function TDIsVCMissing: Boolean; forward; function TDIsVCMissing: Boolean; forward;
@@ -71,19 +81,20 @@ function InitializeSetup: Boolean;
begin begin
UpdatePath := ExpandConstant('{param:UPDATEPATH}') UpdatePath := ExpandConstant('{param:UPDATEPATH}')
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}') ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
VisitedTasksPage := False
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 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 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 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 begin
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe')); idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe'))
end; end;
Result := True; Result := True
end; end;
{ Set the installation path if updating, and prepare download plugin if there are any files to download. } { Set the installation path if updating, and prepare download plugin if there are any files to download. }
@@ -91,12 +102,12 @@ 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 if (idpFilesCount <> 0) then
begin begin
idpDownloadAfter(wpReady); idpDownloadAfter(wpReady)
end; end;
end; end;
@@ -106,12 +117,14 @@ begin
Result := (PageID = wpSelectDir) and (UpdatePath <> '') Result := (PageID = wpSelectDir) and (UpdatePath <> '')
end; end;
{ Check the desktop icon task if not updating. } { Check the desktop icon task if not updating, and dev tools task if already installed. }
procedure CurPageChanged(CurPageID: Integer); procedure CurPageChanged(CurPageID: Integer);
begin begin
if CurPageID = wpSelectTasks then if (CurPageID = wpSelectTasks) and (not VisitedTasksPage) then
begin begin
WizardForm.TasksList.Checked[WizardForm.TasksList.Items.Count-1] := (UpdatePath = ''); WizardForm.TasksList.Checked[WizardForm.TasksList.Items.Count-2] := (UpdatePath = '')
WizardForm.TasksList.Checked[WizardForm.TasksList.Items.Count-1] := FileExists(ExpandConstant('{app}\devtools_resources.pak'))
VisitedTasksPage := True
end; end;
end; end;
@@ -120,7 +133,7 @@ procedure CurStepChanged(CurStep: TSetupStep);
begin begin
if CurStep = ssInstall then if CurStep = ssInstall then
begin begin
TDInstallVCRedist(); TDInstallVCRedist()
end; end;
end; end;
@@ -137,9 +150,9 @@ begin
if (DirExists(ProfileDataFolder) or DirExists(PluginDataFolder)) and (MsgBox('Do you also want to delete your {#MyAppName} profile and plugins?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES) then if (DirExists(ProfileDataFolder) or DirExists(PluginDataFolder)) and (MsgBox('Do you also want to delete your {#MyAppName} profile and plugins?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES) then
begin begin
DelTree(ProfileDataFolder, True, True, True); DelTree(ProfileDataFolder, True, True, True)
DelTree(PluginDataFolder, True, True, True); DelTree(PluginDataFolder, True, True, True)
DelTree(ExpandConstant('{app}'), True, False, False); DelTree(ExpandConstant('{app}'), True, False, False)
end; end;
end; end;
end; end;
@@ -157,11 +170,11 @@ var FrameworkVersion: Cardinal;
begin begin
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
begin begin
Result := FrameworkVersion; Result := FrameworkVersion
Exit; Exit
end; end;
Result := 0; Result := 0
end; end;
{ Check if Visual C++ 2015 or 2017 is installed. } { Check if Visual C++ 2015 or 2017 is installed. }
@@ -176,20 +189,20 @@ begin
begin begin
for Index := 0 to GetArrayLength(Keys)-1 do for Index := 0 to GetArrayLength(Keys)-1 do
begin begin
Key := Keys[Index]; Key := Keys[Index]
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then
begin begin
if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then
begin begin
Result := False; Result := False
Exit; Exit
end; end;
end; end;
end; end;
end; end;
Result := True; Result := True
end; end;
{ Run the Visual C++ installer if downloaded. } { Run the Visual C++ installer if downloaded. }
@@ -202,26 +215,26 @@ begin
if FileExists(InstallFile) then if FileExists(InstallFile) then
begin begin
WizardForm.ProgressGauge.Style := npbstMarquee; WizardForm.ProgressGauge.Style := npbstMarquee
try try
if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin begin
if ResultCode <> 0 then if ResultCode <> 0 then
begin begin
DeleteFile(InstallFile); DeleteFile(InstallFile)
Exit; Exit
end; end;
end else end else
begin 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); 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); DeleteFile(InstallFile)
Exit; Exit
end; end;
finally finally
WizardForm.ProgressGauge.Style := npbstNormal; WizardForm.ProgressGauge.Style := npbstNormal
DeleteFile(InstallFile); DeleteFile(InstallFile)
end; end;
end; end;
end; end;

View File

@@ -24,12 +24,15 @@ DefaultGroupName={#MyAppName}
OutputBaseFilename={#MyAppName}.Portable OutputBaseFilename={#MyAppName}.Portable
VersionInfoVersion={#MyAppVersion} VersionInfoVersion={#MyAppVersion}
SetupIconFile=.\Resources\icon.ico SetupIconFile=.\Resources\icon.ico
CloseApplicationsFilter=*.exe,*.dll,*.pak
RestartApplications=False
Uninstallable=no Uninstallable=no
UsePreviousAppDir=no UsePreviousAppDir=no
PrivilegesRequired=lowest PrivilegesRequired=lowest
Compression=lzma Compression=lzma2/ultra
LZMADictionarySize=15360
SolidCompression=yes SolidCompression=yes
InternalCompressLevel=max InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
#include <idp.iss> #include <idp.iss>
@@ -37,16 +40,25 @@ MinVersion=0,6.1
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "devtools"; Description: "{cm:TaskDevTools}"; GroupDescription: "{cm:AdditionalTasks}"; Flags: unchecked
[Files] [Files]
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.pdb,CefSharp.BrowserSubprocess.exe,devtools_resources.pak,widevinecdmadapter.dll" Source: "..\bin\x86\Release\devtools_resources.pak"; DestDir: "{app}"; Flags: ignoreversion; Tasks: devtools
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "devtools_resources.pak"
[Run] [Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent
[CustomMessages]
AdditionalTasks=Additional components:
TaskDevTools=Install dev tools
[Code] [Code]
var UpdatePath: String; var UpdatePath: String;
var ForceRedistPrompt: String; var ForceRedistPrompt: String;
var VisitedTasksPage: Boolean;
function TDGetNetFrameworkVersion: Cardinal; forward; function TDGetNetFrameworkVersion: Cardinal; forward;
function TDIsVCMissing: Boolean; forward; function TDIsVCMissing: Boolean; forward;
@@ -57,19 +69,20 @@ function InitializeSetup: Boolean;
begin begin
UpdatePath := ExpandConstant('{param:UPDATEPATH}') UpdatePath := ExpandConstant('{param:UPDATEPATH}')
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}') ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
VisitedTasksPage := False
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 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 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 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 begin
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe')); idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe'))
end; end;
Result := True; Result := True
end; end;
{ Set the installation path if updating, and prepare download plugin if there are any files to download. } { Set the installation path if updating, and prepare download plugin if there are any files to download. }
@@ -77,12 +90,12 @@ 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 if (idpFilesCount <> 0) then
begin begin
idpDownloadAfter(wpReady); idpDownloadAfter(wpReady)
end; end;
end; end;
@@ -92,6 +105,16 @@ begin
Result := (PageID = wpSelectDir) and (UpdatePath <> '') Result := (PageID = wpSelectDir) and (UpdatePath <> '')
end; end;
{ Check the dev tools task if already installed. }
procedure CurPageChanged(CurPageID: Integer);
begin
if (CurPageID = wpSelectTasks) and (not VisitedTasksPage) then
begin
WizardForm.TasksList.Checked[WizardForm.TasksList.Items.Count-1] := FileExists(ExpandConstant('{app}\devtools_resources.pak'))
VisitedTasksPage := True
end;
end;
{ Install VC++ if downloaded, and create a 'makeportable' file for portable installs. } { Install VC++ if downloaded, and create a 'makeportable' file for portable installs. }
procedure CurStepChanged(CurStep: TSetupStep); procedure CurStepChanged(CurStep: TSetupStep);
begin begin
@@ -104,7 +127,7 @@ begin
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 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 begin
break; break
end; end;
end; end;
end; end;
@@ -117,11 +140,11 @@ var FrameworkVersion: Cardinal;
begin begin
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
begin begin
Result := FrameworkVersion; Result := FrameworkVersion
Exit; Exit
end; end;
Result := 0; Result := 0
end; end;
{ Check if Visual C++ 2015 or 2017 is installed. } { Check if Visual C++ 2015 or 2017 is installed. }
@@ -136,20 +159,20 @@ begin
begin begin
for Index := 0 to GetArrayLength(Keys)-1 do for Index := 0 to GetArrayLength(Keys)-1 do
begin begin
Key := Keys[Index]; Key := Keys[Index]
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then
begin begin
if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then
begin begin
Result := False; Result := False
Exit; Exit
end; end;
end; end;
end; end;
end; end;
Result := True; Result := True
end; end;
{ Run the Visual C++ installer if downloaded. } { Run the Visual C++ installer if downloaded. }
@@ -162,26 +185,26 @@ begin
if FileExists(InstallFile) then if FileExists(InstallFile) then
begin begin
WizardForm.ProgressGauge.Style := npbstMarquee; WizardForm.ProgressGauge.Style := npbstMarquee
try try
if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin begin
if ResultCode <> 0 then if ResultCode <> 0 then
begin begin
DeleteFile(InstallFile); DeleteFile(InstallFile)
Exit; Exit
end; end;
end else end else
begin 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); 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); DeleteFile(InstallFile)
Exit; Exit
end; end;
finally finally
WizardForm.ProgressGauge.Style := npbstNormal; WizardForm.ProgressGauge.Style := npbstNormal
DeleteFile(InstallFile); DeleteFile(InstallFile)
end; end;
end; end;
end; end;

View File

@@ -13,7 +13,7 @@
[Setup] [Setup]
AppId={{{#MyAppID}} AppId={{{#MyAppID}}
AppName={#MyAppName} Update AppName={#MyAppName}
AppVersion={#MyAppVersion} AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion} AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher} AppPublisher={#MyAppPublisher}
@@ -25,11 +25,14 @@ DefaultGroupName={#MyAppName}
OutputBaseFilename={#MyAppName}.Update OutputBaseFilename={#MyAppName}.Update
VersionInfoVersion={#MyAppVersion} VersionInfoVersion={#MyAppVersion}
SetupIconFile=.\Resources\icon.ico SetupIconFile=.\Resources\icon.ico
CloseApplicationsFilter=*.exe,*.dll,*.pak
RestartApplications=False
Uninstallable=TDIsUninstallable Uninstallable=TDIsUninstallable
UninstallDisplayName={#MyAppName} UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName} UninstallDisplayIcon={app}\{#MyAppExeName}
PrivilegesRequired=lowest PrivilegesRequired=lowest
Compression=lzma/normal Compression=lzma/normal
LZMADictionarySize=512
SolidCompression=True SolidCompression=True
InternalCompressLevel=normal InternalCompressLevel=normal
MinVersion=0,6.1 MinVersion=0,6.1
@@ -60,63 +63,10 @@ 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}\CEFSHARP-LICENSE.txt"
Type: files; Name: "{app}\msvcr120.dll" Type: files; Name: "{app}\LICENSE.txt"
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"
Type: files; Name: "{app}\locales\am.pak"
Type: files; Name: "{app}\locales\ar.pak"
Type: files; Name: "{app}\locales\bg.pak"
Type: files; Name: "{app}\locales\bn.pak"
Type: files; Name: "{app}\locales\ca.pak"
Type: files; Name: "{app}\locales\cs.pak"
Type: files; Name: "{app}\locales\da.pak"
Type: files; Name: "{app}\locales\de.pak"
Type: files; Name: "{app}\locales\el.pak"
Type: files; Name: "{app}\locales\en-GB.pak"
Type: files; Name: "{app}\locales\es-419.pak"
Type: files; Name: "{app}\locales\es.pak"
Type: files; Name: "{app}\locales\et.pak"
Type: files; Name: "{app}\locales\fa.pak"
Type: files; Name: "{app}\locales\fi.pak"
Type: files; Name: "{app}\locales\fil.pak"
Type: files; Name: "{app}\locales\fr.pak"
Type: files; Name: "{app}\locales\gu.pak"
Type: files; Name: "{app}\locales\he.pak"
Type: files; Name: "{app}\locales\hi.pak"
Type: files; Name: "{app}\locales\hr.pak"
Type: files; Name: "{app}\locales\hu.pak"
Type: files; Name: "{app}\locales\id.pak"
Type: files; Name: "{app}\locales\it.pak"
Type: files; Name: "{app}\locales\ja.pak"
Type: files; Name: "{app}\locales\kn.pak"
Type: files; Name: "{app}\locales\ko.pak"
Type: files; Name: "{app}\locales\lt.pak"
Type: files; Name: "{app}\locales\lv.pak"
Type: files; Name: "{app}\locales\ml.pak"
Type: files; Name: "{app}\locales\mr.pak"
Type: files; Name: "{app}\locales\ms.pak"
Type: files; Name: "{app}\locales\nb.pak"
Type: files; Name: "{app}\locales\nl.pak"
Type: files; Name: "{app}\locales\pl.pak"
Type: files; Name: "{app}\locales\pt-BR.pak"
Type: files; Name: "{app}\locales\pt-PT.pak"
Type: files; Name: "{app}\locales\ro.pak"
Type: files; Name: "{app}\locales\ru.pak"
Type: files; Name: "{app}\locales\sk.pak"
Type: files; Name: "{app}\locales\sl.pak"
Type: files; Name: "{app}\locales\sr.pak"
Type: files; Name: "{app}\locales\sv.pak"
Type: files; Name: "{app}\locales\sw.pak"
Type: files; Name: "{app}\locales\ta.pak"
Type: files; Name: "{app}\locales\te.pak"
Type: files; Name: "{app}\locales\th.pak"
Type: files; Name: "{app}\locales\tr.pak"
Type: files; Name: "{app}\locales\uk.pak"
Type: files; Name: "{app}\locales\vi.pak"
Type: files; Name: "{app}\locales\zh-CN.pak"
Type: files; Name: "{app}\locales\zh-TW.pak"
[Code] [Code]
function TDIsUninstallable: Boolean; forward; function TDIsUninstallable: Boolean; forward;
@@ -139,33 +89,34 @@ begin
if UpdatePath = '' then if UpdatePath = '' then
begin begin
MsgBox('{#MyAppName} installation could not be found on your system.', mbCriticalError, MB_OK); MsgBox('{#MyAppName} installation could not be found on your system.', mbCriticalError, MB_OK)
Result := False; Result := False
Exit; Exit
end; end;
if not TDIsMatchingCEFVersion() then if not TDIsMatchingCEFVersion() then
begin 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) 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 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 begin
Result := False; Result := False
Exit; Exit
end; end;
Result := True; Result := True
end; end;
{ Prepare download plugin if there are any files to download, and set the installation path. } { Prepare download plugin if there are any files to download, and set the installation path. }
procedure InitializeWizard(); procedure InitializeWizard();
begin begin
WizardForm.DirEdit.Text := UpdatePath; WizardForm.Caption := WizardForm.Caption + ' Update'
WizardForm.DirEdit.Text := UpdatePath
if idpFilesCount <> 0 then if idpFilesCount <> 0 then
begin begin
idpDownloadAfter(wpReady); idpDownloadAfter(wpReady)
end; end;
end; end;
@@ -177,14 +128,14 @@ var PluginDataFolder: String;
begin begin
if CurUninstallStep = usPostUninstall then if CurUninstallStep = usPostUninstall then
begin begin
ProfileDataFolder := ExpandConstant('{localappdata}\{#MyAppName}'); ProfileDataFolder := ExpandConstant('{localappdata}\{#MyAppName}')
PluginDataFolder := ExpandConstant('{app}\plugins'); PluginDataFolder := ExpandConstant('{app}\plugins')
if (DirExists(ProfileDataFolder) or DirExists(PluginDataFolder)) and (MsgBox('Do you also want to delete your {#MyAppName} profile and plugins?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES) then if (DirExists(ProfileDataFolder) or DirExists(PluginDataFolder)) and (MsgBox('Do you also want to delete your {#MyAppName} profile and plugins?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES) then
begin begin
DelTree(ProfileDataFolder, True, True, True); DelTree(ProfileDataFolder, True, True, True)
DelTree(PluginDataFolder, True, True, True); DelTree(PluginDataFolder, True, True, True)
DelTree(ExpandConstant('{app}'), True, False, False); DelTree(ExpandConstant('{app}'), True, False, False)
end; end;
end; end;
end; end;
@@ -194,12 +145,12 @@ procedure CurStepChanged(CurStep: TSetupStep);
begin begin
if CurStep = ssInstall then if CurStep = ssInstall then
begin begin
TDExecuteFullDownload(); TDExecuteFullDownload()
if TDIsUninstallable() then if TDIsUninstallable() then
begin begin
DeleteFile(ExpandConstant('{app}\unins000.dat')); DeleteFile(ExpandConstant('{app}\unins000.dat'))
DeleteFile(ExpandConstant('{app}\unins000.exe')); DeleteFile(ExpandConstant('{app}\unins000.exe'))
end; end;
end; end;
end; end;
@@ -213,6 +164,7 @@ end;
{ Returns a string used for arguments when running the app after update. } { Returns a string used for arguments when running the app after update. }
function TDGetRunArgs(Param: String): String; function TDGetRunArgs(Param: String): String;
var Args: String; var Args: String;
begin begin
Args := ExpandConstant('{param:RUNARGS}') Args := ExpandConstant('{param:RUNARGS}')
StringChangeEx(Args, '::', '"', True) StringChangeEx(Args, '::', '"', True)
@@ -254,11 +206,11 @@ var FrameworkVersion: Cardinal;
begin begin
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
begin begin
Result := FrameworkVersion; Result := FrameworkVersion
Exit; Exit
end; end;
Result := 0; Result := 0
end; end;
{ Return the name of the full installer file to download from GitHub. } { Return the name of the full installer file to download from GitHub. }
@@ -270,15 +222,8 @@ 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;
@@ -292,17 +237,17 @@ begin
while True do while True do
begin begin
Substr := Copy(CleanVersion, Length(CleanVersion)-1, 2); Substr := Copy(CleanVersion, Length(CleanVersion)-1, 2)
if (CompareStr(Substr, '.0') <> 0) then if (CompareStr(Substr, '.0') <> 0) then
begin begin
break; break
end; end;
CleanVersion := Copy(CleanVersion, 1, Length(CleanVersion)-2); CleanVersion := Copy(CleanVersion, 1, Length(CleanVersion)-2)
end; end;
Result := CleanVersion; Result := CleanVersion
end; end;
{ Run the full package installer if downloaded. } { Run the full package installer if downloaded. }
@@ -315,28 +260,28 @@ begin
if FileExists(InstallFile) then if FileExists(InstallFile) then
begin begin
WizardForm.ProgressGauge.Style := npbstMarquee; WizardForm.ProgressGauge.Style := npbstMarquee
try try
if Exec(InstallFile, '/SP- /SILENT /MERGETASKS="!desktopicon" /UPDATEPATH="'+UpdatePath+'"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then if Exec(InstallFile, '/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH="'+UpdatePath+'"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin begin
if ResultCode <> 0 then if ResultCode <> 0 then
begin begin
DeleteFile(InstallFile); DeleteFile(InstallFile)
Abort(); Abort()
Exit; Exit
end; end;
end else end else
begin begin
MsgBox('Could not run the full installer, please visit {#MyAppURL} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK); MsgBox('Could not run the full installer, please visit {#MyAppURL} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK)
DeleteFile(InstallFile); DeleteFile(InstallFile)
Abort(); Abort()
Exit; Exit
end; end;
finally finally
WizardForm.ProgressGauge.Style := npbstNormal; WizardForm.ProgressGauge.Style := npbstNormal
DeleteFile(InstallFile); DeleteFile(InstallFile)
end; end;
end; end;
end; end;

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="cef.redist.x64" version="3.3282.1731" targetFramework="net452" xmlns="" /> <package id="cef.redist.x64" version="3.3359.1772" targetFramework="net452" />
<package id="cef.redist.x86" version="3.3282.1731" targetFramework="net452" xmlns="" /> <package id="cef.redist.x86" version="3.3359.1772" targetFramework="net452" />
<package id="CefSharp.Common" version="64.0.0-CI2508" targetFramework="net452" xmlns="" /> <package id="CefSharp.Common" version="66.0.0-CI2629" targetFramework="net452" />
<package id="CefSharp.WinForms" version="64.0.0-CI2508" targetFramework="net452" xmlns="" /> <package id="CefSharp.WinForms" version="66.0.0-CI2629" targetFramework="net452" />
</packages> </packages>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
@@ -26,9 +26,9 @@
<StartupObject /> <StartupObject />
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=64.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86"> <Reference Include="CefSharp.BrowserSubprocess.Core, Version=66.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CefSharp.Common.64.0.0-CI2508\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath> <HintPath>..\packages\CefSharp.Common.66.0.0-CI2629\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>