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

Commit 1ea0801

Browse files
committed
WIP: Add fork feature.
1 parent ba29d80 commit 1ea0801

35 files changed

+810
-8
lines changed

GitHubVS.sln

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25420.1
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.27130.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.VisualStudio", "src\GitHub.VisualStudio\GitHub.VisualStudio.csproj", "{11569514-5AE5-4B5B-92A2-F10B0967DE5F}"
77
ProjectSection(ProjectDependencies) = postProject
@@ -213,8 +213,8 @@ Global
213213
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Release|Any CPU.Build.0 = Release|Any CPU
214214
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.ReleaseWithoutVsix|Any CPU.ActiveCfg = Release|Any CPU
215215
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.ReleaseWithoutVsix|Any CPU.Build.0 = Release|Any CPU
216-
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.ActiveCfg = Release|Any CPU
217-
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.Build.0 = Release|Any CPU
216+
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
217+
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
218218
{08DD4305-7787-4823-A53F-4D0F725A07F3}.DebugCodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
219219
{08DD4305-7787-4823-A53F-4D0F725A07F3}.DebugCodeAnalysis|Any CPU.Build.0 = Release|Any CPU
220220
{08DD4305-7787-4823-A53F-4D0F725A07F3}.DebugWithoutVsix|Any CPU.ActiveCfg = Release|Any CPU
@@ -427,4 +427,7 @@ Global
427427
{110B206F-8554-4B51-BF86-94DAA32F5E26} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
428428
{17EB676B-BB91-48B5-AA59-C67695C647C2} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
429429
EndGlobalSection
430+
GlobalSection(ExtensibilityGlobals) = postSolution
431+
SolutionGuid = {556014CF-5B35-4CE5-B3EF-6AB0007001AC}
432+
EndGlobalSection
430433
EndGlobal

script

src/GitHub.App/Api/ApiClient.cs

+5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ public IObservable<Gist> CreateGist(NewGist newGist)
9696
return gitHubClient.Gist.Create(newGist);
9797
}
9898

