Skip to content

Commit cb0b115

Browse files
authored
Notification on outdated Rider package (#2244)
* notification on outdated Rider package * simplify * switch to UserNotifications
1 parent 866fe6a commit cb0b115

File tree

10 files changed

+234
-17
lines changed

10 files changed

+234
-17
lines changed

resharper/resharper-unity/src/Unity.Rider/UnityEditorIntegration/EditorPlugin/UnityPluginInstaller.cs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class UnityPluginInstaller
3838
private readonly UnityVersion myUnityVersion;
3939
private readonly UnitySolutionTracker myUnitySolutionTracker;
4040
private readonly UnityRefresher myRefresher;
41-
private readonly RiderNotificationPopupHost myNotificationPopupHost;
41+
private readonly UserNotifications myUserNotifications;
4242
private readonly IHostProductInfo myHostProductInfo;
4343
private readonly IContextBoundSettingsStoreLive myBoundSettingsStore;
4444
private readonly ProcessingQueue myQueue;
@@ -57,7 +57,7 @@ public UnityPluginInstaller(
5757
UnitySolutionTracker unitySolutionTracker,
5858
UnityRefresher refresher,
5959
IHostProductInfo hostProductInfo,
60-
RiderNotificationPopupHost notificationPopupHost = null)
60+
UserNotifications userNotifications)
6161
{
6262
myPluginInstallations = new JetHashSet<VirtualFileSystemPath>();
6363

@@ -72,7 +72,7 @@ public UnityPluginInstaller(
7272
myUnitySolutionTracker = unitySolutionTracker;
7373
myRefresher = refresher;
7474
myHostProductInfo = hostProductInfo;
75-
myNotificationPopupHost = notificationPopupHost;
75+
myUserNotifications = userNotifications;
7676

7777
myBoundSettingsStore = settingsStore.BoundSettingsStore;
7878
myQueue = new ProcessingQueue(myShellLocks, myLifetime);
@@ -362,8 +362,7 @@ internal void ShowOutOfSyncNotification(Lifetime lifetime)
362362
var entry = myBoundSettingsStore.Schema.GetScalarEntry((UnitySettings s) => s.InstallUnity3DRiderPlugin);
363363
var isEnabled = myBoundSettingsStore.GetValueProperty<bool>(lifetime, entry, null).Value;
364364
if (isEnabled) return;
365-
var notification = RiderNotification.Create(
366-
NotificationSeverity.WARNING, "Unity editor plugin update required",
365+
myUserNotifications.CreateNotification(notificationLifetime.Lifetime, NotificationSeverity.WARNING, "Unity editor plugin update required",
367366
"The Unity editor plugin is out of date and automatic plugin updates are disabled. Advanced Unity integration features are unavailable until the plugin is updated.",
368367
additionalCommands: new[]
369368
{
@@ -377,17 +376,13 @@ internal void ShowOutOfSyncNotification(Lifetime lifetime)
377376
notificationLifetime.Terminate();
378377
});
379378
})
380-
}
381-
);
382-
myNotificationPopupHost.ShowNotification(notificationLifetime.Lifetime, notification);
379+
});
383380
}
384381
else
385382
{
386-
var notification = RiderNotification.Create(
387-
NotificationSeverity.WARNING, "Advanced Unity integration is unavailable",
388-
$"Make sure Rider {myHostProductInfo.VersionMarketingString} is set as the External Editor in Unity preferences."
389-
);
390-
myNotificationPopupHost.ShowNotification(notificationLifetime.Lifetime, notification);
383+
myUserNotifications.CreateNotification(notificationLifetime.Lifetime, NotificationSeverity.WARNING,
384+
"Advanced Unity integration is unavailable",
385+
$"Make sure Rider {myHostProductInfo.VersionMarketingString} is set as the External Editor in Unity preferences.");
391386
}
392387
}
393388
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using JetBrains.Application.Notifications;
4+
using JetBrains.Application.Settings;
5+
using JetBrains.Application.Threading;
6+
using JetBrains.Collections.Viewable;
7+
using JetBrains.DataFlow;
8+
using JetBrains.Lifetimes;
9+
using JetBrains.ProjectModel;
10+
using JetBrains.ProjectModel.DataContext;
11+
using JetBrains.ReSharper.Plugins.Unity.Core.Application.Settings;
12+
using JetBrains.ReSharper.Plugins.Unity.Core.ProjectModel;
13+
using JetBrains.ReSharper.Plugins.Unity.Rider.Protocol;
14+
using JetBrains.ReSharper.Plugins.Unity.UnityEditorIntegration;
15+
using JetBrains.ReSharper.Plugins.Unity.UnityEditorIntegration.Packages;
16+
using JetBrains.ReSharper.Psi.Util;
17+
using JetBrains.Util;
18+
19+
namespace JetBrains.ReSharper.Plugins.Unity.Rider.UnityEditorIntegration
20+
{
21+
[SolutionComponent]
22+
public class RiderPackageUpdateAvailabilityChecker
23+
{
24+
private readonly ILogger myLogger;
25+
private readonly ISolution mySolution;
26+
private readonly IShellLocks myShellLocks;
27+
private readonly PackageManager myPackageManager;
28+
private readonly UnityVersion myUnityVersion;
29+
private readonly ISettingsStore mySettingsStore;
30+
private readonly BackendUnityHost myBackendUnityHost;
31+
private readonly UserNotifications myUserNotifications;
32+
private readonly JetHashSet<VirtualFileSystemPath> myNotificationShown;
33+
private readonly IContextBoundSettingsStoreLive myBoundSettingsStore;
34+
private string packageId = "com.unity.ide.rider";
35+
private Version leastRiderPackageVersion = new Version(3, 0, 9);
36+
37+
public RiderPackageUpdateAvailabilityChecker(
38+
Lifetime lifetime,
39+
ILogger logger,
40+
ISolution solution,
41+
IShellLocks shellLocks,
42+
PackageManager packageManager,
43+
UnitySolutionTracker unitySolutionTracker,
44+
UnityVersion unityVersion,
45+
IApplicationWideContextBoundSettingStore applicationWideContextBoundSettingStore,
46+
ISettingsStore settingsStore,
47+
BackendUnityHost backendUnityHost,
48+
UserNotifications userNotifications
49+
)
50+
{
51+
myLogger = logger;
52+
mySolution = solution;
53+
myShellLocks = shellLocks;
54+
myPackageManager = packageManager;
55+
myUnityVersion = unityVersion;
56+
mySettingsStore = settingsStore;
57+
myBackendUnityHost = backendUnityHost;
58+
myUserNotifications = userNotifications;
59+
myNotificationShown = new JetHashSet<VirtualFileSystemPath>();
60+
myBoundSettingsStore = applicationWideContextBoundSettingStore.BoundSettingsStore;
61+
unitySolutionTracker.IsUnityGeneratedProject.WhenTrue(lifetime, lt =>
62+
{
63+
ShowNotificationIfNeeded(lt, leastRiderPackageVersion);
64+
BindToInstallationSettingChange(lt, leastRiderPackageVersion);
65+
BindToProtocol(lt);
66+
});
67+
}
68+
69+
private void BindToProtocol(Lifetime lt)
70+
{
71+
myBackendUnityHost.BackendUnityModel.ViewNotNull(lt, (l, model) =>
72+
{
73+
model.RiderPackagePotentialUpdateVersion.Advise(l, result =>
74+
{
75+
if (!string.IsNullOrEmpty(result) && Version.TryParse(result, out var resultVersion))
76+
{
77+
ShowNotificationIfNeeded(l, resultVersion);
78+
}
79+
});
80+
});
81+
}
82+
83+
private void BindToInstallationSettingChange(Lifetime lifetime, Version version)
84+
{
85+
var entry = myBoundSettingsStore.Schema.GetScalarEntry((UnitySettings s) =>
86+
s.AllowRiderUpdateNotifications);
87+
myBoundSettingsStore.GetValueProperty<bool>(lifetime, entry, null).Change.Advise_NoAcknowledgement(lifetime,
88+
args =>
89+
{
90+
if (!args.GetNewOrNull()) return;
91+
ShowNotificationIfNeeded(lifetime, version);
92+
});
93+
}
94+
95+
private void ShowNotificationIfNeeded(Lifetime lifetime, Version expectedVersion)
96+
{
97+
if (!myBoundSettingsStore.GetValue((UnitySettings s) => s.AllowRiderUpdateNotifications))
98+
return;
99+
100+
myPackageManager.IsInitialUpdateFinished.WhenTrue(lifetime, lt =>
101+
{
102+
myUnityVersion.ActualVersionForSolution.AdviseNotNull(lt, version =>
103+
{
104+
if (myNotificationShown.Contains(mySolution.SolutionFilePath)) return;
105+
106+
// Version before 2019.2 doesn't have Rider package
107+
// 2019.2.0 - 2019.2.5 : version 1.2.1 is the last one
108+
// 2019.2.6 - present : see: leastRiderPackageVersion
109+
if (version < new Version(2019, 2, 6)) return;
110+
111+
var package = myPackageManager.GetPackageById(packageId);
112+
113+
if (package == null)
114+
{
115+
myNotificationShown.Add(mySolution.SolutionFilePath);
116+
myLogger.Info($"{packageId} is missing.");
117+
myShellLocks.ExecuteOrQueueEx(lt,
118+
"RiderPackageUpdateAvailabilityChecker.ShowNotificationIfNeeded",
119+
() =>
120+
{
121+
myUserNotifications.CreateNotification(lt, NotificationSeverity.WARNING,
122+
"JetBrains Rider package in Unity is missing.",
123+
"Make sure JetBrains Rider package is installed in Unity Package Manager.");
124+
});
125+
}
126+
else if (package.Source == PackageSource.Registry &&
127+
new Version(package.PackageDetails.Version) < expectedVersion)
128+
{
129+
var notificationLifetime = lt.CreateNested();
130+
myNotificationShown.Add(mySolution.SolutionFilePath);
131+
myLogger.Info($"{packageId} {package.PackageDetails.Version} is older then expected.");
132+
133+
myShellLocks.ExecuteOrQueueEx(lt,
134+
"RiderPackageUpdateAvailabilityChecker.ShowNotificationIfNeeded",
135+
() => myUserNotifications.CreateNotification(notificationLifetime.Lifetime,
136+
NotificationSeverity.INFO,
137+
"Update available - JetBrains Rider package.",
138+
"Check for JetBrains Rider package updates in Unity Package Manager.",
139+
additionalCommands: new[]
140+
{
141+
new UserNotificationCommand("Never show for this solution", () =>
142+
{
143+
mySettingsStore.BindToContextTransient(
144+
ContextRange.ManuallyRestrictWritesToOneContext(
145+
mySolution.ToDataContext()))
146+
.SetValue((UnitySettings key) => key.AllowRiderUpdateNotifications, false);
147+
notificationLifetime.Terminate();
148+
})
149+
}));
150+
}
151+
});
152+
});
153+
}
154+
}
155+
}

