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

Commit eb7f1db

Browse files
committed
Remove repo watching responsibilities from TESH
Make ITeamExplorerServiceHolder responsible for holding references to services, but not watching for and marshaling repository change events. Delegate to ITeamExplorerContext for this.
1 parent a4c8386 commit eb7f1db

File tree

6 files changed

+91
-148
lines changed

6 files changed

+91
-148
lines changed

src/GitHub.Exports/Services/ITeamExplorerServiceHolder.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using GitHub.Primitives;
33
using GitHub.Models;
4+
using Microsoft.VisualStudio.Threading;
45

56
namespace GitHub.Services
67
{
@@ -17,6 +18,7 @@ public interface ITeamExplorerServiceHolder
1718
/// changes in the source control context.
1819
/// </summary>
1920
IServiceProvider ServiceProvider { get; set; }
21+
2022
/// <summary>
2123
/// Clears the current ServiceProvider if it matches the one that is passed in.
2224
/// This is usually called on Dispose, which might happen after another section
@@ -25,21 +27,10 @@ public interface ITeamExplorerServiceHolder
2527
/// </summary>
2628
/// <param name="provider">If the current ServiceProvider matches this, clear it</param>
2729
void ClearServiceProvider(IServiceProvider provider);
28-
/// <summary>
29-
/// A IGitRepositoryInfo representing the currently active repository
30-
/// </summary>
31-
ILocalRepositoryModel ActiveRepo { get; }
32-
/// <summary>
33-
/// Subscribe to be notified when the active repository is set and Notify is called.
34-
/// </summary>
35-
/// <param name="who">The instance that is interested in being called (or a unique key/object for that instance)</param>
36-
/// <param name="handler">The handler to call when ActiveRepo is set</param>
37-
void Subscribe(object who, Action<ILocalRepositoryModel> handler);
38-
/// <summary>
39-
/// Unsubscribe from notifications
40-
/// </summary>
41-
/// <param name="who">The instance/key that previously subscribed to notifications</param>
42-
void Unsubscribe(object who);
30+
31+
ITeamExplorerContext TeamExplorerContext { get; }
32+
33+
JoinableTaskFactory JoinableTaskFactory { get; }
4334

4435
IGitAwareItem HomeSection { get; }
4536
}

