diff --git a/Core/Bridge/UpdateBridge.cs b/Core/Bridge/UpdateBridge.cs new file mode 100644 index 00000000..f2b72680 --- /dev/null +++ b/Core/Bridge/UpdateBridge.cs @@ -0,0 +1,68 @@ +using System; +using System.Windows.Forms; +using TweetDuck.Core.Controls; +using TweetDuck.Updates; + +namespace TweetDuck.Core.Bridge{ + class UpdateBridge{ + private readonly UpdateHandler updates; + private readonly Control sync; + + private UpdateInfo nextUpdate = null; + + public event EventHandler<UpdateInfo> UpdateAccepted; + public event EventHandler<UpdateInfo> UpdateDelayed; + public event EventHandler<UpdateInfo> UpdateDismissed; + + public UpdateBridge(UpdateHandler updates, Control sync){ + this.sync = sync; + + this.updates = updates; + this.updates.CheckFinished += updates_CheckFinished; + } + + internal void Cleanup(){ + updates.CheckFinished -= updates_CheckFinished; + nextUpdate?.DeleteInstaller(); + } + + private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ + UpdateInfo foundUpdate = e.Result.HasValue ? e.Result.Value : null; + + if (nextUpdate != null && !nextUpdate.Equals(foundUpdate)){ + nextUpdate.DeleteInstaller(); + } + + nextUpdate = foundUpdate; + } + + private void HandleInteractionEvent(EventHandler<UpdateInfo> eventHandler){ + UpdateInfo tmpInfo = nextUpdate; + + if (tmpInfo != null){ + sync.InvokeAsyncSafe(() => eventHandler?.Invoke(this, tmpInfo)); + } + } + + // Bridge methods + + public void TriggerUpdateCheck(){ + updates.Check(false); + } + + public void OnUpdateAccepted(){ + HandleInteractionEvent(UpdateAccepted); + } + + public void OnUpdateDelayed(){ + HandleInteractionEvent(UpdateDelayed); + } + + public void OnUpdateDismissed(){ + HandleInteractionEvent(UpdateDismissed); + + nextUpdate?.DeleteInstaller(); + nextUpdate = null; + } + } +} diff --git a/Core/FormBrowser.cs b/Core/FormBrowser.cs index 61a2c4a0..667d8ba2 100644 --- a/Core/FormBrowser.cs +++ b/Core/FormBrowser.cs @@ -17,7 +17,6 @@ using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Events; using TweetDuck.Updates; -using TweetDuck.Updates.Events; namespace TweetDuck.Core{ sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{ @@ -50,6 +49,7 @@ public bool IsWaiting{ private readonly UpdateHandler updates; private readonly FormNotificationTweet notification; private readonly ContextMenu contextMenu; + private readonly UpdateBridge updateBridge; private bool isLoaded; private FormWindowState prevState; @@ -62,7 +62,7 @@ public FormBrowser(){ InitializeComponent(); Text = Program.BrandName; - + this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath); this.plugins.Reloaded += plugins_Reloaded; this.plugins.Executed += plugins_Executed; @@ -71,7 +71,15 @@ public FormBrowser(){ this.notification = new FormNotificationTweet(this, plugins); this.notification.Show(); - this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification)); + this.updates = new UpdateHandler(Program.InstallerPath); + this.updates.CheckFinished += updates_CheckFinished; + + this.updateBridge = new UpdateBridge(updates, this); + this.updateBridge.UpdateAccepted += updateBridge_UpdateAccepted; + this.updateBridge.UpdateDelayed += updateBridge_UpdateDelayed; + this.updateBridge.UpdateDismissed += updateBridge_UpdateDismissed; + + this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification), updateBridge); this.contextMenu = ContextMenuBrowser.CreateMenu(this); this.plugins.Register(browser, PluginEnvironment.Browser, this, true); @@ -103,11 +111,6 @@ public FormBrowser(){ UpdateFormIcon(); } - this.updates = new UpdateHandler(browser, Program.InstallerPath); - this.updates.CheckFinished += updates_CheckFinished; - this.updates.UpdateAccepted += updates_UpdateAccepted; - this.updates.UpdateDismissed += updates_UpdateDismissed; - if (Config.AllowDataCollection){ analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath); } @@ -207,7 +210,7 @@ private void FormBrowser_FormClosing(object sender, FormClosingEventArgs e){ private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e){ if (isLoaded && UpdateInstallerPath == null){ - updates.CleanupDownload(); + updateBridge.Cleanup(); } } @@ -252,7 +255,7 @@ private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ string tag = update.VersionTag; if (tag != Program.VersionTag && tag != Config.DismissedUpdate){ - updates.PrepareUpdate(update); + update.BeginSilentDownload(); browser.ShowUpdateNotification(tag, update.ReleaseNotes); } else{ @@ -268,38 +271,61 @@ private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ ignoreUpdateCheckError = true; } - private void updates_UpdateAccepted(object sender, UpdateEventArgs e){ - this.InvokeAsyncSafe(() => { - FormManager.CloseAllDialogs(); + private void updateBridge_UpdateAccepted(object sender, UpdateInfo update){ + FormManager.CloseAllDialogs(); - if (!string.IsNullOrEmpty(Config.DismissedUpdate)){ - Config.DismissedUpdate = null; - Config.Save(); + if (!string.IsNullOrEmpty(Config.DismissedUpdate)){ + Config.DismissedUpdate = null; + Config.Save(); + } + + void OnFinished(){ + UpdateDownloadStatus status = update.DownloadStatus; + + if (status == UpdateDownloadStatus.Done){ + UpdateInstallerPath = update.InstallerPath; + ForceClose(); } + else if (status != UpdateDownloadStatus.Canceled && 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(); + } + } + + if (update.DownloadStatus.IsFinished(true)){ + OnFinished(); + } + else{ + FormUpdateDownload downloadForm = new FormUpdateDownload(update); - updates.BeginUpdateDownload(this, e.UpdateInfo, update => { - UpdateDownloadStatus status = update.DownloadStatus; + downloadForm.VisibleChanged += (sender2, args2) => { + downloadForm.MoveToCenter(this); + Hide(); + }; - if (status == UpdateDownloadStatus.Done){ - UpdateInstallerPath = update.InstallerPath; - ForceClose(); + downloadForm.FormClosed += (sender2, args2) => { + if (downloadForm.DialogResult != DialogResult.OK){ + update.CancelDownload(); } - else if (status != UpdateDownloadStatus.Canceled && 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(); - } - }); - }); + + downloadForm.Dispose(); + OnFinished(); + }; + + downloadForm.Show(); + } } - private void updates_UpdateDismissed(object sender, UpdateEventArgs e){ - this.InvokeAsyncSafe(() => { - Config.DismissedUpdate = e.UpdateInfo.VersionTag; - Config.Save(); - }); + private void updateBridge_UpdateDelayed(object sender, UpdateInfo update){ + // stops the timer + } + + private void updateBridge_UpdateDismissed(object sender, UpdateInfo update){ + Config.DismissedUpdate = update.VersionTag; + Config.Save(); } protected override void WndProc(ref Message m){ diff --git a/Core/Other/Settings/TabSettingsGeneral.cs b/Core/Other/Settings/TabSettingsGeneral.cs index 5bbb3ba4..66d3a121 100644 --- a/Core/Other/Settings/TabSettingsGeneral.cs +++ b/Core/Other/Settings/TabSettingsGeneral.cs @@ -7,7 +7,6 @@ using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Utils; using TweetDuck.Updates; -using TweetDuck.Updates.Events; namespace TweetDuck.Core.Other.Settings{ sealed partial class TabSettingsGeneral : BaseTabSettings{ @@ -210,11 +209,6 @@ private void btnCheckUpdates_Click(object sender, EventArgs e){ btnCheckUpdates.Enabled = false; updateCheckEventId = updates.Check(true); - - if (updateCheckEventId == UpdateHandler.CheckCodeNotOnTweetDeck){ - FormMessage.Error("Update Check", "Updates can only be checked once TweetDeck is fully loaded.", FormMessage.OK); - btnCheckUpdates.Enabled = true; - } } private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ @@ -225,6 +219,8 @@ private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){ if (update.VersionTag == Program.VersionTag){ FormMessage.Information("No Updates Available", "Your version of TweetDuck is up to date.", FormMessage.OK); } + + // TODO allow outside TweetDeck }, ex => { Program.Reporter.HandleException("Update Check Error", "An error occurred while checking for updates.", true, ex); }); diff --git a/Core/TweetDeckBrowser.cs b/Core/TweetDeckBrowser.cs index bb79dd74..d45e089c 100644 --- a/Core/TweetDeckBrowser.cs +++ b/Core/TweetDeckBrowser.cs @@ -9,7 +9,6 @@ using TweetDuck.Core.Controls; using TweetDuck.Core.Handling; using TweetDuck.Core.Handling.General; -using TweetDuck.Core.Management; using TweetDuck.Core.Notification; using TweetDuck.Core.Other.Interfaces; using TweetDuck.Core.Utils; @@ -40,7 +39,7 @@ public bool IsTweetDeckWebsite{ private string prevSoundNotificationPath = null; - public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){ + public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge tdBridge, UpdateBridge updateBridge){ RequestHandlerBrowser requestHandler = new RequestHandlerBrowser(); this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){ @@ -58,7 +57,8 @@ public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){ this.browser.FrameLoadEnd += browser_FrameLoadEnd; this.browser.LoadError += browser_LoadError; - this.browser.RegisterAsyncJsObject("$TD", bridge); + this.browser.RegisterAsyncJsObject("$TD", tdBridge); + this.browser.RegisterAsyncJsObject("$TDU", updateBridge); this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb(); this.browser.Dock = DockStyle.None; diff --git a/TweetDuck.csproj b/TweetDuck.csproj index 5ade89de..846eba1d 100644 --- a/TweetDuck.csproj +++ b/TweetDuck.csproj @@ -58,6 +58,7 @@ <Compile Include="Configuration\SystemConfig.cs" /> <Compile Include="Configuration\UserConfig.cs" /> <Compile Include="Core\Bridge\PropertyBridge.cs" /> + <Compile Include="Core\Bridge\UpdateBridge.cs" /> <Compile Include="Core\Controls\ControlExtensions.cs" /> <Compile Include="Core\Controls\FlatButton.cs"> <SubType>Component</SubType> @@ -283,7 +284,7 @@ <DependentUpon>Resources.resx</DependentUpon> </Compile> <Compile Include="Reporter.cs" /> - <Compile Include="Updates\Events\UpdateCheckEventArgs.cs" /> + <Compile Include="Updates\UpdateCheckEventArgs.cs" /> <Compile Include="Updates\FormUpdateDownload.cs"> <SubType>Form</SubType> </Compile> @@ -306,7 +307,6 @@ <Compile Include="Program.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Resources\ScriptLoader.cs" /> - <Compile Include="Updates\Events\UpdateEventArgs.cs" /> </ItemGroup> <ItemGroup> <BootstrapperPackage Include="Microsoft.Net.Client.3.5"> diff --git a/Updates/Events/UpdateEventArgs.cs b/Updates/Events/UpdateEventArgs.cs deleted file mode 100644 index b3421a38..00000000 --- a/Updates/Events/UpdateEventArgs.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace TweetDuck.Updates.Events{ - sealed class UpdateEventArgs : EventArgs{ - public UpdateInfo UpdateInfo { get; } - - public UpdateEventArgs(UpdateInfo updateInfo){ - this.UpdateInfo = updateInfo; - } - } -} diff --git a/Updates/FormUpdateDownload.cs b/Updates/FormUpdateDownload.cs index d90c3979..6d00ca4a 100644 --- a/Updates/FormUpdateDownload.cs +++ b/Updates/FormUpdateDownload.cs @@ -20,7 +20,7 @@ private void btnCancel_Click(object sender, EventArgs e){ } private void timerDownloadCheck_Tick(object sender, EventArgs e){ - if (updateInfo.DownloadStatus.IsFinished()){ + if (updateInfo.DownloadStatus.IsFinished(false)){ timerDownloadCheck.Stop(); DialogResult = DialogResult.OK; Close(); diff --git a/Updates/Events/UpdateCheckEventArgs.cs b/Updates/UpdateCheckEventArgs.cs similarity index 90% rename from Updates/Events/UpdateCheckEventArgs.cs rename to Updates/UpdateCheckEventArgs.cs index a5a49485..7938290e 100644 --- a/Updates/Events/UpdateCheckEventArgs.cs +++ b/Updates/UpdateCheckEventArgs.cs @@ -1,7 +1,7 @@ using System; using TweetDuck.Data; -namespace TweetDuck.Updates.Events{ +namespace TweetDuck.Updates{ sealed class UpdateCheckEventArgs : EventArgs{ public int EventId { get; } public Result<UpdateInfo> Result { get; } diff --git a/Updates/UpdateDownloadStatus.cs b/Updates/UpdateDownloadStatus.cs index 85de1b66..3b4d7c17 100644 --- a/Updates/UpdateDownloadStatus.cs +++ b/Updates/UpdateDownloadStatus.cs @@ -9,8 +9,8 @@ public enum UpdateDownloadStatus{ } public static class UpdateDownloadStatusExtensions{ - public static bool IsFinished(this UpdateDownloadStatus status){ - return status == UpdateDownloadStatus.AssetMissing || status == UpdateDownloadStatus.Done || status == UpdateDownloadStatus.Failed; + public static bool IsFinished(this UpdateDownloadStatus status, bool canRetry){ + return status == UpdateDownloadStatus.AssetMissing || status == UpdateDownloadStatus.Done || (status == UpdateDownloadStatus.Failed && !canRetry); } } } diff --git a/Updates/UpdateHandler.cs b/Updates/UpdateHandler.cs index d9463d0a..c88c22fc 100644 --- a/Updates/UpdateHandler.cs +++ b/Updates/UpdateHandler.cs @@ -1,38 +1,24 @@ using System; using System.Threading; using System.Threading.Tasks; -using System.Windows.Forms; -using TweetDuck.Core.Controls; -using TweetDuck.Core.Other.Interfaces; using TweetDuck.Data; -using TweetDuck.Updates.Events; using Timer = System.Windows.Forms.Timer; namespace TweetDuck.Updates{ sealed class UpdateHandler : IDisposable{ public const int CheckCodeUpdatesDisabled = -1; - public const int CheckCodeNotOnTweetDeck = -2; private readonly UpdateCheckClient client; private readonly TaskScheduler scheduler; - private readonly ITweetDeckBrowser browser; private readonly Timer timer; - - public event EventHandler<UpdateEventArgs> UpdateAccepted; - public event EventHandler<UpdateEventArgs> UpdateDelayed; - public event EventHandler<UpdateEventArgs> UpdateDismissed; + public event EventHandler<UpdateCheckEventArgs> CheckFinished; - private ushort lastEventId; - private UpdateInfo lastUpdateInfo; - public UpdateHandler(ITweetDeckBrowser browser, string installerFolder){ + public UpdateHandler(string installerFolder){ this.client = new UpdateCheckClient(installerFolder); this.scheduler = TaskScheduler.FromCurrentSynchronizationContext(); - this.browser = browser; - this.browser.RegisterBridge("$TDU", new Bridge(this)); - this.timer = new Timer(); this.timer.Tick += timer_Tick; } @@ -68,10 +54,6 @@ public void StartTimer(){ public int Check(bool force){ if (Program.UserConfig.EnableUpdateCheck || force){ - if (!browser.IsTweetDeckWebsite){ - return CheckCodeNotOnTweetDeck; - } - int nextEventId = unchecked(++lastEventId); Task<UpdateInfo> checkTask = client.Check(); @@ -84,46 +66,6 @@ public int Check(bool force){ return CheckCodeUpdatesDisabled; } - public void PrepareUpdate(UpdateInfo info){ - CleanupDownload(); - lastUpdateInfo = info; - lastUpdateInfo.BeginSilentDownload(); - } - - public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onFinished){ - UpdateDownloadStatus status = updateInfo.DownloadStatus; - - if (status == UpdateDownloadStatus.Done || status == UpdateDownloadStatus.AssetMissing){ - onFinished(updateInfo); - } - else{ - FormUpdateDownload downloadForm = new FormUpdateDownload(updateInfo); - - downloadForm.VisibleChanged += (sender, args) => { - downloadForm.MoveToCenter(ownerForm); - ownerForm.Hide(); - }; - - downloadForm.FormClosed += (sender, args) => { - if (downloadForm.DialogResult != DialogResult.OK){ - updateInfo.CancelDownload(); - } - - downloadForm.Dispose(); - onFinished(updateInfo); - }; - - downloadForm.Show(); - } - } - - public void CleanupDownload(){ - if (lastUpdateInfo != null){ - lastUpdateInfo.DeleteInstaller(); - lastUpdateInfo = null; - } - } - private void HandleUpdateCheckSuccessful(int eventId, UpdateInfo info){ CheckFinished?.Invoke(this, new UpdateCheckEventArgs(eventId, new Result<UpdateInfo>(info))); } @@ -131,48 +73,5 @@ private void HandleUpdateCheckSuccessful(int eventId, UpdateInfo info){ private void HandleUpdateCheckFailed(int eventId, Exception exception){ CheckFinished?.Invoke(this, new UpdateCheckEventArgs(eventId, new Result<UpdateInfo>(exception))); } - - private void TriggerUpdateAcceptedEvent(){ - 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){ - UpdateDismissed?.Invoke(this, new UpdateEventArgs(lastUpdateInfo)); - CleanupDownload(); - } - } - - public sealed class Bridge{ - private readonly UpdateHandler owner; - - public Bridge(UpdateHandler owner){ - this.owner = owner; - } - - public void TriggerUpdateCheck(){ - owner.Check(false); - } - - public void OnUpdateAccepted(){ - owner.TriggerUpdateAcceptedEvent(); - } - - public void OnUpdateDelayed(){ - owner.TriggerUpdateDelayedEvent(); - } - - public void OnUpdateDismissed(){ - owner.TriggerUpdateDismissedEvent(); - } - } } } diff --git a/Updates/UpdateInfo.cs b/Updates/UpdateInfo.cs index f92aafd2..380755bb 100644 --- a/Updates/UpdateInfo.cs +++ b/Updates/UpdateInfo.cs @@ -26,6 +26,11 @@ public UpdateInfo(string versionTag, string releaseNotes, string downloadUrl, st } public void BeginSilentDownload(){ + if (File.Exists(InstallerPath)){ + DownloadStatus = UpdateDownloadStatus.Done; + return; + } + if (DownloadStatus == UpdateDownloadStatus.None || DownloadStatus == UpdateDownloadStatus.Failed){ DownloadStatus = UpdateDownloadStatus.InProgress;