Implement auto-update via Onova

pull/37/head
Alexey Golub 7 years ago
parent 7bfd645e8e
commit 63c835df88

@ -14,7 +14,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
<PackageReference Include="Onova" Version="1.0.0" />
<PackageReference Include="Tyrrrz.Extensions" Version="1.5.0" />
<PackageReference Include="Tyrrrz.Settings" Version="1.3.2" />
</ItemGroup>

@ -0,0 +1,14 @@
using System;
using System.Threading.Tasks;
namespace DiscordChatExporter.Core.Services
{
public interface IUpdateService
{
Task<Version> CheckForUpdatesAsync();
Task PrepareUpdateAsync();
Task ApplyUpdateAsync(bool restart = true);
}
}

@ -0,0 +1,61 @@
using System;
using System.Threading.Tasks;
using Onova;
using Onova.Services;
namespace DiscordChatExporter.Core.Services
{
public class UpdateService : IUpdateService
{
private readonly UpdateManager _updateManager;
private Version _lastVersion;
private bool _applied;
public UpdateService()
{
_updateManager = new UpdateManager(
new GithubPackageResolver("Tyrrrz", "DiscordChatExporter", "DiscordChatExporter.zip"),
new ZipPackageExtractor());
}
public async Task<Version> CheckForUpdatesAsync()
{
#if DEBUG
// Never update in DEBUG mode
return null;
#endif
// Remove some junk left over from last update
_updateManager.Cleanup();
// Check for updates
var check = await _updateManager.CheckForUpdatesAsync();
// Return latest version or null if running latest version already
return check.CanUpdate ? _lastVersion = check.LastVersion : null;
}
public async Task PrepareUpdateAsync()
{
if (_lastVersion == null)
return;
// Download and prepare update
await _updateManager.PreparePackageAsync(_lastVersion);
}
public async Task ApplyUpdateAsync(bool restart = true)
{
if (_lastVersion == null)
return;
if (_applied)
return;
// Enqueue an update
await _updateManager.EnqueueApplyPackageAsync(_lastVersion, restart);
_applied = true;
}
}
}

@ -28,9 +28,7 @@ namespace DiscordChatExporter.Gui
SimpleIoc.Default.Register<IExportService, ExportService>();
SimpleIoc.Default.Register<IMessageGroupService, MessageGroupService>();
SimpleIoc.Default.Register<ISettingsService, SettingsService>();
// Load settings
Resolve<ISettingsService>().Load();
SimpleIoc.Default.Register<IUpdateService, UpdateService>();
// View models
SimpleIoc.Default.Register<IErrorViewModel, ErrorViewModel>(true);
@ -42,8 +40,6 @@ namespace DiscordChatExporter.Gui
public void Cleanup()
{
// Save settings
ServiceLocator.Current.GetInstance<ISettingsService>().Save();
}
}
}

@ -65,14 +65,12 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\MvvmLightLibs.5.3.0.0\lib\net45\System.Windows.Interactivity.dll</HintPath>
</Reference>
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Tyrrrz.Extensions, Version=1.5.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Tyrrrz.Extensions.1.5.0\lib\net45\Tyrrrz.Extensions.dll</HintPath>
</Reference>
@ -84,6 +82,7 @@
<Compile Include="Messages\ShowErrorMessage.cs" />
<Compile Include="Messages\ShowExportDoneMessage.cs" />
<Compile Include="Messages\ShowExportSetupMessage.cs" />
<Compile Include="Messages\ShowNotificationMessage.cs" />
<Compile Include="Messages\ShowSettingsMessage.cs" />
<Compile Include="Messages\StartExportMessage.cs" />
<Compile Include="ViewModels\ErrorViewModel.cs" />

@ -0,0 +1,25 @@
using System;
namespace DiscordChatExporter.Gui.Messages
{
public class ShowNotificationMessage
{
public string Message { get; }
public string CallbackCaption { get; }
public Action Callback { get; }
public ShowNotificationMessage(string message)
{
Message = message;
}
public ShowNotificationMessage(string message, string callbackCaption, Action callback)
: this(message)
{
CallbackCaption = callbackCaption;
Callback = callback;
}
}
}