src/GitHub.TeamFoundation.14/Base/TeamExplorerNavigationItemBase.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.Drawing;
4+
using System.ComponentModel;
45
using GitHub.Api;
56
using GitHub.Extensions;
67
using GitHub.Services;
@@ -14,6 +15,7 @@ namespace GitHub.VisualStudio.Base
1415
{
1516
public class TeamExplorerNavigationItemBase : TeamExplorerItemBase, ITeamExplorerNavigationItem2
1617
{
18+
readonly ITeamExplorerServiceHolder holder;
1719
readonly Octicon octicon;
1820

1921
public TeamExplorerNavigationItemBase(IGitHubServiceProvider serviceProvider,
@@ -24,6 +26,7 @@ public TeamExplorerNavigationItemBase(IGitHubServiceProvider serviceProvider,
2426
Guard.ArgumentNotNull(apiFactory, nameof(apiFactory));
2527
Guard.ArgumentNotNull(holder, nameof(holder));
2628

29+
this.holder = holder;
2730
this.octicon = octicon;
2831

2932
IsVisible = false;
@@ -36,7 +39,31 @@ public TeamExplorerNavigationItemBase(IGitHubServiceProvider serviceProvider,
3639
Invalidate();
3740
};
3841

39-
holder.Subscribe(this, UpdateRepo);
42+
UpdateRepo(holder.TeamExplorerContext.ActiveRepository);
43+
holder.TeamExplorerContext.PropertyChanged += TeamExplorerContext_PropertyChanged;
44+
holder.TeamExplorerContext.StatusChanged += TeamExplorerContext_StatusChanged; ;
45+
}
46+
47+
void TeamExplorerContext_StatusChanged(object sender, EventArgs e)
48+
{
49+
UpdateRepoOnMainThread(holder.TeamExplorerContext.ActiveRepository);
50+
}
51+
52+
void TeamExplorerContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
53+
{
54+
if (e.PropertyName == nameof(holder.TeamExplorerContext.ActiveRepository))
55+
{
56+
UpdateRepoOnMainThread(holder.TeamExplorerContext.ActiveRepository);
57+
}
58+
}
59+
60+
void UpdateRepoOnMainThread(ILocalRepositoryModel repo)
61+
{
62+
holder.JoinableTaskFactory.RunAsync(async () =>
63+
{
64+
await holder.JoinableTaskFactory.SwitchToMainThreadAsync();
65+
UpdateRepo(repo);
66+
}).Task.Forget();
4067
}
4168

4269
public override async void Invalidate()
@@ -75,7 +102,8 @@ protected void OpenInBrowser(Lazy<IVisualStudioBrowser> browser, string endpoint
75102

76103
void Unsubscribe()
77104
{
78-
holder.Unsubscribe(this);
105+
holder.TeamExplorerContext.PropertyChanged -= TeamExplorerContext_PropertyChanged;
106+
holder.TeamExplorerContext.StatusChanged -= TeamExplorerContext_StatusChanged; ;
79107
}
80108

81109
bool disposed;
@@ -109,7 +137,7 @@ public object Icon
109137
Image image;
110138
public Image Image
111139
{
112-
get{ return image; }
140+
get { return image; }
113141
set { image = value; this.RaisePropertyChange(); }
114142
}
115143
}
Lines changed: 6 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.ComponentModel.Composition;
4-
using System.Linq;
53
using GitHub.Extensions;
64
using GitHub.Logging;
7-
using GitHub.Models;
85
using GitHub.Services;
96
using Serilog;
107
using Microsoft.TeamFoundation.Controls;
118
using Microsoft.VisualStudio.Shell;
129
using Microsoft.VisualStudio.Threading;
13-
using System.Windows;
1410

1511
namespace GitHub.VisualStudio.Base
1612
{
@@ -20,20 +16,15 @@ public class TeamExplorerServiceHolder : ITeamExplorerServiceHolder
2016
{
2117
static readonly ILogger log = LogManager.ForContext<TeamExplorerServiceHolder>();
2218

23-
readonly Dictionary<object, Action<ILocalRepositoryModel>> activeRepoHandlers = new Dictionary<object, Action<ILocalRepositoryModel>>();
24-
ILocalRepositoryModel activeRepo;
25-
bool activeRepoNotified = false;
26-
2719
IServiceProvider serviceProvider;
28-
readonly IVSGitExt gitExt;
29-
readonly IGitService gitService;
3020

3121
/// <summary>
3222
/// This class relies on IVSGitExt that provides information when VS switches repositories.
3323
/// </summary>
3424
/// <param name="gitExt">Used for monitoring the active repository.</param>
3525
[ImportingConstructor]
36-
TeamExplorerServiceHolder(IVSGitExt gitExt, IGitService gitService) : this(gitExt, gitService, ThreadHelper.JoinableTaskContext)
26+
TeamExplorerServiceHolder(ITeamExplorerContext teamExplorerContext) :
27+
this(teamExplorerContext, ThreadHelper.JoinableTaskContext)
3728
{
3829
}
3930

@@ -42,23 +33,13 @@ public class TeamExplorerServiceHolder : ITeamExplorerServiceHolder
4233
/// </summary>
4334
/// <param name="gitService">Used for monitoring the active repository.</param>
4435
/// <param name="joinableTaskContext">Used for switching to the Main thread.</param>
45-
public TeamExplorerServiceHolder(IVSGitExt gitExt, IGitService gitService, JoinableTaskContext joinableTaskContext)
36+
public TeamExplorerServiceHolder(ITeamExplorerContext teamExplorerContext, JoinableTaskContext joinableTaskContext)
4637
{
4738
JoinableTaskCollection = joinableTaskContext.CreateCollection();
4839
JoinableTaskCollection.DisplayName = nameof(TeamExplorerServiceHolder);
4940
JoinableTaskFactory = joinableTaskContext.CreateFactory(JoinableTaskCollection);
5041

51-
// HACK: This might be null in SafeMode.
52-
// We should be throwing rather than returning a null MEF service.
53-
if (gitExt == null)
54-
{
55-
return;
56-
}
57-
58-
this.gitExt = gitExt;
59-
this.gitService = gitService;
60-
UpdateActiveRepo();
61-
gitExt.ActiveRepositoriesChanged += UpdateActiveRepo;
42+
TeamExplorerContext = teamExplorerContext;
6243
}
6344

6445
// set by the sections when they get initialized
@@ -76,61 +57,6 @@ public IServiceProvider ServiceProvider
7657
}
7758
}
7859

79-
public ILocalRepositoryModel ActiveRepo
80-
{
81-
get { return activeRepo; }
82-
private set
83-
{
84-
if (activeRepo == value)
85-
return;
86-
if (activeRepo != null)
87-
activeRepo.PropertyChanged -= ActiveRepoPropertyChanged;
88-
activeRepo = value;
89-
if (activeRepo != null)
90-
activeRepo.PropertyChanged += ActiveRepoPropertyChanged;
91-
NotifyActiveRepo();
92-
}
93-
}
94-
95-
public void Subscribe(object who, Action<ILocalRepositoryModel> handler)
96-
{
97-
Guard.ArgumentNotNull(who, nameof(who));
98-
Guard.ArgumentNotNull(handler, nameof(handler));
99-
100-
bool notificationsExist;
101-
ILocalRepositoryModel repo;
102-
lock (activeRepoHandlers)
103-
{
104-
repo = ActiveRepo;
105-
notificationsExist = activeRepoNotified;
106-
if (!activeRepoHandlers.ContainsKey(who))
107-
activeRepoHandlers.Add(who, handler);
108-
else
109-
activeRepoHandlers[who] = handler;
110-
}
111-
112-
// the repo url might have changed and we don't get notifications
113-
// for that, so this is a good place to refresh it in case that happened
114-
if (repo != null)
115-
{
116-
gitService.RefreshCloneUrl(repo);
117-
}
118-
119-
// if the active repo hasn't changed and there's notifications queued up,
120-
// notify the subscriber. If the repo has changed, the set above will trigger
121-
// notifications so we don't have to do it here.
122-
if (repo == ActiveRepo && notificationsExist)
123-
handler(repo);
124-
}
125-
126-
public void Unsubscribe(object who)
127-
{
128-
Guard.ArgumentNotNull(who, nameof(who));
129-
130-
if (activeRepoHandlers.ContainsKey(who))
131-
activeRepoHandlers.Remove(who);
132-
}
133-
13460
/// <summary>
13561
/// Clears the current ServiceProvider if it matches the one that is passed in.
13662
/// This is usually called on Dispose, which might happen after another section
@@ -148,39 +74,6 @@ public void ClearServiceProvider(IServiceProvider provider)
14874
ServiceProvider = null;
14975
}
15076

151-
void NotifyActiveRepo()
152-
{
153-
lock (activeRepoHandlers)
154-
{
155-
activeRepoNotified = true;
156-
foreach (var handler in activeRepoHandlers.Values)
157-
handler(activeRepo);
158-
}
159-
}
160-
161-
void UpdateActiveRepo()
162-
{
163-
var repo = gitExt.ActiveRepositories.FirstOrDefault();
164-
165-
if (!Equals(repo, ActiveRepo))
166-
{
167-
// Fire property change events on Main thread
168-
JoinableTaskFactory.RunAsync(async () =>
169-
{
170-
await JoinableTaskFactory.SwitchToMainThreadAsync();
171-
ActiveRepo = repo;
172-
}).Task.Forget(log);
173-
}
174-
}
175-
176-
void ActiveRepoPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
177-
{
178-
Guard.ArgumentNotNull(e, nameof(e));
179-
180-
if (e.PropertyName == "CloneUrl")
181-
ActiveRepo = sender as ILocalRepositoryModel;
182-
}
183-
18477
public IGitAwareItem HomeSection
18578
{
18679
get
@@ -200,6 +93,7 @@ ITeamExplorerPage PageService
20093
}
20194

20295
public JoinableTaskCollection JoinableTaskCollection { get; }
203-
JoinableTaskFactory JoinableTaskFactory { get; }
96+
public JoinableTaskFactory JoinableTaskFactory { get; }
97+
public ITeamExplorerContext TeamExplorerContext { get; }
20498
}
20599
}