resharper/resharper-unity/src/Unity/Core/Application/Settings/UnitySettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class UnitySettings
1313
{
1414
[SettingsEntry(true, "If this option is enabled, the Rider Unity editor plugin will be automatically installed and updated.")]
1515
public bool InstallUnity3DRiderPlugin;
16+
17+
[SettingsEntry(true, "If this option is disabled, Rider package update notifications would never be shown.")]
18+
public bool AllowRiderUpdateNotifications;
1619

1720
[SettingsEntry(true, "If this option is enabled, Rider will automatically notify the Unity editor to refresh assets.")]
1821
public bool AllowAutomaticRefreshInUnity;

resharper/resharper-unity/src/Unity/Core/Application/UI/Options/UnityOptionsPage.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ private void AddGeneralSection()
6565

6666
AddBoolOption((UnitySettings s) => s.AllowAutomaticRefreshInUnity,
6767
"Automatically refresh assets in Unity");
68+
69+
AddBoolOption((UnitySettings s) => s.AllowRiderUpdateNotifications,
70+
"Notify when Rider package update is available");
6871
}
6972

7073
private void AddCSharpSection()

rider/protocol/src/main/kotlin/model/backendUnity/BackendUnityModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ object BackendUnityModel: Root() {
141141
call("getUnityEditorState", void, Library.UnityEditorState).documentation = "Polled from the backend to get what the editor is currently doing"
142142

143143
property("unityApplicationData", Library.UnityApplicationData)
144+
property ("riderPackagePotentialUpdateVersion", string).documentation = "Version of the latest compatible update for the Rider package"
144145
field("unityApplicationSettings", Library.UnityApplicationSettings)
145146
field("unityProjectSettings", Library.UnityProjectSettings)
146147

unity/EditorPlugin/AfterUnity56/EntryPoint.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ static EntryPoint()
1313

1414
PluginEntryPoint.OnModelInitialization += UnitTesting.Initialization.OnModelInitializationHandler;
1515
PluginEntryPoint.OnModelInitialization += Navigation.Initialization.OnModelInitializationHandler;
16+
PluginEntryPoint.OnModelInitialization += Packages.Initialization.OnModelInitializationHandler;
1617
AppDomain.CurrentDomain.DomainUnload += (EventHandler) ((_, __) =>
1718
{
1819
PluginEntryPoint.OnModelInitialization -= UnitTesting.Initialization.OnModelInitializationHandler;
1920
PluginEntryPoint.OnModelInitialization -= Navigation.Initialization.OnModelInitializationHandler;
21+
PluginEntryPoint.OnModelInitialization -= Packages.Initialization.OnModelInitializationHandler;
2022
});
2123
}
2224
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#if UNITY_2019_2
2+
using System.Linq;
3+
using JetBrains.Diagnostics;
4+
using JetBrains.Rd.Base;
5+
using UnityEditor;
6+
using UnityEditor.PackageManager;
7+
using UnityEditor.PackageManager.Requests;
8+
#endif
9+
10+
namespace JetBrains.Rider.Unity.Editor.AfterUnity56.Packages
11+
{
12+
public static class Initialization
13+
{
14+
#if UNITY_2019_2
15+
private static string packageId = "com.unity.ide.rider";
16+
private static readonly ILog ourLogger = Log.GetLog("Packages.Initialization");
17+
private static SearchRequest ourRequest;
18+
#endif
19+
public static void OnModelInitializationHandler(UnityModelAndLifetime modelAndLifetime)
20+
{
21+
#if UNITY_2019_2
22+
ourRequest = Client.Search(packageId);
23+
modelAndLifetime.Lifetime.OnTermination(() => { ourRequest = null; });
24+
EditorApplication.update += () => Progress(modelAndLifetime);
25+
#endif
26+
}
27+
#if UNITY_2019_2
28+
static void Progress(UnityModelAndLifetime modelAndLifetime)
29+
{
30+
if (ourRequest == null)
31+
{
32+
EditorApplication.update -= () => Progress(modelAndLifetime);
33+
return;
34+
}
35+
36+
if (!ourRequest.IsCompleted) return;
37+
if (ourRequest.Status == StatusCode.Success)
38+
{
39+
var latestCompatible = ourRequest.Result.FirstOrDefault()?.versions.latestCompatible;
40+
if (latestCompatible != null)
41+
{
42+
ourLogger.Info("Found: " + latestCompatible);
43+
modelAndLifetime.Model.RiderPackagePotentialUpdateVersion.Set(latestCompatible);
44+
}
45+
}
46+
else if (ourRequest.Status >= StatusCode.Failure)
47+
{
48+
ourLogger.Error(ourRequest.Error.message);
49+
}
50+
51+
EditorApplication.update -= () => Progress(modelAndLifetime);
52+
}
53+
#endif
54+
}
55+
}