@ -15,6 +15,8 @@ namespace DiscordChatExporter.Gui.ViewModels
Guild SelectedGuild { get; set; }
IReadOnlyList<Channel> AvailableChannels { get; }
RelayCommand ViewLoadedCommand { get; }
RelayCommand ViewClosedCommand { get; }
RelayCommand PullDataCommand { get; }
RelayCommand ShowSettingsCommand { get; }
RelayCommand ShowAboutCommand { get; }

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Windows;
using DiscordChatExporter.Core.Exceptions;
using DiscordChatExporter.Core.Models;
using DiscordChatExporter.Core.Services;
@ -16,6 +17,7 @@ namespace DiscordChatExporter.Gui.ViewModels
public class MainViewModel : ViewModelBase, IMainViewModel
{
private readonly ISettingsService _settingsService;
private readonly IUpdateService _updateService;
private readonly IDataService _dataService;
private readonly IMessageGroupService _messageGroupService;
private readonly IExportService _exportService;
@ -81,15 +83,18 @@ namespace DiscordChatExporter.Gui.ViewModels
private set => Set(ref _availableChannels, value);
}
public RelayCommand ViewLoadedCommand { get; }
public RelayCommand ViewClosedCommand { get; }
public RelayCommand PullDataCommand { get; }
public RelayCommand ShowSettingsCommand { get; }
public RelayCommand ShowAboutCommand { get; }
public RelayCommand<Channel> ShowExportSetupCommand { get; }
public MainViewModel(ISettingsService settingsService, IDataService dataService,
public MainViewModel(ISettingsService settingsService, IUpdateService updateService, IDataService dataService,
IMessageGroupService messageGroupService, IExportService exportService)
{
_settingsService = settingsService;
_updateService = updateService;
_dataService = dataService;
_messageGroupService = messageGroupService;
_exportService = exportService;
@ -97,19 +102,54 @@ namespace DiscordChatExporter.Gui.ViewModels
_guildChannelsMap = new Dictionary<Guild, IReadOnlyList<Channel>>();
// Commands
ViewLoadedCommand = new RelayCommand(ViewLoaded);
ViewClosedCommand = new RelayCommand(ViewClosed);
PullDataCommand = new RelayCommand(PullData, () => Token.IsNotBlank() && !IsBusy);
ShowSettingsCommand = new RelayCommand(ShowSettings);
ShowAboutCommand = new RelayCommand(ShowAbout);
ShowExportSetupCommand = new RelayCommand<Channel>(ShowExportSetup, _ => !IsBusy);
// Messages
MessengerInstance.Register<StartExportMessage>(this, m =>
MessengerInstance.Register<StartExportMessage>(this,
m => { Export(m.Channel, m.FilePath, m.Format, m.From, m.To); });
}
private async void ViewLoaded()
{
// Load settings
_settingsService.Load();
// Set last token
Token = _settingsService.LastToken;
// Check for updates
var lastVersion = await _updateService.CheckForUpdatesAsync();
if (lastVersion != null)
{
Export(m.Channel, m.FilePath, m.Format, m.From, m.To);
});
// Download updates
await _updateService.PrepareUpdateAsync();
// Notify user
MessengerInstance.Send(
new ShowNotificationMessage(
$"DiscordChatExporter v{lastVersion} has been downloaded. " +
"It will be installed once you exit.",
"INSTALL NOW",
async () =>
{
await _updateService.ApplyUpdateAsync();
Application.Current.Shutdown();
}));
}
}
private void ViewClosed()
{
// Save settings
_settingsService.Save();
// Defaults
_token = _settingsService.LastToken;
// Apply updates if available
_updateService.ApplyUpdateAsync(false);
}
private async void PullData()

@ -1,4 +1,5 @@
using MaterialDesignThemes.Wpf
using System.Windows.Interactivity;
using MaterialDesignThemes.Wpf
using MaterialDesignThemes.Wpf.Transitions
Window "DiscordChatExporter.Gui.Views.MainWindow" {
@ -19,15 +20,27 @@ Window "DiscordChatExporter.Gui.Views.MainWindow" {
UseLayoutRounding: true
WindowStartupLocation: CenterScreen
Interaction.Triggers: [
Interactivity.EventTrigger {
EventName: "Loaded"
InvokeCommandAction { Command: bind ViewLoadedCommand }
},
Interactivity.EventTrigger {
EventName: "Closed"
InvokeCommandAction { Command: bind ViewClosedCommand }
}
]
DialogHost {
DockPanel {
IsEnabled: bind IsBusy
convert (bool b) => b ? false : true
SnackbarMessageQueue: bind MessageQueue from "Snackbar"
DockPanel {
// Toolbar
Border {
DockPanel.Dock: Top
Background: resource dyn "PrimaryHueMidBrush"
IsEnabled: bind IsBusy
convert (bool b) => b ? false : true
TextElement.Foreground: resource dyn "SecondaryInverseTextBrush"
StackPanel {
Grid {
@ -104,6 +117,8 @@ Window "DiscordChatExporter.Gui.Views.MainWindow" {
Grid {
DockPanel {
Background: resource dyn "MaterialDesignCardBackground"
IsEnabled: bind IsBusy
convert (bool b) => b ? false : true
Visibility: bind IsDataAvailable
convert (bool b) => b ? Visibility.Visible : Visibility.Hidden
@ -265,6 +280,10 @@ Window "DiscordChatExporter.Gui.Views.MainWindow" {
}
}
}
// Snackbar
Snackbar "Snackbar" {
}
}
}
}

@ -1,4 +1,5 @@
using System.Reflection;
using System;
using System.Reflection;
using DiscordChatExporter.Gui.Messages;
using GalaSoft.MvvmLight.Messaging;
using MaterialDesignThemes.Wpf;
@ -13,7 +14,13 @@ namespace DiscordChatExporter.Gui.Views
InitializeComponent();
Title += $" v{Assembly.GetExecutingAssembly().GetName().Version}";
// Dialogs
Snackbar.MessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(5));
// Notification messages
Messenger.Default.Register<ShowNotificationMessage>(this,
m => Snackbar.MessageQueue.Enqueue(m.Message, m.CallbackCaption, m.Callback));
// Dialog messages
Messenger.Default.Register<ShowErrorMessage>(this,
m => DialogHost.Show(new ErrorDialog()).Forget());
Messenger.Default.Register<ShowExportDoneMessage>(this,

@ -39,6 +39,7 @@ DiscordChatExporter can be used to export message history from a [Discord](https
- [GalaSoft.MVVMLight](http://www.mvvmlight.net)
- [MaterialDesignInXamlToolkit](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit)
- [Newtonsoft.Json](http://www.newtonsoft.com/json)
- [Onova](https://github.com/Tyrrrz/Onova)
- [FluentCommandLineParser](https://github.com/fclp/fluent-command-line-parser)
- [Tyrrrz.Extensions](https://github.com/Tyrrrz/Extensions)
- [Tyrrrz.Settings](https://github.com/Tyrrrz/Settings)

Loading…
Cancel
Save