diff --git a/source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/ModUpdateDialogViewModel.cs b/source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/ModUpdateDialogViewModel.cs
index 21cd96c1..4767e8ce 100644
--- a/source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/ModUpdateDialogViewModel.cs
+++ b/source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/ModUpdateDialogViewModel.cs
@@ -41,15 +41,26 @@ public class ModUpdateDialogViewModel : ObservableObject
///
public bool CanDownload { get; set; }
- ///
- public ModUpdateDialogViewModel(Updater updater, ModUpdateSummary summary)
+ ///
+ /// Creates the ViewModel using precomputed update info.
+ ///
+ public ModUpdateDialogViewModel(Updater updater, ModUpdateSummary summary, ModUpdate[] updateInfo)
{
Updater = updater;
Summary = summary;
- UpdateInfo = Summary.GetUpdateInfo();
+ UpdateInfo = updateInfo;
TotalSize = UpdateInfo.Sum(x => x.UpdateSize);
- SelectedUpdate = UpdateInfo[0];
- CanDownload = true;
+
+ if (UpdateInfo.Length > 0)
+ {
+ SelectedUpdate = UpdateInfo[0];
+ CanDownload = true;
+ }
+ else
+ {
+ SelectedUpdate = null;
+ CanDownload = false;
+ }
}
///
diff --git a/source/Reloaded.Mod.Launcher.Lib/Update.cs b/source/Reloaded.Mod.Launcher.Lib/Update.cs
index 42021d8a..51a739f5 100644
--- a/source/Reloaded.Mod.Launcher.Lib/Update.cs
+++ b/source/Reloaded.Mod.Launcher.Lib/Update.cs
@@ -130,9 +130,14 @@ public static async Task CheckForModUpdatesAsync()
if (updateDetails.HasUpdates())
{
+ // Fetch update metadata (download sizes, changelogs) on a background thread.
+ // Doing this inside the ViewModel constructor blocked the UI thread on
+ // unbounded network I/O, freezing the launcher on startup. See issue #910.
+ var updateInfo = await updateDetails.GetUpdateInfoAsync();
+
Actions.SynchronizationContext.Send(_ =>
{
- Actions.ShowModUpdateDialog.Invoke(new ModUpdateDialogViewModel(updater, updateDetails));
+ Actions.ShowModUpdateDialog.Invoke(new ModUpdateDialogViewModel(updater, updateDetails, updateInfo));
}, null);
return true;
diff --git a/source/Reloaded.Mod.Loader.Update/Structures/ModUpdateSummary.cs b/source/Reloaded.Mod.Loader.Update/Structures/ModUpdateSummary.cs
index dbd35f34..9e0cfece 100644
--- a/source/Reloaded.Mod.Loader.Update/Structures/ModUpdateSummary.cs
+++ b/source/Reloaded.Mod.Loader.Update/Structures/ModUpdateSummary.cs
@@ -39,61 +39,72 @@ public bool HasUpdates()
///
/// Retrieves info about the individual updates.
+ /// Synchronous wrapper; do not call from the UI thread. Prefer .
///
///
- public ModUpdate[] GetUpdateInfo()
+ public ModUpdate[] GetUpdateInfo() => Task.Run(GetUpdateInfoAsync).GetAwaiter().GetResult();
+
+ ///
+ /// Retrieves info about the individual updates without blocking the calling thread.
+ ///
+ ///
+ public async Task GetUpdateInfoAsync()
{
if (_updates != null)
return _updates;
- _updates = new ModUpdate[ManagerModResultPairs.Count];
- Task.Run(async () =>
+ var updates = new ModUpdate[ManagerModResultPairs.Count];
+ for (var x = 0; x < ManagerModResultPairs.Count; x++)
{
- for (var x = 0; x < ManagerModResultPairs.Count; x++)
+ var resultPairs = ManagerModResultPairs[x];
+ var modName = resultPairs.ModTuple.Config.ModName;
+ var modId = resultPairs.ModTuple.Config.ModId;
+ var oldVersion = resultPairs.ModTuple.Config.ModVersion;
+ var newVersion = resultPairs.Result.LastVersion;
+ var resolver = resultPairs.Manager.Resolver;
+ var updateSize = (long)0;
+ string? changelog = null;
+
+ if (resolver is IPackageResolverDownloadSize hasDownloadSize)
{
- var resultPairs = ManagerModResultPairs[x];
- var modName = resultPairs.ModTuple.Config.ModName;
- var modId = resultPairs.ModTuple.Config.ModId;
- var oldVersion = resultPairs.ModTuple.Config.ModVersion;
- var newVersion = resultPairs.Result.LastVersion;
- var resolver = ((IPackageResolverDownloadSize)resultPairs.Manager.Resolver);
- var updateSize = (long)0;
- string? changelog = null;
-
try
{
- updateSize = await resolver.GetDownloadFileSizeAsync(newVersion!, resultPairs.ModTuple.GetVerificationInfo());
+ updateSize = await hasDownloadSize.GetDownloadFileSizeAsync(newVersion!, resultPairs.ModTuple.GetVerificationInfo()).ConfigureAwait(false);
}
catch (Exception) { /* Ignored */ }
+ }
- // Get changelog from supported resolver.
- if (resolver is IPackageResolverGetLatestReleaseMetadata getMetadata)
+ // Get changelog from supported resolver.
+ if (resolver is IPackageResolverGetLatestReleaseMetadata getMetadata)
+ {
+ try
{
- try
- {
- var releaseMetadata = await getMetadata.GetReleaseMetadataAsync(default);
- var extraData = releaseMetadata?.GetExtraData();
- if (extraData != null)
- changelog = extraData.Changelog;
- }
- catch (Exception) { /* Ignored */ }
+ var releaseMetadata = await getMetadata.GetReleaseMetadataAsync(default).ConfigureAwait(false);
+ var extraData = releaseMetadata?.GetExtraData();
+ if (extraData != null)
+ changelog = extraData.Changelog;
}
+ catch (Exception) { /* Ignored */ }
+ }
- // NuGet has special case, since it doesn't support release metadata but supports changelogs in nuspec.
- if (string.IsNullOrEmpty(changelog) && resolver is NuGetUpdateResolver nugetResolver)
+ // NuGet has special case, since it doesn't support release metadata but supports changelogs in nuspec.
+ if (string.IsNullOrEmpty(changelog) && resolver is NuGetUpdateResolver nugetResolver)
+ {
+ try
{
var copiedSettings = nugetResolver.GetResolverSettings();
var repository = NugetRepository.FromSourceUrl(copiedSettings.NugetRepository!.SourceUrl);
- var reader = await repository.DownloadNuspecReaderAsync(new PackageIdentity(copiedSettings.PackageId, newVersion!));
+ var reader = await repository.DownloadNuspecReaderAsync(new PackageIdentity(copiedSettings.PackageId, newVersion!)).ConfigureAwait(false);
if (reader != null)
changelog = reader?.GetReleaseNotes();
}
-
- _updates[x] = new ModUpdate(modId, NuGetVersion.Parse(oldVersion), newVersion!, updateSize, changelog, modName);
+ catch (Exception) { /* Ignored */ }
}
- }).Wait();
+ updates[x] = new ModUpdate(modId, NuGetVersion.Parse(oldVersion), newVersion!, updateSize, changelog, modName);
+ }
+ _updates = updates;
return _updates;
}