src/GitHub.TeamFoundation.14/Connect/GitHubConnectSection.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ namespace GitHub.VisualStudio.TeamExplorer.Connect
2828
public class GitHubConnectSection : TeamExplorerSectionBase, IGitHubConnectSection
2929
{
3030
static readonly ILogger log = LogManager.ForContext<GitHubConnectSection>();
31+
readonly ISimpleApiClientFactory apiFactory;
32+
readonly ITeamExplorerServiceHolder holder;
3133
readonly IPackageSettings packageSettings;
3234
readonly ITeamExplorerServices teamExplorerServices;
3335
readonly int sectionIndex;
@@ -101,8 +103,6 @@ public ILocalRepositoryModel SelectedRepository
101103

102104
public ICommand Clone { get; }
103105

104-
internal ITeamExplorerServiceHolder Holder => holder;
105-
106106
public GitHubConnectSection(IGitHubServiceProvider serviceProvider,
107107
ISimpleApiClientFactory apiFactory,
108108
ITeamExplorerServiceHolder holder,
@@ -127,6 +127,8 @@ public GitHubConnectSection(IGitHubServiceProvider serviceProvider,
127127
IsVisible = false;
128128
sectionIndex = index;
129129

130+
this.apiFactory = apiFactory;
131+
this.holder = holder;
130132
this.packageSettings = packageSettings;
131133
this.teamExplorerServices = teamExplorerServices;
132134
this.localRepositories = localRepositories;
@@ -325,7 +327,7 @@ async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs
325327
try
326328
{
327329
// TODO: Cache the icon state.
328-
var api = await ApiFactory.Create(newrepo.CloneUrl);
330+
var api = await apiFactory.Create(newrepo.CloneUrl);
329331
var repo = await api.GetRepository();
330332
newrepo.SetIcon(repo.Private, repo.Fork);
331333
}
@@ -344,13 +346,13 @@ async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs
344346
.Cast<ILocalRepositoryModel>()
345347
.ForEach(async r =>
346348
{
347-
if (Equals(Holder.ActiveRepo, r))
349+
if (Equals(holder.TeamExplorerContext.ActiveRepository, r))
348350
SelectedRepository = r;
349351

350352
try
351353
{
352354
// TODO: Cache the icon state.
353-
var api = await ApiFactory.Create(r.CloneUrl);
355+
var api = await apiFactory.Create(r.CloneUrl);
354356
var repo = await api.GetRepository();
355357
r.SetIcon(repo.Private, repo.Fork);
356358
}
@@ -457,7 +459,7 @@ public void Retry()
457459

458460
public bool OpenRepository()
459461
{
460-
var old = Repositories.FirstOrDefault(x => x.Equals(Holder.ActiveRepo));
462+
var old = Repositories.FirstOrDefault(x => x.Equals(holder.TeamExplorerContext.ActiveRepository));
461463
if (!Equals(SelectedRepository, old))
462464
{
463465
try

0 commit comments

Comments
 (0)