99+
public IObservable<Repository> GetForks(string owner, string name)
100+
{
101+
return gitHubClient.Repository.Forks.GetAll(owner, name);
102+
}
103+
99104
public IObservable<User> GetUser()
100105
{
101106
return gitHubClient.User.Current();

src/GitHub.App/GitHub.App.csproj

+5
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,12 @@
137137
<Compile Include="Models\IssueCommentModel.cs" />
138138
<Compile Include="Models\PullRequestReviewCommentModel.cs" />
139139
<Compile Include="Models\PullRequestDetailArgument.cs" />
140+
<Compile Include="SampleData\ForkRepositoryExecuteViewModelDesigner.cs" />
141+
<Compile Include="SampleData\ForkRepositorySelectViewModelDesigner.cs" />
140142
<Compile Include="Services\GlobalConnection.cs" />
143+
<Compile Include="ViewModels\Dialog\ForkRepositoryExecuteViewModel.cs" />
144+
<Compile Include="ViewModels\Dialog\ForkRepositoryViewModel.cs" />
145+
<Compile Include="ViewModels\Dialog\ForkRepositorySelectViewModel.cs" />
141146
<Compile Include="ViewModels\Dialog\GistCreationViewModel.cs" />
142147
<Compile Include="ViewModels\Dialog\GitHubDialogWindowViewModel.cs" />
143148
<Compile Include="ViewModels\Dialog\LoginCredentialsViewModel.cs" />

src/GitHub.App/Resources.Designer.cs

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/GitHub.App/Resources.resx

+3
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,7 @@
285285
<data name="WorkingDirectoryHasUncommittedCHanges" xml:space="preserve">
286286
<value>Cannot checkout as your working directory has uncommitted changes.</value>
287287
</data>
288+
<data name="ForkRepositoryTitle" xml:space="preserve">
289+
<value>Fork Repository</value>
290+
</data>
288291
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using GitHub.Models;
4+
using GitHub.ViewModels;
5+
using GitHub.ViewModels.Dialog;
6+
using ReactiveUI;
7+
8+
namespace GitHub.SampleData
9+
{
10+
public class ForkRepositoryExecuteViewModelDesigner : ViewModelBase, IForkRepositoryExecuteViewModel
11+
{
12+
public ForkRepositoryExecuteViewModelDesigner()
13+
{
14+
SourceRepository = new RemoteRepositoryModelDesigner
15+
{
16+
Owner = "github",
17+
Name = "VisualStudio",
18+
CloneUrl = "https://github.com/github/VisualStudio",
19+
};
20+
DestinationRepository = new RemoteRepositoryModelDesigner
21+
{
22+
Owner = "user",
23+
Name = "VisualStudio",
24+
CloneUrl = "https://github.com/user/VisualStudio",
25+
};
26+
}
27+
28+
public IObservable<object> Done => null;
29+
30+
public string Title => null;
31+
32+
public IRepositoryModel SourceRepository { get; set; }
33+
34+
public IRepositoryModel DestinationRepository { get; set; }
35+
36+
public ReactiveCommand<object> Start => null;
37+
38+
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
39+
{
40+
return Task.CompletedTask;
41+
}
42+
43+
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection, IAccount account)
44+
{
45+
throw new NotImplementedException();
46+
}
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using GitHub.Models;
5+
using GitHub.ViewModels;
6+
using GitHub.ViewModels.Dialog;
7+
using ReactiveUI;
8+
9+
namespace GitHub.SampleData
10+
{
11+
public class ForkRepositorySelectViewModelDesigner : ViewModelBase, IForkRepositorySelectViewModel
12+
{
13+
public ForkRepositorySelectViewModelDesigner()
14+
{
15+
Accounts = new[]
16+
{
17+
new AccountDesigner { Login = "Myself" },
18+
new AccountDesigner { Login = "MyOrg1" },
19+
new AccountDesigner { Login = "MyOrg2" },
20+
new AccountDesigner { Login = "MyOrg3" },
21+
new AccountDesigner { Login = "a-long-org-name" },
22+
};
23+
24+
ExistingForks = new[]
25+
{
26+
new RemoteRepositoryModelDesigner { Owner = "MyOrg5", Name = "MyRepo" },
27+
new RemoteRepositoryModelDesigner { Owner = "MyOrg6", Name = "MyRepo" },
28+
};
29+
}
30+
31+
public IReadOnlyList<IAccount> Accounts { get; set; }
32+
33+
public IObservable<object> Done => null;
34+
35+
public IReadOnlyList<IRemoteRepositoryModel> ExistingForks { get; set; }
36+
37+
public bool IsLoading { get; set; }
38+
39+
public string Title => null;
40+
41+
public ReactiveCommand<object> Selected => null;
42+
43+
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
44+
{
45+
return Task.CompletedTask;
46+
}
47+
}
48+
}

src/GitHub.App/Services/DialogService.cs

+10
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,15 @@ public async Task<IConnection> ShowLoginDialog()
7171
var viewModel = factory.CreateViewModel<ILoginViewModel>();
7272
return (IConnection)await showDialog.Show(viewModel);
7373
}
74+
75+
public async Task ShowForkDialog(ILocalRepositoryModel repository, IConnection connection)
76+
{
77+
Guard.ArgumentNotNull(repository, nameof(repository));
78+
Guard.ArgumentNotNull(connection, nameof(connection));
79+
80+
var viewModel = factory.CreateViewModel<IForkRepositoryViewModel>();
81+
await viewModel.InitializeAsync(repository, connection);
82+
await showDialog.Show(viewModel);
83+
}
7484
}
7585
}

src/GitHub.App/Services/ModelService.cs

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ public IObservable<IReadOnlyList<IAccount>> GetAccounts()
9494
.ToReadOnlyList(Create);
9595
}
9696

