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

Compare commits

...

14 Commits
1.13 ... 1.13.1

21 changed files with 347 additions and 117 deletions

View File

@@ -12,6 +12,7 @@ using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Updates;
@@ -38,6 +39,7 @@ namespace TweetDuck.Core{
public string UpdateInstallerPath { get; private set; }
public PluginManager PluginManager => plugins;
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
private readonly TweetDeckBrowser browser;
@@ -58,10 +60,7 @@ namespace TweetDuck.Core{
Text = Program.BrandName;
this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification));
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.plugins = new PluginManager(browser, Program.PluginPath, Program.PluginConfigFilePath);
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
this.plugins.Reloaded += plugins_Reloaded;
this.plugins.Executed += plugins_Executed;
this.plugins.Reload();
@@ -69,6 +68,11 @@ namespace TweetDuck.Core{
this.notification = new FormNotificationTweet(this, plugins);
this.notification.Show();
this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification));
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.plugins.Register(browser, PluginEnvironment.Browser, true);
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
Disposed += (sender, args) => {

View File

@@ -12,7 +12,7 @@ using TweetDuck.Plugins.Enums;
using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{
abstract partial class FormNotificationMain : FormNotificationBase{
abstract partial class FormNotificationMain : FormNotificationBase, ITweetDeckBrowser{
private const string NotificationScriptFile = "notification.js";
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
@@ -81,17 +81,35 @@ namespace TweetDuck.Core.Notification{
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this));
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
plugins.Register(this, PluginEnvironment.Notification);
mouseHookDelegate = MouseHookProc;
Disposed += (sender, args) => StopMouseHook(true);
}
void ITweetDeckBrowser.RegisterBridge(string name, object obj){
browser.RegisterAsyncJsObject(name, obj);
}
void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){
browser.FrameLoadEnd += (sender, args) => {
IFrame frame = args.Frame;
if (frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
callback(frame);
}
};
}
void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){
browser.ExecuteScriptAsync(name, args);
}
// mouse wheel hook
private void StartMouseHook(){
@@ -166,7 +184,6 @@ namespace TweetDuck.Core.Notification{
if (e.Frame.IsMain && NotificationJS != null && browser.Address != "about:blank"){
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
ScriptLoader.ExecuteScript(e.Frame, NotificationJS, NotificationScriptIdentifier);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification);
}
}
@@ -232,7 +249,7 @@ namespace TweetDuck.Core.Notification{
protected override string GetTweetHTML(TweetNotification tweet){
string html = base.GetTweetHTML(tweet);
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.Inject(html);
}

View File

@@ -33,7 +33,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
protected override string GetTweetHTML(TweetNotification tweet){
string html = tweet.GenerateHtml("td-screenshot", false);
foreach(InjectedHTML injection in plugins.Bridge.NotificationInjections){
foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.Inject(html);
}

View File

@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Other.Analytics{
this.saveTimer.Elapsed += saveTimer_Elapsed;
if (this.File.LastCollectionVersion != Program.VersionTag){
ScheduleReportIn(TimeSpan.FromHours(12), string.Empty);
ScheduleReportIn(TimeSpan.FromHours(8), string.Empty);
}
else{
RestartTimer();
@@ -90,7 +90,7 @@ namespace TweetDuck.Core.Other.Analytics{
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes));
currentTimer.Interval = Math.Max(minutesTillNext, 1)*60000;
currentTimer.Interval = Math.Max(minutesTillNext, 2)*60000;
currentTimer.Start();
}

View File

@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using TweetDuck.Core;
using TweetDuck.Data;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
@@ -21,42 +22,46 @@ namespace TweetDuck.Plugins{
public string PathCustomPlugins => Path.Combine(rootPath, "user");
public IEnumerable<Plugin> Plugins => plugins;
public IEnumerable<InjectedHTML> NotificationInjections => bridge.NotificationInjections;
public PluginConfig Config { get; }
public PluginBridge Bridge { get; }
public event EventHandler<PluginErrorEventArgs> Reloaded;
public event EventHandler<PluginErrorEventArgs> Executed;
private readonly ITweetDeckBrowser browser;
private readonly string rootPath;
private readonly string configPath;
private readonly PluginBridge bridge;
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
private readonly Random rand = new Random();
public PluginManager(ITweetDeckBrowser browser, string rootPath, string configPath){
this.browser = browser;
private ITweetDeckBrowser mainBrowser;
public PluginManager(string rootPath, string configPath){
this.rootPath = rootPath;
this.configPath = configPath;
this.Config = new PluginConfig();
this.Bridge = new PluginBridge(this);
this.browser.OnFrameLoaded(OnFrameLoaded);
this.browser.RegisterBridge("$TDP", Bridge);
this.bridge = new PluginBridge(this);
Config.Load(configPath);
Config.PluginChangedState += Config_PluginChangedState;
}
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
Config.Save(configPath);
public void Register(ITweetDeckBrowser browser, PluginEnvironment environment, bool asMainBrowser = false){
browser.OnFrameLoaded(frame => ExecutePlugins(frame, environment));
browser.RegisterBridge("$TDP", bridge);
if (asMainBrowser){
mainBrowser = browser;
}
}
private void OnFrameLoaded(IFrame frame){
ExecutePlugins(frame, PluginEnvironment.Browser);
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
mainBrowser?.ExecuteFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
Config.Save(configPath);
}
public bool IsPluginInstalled(string identifier){
@@ -68,12 +73,12 @@ namespace TweetDuck.Plugins{
}
public bool IsPluginConfigurable(Plugin plugin){
return plugin.HasConfig || Bridge.WithConfigureFunction.Contains(plugin);
return plugin.HasConfig || bridge.WithConfigureFunction.Contains(plugin);
}
public void ConfigurePlugin(Plugin plugin){
if (Bridge.WithConfigureFunction.Contains(plugin)){
browser.ExecuteFunction("TDPF_configurePlugin", plugin);
if (bridge.WithConfigureFunction.Contains(plugin)){
mainBrowser?.ExecuteFunction("TDPF_configurePlugin", plugin);
}
else if (plugin.HasConfig){
if (File.Exists(plugin.ConfigPath)){
@@ -143,7 +148,7 @@ namespace TweetDuck.Plugins{
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
}
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
private void ExecutePlugins(IFrame frame, PluginEnvironment environment){
if (!HasAnyPlugin(environment)){
return;
}

View File

@@ -20,7 +20,7 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.13";
public const string VersionTag = "1.13.1";
public static readonly bool IsPortable = File.Exists("makeportable");

View File

@@ -12,10 +12,9 @@ The program was built using Visual Studio 2017. Before opening the solution, ple
* **Desktop development with C++**
* VC++ 2017 v141 toolset
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**:
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running this command in the **Package Manager Console**:
```
PM> Install-Package CefSharp.WinForms -Version 63.0.0-pre01 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
PM> Install-Package Microsoft.VC120.CRT.JetBrains
PM> Install-Package CefSharp.WinForms -Version 64.0.0-CI2508 -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.

View File

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

View File

@@ -23,6 +23,8 @@ enabled(){
this.firstTimeLoad = null;
var me = this;
// modal dialog loading
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
this.htmlModal = contents;
@@ -137,8 +139,6 @@ enabled(){
};
// modal dialog setup
var me = this;
var updateKey = function(key, value){
me.config[key] = value;
@@ -238,7 +238,12 @@ enabled(){
});
// THEMES
let selectedTheme = me.config.themeOverride || TD.settings.getTheme();
let selectedTheme = TD.settings.getTheme();
if (selectedTheme === "dark" && me.config.themeOverride === "black"){
selectedTheme = me.config.themeOverride;
}
modal.find("[data-td-theme='"+selectedTheme+"']").prop("checked", true);
modal.find("[data-td-theme]").change(function(){
@@ -379,22 +384,26 @@ enabled(){
this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
let currentTheme = TD.settings.getTheme();
if (currentTheme === "dark" && this.config.themeOverride){
currentTheme = this.config.themeOverride;
}
let notificationScrollbarColor = null;
if (this.config.themeColorTweaks){
switch(TD.settings.getTheme()){
case "dark":
if (this.config.themeOverride === "black"){
switch(currentTheme){
case "black":
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
notificationScrollbarColor = "666";
}
else{
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }");
}
break;
case "dark":
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }");
break;
case "light":
@@ -539,10 +548,11 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
document.head.appendChild(this.icons);
}
if (this.config.themeOverride === "black"){
if (currentTheme === "black"){
$TDP.readFileRoot(this.$token, "theme.black.css").then(contents => {
if (this.theme){
this.theme.element.innerHTML = contents;
TD.settings.setTheme("dark"); // forces refresh of notification head tag
}
});
}
@@ -598,9 +608,9 @@ ${this.config.revertIcons ? `
#tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important}
` : ``}
${this.config.themeOverride === "black" ? `
${currentTheme === "black" ? `
html.dark a, html.dark a:hover, html.dark a:focus, html.dark a:active { color: #8bd }
.btn-neutral-positive { color: #8bd !important }
#tduck-show-thread { color: #8bd !important }
.quoted-tweet { border-color: #292f33 !important }
` : ``}

View File

@@ -290,7 +290,7 @@ html.dark .app-nav-tab{color:#8899A6}
html.dark .app-nav-tab:hover{color:#fff}
html.dark .app-nav-tab.is-selected{color:#292f33}
html.dark .app-nav-tab.is-selected:hover{color:#292f33}
html.dark .attach-compose-buttons .tweet-button{background-color:#485865}
html.dark .attach-compose-buttons .tweet-button{background-color:#485865!important}
html.dark .with-nav-border-t:before{border-top:1px solid #777}
html.dark .app-search-input,html.dark .app-search-fake{background-color:#14171A;color:#aab8c2;box-shadow:inset 0 1px 1px rgba(17,17,17,0.5)}
html.dark .app-search-input::-webkit-input-placeholder{color:#aab8c2}
@@ -849,3 +849,4 @@ html.dark .DrawerModal{color:#14171A}
/* fixes */
html.dark .app-search-fake{border-color:transparent}
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(80%)brightness(93%)}
html.dark .tweet>.color-twitter-blue{color:#8bd!important}

View File

@@ -8,7 +8,7 @@ Templates
chylex
[version]
1.0.3
1.0.4
[website]
https://tweetduck.chylex.com

View File

@@ -34,7 +34,7 @@ enabled(){
// button
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--9"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
TD.mustaches["compose/docked_compose.mustache"] = TD.mustaches["compose/docked_compose.mustache"].replace('<div class="js-tweet-type-button">', buttonHTML+'<div class="js-tweet-type-button">');

View File

@@ -199,7 +199,7 @@
html.find(".js-user-actions-menu").parent().remove();
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
}
else if (type.startsWith("favorite") || type.startsWith("retweet")){
else if ((type.startsWith("favorite") || type.startsWith("retweet")) && tweet.isAboutYou()){
html.children().first().addClass("td-notification-padded");
}
else if (type.includes("list_member")){
@@ -294,6 +294,8 @@
tags.push("<style type='text/css'>");
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" !important }"); // set background color
tags.push("body::before { content: none !important }"); // remove background gradient
tags.push(".column { background: transparent !important }"); // remove background color from columns
tags.push("a[data-full-url] { word-break: break-all !important }"); // break long urls
tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets

View File

@@ -38,7 +38,7 @@
/* Square-ify stuff */
/********************/
.btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
button, .btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
border-radius: 1px !important;
}
@@ -46,7 +46,7 @@
border-radius: 1px !important;
}
.btn-compose, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder, .detail-view-inline-text {
.tweet-button, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder, .detail-view-inline-text {
border-radius: 0 !important;
}
@@ -54,6 +54,40 @@
border-radius: 0 !important;
}
/***********************************/
/* Revert various design decisions */
/***********************************/
button {
font-weight: normal !important;
}
.tweet-button:not(:hover) {
background-color: #2B7BB9 !important;
border-color: #2B7BB9 !important;
}
#tduck .js-compose-scroller button > i {
font-size: 20px !important;
vertical-align: -4px !important;
}
.js-show-drawer {
text-align: left;
}
.js-show-drawer .icon-compose {
display: inline-block !important;
}
.js-show-drawer span::before {
content: "New ";
}
.js-account-safeguard-checkbox, .js-account-safeguard-checkbox label {
margin-bottom: 0 !important;
}
/***********************/
/* Hide TweetDeck logo */
/***********************/
@@ -118,19 +152,19 @@ html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
opacity: 0.25;
}
.stream-item[data-key^="favorite"] .item-img, .stream-item[data-key^="retweet"] .item-img {
.stream-item[data-key^="favorite"] .has-source-avatar .item-img, .stream-item[data-key^="retweet"] .has-source-avatar .item-img {
position: absolute;
left: 21px;
top: 48px;
width: 0 !important;
}
.stream-item[data-key^="favorite"] .activity-header > .nbfc, .stream-item[data-key^="retweet"] .activity-header > .nbfc {
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc, .stream-item[data-key^="retweet"] .has-source-avatar > .nbfc {
margin-left: 46px;
line-height: unset !important;
}
.stream-item[data-key^="favorite"] .activity-header > .nbfc > .avatar, .stream-item[data-key^="retweet"] .activity-header > .nbfc > .avatar {
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc > .avatar, .stream-item[data-key^="retweet"] .has-source-avatar > .nbfc > .avatar {
position: absolute;
margin-left: -34px;
}
@@ -183,6 +217,12 @@ a[data-full-url] {
vertical-align: -10% !important;
}
#tduck .nav-user-info .hide-condensed {
/* move login account info */
margin: 0px !important;
padding-left: 2px !important;
}
html[data-td-font='smallest'] .sprite-verified-mini {
/* fix cut off badge when zoomed in */
width: 13px !important;
@@ -230,6 +270,16 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
box-shadow: none !important;
}
#tduck .js-follow-button .icon-follow {
/* follow icon is too small which makes it aliased and misaligned */
font-size: 17px !important;
}
.js-follow-button .following-text, .js-follow-button .unfollow-text {
/* unfollow button has a border for some reason */
border-width: 0 !important;
}
/***************************************************************/
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
/***************************************************************/

View File

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

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props')" />
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props')" />
<Import Project="packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" />
<Import Project="packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
@@ -398,8 +398,6 @@
del "$(TargetDir)LICENSE.txt"
ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
xcopy "$(ProjectDir)bld\Resources\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y
rmdir "$(TargetDir)scripts" /S /Q
mkdir "$(TargetDir)scripts"
@@ -444,11 +442,11 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
</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.x86.3.3282.1731\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets'))" />
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets'))" />
</Target>
<Import Project="packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets')" />
<Import Project="packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2508\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2508\build\CefSharp.WinForms.targets')" />
</Project>

View File

@@ -4,9 +4,11 @@
#define MyAppName "TweetDuck"
#define MyAppPublisher "chylex"
#define MyAppURL "https://tweetduck.chylex.com"
#define MyAppShortURL "https://td.chylex.com"
#define MyAppExeName "TweetDuck.exe"
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
#define VCRedistLink "releases/download/1.13/vc_redist.x86.exe"
[Setup]
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
@@ -30,6 +32,8 @@ SolidCompression=yes
InternalCompressLevel=max
MinVersion=0,6.1
#include <idp.iss>
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -56,36 +60,44 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
[Code]
var UpdatePath: String;
var ForceRedistPrompt: String;
function TDGetNetFrameworkVersion: Cardinal; forward;
function TDIsVCMissing: Boolean; forward;
procedure TDInstallVCRedist; forward;
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
function InitializeSetup: Boolean;
begin
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
if TDGetNetFrameworkVersion() >= 379893 then
begin
Result := True;
Exit;
end;
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
begin
Result := False;
Exit;
end;
if (TDIsVCMissing() or (ForceRedistPrompt = '1')) and (MsgBox('Microsoft Visual C++ 2015 appears to be missing, would you like to automatically install it?', mbConfirmation, MB_YESNO) = IDYES) then
begin
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe'));
end;
Result := True;
end;
{ Set the installation path if updating. }
{ Set the installation path if updating, and prepare download plugin if there are any files to download. }
procedure InitializeWizard();
begin
if (UpdatePath <> '') then
begin
WizardForm.DirEdit.Text := UpdatePath;
end;
if (idpFilesCount <> 0) then
begin
idpDownloadAfter(wpReady);
end;
end;
{ Skip the install path selection page if running from an update installer. }
@@ -94,15 +106,12 @@ begin
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
end;
{ Check for an old TweetDeck profile and show a warning before installation. }
{ Install VC++ if downloaded. }
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssInstall then
begin
if DirExists(ExpandConstant('{localappdata}\twitter\TweetDeck')) then
begin
MsgBox('Detected a profile from an old TweetDeck installation, you may uninstall the old client to free up some space.', mbInformation, MB_OK)
end;
TDInstallVCRedist();
end;
end;
@@ -145,3 +154,65 @@ begin
Result := 0;
end;
{ Check if Visual C++ 2015 or 2017 is installed. }
function TDIsVCMissing: Boolean;
var Keys: TArrayOfString;
var Index: Integer;
var Key: String;
var DisplayName: String;
begin
if RegGetSubkeyNames(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies', Keys) then
begin
for Index := 0 to GetArrayLength(Keys)-1 do
begin
Key := Keys[Index];
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then
begin
if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then
begin
Result := False;
Exit;
end;
end;
end;
end;
Result := True;
end;
{ Run the Visual C++ installer if downloaded. }
procedure TDInstallVCRedist;
var InstallFile: String;
var ResultCode: Integer;
begin
InstallFile := ExpandConstant('{tmp}\{#MyAppName}.VC.exe')
if FileExists(InstallFile) then
begin
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
if ResultCode <> 0 then
begin
DeleteFile(InstallFile);
Exit;
end;
end else
begin
MsgBox('Could not run the Visual C++ installer, please visit https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
DeleteFile(InstallFile);
Exit;
end;
finally
WizardForm.ProgressGauge.Style := npbstNormal;
DeleteFile(InstallFile);
end;
end;
end;

View File

@@ -4,9 +4,11 @@
#define MyAppName "TweetDuck"
#define MyAppPublisher "chylex"
#define MyAppURL "https://tweetduck.chylex.com"
#define MyAppShortURL "https://td.chylex.com"
#define MyAppExeName "TweetDuck.exe"
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
#define VCRedistLink "releases/download/1.13/vc_redist.x86.exe"
[Setup]
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
@@ -30,6 +32,8 @@ SolidCompression=yes
InternalCompressLevel=max
MinVersion=0,6.1
#include <idp.iss>
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -42,36 +46,44 @@ Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChang
[Code]
var UpdatePath: String;
var ForceRedistPrompt: String;
function TDGetNetFrameworkVersion: Cardinal; forward;
function TDIsVCMissing: Boolean; forward;
procedure TDInstallVCRedist; forward;
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
function InitializeSetup: Boolean;
begin
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
ForceRedistPrompt := ExpandConstant('{param:PROMPTREDIST}')
if TDGetNetFrameworkVersion() >= 379893 then
begin
Result := True;
Exit;
end;
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
begin
Result := False;
Exit;
end;
if (TDIsVCMissing() or (ForceRedistPrompt = '1')) and (MsgBox('Microsoft Visual C++ 2015 appears to be missing, would you like to automatically install it?', mbConfirmation, MB_YESNO) = IDYES) then
begin
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink}', ExpandConstant('{tmp}\{#MyAppName}.VC.exe'));
end;
Result := True;
end;
{ Set the installation path if updating. }
{ Set the installation path if updating, and prepare download plugin if there are any files to download. }
procedure InitializeWizard();
begin
if (UpdatePath <> '') then
begin
WizardForm.DirEdit.Text := UpdatePath;
end;
if (idpFilesCount <> 0) then
begin
idpDownloadAfter(wpReady);
end;
end;
{ Skip the install path selection page if running from an update installer. }
@@ -80,6 +92,24 @@ begin
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
end;
{ Install VC++ if downloaded, and create a 'makeportable' file for portable installs. }
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssInstall then
begin
TDInstallVCRedist();
end else if CurStep = ssPostInstall then
begin
while not SaveStringToFile(ExpandConstant('{app}\makeportable'), '', False) do
begin
if MsgBox('Could not create a ''makeportable'' file in the installation folder. If the file is not present, the installation will not be fully portable.', mbCriticalError, MB_RETRYCANCEL) <> IDRETRY then
begin
break;
end;
end;
end;
end;
{ Return DWORD value containing the build version of .NET Framework. }
function TDGetNetFrameworkVersion: Cardinal;
var FrameworkVersion: Cardinal;
@@ -94,17 +124,64 @@ begin
Result := 0;
end;
{ Create a 'makeportable' file if running in portable mode. }
procedure CurStepChanged(CurStep: TSetupStep);
{ Check if Visual C++ 2015 or 2017 is installed. }
function TDIsVCMissing: Boolean;
var Keys: TArrayOfString;
var Index: Integer;
var Key: String;
var DisplayName: String;
begin
if CurStep = ssPostInstall then
if RegGetSubkeyNames(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies', Keys) then
begin
while not SaveStringToFile(ExpandConstant('{app}\makeportable'), '', False) do
for Index := 0 to GetArrayLength(Keys)-1 do
begin
if MsgBox('Could not create a ''makeportable'' file in the installation folder. If the file is not present, the installation will not be fully portable.', mbCriticalError, MB_RETRYCANCEL) <> IDRETRY then
Key := Keys[Index];
if RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Classes\Installer\Dependencies\'+Key, 'DisplayName', DisplayName) then
begin
break;
if (Pos('Microsoft Visual C++', DisplayName) = 1) and (Pos('(x86)', DisplayName) > 1) and ((Pos(' 2015 ', DisplayName) > 1) or (Pos(' 2017 ', DisplayName) > 1)) then
begin
Result := False;
Exit;
end;
end;
end;
end;
Result := True;
end;
{ Run the Visual C++ installer if downloaded. }
procedure TDInstallVCRedist;
var InstallFile: String;
var ResultCode: Integer;
begin
InstallFile := ExpandConstant('{tmp}\{#MyAppName}.VC.exe')
if FileExists(InstallFile) then
begin
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if Exec(InstallFile, '/passive /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
if ResultCode <> 0 then
begin
DeleteFile(InstallFile);
Exit;
end;
end else
begin
MsgBox('Could not run the Visual C++ installer, please visit https://github.com/{#MyAppPublisher}/{#MyAppName}/{#VCRedistLink} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
DeleteFile(InstallFile);
Exit;
end;
finally
WizardForm.ProgressGauge.Style := npbstNormal;
DeleteFile(InstallFile);
end;
end;
end;

View File

@@ -4,6 +4,7 @@
#define MyAppName "TweetDuck"
#define MyAppPublisher "chylex"
#define MyAppURL "https://tweetduck.chylex.com"
#define MyAppShortURL "https://td.chylex.com"
#define MyAppExeName "TweetDuck.exe"
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
@@ -59,6 +60,8 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
[InstallDelete]
Type: files; Name: "{app}\msvcp120.dll"
Type: files; Name: "{app}\msvcr120.dll"
Type: files; Name: "{app}\TweetLib.Audio.dll"
Type: filesandordirs; Name: "{app}\scripts"
Type: filesandordirs; Name: "{app}\plugins\official"
@@ -146,13 +149,7 @@ begin
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/'+TDGetFullDownloadFileName(), ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
end;
if TDGetNetFrameworkVersion() >= 379893 then
begin
Result := True;
Exit;
end;
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
if (TDGetNetFrameworkVersion() < 379893) and (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please visit {#MyAppShortURL} for a download link.'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
begin
Result := False;
Exit;
@@ -273,8 +270,15 @@ end;
{ Return whether the version of the installed libcef.dll library matches internal one. }
function TDIsMatchingCEFVersion: Boolean;
var CEFVersion: String;
var TmpTDVersion: String;
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))
end;

View File

@@ -2,7 +2,7 @@
<packages>
<package id="cef.redist.x64" version="3.3282.1731" targetFramework="net452" xmlns="" />
<package id="cef.redist.x86" version="3.3282.1731" targetFramework="net452" xmlns="" />
<package id="CefSharp.Common" version="64.0.0-CI2497" targetFramework="net452" xmlns="" />
<package id="CefSharp.WinForms" version="64.0.0-CI2497" targetFramework="net452" xmlns="" />
<package id="CefSharp.Common" version="64.0.0-CI2508" targetFramework="net452" xmlns="" />
<package id="CefSharp.WinForms" version="64.0.0-CI2508" targetFramework="net452" xmlns="" />
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" />
</packages>

View File

@@ -24,9 +24,9 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=63.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=64.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CefSharp.Common.64.0.0-CI2497\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
<HintPath>..\packages\CefSharp.Common.64.0.0-CI2508\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>