mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-04-17 12:15:47 +02:00
Begin rewriting update checker to run within C#
This commit is contained in:
parent
f99d035621
commit
ad45cf8c72
@ -78,8 +78,9 @@ public FormBrowser(UpdaterSettings updaterSettings){
|
|||||||
Disposed += (sender, args) => {
|
Disposed += (sender, args) => {
|
||||||
Config.MuteToggled -= Config_MuteToggled;
|
Config.MuteToggled -= Config_MuteToggled;
|
||||||
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
||||||
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
|
updates.Dispose();
|
||||||
contextMenu.Dispose();
|
contextMenu.Dispose();
|
||||||
|
|
||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
@ -96,6 +97,7 @@ public FormBrowser(UpdaterSettings updaterSettings){
|
|||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, updaterSettings);
|
this.updates = new UpdateHandler(browser, updaterSettings);
|
||||||
|
this.updates.CheckFinished += updates_CheckFinished;
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
this.updates.UpdateDismissed += updates_UpdateDismissed;
|
||||||
|
|
||||||
@ -233,6 +235,18 @@ private static void plugins_Executed(object sender, PluginErrorEventArgs e){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
||||||
|
this.InvokeAsyncSafe(() => {
|
||||||
|
e.Result.Handle(update => {
|
||||||
|
if (!update.IsUpdateNew && !update.IsUpdateDismissed){
|
||||||
|
updates.StartTimer();
|
||||||
|
}
|
||||||
|
}, ex => {
|
||||||
|
// TODO show error and ask if the user wants to temporarily disable checks -- or maybe only do that for the first check of the day
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
||||||
this.InvokeAsyncSafe(() => {
|
this.InvokeAsyncSafe(() => {
|
||||||
FormManager.CloseAllDialogs();
|
FormManager.CloseAllDialogs();
|
||||||
@ -241,13 +255,21 @@ private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
|
|||||||
Config.DismissedUpdate = null;
|
Config.DismissedUpdate = null;
|
||||||
Config.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
|
||||||
if (update.DownloadStatus == UpdateDownloadStatus.Done){
|
|
||||||
UpdateInstallerPath = update.InstallerPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
ForceClose();
|
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
|
||||||
|
UpdateDownloadStatus status = update.DownloadStatus;
|
||||||
|
|
||||||
|
if (status == UpdateDownloadStatus.Done){
|
||||||
|
UpdateInstallerPath = update.InstallerPath;
|
||||||
|
ForceClose();
|
||||||
|
}
|
||||||
|
else if (FormMessage.Error("Update Has Failed", "Could not automatically download the update: "+(update.DownloadError?.Message ?? "unknown error")+"\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
BrowserUtils.OpenExternalBrowser(Program.Website);
|
||||||
|
ForceClose();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Show();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -222,9 +222,13 @@ private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
|
|||||||
if (e.EventId == updateCheckEventId){
|
if (e.EventId == updateCheckEventId){
|
||||||
btnCheckUpdates.Enabled = true;
|
btnCheckUpdates.Enabled = true;
|
||||||
|
|
||||||
if (!e.IsUpdateAvailable){
|
e.Result.Handle(update => {
|
||||||
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
if (!update.IsUpdateNew){
|
||||||
}
|
FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK);
|
||||||
|
}
|
||||||
|
}, ex => {
|
||||||
|
Program.Reporter.HandleException("Update Check", "Encountered an error while checking updates.", true, ex);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,9 @@
|
|||||||
(function($, $TDU){
|
(function($, $TDU){
|
||||||
//
|
|
||||||
// Variable: Current timeout ID for update checking.
|
|
||||||
//
|
|
||||||
var updateCheckTimeoutID;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Update exe file name.
|
|
||||||
//
|
|
||||||
const updateFileName = "TweetDuck.Update.exe";
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Url that returns JSON data about latest version.
|
|
||||||
//
|
|
||||||
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Url that returns JSON data about all versions, including prereleases.
|
|
||||||
//
|
|
||||||
const updateCheckUrlAll = "https://api.github.com/repos/chylex/TweetDuck/releases";
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constant: Fallback url in case the update installer file is missing.
|
|
||||||
//
|
|
||||||
const updateDownloadFallback = "https://tweetduck.chylex.com";
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Creates the update notification element. Removes the old one if already exists.
|
// Function: Creates the update notification element. Removes the old one if already exists.
|
||||||
//
|
//
|
||||||
var displayNotification = function(version, download, changelog){
|
var displayNotification = function(version, changelog){
|
||||||
|
|
||||||
// styles
|
// styles
|
||||||
var css = $("#tweetduck-update-css");
|
var css = $("#tweetduck-update-css");
|
||||||
|
|
||||||
@ -167,7 +143,7 @@
|
|||||||
<div id='tweetduck-changelog'>
|
<div id='tweetduck-changelog'>
|
||||||
<div id='tweetduck-changelog-box'>
|
<div id='tweetduck-changelog-box'>
|
||||||
<h2>TweetDuck Update ${version}</h2>
|
<h2>TweetDuck Update ${version}</h2>
|
||||||
${changelog}
|
${markdown(atob(changelog))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).appendTo(document.body).css("display", "none");
|
`).appendTo(document.body).css("display", "none");
|
||||||
@ -219,17 +195,11 @@
|
|||||||
|
|
||||||
buttonDiv.children(".tdu-btn-download").click(function(){
|
buttonDiv.children(".tdu-btn-download").click(function(){
|
||||||
hide();
|
hide();
|
||||||
|
$TDU.onUpdateAccepted();
|
||||||
if (download){
|
|
||||||
$TDU.onUpdateAccepted();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
$TDU.openBrowser(updateDownloadFallback);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonDiv.children(".tdu-btn-later").click(function(){
|
buttonDiv.children(".tdu-btn-later").click(function(){
|
||||||
clearTimeout(updateCheckTimeoutID);
|
$TDU.onUpdateDelayed();
|
||||||
slide();
|
slide();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -245,15 +215,6 @@
|
|||||||
return ele;
|
return ele;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Function: Returns milliseconds until the start of the next hour, with an extra offset in seconds that can skip an hour if the clock would roll over too soon.
|
|
||||||
//
|
|
||||||
var getTimeUntilNextHour = function(extra){
|
|
||||||
var now = new Date();
|
|
||||||
var offset = new Date(+now+extra*1000);
|
|
||||||
return new Date(offset.getFullYear(), offset.getMonth(), offset.getDate(), offset.getHours()+1, 0, 0)-now;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Ghetto-converts markdown to HTML.
|
// Function: Ghetto-converts markdown to HTML.
|
||||||
//
|
//
|
||||||
@ -273,33 +234,6 @@
|
|||||||
.replace(/\n\r?\n\r?/g, "<br>");
|
.replace(/\n\r?\n\r?/g, "<br>");
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Function: Runs an update check and updates all DOM elements appropriately.
|
|
||||||
//
|
|
||||||
var runUpdateCheck = function(eventID, versionTag, dismissedVersionTag, allowPre){
|
|
||||||
clearTimeout(updateCheckTimeoutID);
|
|
||||||
updateCheckTimeoutID = setTimeout($TDU.triggerUpdateCheck, getTimeUntilNextHour(60*30)); // 30 minute offset
|
|
||||||
|
|
||||||
$.getJSON(allowPre ? updateCheckUrlAll : updateCheckUrlLatest, function(response){
|
|
||||||
var release = allowPre ? response[0] : response;
|
|
||||||
|
|
||||||
var tagName = release.tag_name;
|
|
||||||
var hasUpdate = tagName !== versionTag && tagName !== dismissedVersionTag && release.assets.length > 0;
|
|
||||||
|
|
||||||
if (hasUpdate){
|
|
||||||
var obj = release.assets.find(asset => asset.name === updateFileName) || { browser_download_url: "" };
|
|
||||||
displayNotification(tagName, obj.browser_download_url, markdown(release.body));
|
|
||||||
|
|
||||||
if (eventID){ // ignore undefined and 0
|
|
||||||
$TDU.onUpdateCheckFinished(eventID, tagName, obj.browser_download_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (eventID){ // ignore undefined and 0
|
|
||||||
$TDU.onUpdateCheckFinished(eventID, null, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Check updates on startup.
|
// Block: Check updates on startup.
|
||||||
//
|
//
|
||||||
@ -310,5 +244,5 @@
|
|||||||
//
|
//
|
||||||
// Block: Setup global functions.
|
// Block: Setup global functions.
|
||||||
//
|
//
|
||||||
window.TDUF_runUpdateCheck = runUpdateCheck;
|
window.TDUF_displayNotification = displayNotification;
|
||||||
})($, $TDU);
|
})($, $TDU);
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Web.Extensions" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -316,6 +317,7 @@
|
|||||||
<Compile Include="Core\Management\BrowserCache.cs" />
|
<Compile Include="Core\Management\BrowserCache.cs" />
|
||||||
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
<Compile Include="Core\Utils\BrowserUtils.cs" />
|
||||||
<Compile Include="Core\Utils\NativeMethods.cs" />
|
<Compile Include="Core\Utils\NativeMethods.cs" />
|
||||||
|
<Compile Include="Updates\UpdateCheckClient.cs" />
|
||||||
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
<Compile Include="Updates\UpdateDownloadStatus.cs" />
|
||||||
<Compile Include="Updates\UpdateHandler.cs" />
|
<Compile Include="Updates\UpdateHandler.cs" />
|
||||||
<Compile Include="Updates\UpdateInfo.cs" />
|
<Compile Include="Updates\UpdateInfo.cs" />
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
|
||||||
namespace TweetDuck.Updates.Events{
|
namespace TweetDuck.Updates.Events{
|
||||||
sealed class UpdateCheckEventArgs : EventArgs{
|
sealed class UpdateCheckEventArgs : EventArgs{
|
||||||
public int EventId { get; }
|
public int EventId { get; }
|
||||||
public bool IsUpdateAvailable { get; }
|
public Result<UpdateInfo> Result { get; }
|
||||||
|
|
||||||
public UpdateCheckEventArgs(int eventId, bool isUpdateAvailable){
|
public UpdateCheckEventArgs(int eventId, Result<UpdateInfo> result){
|
||||||
this.EventId = eventId;
|
this.EventId = eventId;
|
||||||
this.IsUpdateAvailable = isUpdateAvailable;
|
this.Result = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Other;
|
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed partial class FormUpdateDownload : Form{
|
sealed partial class FormUpdateDownload : Form{
|
||||||
@ -22,19 +20,8 @@ private void btnCancel_Click(object sender, EventArgs e){
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void timerDownloadCheck_Tick(object sender, EventArgs e){
|
private void timerDownloadCheck_Tick(object sender, EventArgs e){
|
||||||
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
|
if (updateInfo.DownloadStatus.IsFinished()){
|
||||||
timerDownloadCheck.Stop();
|
timerDownloadCheck.Stop();
|
||||||
DialogResult = DialogResult.OK;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
else if (updateInfo.DownloadStatus == UpdateDownloadStatus.Failed){
|
|
||||||
timerDownloadCheck.Stop();
|
|
||||||
|
|
||||||
if (FormMessage.Error("Update Has Failed", "Could not download the update: "+(updateInfo.DownloadError?.Message ?? "unknown error")+"\n\nDo you want to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
|
||||||
BrowserUtils.OpenExternalBrowser(Program.Website);
|
|
||||||
DialogResult = DialogResult.OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
Updates/UpdateCheckClient.cs
Normal file
63
Updates/UpdateCheckClient.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web.Script.Serialization;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
|
||||||
|
|
||||||
|
namespace TweetDuck.Updates{
|
||||||
|
sealed class UpdateCheckClient{
|
||||||
|
private const string ApiLatestRelease = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
|
||||||
|
private const string UpdaterAssetName = "TweetDuck.Update.exe";
|
||||||
|
|
||||||
|
private readonly UpdaterSettings settings;
|
||||||
|
|
||||||
|
public UpdateCheckClient(UpdaterSettings settings){
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UpdateInfo> Check(){
|
||||||
|
TaskCompletionSource<UpdateInfo> result = new TaskCompletionSource<UpdateInfo>();
|
||||||
|
|
||||||
|
WebClient client = BrowserUtils.CreateWebClient();
|
||||||
|
client.Headers[HttpRequestHeader.Accept] = "application/vnd.github.v3+json"; // TODO could use .html to avoid custom markdown parsing
|
||||||
|
|
||||||
|
client.DownloadStringTaskAsync(ApiLatestRelease).ContinueWith(task => {
|
||||||
|
if (task.IsCanceled){
|
||||||
|
result.SetCanceled();
|
||||||
|
}
|
||||||
|
else if (task.IsFaulted){
|
||||||
|
result.SetException(task.Exception.InnerException);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
try{
|
||||||
|
result.SetResult(ParseFromJson(task.Result));
|
||||||
|
}catch(Exception e){
|
||||||
|
result.SetException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdateInfo ParseFromJson(string json){
|
||||||
|
bool IsUpdaterAsset(JsonObject obj){
|
||||||
|
return UpdaterAssetName == (string)obj["name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
string AssetDownloadUrl(JsonObject obj){
|
||||||
|
return (string)obj["browser_download_url"];
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject root = (JsonObject)new JavaScriptSerializer().DeserializeObject(json);
|
||||||
|
|
||||||
|
string versionTag = (string)root["tag_name"];
|
||||||
|
string releaseNotes = (string)root["body"];
|
||||||
|
string downloadUrl = ((Array)root["assets"]).Cast<JsonObject>().Where(IsUpdaterAsset).Select(AssetDownloadUrl).FirstOrDefault();
|
||||||
|
|
||||||
|
return new UpdateInfo(settings, versionTag, releaseNotes, downloadUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,14 @@
|
|||||||
public enum UpdateDownloadStatus{
|
public enum UpdateDownloadStatus{
|
||||||
None = 0,
|
None = 0,
|
||||||
InProgress,
|
InProgress,
|
||||||
|
AssetMissing,
|
||||||
Done,
|
Done,
|
||||||
Failed
|
Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class UpdateDownloadStatusExtensions{
|
||||||
|
public static bool IsFinished(this UpdateDownloadStatus status){
|
||||||
|
return status == UpdateDownloadStatus.AssetMissing || status == UpdateDownloadStatus.Done || status == UpdateDownloadStatus.Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other.Interfaces;
|
using TweetDuck.Core.Other.Interfaces;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Updates.Events;
|
using TweetDuck.Updates.Events;
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateHandler{
|
sealed class UpdateHandler : IDisposable{
|
||||||
public const int CheckCodeUpdatesDisabled = -1;
|
public const int CheckCodeUpdatesDisabled = -1;
|
||||||
public const int CheckCodeNotOnTweetDeck = -2;
|
public const int CheckCodeNotOnTweetDeck = -2;
|
||||||
|
|
||||||
private readonly ITweetDeckBrowser browser;
|
|
||||||
private readonly UpdaterSettings settings;
|
private readonly UpdaterSettings settings;
|
||||||
|
private readonly UpdateCheckClient client;
|
||||||
|
private readonly ITweetDeckBrowser browser;
|
||||||
|
private readonly Timer timer;
|
||||||
|
|
||||||
public event EventHandler<UpdateEventArgs> UpdateAccepted;
|
public event EventHandler<UpdateEventArgs> UpdateAccepted;
|
||||||
|
public event EventHandler<UpdateEventArgs> UpdateDelayed;
|
||||||
public event EventHandler<UpdateEventArgs> UpdateDismissed;
|
public event EventHandler<UpdateEventArgs> UpdateDismissed;
|
||||||
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
||||||
|
|
||||||
@ -23,11 +28,44 @@ sealed class UpdateHandler{
|
|||||||
private UpdateInfo lastUpdateInfo;
|
private UpdateInfo lastUpdateInfo;
|
||||||
|
|
||||||
public UpdateHandler(ITweetDeckBrowser browser, UpdaterSettings settings){
|
public UpdateHandler(ITweetDeckBrowser browser, UpdaterSettings settings){
|
||||||
this.browser = browser;
|
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.client = new UpdateCheckClient(settings);
|
||||||
|
|
||||||
|
this.browser = browser;
|
||||||
|
this.browser.OnFrameLoaded(OnFrameLoaded);
|
||||||
|
this.browser.RegisterBridge("$TDU", new Bridge(this));
|
||||||
|
|
||||||
browser.OnFrameLoaded(OnFrameLoaded);
|
this.timer = new Timer();
|
||||||
browser.RegisterBridge("$TDU", new Bridge(this));
|
this.timer.Tick += timer_Tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
timer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timer_Tick(object sender, EventArgs e){
|
||||||
|
timer.Stop();
|
||||||
|
Check(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartTimer(){
|
||||||
|
if (timer.Enabled){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Stop();
|
||||||
|
|
||||||
|
if (Program.UserConfig.EnableUpdateCheck){
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
|
||||||
|
|
||||||
|
if (nextHour.TotalMinutes < 15){
|
||||||
|
nextHour = nextHour.Add(TimeSpan.FromHours(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Interval = (int)Math.Ceiling(nextHour.TotalMilliseconds);
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFrameLoaded(IFrame frame){
|
private void OnFrameLoaded(IFrame frame){
|
||||||
@ -43,17 +81,24 @@ public int Check(bool force){
|
|||||||
if (!browser.IsTweetDeckWebsite){
|
if (!browser.IsTweetDeckWebsite){
|
||||||
return CheckCodeNotOnTweetDeck;
|
return CheckCodeNotOnTweetDeck;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nextEventId = unchecked(++lastEventId);
|
||||||
|
Task<UpdateInfo> checkTask = client.Check();
|
||||||
|
|
||||||
browser.ExecuteFunction("TDUF_runUpdateCheck", (int)unchecked(++lastEventId), Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases);
|
checkTask.ContinueWith(task => HandleUpdateCheckSuccessful(nextEventId, task.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||||
return lastEventId;
|
checkTask.ContinueWith(task => HandleUpdateCheckFailed(nextEventId, task.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
|
||||||
|
|
||||||
|
return nextEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CheckCodeUpdatesDisabled;
|
return CheckCodeUpdatesDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){
|
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onFinished){
|
||||||
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
|
UpdateDownloadStatus status = updateInfo.DownloadStatus;
|
||||||
onSuccess(updateInfo);
|
|
||||||
|
if (status == UpdateDownloadStatus.Done || status == UpdateDownloadStatus.AssetMissing){
|
||||||
|
onFinished(updateInfo);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
FormUpdateDownload downloadForm = new FormUpdateDownload(updateInfo);
|
FormUpdateDownload downloadForm = new FormUpdateDownload(updateInfo);
|
||||||
@ -65,13 +110,7 @@ public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<Up
|
|||||||
|
|
||||||
downloadForm.FormClosed += (sender, args) => {
|
downloadForm.FormClosed += (sender, args) => {
|
||||||
downloadForm.Dispose();
|
downloadForm.Dispose();
|
||||||
|
onFinished(updateInfo);
|
||||||
if (downloadForm.DialogResult == DialogResult.OK){ // success or manual download
|
|
||||||
onSuccess(updateInfo);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
ownerForm.Show();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
downloadForm.Show();
|
downloadForm.Show();
|
||||||
@ -85,17 +124,41 @@ public void CleanupDownload(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerUpdateAcceptedEvent(UpdateEventArgs args){
|
private void HandleUpdateCheckSuccessful(int eventId, UpdateInfo info){
|
||||||
UpdateAccepted?.Invoke(this, args);
|
if (info.IsUpdateNew && !info.IsUpdateDismissed){
|
||||||
|
CleanupDownload();
|
||||||
|
lastUpdateInfo = info;
|
||||||
|
lastUpdateInfo.BeginSilentDownload();
|
||||||
|
|
||||||
|
browser.ExecuteFunction("TDUF_displayNotification", lastUpdateInfo.VersionTag, Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(lastUpdateInfo.ReleaseNotes))); // TODO move browser stuff outside
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckFinished?.Invoke(this, new UpdateCheckEventArgs(eventId, new Result<UpdateInfo>(info)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerUpdateDismissedEvent(UpdateEventArgs args){
|
private void HandleUpdateCheckFailed(int eventId, Exception exception){
|
||||||
settings.DismissedUpdate = args.UpdateInfo.VersionTag;
|
CheckFinished?.Invoke(this, new UpdateCheckEventArgs(eventId, new Result<UpdateInfo>(exception)));
|
||||||
UpdateDismissed?.Invoke(this, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
|
private void TriggerUpdateAcceptedEvent(){
|
||||||
CheckFinished?.Invoke(this, args);
|
if (lastUpdateInfo != null){
|
||||||
|
UpdateAccepted?.Invoke(this, new UpdateEventArgs(lastUpdateInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUpdateDelayedEvent(){
|
||||||
|
if (lastUpdateInfo != null){
|
||||||
|
UpdateDelayed?.Invoke(this, new UpdateEventArgs(lastUpdateInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUpdateDismissedEvent(){
|
||||||
|
if (lastUpdateInfo != null){
|
||||||
|
settings.DismissedUpdate = lastUpdateInfo.VersionTag;
|
||||||
|
UpdateDismissed?.Invoke(this, new UpdateEventArgs(lastUpdateInfo));
|
||||||
|
|
||||||
|
CleanupDownload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Bridge{
|
public sealed class Bridge{
|
||||||
@ -109,31 +172,16 @@ public void TriggerUpdateCheck(){
|
|||||||
owner.Check(false);
|
owner.Check(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateCheckFinished(int eventId, string versionTag, string downloadUrl){
|
public void OnUpdateAccepted(){
|
||||||
if (versionTag != null && (owner.lastUpdateInfo == null || owner.lastUpdateInfo.VersionTag != versionTag)){
|
owner.TriggerUpdateAcceptedEvent();
|
||||||
owner.CleanupDownload();
|
|
||||||
owner.lastUpdateInfo = new UpdateInfo(owner.settings, eventId, versionTag, downloadUrl);
|
|
||||||
owner.lastUpdateInfo.BeginSilentDownload();
|
|
||||||
}
|
|
||||||
|
|
||||||
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, owner.lastUpdateInfo != null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateAccepted(){
|
public void OnUpdateDelayed(){
|
||||||
if (owner.lastUpdateInfo != null){
|
owner.TriggerUpdateDelayedEvent();
|
||||||
owner.TriggerUpdateAcceptedEvent(new UpdateEventArgs(owner.lastUpdateInfo));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnUpdateDismissed(){
|
public void OnUpdateDismissed(){
|
||||||
if (owner.lastUpdateInfo != null){
|
owner.TriggerUpdateDismissedEvent();
|
||||||
owner.TriggerUpdateDismissedEvent(new UpdateEventArgs(owner.lastUpdateInfo));
|
|
||||||
owner.CleanupDownload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenBrowser(string url){
|
|
||||||
BrowserUtils.OpenExternalBrowser(url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,40 +5,43 @@
|
|||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed class UpdateInfo{
|
sealed class UpdateInfo{
|
||||||
public int EventId { get; }
|
|
||||||
public string VersionTag { get; }
|
public string VersionTag { get; }
|
||||||
|
public string ReleaseNotes { get; }
|
||||||
public string InstallerPath { get; }
|
public string InstallerPath { get; }
|
||||||
|
|
||||||
|
public bool IsUpdateNew => VersionTag != Program.VersionTag;
|
||||||
|
public bool IsUpdateDismissed => VersionTag == settings.DismissedUpdate;
|
||||||
|
|
||||||
public UpdateDownloadStatus DownloadStatus { get; private set; }
|
public UpdateDownloadStatus DownloadStatus { get; private set; }
|
||||||
public Exception DownloadError { get; private set; }
|
public Exception DownloadError { get; private set; }
|
||||||
|
|
||||||
private readonly string installerFolder;
|
private readonly UpdaterSettings settings;
|
||||||
private readonly string downloadUrl;
|
private readonly string downloadUrl;
|
||||||
private WebClient currentDownload;
|
private WebClient currentDownload;
|
||||||
|
|
||||||
public UpdateInfo(UpdaterSettings settings, int eventId, string versionTag, string downloadUrl){
|
public UpdateInfo(UpdaterSettings settings, string versionTag, string releaseNotes, string downloadUrl){
|
||||||
this.installerFolder = settings.InstallerDownloadFolder;
|
this.settings = settings;
|
||||||
this.downloadUrl = downloadUrl;
|
this.downloadUrl = downloadUrl;
|
||||||
|
|
||||||
this.EventId = eventId;
|
|
||||||
this.VersionTag = versionTag;
|
this.VersionTag = versionTag;
|
||||||
this.InstallerPath = Path.Combine(installerFolder, "TweetDuck."+versionTag+".exe");
|
this.ReleaseNotes = releaseNotes;
|
||||||
|
this.InstallerPath = Path.Combine(settings.InstallerDownloadFolder, "TweetDuck."+versionTag+".exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginSilentDownload(){
|
public void BeginSilentDownload(){
|
||||||
if (DownloadStatus == UpdateDownloadStatus.None || DownloadStatus == UpdateDownloadStatus.Failed){
|
if (DownloadStatus == UpdateDownloadStatus.None || DownloadStatus == UpdateDownloadStatus.Failed){
|
||||||
DownloadStatus = UpdateDownloadStatus.InProgress;
|
DownloadStatus = UpdateDownloadStatus.InProgress;
|
||||||
|
|
||||||
try{
|
if (string.IsNullOrEmpty(downloadUrl)){
|
||||||
Directory.CreateDirectory(installerFolder);
|
DownloadError = new InvalidDataException("Missing installer asset.");
|
||||||
}catch(Exception e){
|
DownloadStatus = UpdateDownloadStatus.AssetMissing;
|
||||||
DownloadError = e;
|
|
||||||
DownloadStatus = UpdateDownloadStatus.Failed;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(downloadUrl)){
|
try{
|
||||||
DownloadError = new UriFormatException("Could not determine URL of the update installer");
|
Directory.CreateDirectory(settings.InstallerDownloadFolder);
|
||||||
|
}catch(Exception e){
|
||||||
|
DownloadError = e;
|
||||||
DownloadStatus = UpdateDownloadStatus.Failed;
|
DownloadStatus = UpdateDownloadStatus.Failed;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -68,5 +71,13 @@ public void DeleteInstaller(){
|
|||||||
// rip
|
// rip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj){
|
||||||
|
return obj is UpdateInfo info && VersionTag == info.VersionTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode(){
|
||||||
|
return VersionTag.GetHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user