97+
public IObservable<IRemoteRepositoryModel> GetForks(IRepositoryModel repository)
98+
{
99+
return ApiClient.GetForks(repository.Owner, repository.Name)
100+
.Select(x => new RemoteRepositoryModel(x));
101+
}
102+
97103
IObservable<LicenseCacheItem> GetLicensesFromApi()
98104
{
99105
return ApiClient.GetLicenses()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.ComponentModel.Composition;
3+
using System.Threading.Tasks;
4+
using GitHub.App;
5+
using GitHub.Logging;
6+
using GitHub.Models;
7+
using GitHub.Primitives;
8+
using ReactiveUI;
9+
using Serilog;
10+
11+
namespace GitHub.ViewModels.Dialog
12+
{
13+
[Export(typeof(IForkRepositoryExecuteViewModel))]
14+
[PartCreationPolicy(CreationPolicy.NonShared)]
15+
public class ForkRepositoryExecuteViewModel : ViewModelBase, IForkRepositoryExecuteViewModel
16+
{
17+
static readonly ILogger log = LogManager.ForContext<ForkRepositoryViewModel>();
18+
19+
public ForkRepositoryExecuteViewModel()
20+
{
21+
Start = ReactiveCommand.Create();
22+
}
23+
24+
public IRepositoryModel SourceRepository { get; private set; }
25+
26+
public IRepositoryModel DestinationRepository { get; private set; }
27+
28+
public ReactiveCommand<object> Start { get; }
29+
30+
public string Title => Resources.ForkRepositoryTitle;
31+
32+
public IObservable<object> Done => Start;
33+
34+
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection, IAccount account)
35+
{
36+
SourceRepository = repository;
37+
DestinationRepository = new RemoteRepositoryModel(
38+
0,
39+
repository.Name,
40+
CreateForkUri(repository.CloneUrl, account.Login),
41+
false,
42+
true,
43+
account,
44+
null);
45+
return Task.CompletedTask;
46+
}
47+
48+
UriString CreateForkUri(UriString url, string login)
49+
{
50+
var original = url.ToRepositoryUrl();
51+
return new UriString(
52+
string.Format("{0}://{1}/{2}/{3}",
53+
original.Scheme,
54+
original.Authority,
55+
login,
56+
url.RepositoryName));
57+
}
58+
}
59+
}
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 System.Linq;
5+
using System.Reactive.Linq;
6+
using System.Threading.Tasks;
7+
using GitHub.App;
8+
using GitHub.Factories;
9+
using GitHub.Logging;
10+
using GitHub.Models;
11+
using ReactiveUI;
12+
using Serilog;
13+
14+
namespace GitHub.ViewModels.Dialog
15+
{
16+
[Export(typeof(IForkRepositorySelectViewModel))]
17+
[PartCreationPolicy(CreationPolicy.NonShared)]
18+
public class ForkRepositorySelectViewModel : ViewModelBase, IForkRepositorySelectViewModel
19+
{
20+
static readonly ILogger log = LogManager.ForContext<ForkRepositoryViewModel>();
21+
22+
readonly IModelServiceFactory modelServiceFactory;
23+
IReadOnlyList<IAccount> accounts;
24+
IReadOnlyList<IRemoteRepositoryModel> existingForks;
25+
bool isLoading;
26+
27+
[ImportingConstructor]
28+
public ForkRepositorySelectViewModel(IModelServiceFactory modelServiceFactory)
29+
{
30+
this.modelServiceFactory = modelServiceFactory;
31+
Selected = ReactiveCommand.Create();
32+
}
33+
34+
public string Title => Resources.ForkRepositoryTitle;
35+
36+
public IReadOnlyList<IAccount> Accounts
37+
{
38+
get { return accounts; }
39+
private set { this.RaiseAndSetIfChanged(ref accounts, value); }
40+
}
41+
42+
public IReadOnlyList<IRemoteRepositoryModel> ExistingForks
43+
{
44+
get { return existingForks; }
45+
private set { this.RaiseAndSetIfChanged(ref existingForks, value); }
46+
}
47+
48+
public bool IsLoading
49+
{
50+
get { return isLoading; }
51+
private set { this.RaiseAndSetIfChanged(ref isLoading, value); }
52+
}
53+
54+
public ReactiveCommand<object> Selected { get; }
55+
56+
public IObservable<object> Done => Selected;
57+
58+
public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
59+
{
60+
IsLoading = true;
61+
62+
try
63+
{
64+
var modelService = await modelServiceFactory.CreateAsync(connection);
65+
66+
Observable.CombineLatest(
67+
modelService.GetAccounts(),
68+
modelService.GetForks(repository).ToList(),
69+
(a, f) => new { Accounts = a, Forks = f })
70+
.Finally(() => IsLoading = false)
71+
.Subscribe(x =>
72+
{
73+
Accounts = BuildAccounts(x.Accounts, x.Forks);
74+
ExistingForks = BuildExistingForks(x.Accounts, x.Forks);
75+
});
76+
77+
}
78+
catch (Exception ex)
79+
{
80+
log.Error(ex, "Error initializing ForkRepositoryViewModel");
81+
IsLoading = false;
82+
}
83+
}
84+
85+
IReadOnlyList<IAccount> BuildAccounts(IReadOnlyList<IAccount> accounts, IList<IRemoteRepositoryModel> forks)
86+
{
87+
var forksByOwner = forks.ToDictionary(x => x.Owner, x => x);
88+
return accounts.Where(x => !forksByOwner.ContainsKey(x.Login)).ToList();
89+
}
90+
91+
IReadOnlyList<IRemoteRepositoryModel> BuildExistingForks(IReadOnlyList<IAccount> accounts, IList<IRemoteRepositoryModel> forks)
92+
{
93+
var accountsByLogin = accounts.ToDictionary(x => x.Login, x => x);
94+
return forks.Where(x => accountsByLogin.ContainsKey(x.Owner)).ToList();
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)