unity/EditorPlugin/EditorPluginNet46.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@
55
<RootNamespace>JetBrains.Rider.Unity.Editor</RootNamespace>
66
<DebugType>portable</DebugType>
77
<DebugSymbols>true</DebugSymbols>
8-
<DefineConstants>UNITY_2017_3;RIDER_EDITOR_PLUGIN</DefineConstants>
8+
<DefineConstants>UNITY_2019_2;RIDER_EDITOR_PLUGIN</DefineConstants>
99
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1010
<AssemblyOriginatorKeyFile>..\..\sign.snk</AssemblyOriginatorKeyFile>
1111
<LangVersion>9</LangVersion>
1212
</PropertyGroup>
1313
<ItemGroup>
14-
<PackageReference Include="JetBrains.Unity.Libs.Ref.2017.3.0f3" Version="2020.6.10" />
14+
<PackageReference Include="JetBrains.Unity.Libs.Ref.2019.2.0f1" Version="2021.11.29" />
1515
<PackageReference Include="JetBrains.Build.ILRepack" Version="0.0.3" />
1616
<PackageReference Include="JetBrains.Toolset.RefAsm.net461.NetStandard" Version="2.0.20190130.182358" />
1717
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.2">
1818
<PrivateAssets>all</PrivateAssets>
1919
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2020
</PackageReference>
21+
<PackageReference Include="NUnit" Version="3.13.2" />
2122
</ItemGroup>
2223

