autoupdate: wait longer for periodic check if user dismissed updates

This commit is contained in:
goat 2024-06-20 01:14:40 +02:00
parent 79392230c4
commit d244f7e09e

View file

@ -11,6 +11,7 @@ using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.EventArgs;
using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.Internal; using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.DesignSystem; using Dalamud.Interface.Internal.DesignSystem;
@ -40,7 +41,13 @@ internal class AutoUpdateManager : IServiceType
/// <summary> /// <summary>
/// Time we should wait between scheduled update checks. /// Time we should wait between scheduled update checks.
/// </summary> /// </summary>
private static readonly TimeSpan TimeBetweenUpdateChecks = TimeSpan.FromHours(1.5); private static readonly TimeSpan TimeBetweenUpdateChecks = TimeSpan.FromHours(2);
/// <summary>
/// Time we should wait between scheduled update checks if the user has dismissed the notification,
/// instead of updating. We don't want to spam the user with notifications.
/// </summary>
private static readonly TimeSpan TimeBetweenUpdateChecksIfDismissed = TimeSpan.FromHours(12);
/// <summary> /// <summary>
/// Time we should wait after unblocking to nag the user. /// Time we should wait after unblocking to nag the user.
@ -63,12 +70,13 @@ internal class AutoUpdateManager : IServiceType
private readonly IConsoleVariable<bool> isDryRun; private readonly IConsoleVariable<bool> isDryRun;
private DateTime? loginTime; private DateTime? loginTime;
private DateTime? lastUpdateCheckTime; private DateTime? nextUpdateCheckTime;
private DateTime? unblockedSince; private DateTime? unblockedSince;
private bool hasStartedInitialUpdateThisSession; private bool hasStartedInitialUpdateThisSession;
private IActiveNotification? updateNotification; private IActiveNotification? updateNotification;
private bool notificationHasStartedUpdate; // Used to track if the user has started an update from the notification.
private Task? autoUpdateTask; private Task? autoUpdateTask;
@ -96,7 +104,7 @@ internal class AutoUpdateManager : IServiceType
}); });
console.AddCommand("dalamud.autoupdate.force_check", "Force a check for updates", () => console.AddCommand("dalamud.autoupdate.force_check", "Force a check for updates", () =>
{ {
this.lastUpdateCheckTime = DateTime.Now - TimeBetweenUpdateChecks; this.nextUpdateCheckTime = DateTime.Now + TimeSpan.FromSeconds(5);
return true; return true;
}); });
} }
@ -129,13 +137,13 @@ internal class AutoUpdateManager : IServiceType
}; };
} }
private static void DrawOpenInstallerNotificationButton(bool primary, IActiveNotification notification) private static void DrawOpenInstallerNotificationButton(bool primary, PluginInstallerOpenKind kind, IActiveNotification notification)
{ {
if (primary ? if (primary ?
DalamudComponents.PrimaryButton(Locs.NotificationButtonOpenPluginInstaller) : DalamudComponents.PrimaryButton(Locs.NotificationButtonOpenPluginInstaller) :
DalamudComponents.SecondaryButton(Locs.NotificationButtonOpenPluginInstaller)) DalamudComponents.SecondaryButton(Locs.NotificationButtonOpenPluginInstaller))
{ {
Service<DalamudInterface>.Get().OpenPluginInstallerTo(PluginInstallerOpenKind.UpdateablePlugins); Service<DalamudInterface>.Get().OpenPluginInstallerTo(kind);
notification.DismissNow(); notification.DismissNow();
} }
} }
@ -182,11 +190,9 @@ internal class AutoUpdateManager : IServiceType
// the only time we actually install updates automatically. // the only time we actually install updates automatically.
if (!this.hasStartedInitialUpdateThisSession && DateTime.Now > this.loginTime.Value.Add(UpdateTimeAfterLogin)) if (!this.hasStartedInitialUpdateThisSession && DateTime.Now > this.loginTime.Value.Add(UpdateTimeAfterLogin))
{ {
this.lastUpdateCheckTime = DateTime.Now;
this.hasStartedInitialUpdateThisSession = true; this.hasStartedInitialUpdateThisSession = true;
var currentlyUpdatablePlugins = this.GetAvailablePluginUpdates(DecideUpdateListingRestriction(behavior)); var currentlyUpdatablePlugins = this.GetAvailablePluginUpdates(DecideUpdateListingRestriction(behavior));
if (currentlyUpdatablePlugins.Count == 0) if (currentlyUpdatablePlugins.Count == 0)
{ {
this.IsAutoUpdateComplete = true; this.IsAutoUpdateComplete = true;
@ -198,12 +204,13 @@ internal class AutoUpdateManager : IServiceType
if (behavior == AutoUpdateBehavior.OnlyNotify) if (behavior == AutoUpdateBehavior.OnlyNotify)
{ {
// List all plugins in the notification // List all plugins in the notification
Log.Verbose("Ran initial update, notifying for {Num} plugins", currentlyUpdatablePlugins.Count); Log.Verbose("Running initial auto-update, notifying for {Num} plugins", currentlyUpdatablePlugins.Count);
this.NotifyUpdatesAreAvailable(currentlyUpdatablePlugins); this.NotifyUpdatesAreAvailable(currentlyUpdatablePlugins);
return; return;
} }
Log.Verbose("Ran initial update, updating {Num} plugins", currentlyUpdatablePlugins.Count); Log.Verbose("Running initial auto-update, updating {Num} plugins", currentlyUpdatablePlugins.Count);
this.notificationHasStartedUpdate = true;
this.KickOffAutoUpdates(currentlyUpdatablePlugins); this.KickOffAutoUpdates(currentlyUpdatablePlugins);
return; return;
} }
@ -211,10 +218,13 @@ internal class AutoUpdateManager : IServiceType
// 2. Continuously check for updates while the game is running. We run these every once in a while and // 2. Continuously check for updates while the game is running. We run these every once in a while and
// will only show a notification here that lets people start the update or open the installer. // will only show a notification here that lets people start the update or open the installer.
if (this.config.CheckPeriodicallyForUpdates && if (this.config.CheckPeriodicallyForUpdates &&
this.lastUpdateCheckTime != null && this.nextUpdateCheckTime != null &&
DateTime.Now - this.lastUpdateCheckTime > TimeBetweenUpdateChecks && DateTime.Now > this.nextUpdateCheckTime &&
this.updateNotification == null) this.updateNotification == null)
{ {
this.nextUpdateCheckTime = null;
Log.Verbose("Starting periodic update check");
this.pluginManager.ReloadPluginMastersAsync() this.pluginManager.ReloadPluginMastersAsync()
.ContinueWith( .ContinueWith(
t => t =>
@ -228,8 +238,6 @@ internal class AutoUpdateManager : IServiceType
this.GetAvailablePluginUpdates( this.GetAvailablePluginUpdates(
DecideUpdateListingRestriction(behavior))); DecideUpdateListingRestriction(behavior)));
}); });
this.lastUpdateCheckTime = DateTime.Now;
} }
} }
@ -238,20 +246,34 @@ internal class AutoUpdateManager : IServiceType
if (this.updateNotification != null) if (this.updateNotification != null)
throw new InvalidOperationException("Already showing a notification"); throw new InvalidOperationException("Already showing a notification");
if (this.notificationHasStartedUpdate)
throw new InvalidOperationException("Lost track of notification state");
this.updateNotification = this.notificationManager.AddNotification(notification); this.updateNotification = this.notificationManager.AddNotification(notification);
this.updateNotification.Dismiss += _ => this.updateNotification.Dismiss += _ =>
{ {
this.updateNotification = null; this.updateNotification = null;
this.lastUpdateCheckTime = DateTime.Now;
// If the user just clicked off the notification, we don't want to bother them again for quite a while.
if (this.notificationHasStartedUpdate)
{
this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecks;
Log.Verbose("User started update, next check at {Time}", this.nextUpdateCheckTime);
}
else
{
this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecksIfDismissed;
Log.Verbose("User dismissed update notification, next check at {Time}", this.nextUpdateCheckTime);
}
}; };
return this.updateNotification!; return this.updateNotification!;
} }
private void KickOffAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins) private void KickOffAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins, IActiveNotification? notification = null)
{ {
this.autoUpdateTask = this.autoUpdateTask =
Task.Run(() => this.RunAutoUpdates(updatablePlugins)) Task.Run(() => this.RunAutoUpdates(updatablePlugins, notification))
.ContinueWith(t => .ContinueWith(t =>
{ {
if (t.IsFaulted) if (t.IsFaulted)
@ -268,25 +290,23 @@ internal class AutoUpdateManager : IServiceType
}); });
} }
private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins) private async Task RunAutoUpdates(ICollection<AvailablePluginUpdate> updatablePlugins, IActiveNotification? notification = null)
{ {
Log.Information("Found {UpdatablePluginsCount} plugins to update", updatablePlugins.Count); Log.Information("Found {UpdatablePluginsCount} plugins to update", updatablePlugins.Count);
if (updatablePlugins.Count == 0) if (updatablePlugins.Count == 0)
return; return;
var notification = this.GetBaseNotification(new Notification notification ??= this.GetBaseNotification(new Notification());
{ notification.Title = Locs.NotificationTitleUpdatingPlugins;
Title = Locs.NotificationTitleUpdatingPlugins, notification.Content = Locs.NotificationContentPreparingToUpdate(updatablePlugins.Count);
Content = Locs.NotificationContentPreparingToUpdate(updatablePlugins.Count), notification.Type = NotificationType.Info;
Type = NotificationType.Info, notification.InitialDuration = TimeSpan.MaxValue;
InitialDuration = TimeSpan.MaxValue, notification.ShowIndeterminateIfNoExpiry = false;
ShowIndeterminateIfNoExpiry = false, notification.UserDismissable = false;
UserDismissable = false, notification.Progress = 0;
Progress = 0, notification.Icon = INotificationIcon.From(FontAwesomeIcon.Download);
Icon = INotificationIcon.From(FontAwesomeIcon.Download), notification.Minimized = false;
Minimized = false,
});
var progress = new Progress<PluginManager.PluginUpdateProgress>(); var progress = new Progress<PluginManager.PluginUpdateProgress>();
progress.ProgressChanged += (_, updateProgress) => progress.ProgressChanged += (_, updateProgress) =>
@ -304,7 +324,7 @@ internal class AutoUpdateManager : IServiceType
notification.DrawActions += _ => notification.DrawActions += _ =>
{ {
ImGuiHelpers.ScaledDummy(2); ImGuiHelpers.ScaledDummy(2);
DrawOpenInstallerNotificationButton(true, notification); DrawOpenInstallerNotificationButton(true, PluginInstallerOpenKind.InstalledPlugins, notification);
}; };
// Update the notification to show the final state // Update the notification to show the final state
@ -340,6 +360,8 @@ internal class AutoUpdateManager : IServiceType
{ {
if (updatablePlugins.Count == 0) if (updatablePlugins.Count == 0)
return; return;
this.notificationHasStartedUpdate = false;
var notification = this.GetBaseNotification(new Notification var notification = this.GetBaseNotification(new Notification
{ {
@ -352,19 +374,22 @@ internal class AutoUpdateManager : IServiceType
Icon = INotificationIcon.From(FontAwesomeIcon.Download), Icon = INotificationIcon.From(FontAwesomeIcon.Download),
}); });
notification.DrawActions += _ => void DrawNotificationContent(INotificationDrawArgs args)
{ {
ImGuiHelpers.ScaledDummy(2); ImGuiHelpers.ScaledDummy(2);
if (DalamudComponents.PrimaryButton(Locs.NotificationButtonUpdate)) if (DalamudComponents.PrimaryButton(Locs.NotificationButtonUpdate))
{ {
this.KickOffAutoUpdates(updatablePlugins); notification.DrawActions -= DrawNotificationContent;
notification.DismissNow(); this.KickOffAutoUpdates(updatablePlugins, notification);
this.notificationHasStartedUpdate = true;
} }
ImGui.SameLine(); ImGui.SameLine();
DrawOpenInstallerNotificationButton(false, notification); DrawOpenInstallerNotificationButton(false, PluginInstallerOpenKind.UpdateablePlugins, notification);
}; }
notification.DrawActions += DrawNotificationContent;
} }
private List<AvailablePluginUpdate> GetAvailablePluginUpdates(UpdateListingRestriction restriction) private List<AvailablePluginUpdate> GetAvailablePluginUpdates(UpdateListingRestriction restriction)