diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index 5c2378f68..4df38d6df 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -249,7 +249,7 @@ internal sealed class DalamudConfiguration : IInternalDisposableService /// Gets or sets a value indicating whether or not docking should be globally enabled in ImGui. /// public bool IsDocking { get; set; } - + /// /// Gets or sets a value indicating whether or not plugin user interfaces should trigger sound effects. /// This setting is effected by the in-game "System Sounds" option and volume. @@ -484,10 +484,15 @@ public string EffectiveLanguage public AutoUpdateBehavior? AutoUpdateBehavior { get; set; } = null; /// - /// Gets or sets a value indicating whether or not users should be notified regularly about pending updates. + /// Gets or sets a value indicating whether users should be notified regularly about pending updates. /// public bool CheckPeriodicallyForUpdates { get; set; } = true; + /// + /// Gets or sets a value indicating whether users should be notified about updates in chat. + /// + public bool SendUpdateNotificationToChat { get; set; } = false; + /// /// Load a configuration from the provided path. /// @@ -504,7 +509,7 @@ public static DalamudConfiguration Load(string path, ReliableFileStorage fs) { deserialized = JsonConvert.DeserializeObject(text, SerializerSettings); - + // If this reads as null, the file was empty, that's no good if (deserialized == null) throw new Exception("Read config was null."); @@ -530,7 +535,7 @@ public static DalamudConfiguration Load(string path, ReliableFileStorage fs) { Log.Error(e, "Failed to set defaults for DalamudConfiguration"); } - + return deserialized; } @@ -549,7 +554,7 @@ public void ForceSave() { this.Save(); } - + /// void IInternalDisposableService.DisposeService() { @@ -595,14 +600,14 @@ private void SetDefaults() this.ReduceMotions = winAnimEnabled == 0; } } - + // Migrate old auto-update setting to new auto-update behavior this.AutoUpdateBehavior ??= this.AutoUpdatePlugins ? Plugin.Internal.AutoUpdate.AutoUpdateBehavior.UpdateAll : Plugin.Internal.AutoUpdate.AutoUpdateBehavior.OnlyNotify; #pragma warning restore CS0618 } - + private void Save() { ThreadSafety.AssertMainThread(); diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index f51f7e08b..f98d372eb 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -5,7 +5,7 @@ - 11.0.2.0 + 11.0.3.0 XIV Launcher addon framework $(DalamudVersion) $(DalamudVersion) diff --git a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs index d2b1b4a36..10f177590 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs @@ -272,15 +272,14 @@ public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector4 /// Width. public static float GetIconButtonWithTextWidth(FontAwesomeIcon icon, string text) { + Vector2 iconSize; using (ImRaii.PushFont(UiBuilder.IconFont)) { - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - - var textSize = ImGui.CalcTextSize(text); - - var iconPadding = 3 * ImGuiHelpers.GlobalScale; - - return iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; + iconSize = ImGui.CalcTextSize(icon.ToIconString()); } + + var textSize = ImGui.CalcTextSize(text); + var iconPadding = 3 * ImGuiHelpers.GlobalScale; + return iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; } } diff --git a/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs b/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs index f0ce6bc82..3d31bbda6 100644 --- a/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs +++ b/Dalamud/Interface/Internal/DesignSystem/DalamudComponents.PluginPicker.cs @@ -32,19 +32,21 @@ internal static uint DrawPluginPicker(string id, ref string pickerSearch, Action var pm = Service.GetNullable(); if (pm == null) return 0; - + var addPluginToProfilePopupId = ImGui.GetID(id); using var popup = ImRaii.Popup(id); if (popup.Success) { var width = ImGuiHelpers.GlobalScale * 300; - + ImGui.SetNextItemWidth(width); ImGui.InputTextWithHint("###pluginPickerSearch", Locs.SearchHint, ref pickerSearch, 255); var currentSearchString = pickerSearch; - if (ImGui.BeginListBox("###pluginPicker", new Vector2(width, width - 80))) + + using var listBox = ImRaii.ListBox("###pluginPicker", new Vector2(width, width - 80)); + if (listBox.Success) { // TODO: Plugin searching should be abstracted... installer and this should use the same search var plugins = pm.InstalledPlugins.Where( @@ -53,19 +55,15 @@ internal static uint DrawPluginPicker(string id, ref string pickerSearch, Action currentSearchString, StringComparison.InvariantCultureIgnoreCase))) .Where(pluginFiltered ?? (_ => true)); - + foreach (var plugin in plugins) { - using var disabled2 = - ImRaii.Disabled(pluginDisabled(plugin)); - + using var disabled2 = ImRaii.Disabled(pluginDisabled(plugin)); if (ImGui.Selectable($"{plugin.Manifest.Name}{(plugin is LocalDevPlugin ? "(dev plugin)" : string.Empty)}###selector{plugin.Manifest.InternalName}")) { onClicked(plugin); } } - - ImGui.EndListBox(); } } diff --git a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs index 77c79c96d..9356131ad 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Tabs/SettingsTabAutoUpdate.cs @@ -23,10 +23,11 @@ public class SettingsTabAutoUpdates : SettingsTab { private AutoUpdateBehavior behavior; private bool checkPeriodically; + private bool chatNotification; private string pickerSearch = string.Empty; private List autoUpdatePreferences = []; - - public override SettingsEntry[] Entries { get; } = Array.Empty(); + + public override SettingsEntry[] Entries { get; } = []; public override string Title => Loc.Localize("DalamudSettingsAutoUpdates", "Auto-Updates"); @@ -36,15 +37,15 @@ public override void Draw() "Dalamud can update your plugins automatically, making sure that you always " + "have the newest features and bug fixes. You can choose when and how auto-updates are run here.")); ImGuiHelpers.ScaledDummy(2); - + ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsAutoUpdateDisclaimer1", "You can always update your plugins manually by clicking the update button in the plugin list. " + "You can also opt into updates for specific plugins by right-clicking them and selecting \"Always auto-update\".")); ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsAutoUpdateDisclaimer2", "Dalamud will only notify you about updates while you are idle.")); - + ImGuiHelpers.ScaledDummy(8); - + ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudWhite, Loc.Localize("DalamudSettingsAutoUpdateBehavior", "When the game starts...")); var behaviorInt = (int)this.behavior; @@ -62,20 +63,21 @@ public override void Draw() "These updates are not reviewed by the Dalamud team and may contain malicious code."); ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudOrange, warning); } - + ImGuiHelpers.ScaledDummy(8); - + + ImGui.Checkbox(Loc.Localize("DalamudSettingsAutoUpdateChatMessage", "Show notification about updates available in chat"), ref this.chatNotification); ImGui.Checkbox(Loc.Localize("DalamudSettingsAutoUpdatePeriodically", "Periodically check for new updates while playing"), ref this.checkPeriodically); ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsAutoUpdatePeriodicallyHint", "Plugins won't update automatically after startup, you will only receive a notification while you are not actively playing.")); - + ImGuiHelpers.ScaledDummy(5); ImGui.Separator(); ImGuiHelpers.ScaledDummy(5); - + ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudWhite, Loc.Localize("DalamudSettingsAutoUpdateOptedIn", "Per-plugin overrides")); - + ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudWhite, Loc.Localize("DalamudSettingsAutoUpdateOverrideHint", "Here, you can choose to receive or not to receive updates for specific plugins. " + "This will override the settings above for the selected plugins.")); @@ -83,25 +85,25 @@ public override void Draw() if (this.autoUpdatePreferences.Count == 0) { ImGuiHelpers.ScaledDummy(20); - + using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey)) { ImGuiHelpers.CenteredText(Loc.Localize("DalamudSettingsAutoUpdateOptedInHint2", "You don't have auto-update rules for any plugins.")); } - + ImGuiHelpers.ScaledDummy(2); } else { ImGuiHelpers.ScaledDummy(5); - + var pic = Service.Get(); var windowSize = ImGui.GetWindowSize(); var pluginLineHeight = 32 * ImGuiHelpers.GlobalScale; Guid? wantRemovePluginGuid = null; - + foreach (var preference in this.autoUpdatePreferences) { var pmPlugin = Service.Get().InstalledPlugins @@ -120,11 +122,12 @@ public override void Draw() if (pmPlugin.IsDev) { ImGui.SetCursorPos(cursorBeforeIcon); - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.7f); - ImGui.Image(pic.DevPluginIcon.ImGuiHandle, new Vector2(pluginLineHeight)); - ImGui.PopStyleVar(); + using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, 0.7f)) + { + ImGui.Image(pic.DevPluginIcon.ImGuiHandle, new Vector2(pluginLineHeight)); + } } - + ImGui.SameLine(); var text = $"{pmPlugin.Name}{(pmPlugin.IsDev ? " (dev plugin" : string.Empty)}"; @@ -147,7 +150,7 @@ public override void Draw() ImGui.SetCursorPosY(ImGui.GetCursorPosY() + (pluginLineHeight / 2) - (textHeight.Y / 2)); ImGui.TextUnformatted(text); - + ImGui.SetCursorPos(before); } @@ -166,19 +169,18 @@ string OptKindToString(AutoUpdatePreference.OptKind kind) } ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 250); - if (ImGui.BeginCombo( - $"###autoUpdateBehavior{preference.WorkingPluginId}", - OptKindToString(preference.Kind))) + using (var combo = ImRaii.Combo($"###autoUpdateBehavior{preference.WorkingPluginId}", OptKindToString(preference.Kind))) { - foreach (var kind in Enum.GetValues()) + if (combo.Success) { - if (ImGui.Selectable(OptKindToString(kind))) + foreach (var kind in Enum.GetValues()) { - preference.Kind = kind; + if (ImGui.Selectable(OptKindToString(kind))) + { + preference.Kind = kind; + } } } - - ImGui.EndCombo(); } ImGui.SameLine(); @@ -193,7 +195,7 @@ string OptKindToString(AutoUpdatePreference.OptKind kind) if (ImGui.IsItemHovered()) ImGui.SetTooltip(Loc.Localize("DalamudSettingsAutoUpdateOptInRemove", "Remove this override")); } - + if (wantRemovePluginGuid != null) { this.autoUpdatePreferences.RemoveAll(x => x.WorkingPluginId == wantRemovePluginGuid); @@ -205,19 +207,19 @@ void OnPluginPicked(LocalPlugin plugin) var id = plugin.EffectiveWorkingPluginId; if (id == Guid.Empty) throw new InvalidOperationException("Plugin ID is empty."); - + this.autoUpdatePreferences.Add(new AutoUpdatePreference(id)); } - + bool IsPluginDisabled(LocalPlugin plugin) => this.autoUpdatePreferences.Any(x => x.WorkingPluginId == plugin.EffectiveWorkingPluginId); - + bool IsPluginFiltered(LocalPlugin plugin) => !plugin.IsDev; - + var pickerId = DalamudComponents.DrawPluginPicker( "###autoUpdatePicker", ref this.pickerSearch, OnPluginPicked, IsPluginDisabled, IsPluginFiltered); - + const FontAwesomeIcon addButtonIcon = FontAwesomeIcon.Plus; var addButtonText = Loc.Localize("DalamudSettingsAutoUpdateOptInAdd", "Add new override"); ImGuiHelpers.CenterCursorFor(ImGuiComponents.GetIconButtonWithTextWidth(addButtonIcon, addButtonText)); @@ -235,20 +237,22 @@ public override void Load() var configuration = Service.Get(); this.behavior = configuration.AutoUpdateBehavior ?? AutoUpdateBehavior.None; + this.chatNotification = configuration.SendUpdateNotificationToChat; this.checkPeriodically = configuration.CheckPeriodicallyForUpdates; this.autoUpdatePreferences = configuration.PluginAutoUpdatePreferences; - + base.Load(); } public override void Save() { var configuration = Service.Get(); - + configuration.AutoUpdateBehavior = this.behavior; + configuration.SendUpdateNotificationToChat = this.chatNotification; configuration.CheckPeriodicallyForUpdates = this.checkPeriodically; configuration.PluginAutoUpdatePreferences = this.autoUpdatePreferences; - + base.Save(); } } diff --git a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs index c25ec4ee4..99850ddb4 100644 --- a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs +++ b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs @@ -9,6 +9,10 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; +using Dalamud.Game.Gui; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification.EventArgs; @@ -31,17 +35,17 @@ namespace Dalamud.Plugin.Internal.AutoUpdate; internal class AutoUpdateManager : IServiceType { private static readonly ModuleLog Log = new("AUTOUPDATE"); - + /// /// Time we should wait after login to update. /// private static readonly TimeSpan UpdateTimeAfterLogin = TimeSpan.FromSeconds(20); - + /// /// Time we should wait between scheduled update checks. /// private static readonly TimeSpan TimeBetweenUpdateChecks = TimeSpan.FromHours(2); - + /// /// 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. @@ -56,28 +60,30 @@ internal class AutoUpdateManager : IServiceType [ServiceManager.ServiceDependency] private readonly PluginManager pluginManager = Service.Get(); - + [ServiceManager.ServiceDependency] private readonly DalamudConfiguration config = Service.Get(); - + [ServiceManager.ServiceDependency] private readonly NotificationManager notificationManager = Service.Get(); - + [ServiceManager.ServiceDependency] private readonly DalamudInterface dalamudInterface = Service.Get(); - + private readonly IConsoleVariable isDryRun; - + private DateTime? loginTime; private DateTime? nextUpdateCheckTime; private DateTime? unblockedSince; - + private bool hasStartedInitialUpdateThisSession; private IActiveNotification? updateNotification; - + private Task? autoUpdateTask; - + + private readonly Task openInstallerWindowLink; + /// /// Initializes a new instance of the class. /// @@ -92,7 +98,18 @@ public AutoUpdateManager(ConsoleManager console) t.Result.Logout += (int type, int code) => this.OnLogout(); }); Service.GetAsync().ContinueWith(t => { t.Result.Update += this.OnUpdate; }); - + + this.openInstallerWindowLink = + Service.GetAsync().ContinueWith( + chatGuiTask => chatGuiTask.Result.AddChatLinkHandler( + "Dalamud", + 1001, + (_, _) => + { + Service.GetNullable()?.OpenPluginInstallerTo(PluginInstallerOpenKind.InstalledPlugins); + })); + + this.isDryRun = console.AddVariable("dalamud.autoupdate.dry_run", "Simulate updates instead", false); console.AddCommand("dalamud.autoupdate.trigger_login", "Trigger a login event", () => { @@ -106,36 +123,36 @@ public AutoUpdateManager(ConsoleManager console) return true; }); } - + private enum UpdateListingRestriction { Unrestricted, AllowNone, AllowMainRepo, } - + /// /// Gets a value indicating whether or not auto-updates have already completed this session. /// public bool IsAutoUpdateComplete { get; private set; } - + /// /// Gets the time of the next scheduled update check. /// public DateTime? NextUpdateCheckTime => this.nextUpdateCheckTime; - + /// /// Gets the time the auto-update was unblocked. /// public DateTime? UnblockedSince => this.unblockedSince; - + private static UpdateListingRestriction DecideUpdateListingRestriction(AutoUpdateBehavior behavior) { return behavior switch { // We don't generally allow any updates in this mode, but specific opt-ins. AutoUpdateBehavior.None => UpdateListingRestriction.AllowNone, - + // If we're only notifying, I guess it's fine to list all plugins. AutoUpdateBehavior.OnlyNotify => UpdateListingRestriction.Unrestricted, @@ -144,7 +161,7 @@ private static UpdateListingRestriction DecideUpdateListingRestriction(AutoUpdat _ => throw new ArgumentOutOfRangeException(nameof(behavior), behavior, null), }; } - + private static void DrawOpenInstallerNotificationButton(bool primary, PluginInstallerOpenKind kind, IActiveNotification notification) { if (primary ? @@ -179,7 +196,7 @@ private void OnUpdate(IFramework framework) this.updateNotification = null; } } - + // If we're blocked, we don't do anything. if (!isUnblocked) return; @@ -199,16 +216,16 @@ private void OnUpdate(IFramework framework) if (!this.hasStartedInitialUpdateThisSession && DateTime.Now > this.loginTime.Value.Add(UpdateTimeAfterLogin)) { this.hasStartedInitialUpdateThisSession = true; - + var currentlyUpdatablePlugins = this.GetAvailablePluginUpdates(DecideUpdateListingRestriction(behavior)); if (currentlyUpdatablePlugins.Count == 0) { this.IsAutoUpdateComplete = true; this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecks; - + return; } - + // TODO: This is not 100% what we want... Plugins that are opted-in should be updated regardless of the behavior, // and we should show a notification for the others afterwards. if (behavior == AutoUpdateBehavior.OnlyNotify) @@ -241,6 +258,7 @@ private void OnUpdate(IFramework framework) Log.Error(t.Exception!, "Failed to reload plugin masters for auto-update"); } + Log.Verbose($"Available Updates: {string.Join(", ", this.pluginManager.UpdatablePlugins.Select(s => s.UpdateManifest.InternalName))}"); var updatable = this.GetAvailablePluginUpdates( DecideUpdateListingRestriction(behavior)); @@ -252,7 +270,7 @@ private void OnUpdate(IFramework framework) { this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecks; Log.Verbose( - "Auto update found nothing to do, next update at {Time}", + "Auto update found nothing to do, next update at {Time}", this.nextUpdateCheckTime); } }); @@ -263,13 +281,13 @@ private IActiveNotification GetBaseNotification(Notification notification) { if (this.updateNotification != null) throw new InvalidOperationException("Already showing a notification"); - + this.updateNotification = this.notificationManager.AddNotification(notification); this.updateNotification.Dismiss += _ => { this.updateNotification = null; - + // Schedule the next update opportunistically for when this closes. this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecks; }; @@ -291,7 +309,7 @@ private void KickOffAutoUpdates(ICollection updatablePlug { Log.Warning("Auto-update task was canceled"); } - + this.autoUpdateTask = null; this.IsAutoUpdateComplete = true; }); @@ -321,20 +339,20 @@ private async Task RunAutoUpdates(ICollection updatablePl notification.Content = Locs.NotificationContentUpdating(updateProgress.CurrentPluginManifest.Name); notification.Progress = (float)updateProgress.PluginsProcessed / updateProgress.TotalPlugins; }; - + var pluginStates = (await this.pluginManager.UpdatePluginsAsync(updatablePlugins, this.isDryRun.Value, true, progress)).ToList(); this.pluginManager.PrintUpdatedPlugins(pluginStates, Loc.Localize("DalamudPluginAutoUpdate", "The following plugins were auto-updated:")); notification.Progress = 1; notification.UserDismissable = true; notification.HardExpiry = DateTime.Now.AddSeconds(30); - + notification.DrawActions += _ => { ImGuiHelpers.ScaledDummy(2); DrawOpenInstallerNotificationButton(true, PluginInstallerOpenKind.InstalledPlugins, notification); }; - + // Update the notification to show the final state if (pluginStates.All(x => x.Status == PluginUpdateStatus.StatusKind.Success)) { @@ -342,7 +360,7 @@ private async Task RunAutoUpdates(ICollection updatablePl // Janky way to make sure the notification does not change before it's minimized... await Task.Delay(500); - + notification.Title = Locs.NotificationTitleUpdatesSuccessful; notification.MinimizedText = Locs.NotificationContentUpdatesSuccessfulMinimized; notification.Type = NotificationType.Success; @@ -354,11 +372,11 @@ private async Task RunAutoUpdates(ICollection updatablePl notification.MinimizedText = Locs.NotificationContentUpdatesFailedMinimized; notification.Type = NotificationType.Error; notification.Content = Locs.NotificationContentUpdatesFailed; - + var failedPlugins = pluginStates .Where(x => x.Status != PluginUpdateStatus.StatusKind.Success) .Select(x => x.Name).ToList(); - + notification.Content += "\n" + Locs.NotificationContentFailedPlugins(failedPlugins); } } @@ -367,7 +385,7 @@ private void NotifyUpdatesAreAvailable(ICollection updata { if (updatablePlugins.Count == 0) return; - + var notification = this.GetBaseNotification(new Notification { Title = Locs.NotificationTitleUpdatesAvailable, @@ -400,16 +418,44 @@ void DrawNotificationContent(INotificationDrawArgs args) notification.Dismiss += args => { if (args.Reason != NotificationDismissReason.Manual) return; - + this.nextUpdateCheckTime = DateTime.Now + TimeBetweenUpdateChecksIfDismissed; Log.Verbose("User dismissed update notification, next check at {Time}", this.nextUpdateCheckTime); }; + + // Send out a chat message only if the user requested so + if (!this.config.SendUpdateNotificationToChat) + return; + + var chatGui = Service.GetNullable(); + if (chatGui == null) + { + Log.Verbose("Unable to get chat gui, discard notification for chat."); + return; + } + + chatGui.Print(new XivChatEntry + { + Message = new SeString(new List + { + new TextPayload(Locs.NotificationContentUpdatesAvailableMinimized(updatablePlugins.Count)), + new TextPayload(" ["), + new UIForegroundPayload(500), + this.openInstallerWindowLink.Result, + new TextPayload(Loc.Localize("DalamudInstallerHelp", "Open the plugin installer")), + RawPayload.LinkTerminator, + new UIForegroundPayload(0), + new TextPayload("]"), + }), + + Type = XivChatType.Urgent, + }); } - + private List GetAvailablePluginUpdates(UpdateListingRestriction restriction) { var optIns = this.config.PluginAutoUpdatePreferences.ToArray(); - + // Get all of our updatable plugins and do some initial filtering that must apply to all plugins. var updateablePlugins = this.pluginManager.UpdatablePlugins .Where( @@ -423,14 +469,14 @@ private List GetAvailablePluginUpdates(UpdateListingRestr bool FilterPlugin(AvailablePluginUpdate availablePluginUpdate) { var optIn = optIns.FirstOrDefault(x => x.WorkingPluginId == availablePluginUpdate.InstalledPlugin.EffectiveWorkingPluginId); - + // If this is an opt-out, we don't update. if (optIn is { Kind: AutoUpdatePreference.OptKind.NeverUpdate }) return false; if (restriction == UpdateListingRestriction.AllowNone && optIn is not { Kind: AutoUpdatePreference.OptKind.AlwaysUpdate }) return false; - + if (restriction == UpdateListingRestriction.AllowMainRepo && availablePluginUpdate.InstalledPlugin.IsThirdParty) return false; @@ -442,7 +488,7 @@ private void OnLogin() { this.loginTime = DateTime.Now; } - + private void OnLogout() { this.loginTime = null; @@ -452,7 +498,7 @@ private bool CanUpdateOrNag() { var condition = Service.Get(); return this.IsPluginManagerReady() && - !this.dalamudInterface.IsPluginInstallerOpen && + !this.dalamudInterface.IsPluginInstallerOpen && condition.OnlyAny(ConditionFlag.NormalConditions, ConditionFlag.Jumping, ConditionFlag.Mounted, @@ -469,21 +515,21 @@ private static class Locs public static string NotificationButtonOpenPluginInstaller => Loc.Localize("AutoUpdateOpenPluginInstaller", "Open installer"); public static string NotificationButtonUpdate => Loc.Localize("AutoUpdateUpdate", "Update"); - + public static string NotificationTitleUpdatesAvailable => Loc.Localize("AutoUpdateUpdatesAvailable", "Updates available!"); - + public static string NotificationTitleUpdatesSuccessful => Loc.Localize("AutoUpdateUpdatesSuccessful", "Updates successful!"); - + public static string NotificationTitleUpdatingPlugins => Loc.Localize("AutoUpdateUpdatingPlugins", "Updating plugins..."); - + public static string NotificationTitleUpdatesFailed => Loc.Localize("AutoUpdateUpdatesFailed", "Updates failed!"); - + public static string NotificationContentUpdatesSuccessful => Loc.Localize("AutoUpdateUpdatesSuccessfulContent", "All plugins have been updated successfully."); - + public static string NotificationContentUpdatesSuccessfulMinimized => Loc.Localize("AutoUpdateUpdatesSuccessfulContentMinimized", "Plugins updated successfully."); - + public static string NotificationContentUpdatesFailed => Loc.Localize("AutoUpdateUpdatesFailedContent", "Some plugins failed to update. Please check the plugin installer for more information."); - + public static string NotificationContentUpdatesFailedMinimized => Loc.Localize("AutoUpdateUpdatesFailedContentMinimized", "Plugins failed to update."); public static string NotificationContentUpdatesAvailable(ICollection updatablePlugins) @@ -497,20 +543,20 @@ public static string NotificationContentUpdatesAvailable(ICollection x.InstalledPlugin.Manifest.Name)); - + public static string NotificationContentUpdatesAvailableMinimized(int numUpdates) => numUpdates == 1 ? - Loc.Localize("AutoUpdateUpdatesAvailableContentMinimizedSingular", "1 plugin update available") : + Loc.Localize("AutoUpdateUpdatesAvailableContentMinimizedSingular", "1 plugin update available") : string.Format(Loc.Localize("AutoUpdateUpdatesAvailableContentMinimizedPlural", "{0} plugin updates available"), numUpdates); - + public static string NotificationContentPreparingToUpdate(int numPlugins) => numPlugins == 1 ? - Loc.Localize("AutoUpdatePreparingToUpdateSingular", "Preparing to update 1 plugin...") : + Loc.Localize("AutoUpdatePreparingToUpdateSingular", "Preparing to update 1 plugin...") : string.Format(Loc.Localize("AutoUpdatePreparingToUpdatePlural", "Preparing to update {0} plugins..."), numPlugins); - + public static string NotificationContentUpdating(string name) => string.Format(Loc.Localize("AutoUpdateUpdating", "Updating {0}..."), name); - + public static string NotificationContentFailedPlugins(IEnumerable failedPlugins) => string.Format(Loc.Localize("AutoUpdateFailedPlugins", "Failed plugin(s): {0}"), string.Join(", ", failedPlugins)); } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index df03181cc..cc98a564d 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit df03181ccbbbfead3db116b59359dae4a31cb07d +Subproject commit cc98a564d0787813d4be082bf75f5bb98e0ed12f