2324
<Target Name="WriteRiderDev" AfterTargets="CopyFilesToOutputDirectory">
@@ -41,7 +42,7 @@
4142
<Compile Remove="AssetPostprocessors\SlnAssetPostprocessor.cs" />
4243
<Compile Remove="RiderMenu.cs" />
4344
</ItemGroup>
44-
<Import Project="$(DotNetSdkPath)\Build\PackageReference.JetBrains.RdFramework.Props"/>
45+
<Import Project="$(DotNetSdkPath)\Build\PackageReference.JetBrains.RdFramework.Props" />
4546
<Import Project="ILRepack.targets" />
4647
<Import Project="Model.targets" />
4748
</Project>

unity/EditorPlugin/EditorPluginUnity56.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<ItemGroup>
2626
<Compile Remove="AfterUnity56\UnitTesting\CompiledAssembliesTracker.cs" />
2727
</ItemGroup>
28-
<Import Project="$(DotNetSdkPath)\Build\PackageReference.JetBrains.RdFramework.Props"/>
28+
<Import Project="$(DotNetSdkPath)\Build\PackageReference.JetBrains.RdFramework.Props" />
2929
<Import Project="ILRepack.targets" />
3030
<Import Project="Model.targets" />
3131
</Project>

unity/EditorPlugin/PluginSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ public static bool LogEventsCollectorEnabled
157157
/// <remarks>
158158
/// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off
159159
/// </remarks>
160+
#if !UNITY_2019_2 // this is not loaded, for Rider package, so remove it to avoid compilation warning
160161
[PreferenceItem("Rider")]
162+
#endif
161163
private static void RiderPreferencesItem()
162164
{
163165
EditorGUIUtility.labelWidth = 200f;

0 commit comments

Comments
 (0)