Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 9262022

Browse files
committed
Add options page for storing user settings
Also add a setting for opting out of usage data
1 parent b7f8150 commit 9262022

File tree

11 files changed

+394
-0
lines changed

11 files changed

+394
-0
lines changed

src/GitHub.Exports/GitHub.Exports.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
<Compile Include="Helpers\INotifyPropertySource.cs" />
103103
<Compile Include="Extensions\PropertyNotifierExtensions.cs" />
104104
<Compile Include="Extensions\SimpleRepositoryModelExtensions.cs" />
105+
<Compile Include="Helpers\SettingsStore.cs" />
105106
<Compile Include="Info\ApplicationInfo.cs" />
106107
<Compile Include="Models\IAccount.cs" />
107108
<Compile Include="Models\IConnectionManager.cs" />
@@ -116,11 +117,13 @@
116117
<Compile Include="Services\IMenuHandler.cs" />
117118
<Compile Include="Services\IMenuProvider.cs" />
118119
<Compile Include="Services\INotificationDispatcher.cs" />
120+
<Compile Include="Services\IPackageSettings.cs" />
119121
<Compile Include="Services\IStatusBarNotificationService.cs" />
120122
<Compile Include="Services\ITeamExplorerServices.cs" />
121123
<Compile Include="Services\ITeamExplorerServiceHolder.cs" />
122124
<Compile Include="Services\INotificationService.cs" />
123125
<Compile Include="Services\Logger.cs" />
126+
<Compile Include="Services\PackageSettings.cs" />
124127
<Compile Include="Services\Services.cs" />
125128
<Compile Include="Services\StatusBarNotificationService.cs" />
126129
<Compile Include="Services\TeamExplorerServices.cs" />
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.IO;
2+
using Microsoft.VisualStudio.Settings;
3+
4+
namespace GitHub.Helpers
5+
{
6+
internal class SettingsStore
7+
{
8+
readonly WritableSettingsStore store;
9+
readonly string root;
10+
public SettingsStore(WritableSettingsStore store, string root)
11+
{
12+
this.store = store;
13+
this.root = root;
14+
}
15+
16+
public object Read(string property, object defaultValue)
17+
{
18+
return Read(null, property, defaultValue);
19+
}
20+
21+
/// <summary>
22+
/// Read from a settings store
23+
/// </summary>
24+
/// <typeparam name="T"></typeparam>
25+
/// <param name="subpath">The subcollection path (appended to the path passed to the constructor)</param>
26+
/// <param name="property">The property name to read</param>
27+
/// <param name="defaultValue">The default value to use in case the property doesn't exist.
28+
/// The type of the default value will be used to figure out the proper way to read the property, so if pass null,
29+
/// the property will be read as a string (which may or may not be what you want)</param>
30+
/// <returns></returns>
31+
public object Read(string subpath, string property, object defaultValue)
32+
{
33+
var collection = subpath != null ? Path.Combine(root, subpath) : root;
34+
store.CreateCollection(collection);
35+
36+
if (defaultValue is bool)
37+
return store.GetBoolean(collection, property, (bool)defaultValue);
38+
else if (defaultValue is int)
39+
return store.GetInt32(collection, property, (int)defaultValue);
40+
else if (defaultValue is uint)
41+
return store.GetUInt32(collection, property, (uint)defaultValue);
42+
else if (defaultValue is long)
43+
return store.GetInt64(collection, property, (long)defaultValue);
44+
else if (defaultValue is ulong)
45+
return store.GetUInt64(collection, property, (ulong)defaultValue);
46+
return store.GetString(collection, property, defaultValue?.ToString() ?? "");
47+
}
48+
49+
public void Write(string property, object value)
50+
{
51+
Write(null, property, value);
52+
}
53+
54+
public void Write(string subpath, string property, object value)
55+
{
56+
var collection = subpath != null ? Path.Combine(root, subpath) : root;
57+
store.CreateCollection(collection);
58+
59+
if (value is bool)
60+
store.SetBoolean(collection, property, (bool)value);
61+
else if (value is int)
62+
store.SetInt32(collection, property, (int)value);
63+
else if (value is uint)
64+
store.SetUInt32(collection, property, (uint)value);
65+
else if (value is long)
66+
store.SetInt64(collection, property, (long)value);
67+
else if (value is ulong)
68+
store.SetUInt64(collection, property, (ulong)value);
69+
else
70+
store.SetString(collection, property, value?.ToString() ?? "");
71+
}
72+
}
73+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using GitHub.Models;
2+
using System.Linq;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
6+
namespace GitHub.Services
7+
{
8+
public interface IPackageSettings
9+
{
10+
bool CollectMetrics { get; set; }
11+
void Save();
12+
}
13+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel.Composition;
4+
using GitHub.VisualStudio;
5+
using Microsoft.VisualStudio.Settings;
6+
using Microsoft.VisualStudio.Shell;
7+
using Microsoft.VisualStudio.Shell.Settings;
8+
using SettingsStore = GitHub.Helpers.SettingsStore;
9+
using System.Diagnostics;
10+
11+
namespace GitHub.Services
12+
{
13+
[Export(typeof(IPackageSettings))]
14+
[PartCreationPolicy(CreationPolicy.NonShared)]
15+
public class PackageSettings : IPackageSettings
16+
{
17+
readonly SettingsStore settingsStore;
18+
19+
readonly Dictionary<string, object> SettingsProperties = new Dictionary<string, object> {
20+
{ "CollectMetrics", true },
21+
};
22+
23+
Dictionary<string, object> settings = new Dictionary<string, object>();
24+
25+
public bool CollectMetrics
26+
{
27+
get
28+
{
29+
object val;
30+
TryGetSetting(nameof(CollectMetrics), out val);
31+
return (bool)val;
32+
}
33+
set { settings[nameof(CollectMetrics)] = value; }
34+
}
35+
36+
[ImportingConstructor]
37+
public PackageSettings([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider)
38+
{
39+
var sm = new ShellSettingsManager(serviceProvider);
40+
settingsStore = new SettingsStore(sm.GetWritableSettingsStore(SettingsScope.UserSettings), Info.ApplicationInfo.ApplicationSafeName);
41+
LoadSettings();
42+
}
43+
44+
public void Save()
45+
{
46+
SaveSettings();
47+
}
48+
49+
bool TryGetSetting(string name, out object value)
50+
{
51+
if (!settings.ContainsKey(name))
52+
{
53+
if (!SettingsProperties.ContainsKey(name))
54+
Debug.Assert(false, "PackageSettings: " + name + " setting missing. Did you add this setting to the SettingsProperties dictionary?");
55+
else
56+
Debug.Assert(false, "PackageSettings: " + name + " setting missing. Registry might be corrupt.");
57+
#if !DEBUG
58+
VsOutputLogger.WriteLine("PackageSettings: " + name + " setting missing.");
59+
#endif
60+
value = SettingsProperties[name]; // default value
61+
return false;
62+
}
63+
value = settings[name];
64+
return true;
65+
}
66+
67+
void LoadSettings()
68+
{
69+
try
70+
{
71+
foreach (var kvp in SettingsProperties)
72+
{
73+
settings[kvp.Key] = settingsStore.Read(kvp.Key, kvp.Value);
74+
}
75+
}
76+
catch (Exception ex)
77+
{
78+
VsOutputLogger.WriteLine("PackageSettings: Unable to load settings. {0}", ex);
79+
}
80+
}
81+
82+
void SaveSettings()
83+
{
84+
try
85+
{
86+
foreach (var kvp in SettingsProperties)
87+
{
88+
settingsStore.Write(kvp.Key, settings[kvp.Key]);
89+
}
90+
}
91+
catch (Exception ex)
92+
{
93+
VsOutputLogger.WriteLine("PackageSettings: Unable to load settings. {0}", ex);
94+
}
95+
}
96+
}
97+
}

src/GitHub.VisualStudio/GitHub.VisualStudio.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@
244244
<Compile Include="Helpers\Colors.cs" />
245245
<Compile Include="Helpers\Constants.cs" />
246246
<Compile Include="Services\ConnectionManager.cs" />
247+
<Compile Include="Services\OptionsPage.cs">
248+
<SubType>Component</SubType>
249+
</Compile>
247250
<Compile Include="Services\Program.cs" />
248251
<Compile Include="Services\SharedResources.cs" />
249252
<Compile Include="Settings.cs" />
@@ -273,6 +276,9 @@
273276
<Compile Include="Base\EnsureLoggedInSection.cs" />
274277
<Compile Include="UI\DrawingExtensions.cs" />
275278
<Compile Include="UI\GitHubPane.cs" />
279+
<Compile Include="UI\Options\OptionsControl.xaml.cs">
280+
<DependentUpon>OptionsControl.xaml</DependentUpon>
281+
</Compile>
276282
<Compile Include="UI\Views\Controls\RepositoryCloneControl.xaml.cs">
277283
<DependentUpon>RepositoryCloneControl.xaml</DependentUpon>
278284
</Compile>
@@ -410,6 +416,11 @@
410416
<Generator>MSBuild:Compile</Generator>
411417
<SubType>Designer</SubType>
412418
</Page>
419+
<Page Include="UI\Options\OptionsControl.xaml">
420+
<SubType>Designer</SubType>
421+
<Generator>MSBuild:Compile</Generator>
422+
<CustomToolNamespace>GitHub.VisualStudio.UI</CustomToolNamespace>
423+
</Page>
413424
<Page Include="UI\Views\Controls\ActionLinkButton.xaml">
414425
<SubType>Designer</SubType>
415426
<Generator>MSBuild:Compile</Generator>

src/GitHub.VisualStudio/GitHubPackage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ namespace GitHub.VisualStudio
3535
//[ProvideAutoLoad(UIContextGuids.NoSolution)]
3636
[ProvideAutoLoad("11B8E6D7-C08B-4385-B321-321078CDD1F8")]
3737
[ProvideToolWindow(typeof(GitHubPane), Orientation = ToolWindowOrientation.Right, Style = VsDockStyle.Tabbed, Window = EnvDTE.Constants.vsWindowKindSolutionExplorer)]
38+
[ProvideOptionPage(typeof(OptionsPage), "GitHub for Visual Studio", "General", 0, 0, supportsAutomation: true)]
3839
public class GitHubPackage : PackageBase
3940
{
4041
public GitHubPackage()

src/GitHub.VisualStudio/Resources.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/GitHub.VisualStudio/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,10 @@
288288
<data name="Error_FailedToCopyToClipboard" xml:space="preserve">
289289
<value>Could not copy to the clipboard. Please try again.</value>
290290
</data>
291+
<data name="Options_MetricsLabel" xml:space="preserve">
292+
<value>Help us improve by sending anonymous usage data</value>
293+
</data>
294+
<data name="Options_PrivacyTitle" xml:space="preserve">
295+
<value>Privacy</value>
296+
</data>
291297
</root>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using GitHub.Services;
2+
using GitHub.VisualStudio.UI;
3+
using Microsoft.VisualStudio.Shell;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.ComponentModel;
7+
using System.Linq;
8+
using System.Runtime.InteropServices;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
using System.Windows;
12+
13+
namespace GitHub.VisualStudio
14+
{
15+
[ClassInterface(ClassInterfaceType.AutoDual)]
16+
[ComVisible(true)]
17+
[Guid("68C87C7B-0212-4256-BB6D-6A6BB847A3A7")]
18+
public class OptionsPage : UIElementDialogPage
19+
{
20+
OptionsControl child;
21+
IPackageSettings packageSettings;
22+
23+
protected override UIElement Child
24+
{
25+
get { return child ?? (child = new OptionsControl()); }
26+
}
27+
28+
protected override void OnActivate(CancelEventArgs e)
29+
{
30+
base.OnActivate(e);
31+
packageSettings = Services.DefaultExportProvider.GetExportedValue<IPackageSettings>();
32+
LoadSettings();
33+
}
34+
35+
void LoadSettings()
36+
{
37+
child.CollectMetrics = packageSettings.CollectMetrics;
38+
}
39+
40+
void SaveSettings()
41+
{
42+
packageSettings.CollectMetrics = child.CollectMetrics;
43+
packageSettings.Save();
44+
}
45+
46+
protected override void OnApply(PageApplyEventArgs args)
47+
{
48+
if (args.ApplyBehavior == ApplyKind.Apply)
49+
{
50+
SaveSettings();
51+
}
52+
53+
base.OnApply(args);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)