add partial background downloading and update prompt
This commit is contained in:
@@ -53,10 +53,6 @@ namespace BlueMine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async void OnStartup(object sender, StartupEventArgs e)
|
private async void OnStartup(object sender, StartupEventArgs e)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
|
||||||
var update = new UpdateManager();
|
|
||||||
await update.CheckAndInstallAsync();
|
|
||||||
#endif
|
|
||||||
await _host.StartAsync();
|
await _host.StartAsync();
|
||||||
var mainWindow = _host.Services.GetRequiredService<MainWindow>();
|
var mainWindow = _host.Services.GetRequiredService<MainWindow>();
|
||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
|
|||||||
@@ -28,5 +28,41 @@
|
|||||||
"Nem elszámolható hívások, email, chat",
|
"Nem elszámolható hívások, email, chat",
|
||||||
"Nem elszámolható telefon, chat, email kommunikáció",
|
"Nem elszámolható telefon, chat, email kommunikáció",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static readonly string UpdateScript = @"# Wait for the main app to close completely
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
|
$exePath = '{currentExe}'
|
||||||
|
$zipPath = '{tempZip}'
|
||||||
|
$destDir = '{appDir}'
|
||||||
|
|
||||||
|
# Retry logic for deletion (in case antivirus or OS holds the lock)
|
||||||
|
$maxRetries = 10
|
||||||
|
$retryCount = 0
|
||||||
|
|
||||||
|
while ($retryCount -lt $maxRetries) {{
|
||||||
|
try {{
|
||||||
|
# Attempt to delete the old executable
|
||||||
|
if (Test-Path $exePath) {{ Remove-Item $exePath -Force -ErrorAction Stop }}
|
||||||
|
break # If successful, exit loop
|
||||||
|
}}
|
||||||
|
catch {{
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
$retryCount++
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
# Unzip the new version
|
||||||
|
Expand-Archive -Path $zipPath -DestinationPath $destDir -Force
|
||||||
|
|
||||||
|
# CLEANUP: Delete the zip
|
||||||
|
Remove-Item $zipPath -Force
|
||||||
|
|
||||||
|
# RESTART: Launch the new executable
|
||||||
|
# 'Start-Process' is the robust way to launch detached processes in PS
|
||||||
|
Start-Process -FilePath $exePath -WorkingDirectory $destDir
|
||||||
|
|
||||||
|
# SELF-DESTRUCT: Remove this script
|
||||||
|
Remove-Item -LiteralPath $MyInvocation.MyCommand.Path -Force";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,6 +256,15 @@
|
|||||||
|
|
||||||
<ui:ProgressRing x:Name="progressRing" Grid.Row="4" Height="10" Width="10" Margin="10" HorizontalAlignment="Left" IsIndeterminate="True" />
|
<ui:ProgressRing x:Name="progressRing" Grid.Row="4" Height="10" Width="10" Margin="10" HorizontalAlignment="Left" IsIndeterminate="True" />
|
||||||
<ui:TextBlock x:Name="statusTextBlock" Grid.Row="4" Grid.ColumnSpan="6" FontSize="8" Text="Staus: OK" Margin="30, 10, 10, 10" />
|
<ui:TextBlock x:Name="statusTextBlock" Grid.Row="4" Grid.ColumnSpan="6" FontSize="8" Text="Staus: OK" Margin="30, 10, 10, 10" />
|
||||||
<ui:TextBlock x:Name="versionTextBlock" Grid.Row="4" Grid.Column="2" HorizontalAlignment="Right" FontSize="8" Text="0.0.0" Margin="10" />
|
<Grid Grid.Row="4" Grid.Column="2" >
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="1*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ui:SymbolIcon x:Name="updateIcon" Symbol="ArrowCircleUp24" Grid.Column="1" Margin="10" Filled="True"
|
||||||
|
Foreground="{ui:ThemeResource AccentTextFillColorPrimaryBrush}" Visibility="Hidden" />
|
||||||
|
<ui:TextBlock x:Name="versionTextBlock" Grid.Column="2" HorizontalAlignment="Right" FontSize="8" Text="0.0.0" Margin="10" />
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:FluentWindow>
|
</ui:FluentWindow>
|
||||||
|
|||||||
@@ -45,9 +45,35 @@ namespace BlueMine
|
|||||||
Task getHoursTask = GetHours();
|
Task getHoursTask = GetHours();
|
||||||
|
|
||||||
await Task.WhenAll(loadIssuesTask, getHoursTask);
|
await Task.WhenAll(loadIssuesTask, getHoursTask);
|
||||||
|
#if !DEBUG
|
||||||
|
if(await UpdateManager.IsUpdateAvailable())
|
||||||
|
{
|
||||||
|
updateIcon.Visibility = Visibility.Visible;
|
||||||
|
UpdateManager.DownloadCompleted += UpdateManager_DownloadCompleted;
|
||||||
|
await UpdateManager.DownloadUpdateAsync();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void UpdateManager_DownloadCompleted()
|
||||||
|
{
|
||||||
|
await Dispatcher.Invoke(async () =>
|
||||||
|
{
|
||||||
|
var result = await new Wpf.Ui.Controls.MessageBox
|
||||||
|
{
|
||||||
|
Title = "Frissítés elérhető",
|
||||||
|
Content = "Szeretnél most frissíteni?",
|
||||||
|
PrimaryButtonText = "Frissítés",
|
||||||
|
SecondaryButtonText = "Később",
|
||||||
|
IsCloseButtonEnabled = false,
|
||||||
|
}.ShowDialogAsync();
|
||||||
|
|
||||||
|
if (result == Wpf.Ui.Controls.MessageBoxResult.Primary)
|
||||||
|
await UpdateManager.PerformUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void CalendarButtonClicked(object sender, RoutedEventArgs e)
|
private void CalendarButtonClicked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
flyoutCalendar.IsOpen = true;
|
flyoutCalendar.IsOpen = true;
|
||||||
|
|||||||
@@ -2,33 +2,69 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Windows.Media.Protection.PlayReady;
|
||||||
|
|
||||||
namespace Blueberry
|
namespace Blueberry
|
||||||
{
|
{
|
||||||
public class UpdateManager
|
public static class UpdateManager
|
||||||
{
|
{
|
||||||
private const string releaseUrl = "https://git.technopunk.space/api/v1/repos/tomi/Blueberry/releases/latest";
|
private const string releaseUrl = "https://git.technopunk.space/api/v1/repos/tomi/Blueberry/releases/latest";
|
||||||
public const string CurrentVersion = "0.1.1";
|
public const string CurrentVersion = "0.1.2";
|
||||||
|
private static readonly string AppDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
private static readonly HttpClient client = new();
|
||||||
|
public delegate void DownloadCompletedEventArgs();
|
||||||
|
public static event DownloadCompletedEventArgs DownloadCompleted;
|
||||||
|
private static bool isDownloading = false;
|
||||||
|
|
||||||
public async Task CheckAndInstallAsync()
|
public static async Task<bool> IsUpdateAvailable()
|
||||||
{
|
{
|
||||||
using var client = new HttpClient();
|
var json = await client.GetStringAsync(releaseUrl);
|
||||||
|
var release = JsonSerializer.Deserialize<Root>(json);
|
||||||
|
return release != null && release.tag_name != CurrentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DownloadUpdateAsync()
|
||||||
|
{
|
||||||
client.DefaultRequestHeaders.Add("User-Agent", "Blueberry-Updater");
|
client.DefaultRequestHeaders.Add("User-Agent", "Blueberry-Updater");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = client.GetStringAsync(releaseUrl).ConfigureAwait(false).GetAwaiter().GetResult();
|
// 1. Use await here, don't block
|
||||||
|
var json = await client.GetStringAsync(releaseUrl);
|
||||||
var release = JsonSerializer.Deserialize<Root>(json);
|
var release = JsonSerializer.Deserialize<Root>(json);
|
||||||
|
|
||||||
if (release == null)
|
if (release == null) return;
|
||||||
throw new NullReferenceException();
|
|
||||||
|
|
||||||
if (release.tag_name != CurrentVersion)
|
if (release.tag_name != CurrentVersion)
|
||||||
{
|
{
|
||||||
var file = release.assets.Find(x => x.name.Contains(".zip")) ?? throw new NullReferenceException();
|
var file = release.assets.Find(x => x.name.Contains(".zip"));
|
||||||
string downloadUrl = file.browser_download_url;
|
if (file == null) return;
|
||||||
await PerformUpdate(client, downloadUrl);
|
|
||||||
|
string zipPath = Path.Combine(AppDir, "blueberry_update.zip");
|
||||||
|
long offset = 0;
|
||||||
|
|
||||||
|
if (File.Exists(zipPath))
|
||||||
|
{
|
||||||
|
long localSize = new FileInfo(zipPath).Length;
|
||||||
|
|
||||||
|
if (localSize == file.size)
|
||||||
|
{
|
||||||
|
DownloadCompleted?.Invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localSize > file.size)
|
||||||
|
{
|
||||||
|
File.Delete(zipPath);
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset = localSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isDownloading)
|
||||||
|
await Download(client, file.browser_download_url, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
@@ -37,55 +73,45 @@ namespace Blueberry
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PerformUpdate(HttpClient client, string url)
|
private static async Task Download(HttpClient client, string url, long offset = 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
isDownloading = true;
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
|
||||||
|
// If we have an offset, ask the server for the rest of the file
|
||||||
|
if (offset > 0)
|
||||||
|
{
|
||||||
|
request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(offset, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
// If offset > 0, we APPEND. If 0, we CREATE/OVERWRITE.
|
||||||
|
var fileMode = offset > 0 ? FileMode.Append : FileMode.Create;
|
||||||
|
string filePath = Path.Combine(AppDir, "blueberry_update.zip");
|
||||||
|
|
||||||
|
using var contentStream = await response.Content.ReadAsStreamAsync();
|
||||||
|
using var fileStream = new FileStream(filePath, fileMode, FileAccess.Write, FileShare.None);
|
||||||
|
|
||||||
|
await contentStream.CopyToAsync(fileStream);
|
||||||
|
isDownloading = false;
|
||||||
|
DownloadCompleted?.Invoke();
|
||||||
|
} catch (Exception)
|
||||||
|
{
|
||||||
|
isDownloading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task PerformUpdate()
|
||||||
{
|
{
|
||||||
string tempZip = Path.Combine(Path.GetTempPath(), "blueberry_update.zip");
|
string tempZip = Path.Combine(Path.GetTempPath(), "blueberry_update.zip");
|
||||||
string currentExe = Process.GetCurrentProcess().MainModule.FileName;
|
string currentExe = Process.GetCurrentProcess().MainModule.FileName;
|
||||||
string appDir = AppDomain.CurrentDomain.BaseDirectory;
|
string appDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
// 1. Download
|
string psScript = Constants.UpdateScript;
|
||||||
var data = await client.GetByteArrayAsync(url);
|
|
||||||
File.WriteAllBytes(tempZip, data);
|
|
||||||
|
|
||||||
// 2. Create a temporary batch script to handle the swap
|
|
||||||
// We use a small delay (timeout) to allow the main app to close fully
|
|
||||||
string psScript = $@"
|
|
||||||
# Wait for the main app to close completely
|
|
||||||
Start-Sleep -Seconds 2
|
|
||||||
|
|
||||||
$exePath = '{currentExe}'
|
|
||||||
$zipPath = '{tempZip}'
|
|
||||||
$destDir = '{appDir}'
|
|
||||||
|
|
||||||
# Retry logic for deletion (in case antivirus or OS holds the lock)
|
|
||||||
$maxRetries = 10
|
|
||||||
$retryCount = 0
|
|
||||||
|
|
||||||
while ($retryCount -lt $maxRetries) {{
|
|
||||||
try {{
|
|
||||||
# Attempt to delete the old executable
|
|
||||||
if (Test-Path $exePath) {{ Remove-Item $exePath -Force -ErrorAction Stop }}
|
|
||||||
break # If successful, exit loop
|
|
||||||
}}
|
|
||||||
catch {{
|
|
||||||
Start-Sleep -Milliseconds 500
|
|
||||||
$retryCount++
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
|
|
||||||
# Unzip the new version
|
|
||||||
Expand-Archive -Path $zipPath -DestinationPath $destDir -Force
|
|
||||||
|
|
||||||
# CLEANUP: Delete the zip
|
|
||||||
Remove-Item $zipPath -Force
|
|
||||||
|
|
||||||
# RESTART: Launch the new executable
|
|
||||||
# 'Start-Process' is the robust way to launch detached processes in PS
|
|
||||||
Start-Process -FilePath $exePath -WorkingDirectory $destDir
|
|
||||||
|
|
||||||
# SELF-DESTRUCT: Remove this script
|
|
||||||
Remove-Item -LiteralPath $MyInvocation.MyCommand.Path -Force
|
|
||||||
";
|
|
||||||
string psPath = Path.Combine(Path.GetTempPath(), "blueberry_updater.ps1");
|
string psPath = Path.Combine(Path.GetTempPath(), "blueberry_updater.ps1");
|
||||||
File.WriteAllText(psPath, psScript);
|
File.WriteAllText(psPath, psScript);
|
||||||
|
|
||||||
@@ -114,6 +140,7 @@ Remove-Item -LiteralPath $MyInvocation.MyCommand.Path -Force
|
|||||||
public class Asset
|
public class Asset
|
||||||
{
|
{
|
||||||
public string name { get; set; }
|
public string name { get; set; }
|
||||||
|
public long size { get; set; }
|
||||||
public string browser_download_url { get; set; }
|
public string browser_download_url { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user