зеркало из https://github.com/github/VisualStudio.git
WIP: Add fork feature.
This commit is contained in:
Родитель
ba29d80269
Коммит
1ea080147d
11
GitHubVS.sln
11
GitHubVS.sln
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.VisualStudio", "src\GitHub.VisualStudio\GitHub.VisualStudio.csproj", "{11569514-5AE5-4B5B-92A2-F10B0967DE5F}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
|
@ -213,8 +213,8 @@ Global
|
|||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.ReleaseWithoutVsix|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.ReleaseWithoutVsix|Any CPU.Build.0 = Release|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.Build.0 = Release|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.DebugCodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.DebugCodeAnalysis|Any CPU.Build.0 = Release|Any CPU
|
||||
{08DD4305-7787-4823-A53F-4D0F725A07F3}.DebugWithoutVsix|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
@ -427,4 +427,7 @@ Global
|
|||
{110B206F-8554-4B51-BF86-94DAA32F5E26} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
|
||||
{17EB676B-BB91-48B5-AA59-C67695C647C2} = {8A7DA2E7-262B-4581-807A-1C45CE79CDFD}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {556014CF-5B35-4CE5-B3EF-6AB0007001AC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
2
script
2
script
|
@ -1 +1 @@
|
|||
Subproject commit 02618c8047b0dcfd2d83c9a7e5a9c89ce9c97c98
|
||||
Subproject commit 5ed9b3d7bceee50d27a4a5838d4c0265bd35cc8e
|
|
@ -96,6 +96,11 @@ namespace GitHub.Api
|
|||
return gitHubClient.Gist.Create(newGist);
|
||||
}
|
||||
|
||||
public IObservable<Repository> GetForks(string owner, string name)
|
||||
{
|
||||
return gitHubClient.Repository.Forks.GetAll(owner, name);
|
||||
}
|
||||
|
||||
public IObservable<User> GetUser()
|
||||
{
|
||||
return gitHubClient.User.Current();
|
||||
|
|
|
@ -137,7 +137,12 @@
|
|||
<Compile Include="Models\IssueCommentModel.cs" />
|
||||
<Compile Include="Models\PullRequestReviewCommentModel.cs" />
|
||||
<Compile Include="Models\PullRequestDetailArgument.cs" />
|
||||
<Compile Include="SampleData\ForkRepositoryExecuteViewModelDesigner.cs" />
|
||||
<Compile Include="SampleData\ForkRepositorySelectViewModelDesigner.cs" />
|
||||
<Compile Include="Services\GlobalConnection.cs" />
|
||||
<Compile Include="ViewModels\Dialog\ForkRepositoryExecuteViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\ForkRepositoryViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\ForkRepositorySelectViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\GistCreationViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\GitHubDialogWindowViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\LoginCredentialsViewModel.cs" />
|
||||
|
|
|
@ -168,6 +168,15 @@ namespace GitHub.App {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fork Repository.
|
||||
/// </summary>
|
||||
internal static string ForkRepositoryTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ForkRepositoryTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to [invalid].
|
||||
/// </summary>
|
||||
|
|
|
@ -285,4 +285,7 @@
|
|||
<data name="WorkingDirectoryHasUncommittedCHanges" xml:space="preserve">
|
||||
<value>Cannot checkout as your working directory has uncommitted changes.</value>
|
||||
</data>
|
||||
<data name="ForkRepositoryTitle" xml:space="preserve">
|
||||
<value>Fork Repository</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Models;
|
||||
using GitHub.ViewModels;
|
||||
using GitHub.ViewModels.Dialog;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.SampleData
|
||||
{
|
||||
public class ForkRepositoryExecuteViewModelDesigner : ViewModelBase, IForkRepositoryExecuteViewModel
|
||||
{
|
||||
public ForkRepositoryExecuteViewModelDesigner()
|
||||
{
|
||||
SourceRepository = new RemoteRepositoryModelDesigner
|
||||
{
|
||||
Owner = "github",
|
||||
Name = "VisualStudio",
|
||||
CloneUrl = "https://github.com/github/VisualStudio",
|
||||
};
|
||||
DestinationRepository = new RemoteRepositoryModelDesigner
|
||||
{
|
||||
Owner = "user",
|
||||
Name = "VisualStudio",
|
||||
CloneUrl = "https://github.com/user/VisualStudio",
|
||||
};
|
||||
}
|
||||
|
||||
public IObservable<object> Done => null;
|
||||
|
||||
public string Title => null;
|
||||
|
||||
public IRepositoryModel SourceRepository { get; set; }
|
||||
|
||||
public IRepositoryModel DestinationRepository { get; set; }
|
||||
|
||||
public ReactiveCommand<object> Start => null;
|
||||
|
||||
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection, IAccount account)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Models;
|
||||
using GitHub.ViewModels;
|
||||
using GitHub.ViewModels.Dialog;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.SampleData
|
||||
{
|
||||
public class ForkRepositorySelectViewModelDesigner : ViewModelBase, IForkRepositorySelectViewModel
|
||||
{
|
||||
public ForkRepositorySelectViewModelDesigner()
|
||||
{
|
||||
Accounts = new[]
|
||||
{
|
||||
new AccountDesigner { Login = "Myself" },
|
||||
new AccountDesigner { Login = "MyOrg1" },
|
||||
new AccountDesigner { Login = "MyOrg2" },
|
||||
new AccountDesigner { Login = "MyOrg3" },
|
||||
new AccountDesigner { Login = "a-long-org-name" },
|
||||
};
|
||||
|
||||
ExistingForks = new[]
|
||||
{
|
||||
new RemoteRepositoryModelDesigner { Owner = "MyOrg5", Name = "MyRepo" },
|
||||
new RemoteRepositoryModelDesigner { Owner = "MyOrg6", Name = "MyRepo" },
|
||||
};
|
||||
}
|
||||
|
||||
public IReadOnlyList<IAccount> Accounts { get; set; }
|
||||
|
||||
public IObservable<object> Done => null;
|
||||
|
||||
public IReadOnlyList<IRemoteRepositoryModel> ExistingForks { get; set; }
|
||||
|
||||
public bool IsLoading { get; set; }
|
||||
|
||||
public string Title => null;
|
||||
|
||||
public ReactiveCommand<object> Selected => null;
|
||||
|
||||
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,5 +71,15 @@ namespace GitHub.Services
|
|||
var viewModel = factory.CreateViewModel<ILoginViewModel>();
|
||||
return (IConnection)await showDialog.Show(viewModel);
|
||||
}
|
||||
|
||||
public async Task ShowForkDialog(ILocalRepositoryModel repository, IConnection connection)
|
||||
{
|
||||
Guard.ArgumentNotNull(repository, nameof(repository));
|
||||
Guard.ArgumentNotNull(connection, nameof(connection));
|
||||
|
||||
var viewModel = factory.CreateViewModel<IForkRepositoryViewModel>();
|
||||
await viewModel.InitializeAsync(repository, connection);
|
||||
await showDialog.Show(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,12 @@ namespace GitHub.Services
|
|||
.ToReadOnlyList(Create);
|
||||
}
|
||||
|
||||
public IObservable<IRemoteRepositoryModel> GetForks(IRepositoryModel repository)
|
||||
{
|
||||
return ApiClient.GetForks(repository.Owner, repository.Name)
|
||||
.Select(x => new RemoteRepositoryModel(x));
|
||||
}
|
||||
|
||||
IObservable<LicenseCacheItem> GetLicensesFromApi()
|
||||
{
|
||||
return ApiClient.GetLicenses()
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.App;
|
||||
using GitHub.Logging;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using ReactiveUI;
|
||||
using Serilog;
|
||||
|
||||
namespace GitHub.ViewModels.Dialog
|
||||
{
|
||||
[Export(typeof(IForkRepositoryExecuteViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class ForkRepositoryExecuteViewModel : ViewModelBase, IForkRepositoryExecuteViewModel
|
||||
{
|
||||
static readonly ILogger log = LogManager.ForContext<ForkRepositoryViewModel>();
|
||||
|
||||
public ForkRepositoryExecuteViewModel()
|
||||
{
|
||||
Start = ReactiveCommand.Create();
|
||||
}
|
||||
|
||||
public IRepositoryModel SourceRepository { get; private set; }
|
||||
|
||||
public IRepositoryModel DestinationRepository { get; private set; }
|
||||
|
||||
public ReactiveCommand<object> Start { get; }
|
||||
|
||||
public string Title => Resources.ForkRepositoryTitle;
|
||||
|
||||
public IObservable<object> Done => Start;
|
||||
|
||||
public Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection, IAccount account)
|
||||
{
|
||||
SourceRepository = repository;
|
||||
DestinationRepository = new RemoteRepositoryModel(
|
||||
0,
|
||||
repository.Name,
|
||||
CreateForkUri(repository.CloneUrl, account.Login),
|
||||
false,
|
||||
true,
|
||||
account,
|
||||
null);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
UriString CreateForkUri(UriString url, string login)
|
||||
{
|
||||
var original = url.ToRepositoryUrl();
|
||||
return new UriString(
|
||||
string.Format("{0}://{1}/{2}/{3}",
|
||||
original.Scheme,
|
||||
original.Authority,
|
||||
login,
|
||||
url.RepositoryName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.App;
|
||||
using GitHub.Factories;
|
||||
using GitHub.Logging;
|
||||
using GitHub.Models;
|
||||
using ReactiveUI;
|
||||
using Serilog;
|
||||
|
||||
namespace GitHub.ViewModels.Dialog
|
||||
{
|
||||
[Export(typeof(IForkRepositorySelectViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class ForkRepositorySelectViewModel : ViewModelBase, IForkRepositorySelectViewModel
|
||||
{
|
||||
static readonly ILogger log = LogManager.ForContext<ForkRepositoryViewModel>();
|
||||
|
||||
readonly IModelServiceFactory modelServiceFactory;
|
||||
IReadOnlyList<IAccount> accounts;
|
||||
IReadOnlyList<IRemoteRepositoryModel> existingForks;
|
||||
bool isLoading;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ForkRepositorySelectViewModel(IModelServiceFactory modelServiceFactory)
|
||||
{
|
||||
this.modelServiceFactory = modelServiceFactory;
|
||||
Selected = ReactiveCommand.Create();
|
||||
}
|
||||
|
||||
public string Title => Resources.ForkRepositoryTitle;
|
||||
|
||||
public IReadOnlyList<IAccount> Accounts
|
||||
{
|
||||
get { return accounts; }
|
||||
private set { this.RaiseAndSetIfChanged(ref accounts, value); }
|
||||
}
|
||||
|
||||
public IReadOnlyList<IRemoteRepositoryModel> ExistingForks
|
||||
{
|
||||
get { return existingForks; }
|
||||
private set { this.RaiseAndSetIfChanged(ref existingForks, value); }
|
||||
}
|
||||
|
||||
public bool IsLoading
|
||||
{
|
||||
get { return isLoading; }
|
||||
private set { this.RaiseAndSetIfChanged(ref isLoading, value); }
|
||||
}
|
||||
|
||||
public ReactiveCommand<object> Selected { get; }
|
||||
|
||||
public IObservable<object> Done => Selected;
|
||||
|
||||
public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
try
|
||||
{
|
||||
var modelService = await modelServiceFactory.CreateAsync(connection);
|
||||
|
||||
Observable.CombineLatest(
|
||||
modelService.GetAccounts(),
|
||||
modelService.GetForks(repository).ToList(),
|
||||
(a, f) => new { Accounts = a, Forks = f })
|
||||
.Finally(() => IsLoading = false)
|
||||
.Subscribe(x =>
|
||||
{
|
||||
Accounts = BuildAccounts(x.Accounts, x.Forks);
|
||||
ExistingForks = BuildExistingForks(x.Accounts, x.Forks);
|
||||
});
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, "Error initializing ForkRepositoryViewModel");
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
IReadOnlyList<IAccount> BuildAccounts(IReadOnlyList<IAccount> accounts, IList<IRemoteRepositoryModel> forks)
|
||||
{
|
||||
var forksByOwner = forks.ToDictionary(x => x.Owner, x => x);
|
||||
return accounts.Where(x => !forksByOwner.ContainsKey(x.Login)).ToList();
|
||||
}
|
||||
|
||||
IReadOnlyList<IRemoteRepositoryModel> BuildExistingForks(IReadOnlyList<IAccount> accounts, IList<IRemoteRepositoryModel> forks)
|
||||
{
|
||||
var accountsByLogin = accounts.ToDictionary(x => x.Login, x => x);
|
||||
return forks.Where(x => accountsByLogin.ContainsKey(x.Owner)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Models;
|
||||
|
||||
namespace GitHub.ViewModels.Dialog
|
||||
{
|
||||
[Export(typeof(IForkRepositoryViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class ForkRepositoryViewModel : PagedDialogViewModelBase, IForkRepositoryViewModel
|
||||
{
|
||||
readonly IForkRepositorySelectViewModel selectPage;
|
||||
readonly IForkRepositoryExecuteViewModel executePage;
|
||||
ILocalRepositoryModel repository;
|
||||
IConnection connection;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ForkRepositoryViewModel(
|
||||
IForkRepositorySelectViewModel selectPage,
|
||||
IForkRepositoryExecuteViewModel executePage)
|
||||
{
|
||||
this.selectPage = selectPage;
|
||||
this.executePage = executePage;
|
||||
selectPage.Done.Subscribe(x => ShowExecutePage((IAccount)x).Forget());
|
||||
}
|
||||
|
||||
public override IObservable<object> Done => null;
|
||||
|
||||
public async Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection)
|
||||
{
|
||||
this.repository = repository;
|
||||
this.connection = connection;
|
||||
await selectPage.InitializeAsync(repository, connection);
|
||||
Content = selectPage;
|
||||
}
|
||||
|
||||
async Task ShowExecutePage(IAccount account)
|
||||
{
|
||||
await executePage.InitializeAsync(repository, connection, account);
|
||||
Content = executePage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ namespace GitHub.ViewModels.Dialog
|
|||
{
|
||||
subscription?.Dispose();
|
||||
Content = viewModel;
|
||||
subscription = viewModel.Done.Subscribe(done);
|
||||
subscription = viewModel.Done?.Subscribe(done);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace GitHub.Api
|
|||
bool useOldScopes = false,
|
||||
bool useFingerprint = true);
|
||||
|
||||
IObservable<Repository> GetForks(string owner, string name);
|
||||
IObservable<string> GetGitIgnoreTemplates();
|
||||
IObservable<LicenseMetadata> GetLicenses();
|
||||
IObservable<Unit> DeleteApplicationAuthorization(int id, string twoFactorAuthorizationCode);
|
||||
|
|
|
@ -122,6 +122,9 @@
|
|||
<Compile Include="Services\LocalRepositoriesExtensions.cs" />
|
||||
<Compile Include="Services\ModelServiceExtensions.cs" />
|
||||
<Compile Include="ViewModels\Dialog\IDialogContentViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\IForkRepositoryExecuteViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\IForkRepositoryViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\IForkRepositorySelectViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\IGitHubDialogWindowViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\IGistCreationViewModel.cs" />
|
||||
<Compile Include="ViewModels\Dialog\ILogin2FaViewModel.cs" />
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace GitHub.Services
|
|||
IObservable<IReadOnlyList<IAccount>> GetAccounts();
|
||||
IObservable<IRemoteRepositoryModel> GetRepository(string owner, string repo);
|
||||
ITrackingCollection<IRemoteRepositoryModel> GetRepositories(ITrackingCollection<IRemoteRepositoryModel> collection);
|
||||
IObservable<IRemoteRepositoryModel> GetForks(IRepositoryModel repository);
|
||||
IObservable<LicenseItem> GetLicenses();
|
||||
IObservable<GitIgnoreItem> GetGitIgnoreTemplates();
|
||||
IObservable<IPullRequestModel> GetPullRequest(string owner, string name, int number);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Models;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.ViewModels.Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// View model for selecting the account to fork a repository to.
|
||||
/// </summary>
|
||||
public interface IForkRepositoryExecuteViewModel : IDialogContentViewModel
|
||||
{
|
||||
IRepositoryModel SourceRepository { get; }
|
||||
|
||||
IRepositoryModel DestinationRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a command that is executed when the user clicks the "Fork" button.
|
||||
/// </summary>
|
||||
ReactiveCommand<object> Start { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the view model.
|
||||
/// </summary>
|
||||
/// <param name="repository">The repository to fork.</param>
|
||||
/// <param name="connection">The connection to use.</param>
|
||||
/// <param name="account">The account to fork to.</param>
|
||||
Task InitializeAsync(
|
||||
ILocalRepositoryModel repository,
|
||||
IConnection connection,
|
||||
IAccount account);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Models;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.ViewModels.Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// View model for selecting the account to fork a repository to.
|
||||
/// </summary>
|
||||
public interface IForkRepositorySelectViewModel : IDialogContentViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list of accounts that the repository can be forked to.
|
||||
/// </summary>
|
||||
IReadOnlyList<IAccount> Accounts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of existing forks for accounts that the owner has access to.
|
||||
/// </summary>
|
||||
IReadOnlyList<IRemoteRepositoryModel> ExistingForks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the view model is loading.
|
||||
/// </summary>
|
||||
bool IsLoading { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a command that is executed when the user selects an item in <see cref="Accounts"/>.
|
||||
/// </summary>
|
||||
ReactiveCommand<object> Selected { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the view model.
|
||||
/// </summary>
|
||||
/// <param name="repository">The repository to fork.</param>
|
||||
/// <param name="connection">The connection to use.</param>
|
||||
Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Models;
|
||||
|
||||
namespace GitHub.ViewModels.Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// View model for forking a repository.
|
||||
/// </summary>
|
||||
public interface IForkRepositoryViewModel : IDialogContentViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the currently displayed page.
|
||||
/// </summary>
|
||||
IDialogContentViewModel Content { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the view model.
|
||||
/// </summary>
|
||||
/// <param name="repository">The repository to fork.</param>
|
||||
/// <param name="connection">The connection to use.</param>
|
||||
Task InitializeAsync(ILocalRepositoryModel repository, IConnection connection);
|
||||
}
|
||||
}
|
|
@ -57,5 +57,12 @@ namespace GitHub.Services
|
|||
/// unsuccessful.
|
||||
/// </returns>
|
||||
Task<IConnection> ShowLoginDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Shows the Fork Repository dialog.
|
||||
/// </summary>
|
||||
/// <param name="repository">The repository to fork.</param>
|
||||
/// <param name="connection">The connection to use. May not be null.</param>
|
||||
Task ShowForkDialog(ILocalRepositoryModel repository, IConnection connection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,5 +38,14 @@ namespace GitHub.Extensions
|
|||
: base(collection)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the elements of the specified collection to the end of the list.
|
||||
/// </summary>
|
||||
/// <param name="items">The items to add.</param>
|
||||
public void AddRange(IEnumerable<T> items)
|
||||
{
|
||||
foreach (var item in items) Add(item);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -132,6 +132,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Services\VSGitExt.cs" />
|
||||
<Compile Include="Home\ForkNavigationItem.cs" />
|
||||
<Compile Include="RegistryHelper.cs" />
|
||||
<Compile Include="Services\VSGitServices.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Reactive.Linq;
|
||||
using GitHub.Api;
|
||||
using GitHub.Services;
|
||||
using GitHub.VisualStudio.Base;
|
||||
using GitHub.VisualStudio.Helpers;
|
||||
using Microsoft.TeamFoundation.Controls;
|
||||
using GitHub.UI;
|
||||
using GitHub.VisualStudio.UI;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Logging;
|
||||
using Serilog;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace GitHub.VisualStudio.TeamExplorer.Home
|
||||
{
|
||||
[TeamExplorerNavigationItem(ForkNavigationItemId, NavigationItemPriority.Fork)]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class ForkNavigationItem : TeamExplorerNavigationItemBase
|
||||
{
|
||||
static readonly ILogger log = LogManager.ForContext<ForkNavigationItem>();
|
||||
|
||||
public const string ForkNavigationItemId = "5245767A-B657-4F8E-BFEE-F04159F1DDA6";
|
||||
|
||||
readonly IDialogService dialogService;
|
||||
IConnectionManager connectionManager;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ForkNavigationItem(IGitHubServiceProvider serviceProvider,
|
||||
ISimpleApiClientFactory apiFactory,
|
||||
ITeamExplorerServiceHolder holder,
|
||||
IDialogService dialogService)
|
||||
: base(serviceProvider, apiFactory, holder, Octicon.repo_forked)
|
||||
{
|
||||
this.dialogService = dialogService;
|
||||
|
||||
Text = Resources.ForkNavigationItemText;
|
||||
ArgbColor = Colors.PurpleNavigationItem.ToInt32();
|
||||
ConnectionManager.Connections.CollectionChanged += ConnectionsChanged;
|
||||
}
|
||||
|
||||
IConnectionManager ConnectionManager
|
||||
{
|
||||
get
|
||||
{
|
||||
// We can't receive IConnectionManager in the constructor because Invalidate()
|
||||
// is called from the base constructor and so we don't have chance to save it
|
||||
// to a field.
|
||||
if (connectionManager == null)
|
||||
{
|
||||
connectionManager = ServiceProvider.GetService<IConnectionManager>();
|
||||
}
|
||||
|
||||
return connectionManager;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
ConnectionManager.Connections.CollectionChanged -= ConnectionsChanged;
|
||||
}
|
||||
|
||||
public override async void Execute()
|
||||
{
|
||||
var connection = await connectionManager.GetConnection(ActiveRepo);
|
||||
|
||||
if (connection?.IsLoggedIn == true)
|
||||
{
|
||||
await dialogService.ShowForkDialog(ActiveRepo, connection);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void Invalidate()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsVisible = false;
|
||||
|
||||
if (await IsAGitHubRepo())
|
||||
{
|
||||
var connection = await ConnectionManager.GetConnection(ActiveRepo);
|
||||
IsVisible = connection?.IsLoggedIn ?? false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, "Error updating ForkNavigationItem visibility");
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectionsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ namespace GitHub.VisualStudio.TeamExplorer.Home
|
|||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class PulseNavigationItem : TeamExplorerNavigationItemBase
|
||||
{
|
||||
public const string PulseNavigationItemId = "5245767A-B657-4F8E-BFEE-F04159F1DDA2";
|
||||
public const string PulseNavigationItemId = "5245767A-B657-4F8E-BFEE-F04159F1DDA5";
|
||||
|
||||
readonly Lazy<IVisualStudioBrowser> browser;
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ namespace GitHub.VisualStudio
|
|||
static class NavigationItemPriority
|
||||
{
|
||||
public const int PullRequests = TeamExplorerNavigationItemPriority.GitCommits - 1;
|
||||
public const int Wiki = TeamExplorerNavigationItemPriority.Settings - 1;
|
||||
public const int Wiki = TeamExplorerNavigationItemPriority.Settings - 2;
|
||||
public const int Fork = TeamExplorerNavigationItemPriority.Settings - 1;
|
||||
public const int Pulse = Wiki - 3;
|
||||
public const int Graphs = Wiki - 2;
|
||||
public const int Issues = Wiki - 1;
|
||||
|
|
|
@ -167,6 +167,9 @@
|
|||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\GitHub.TeamFoundation.14\Home\ForkNavigationItem.cs">
|
||||
<Link>Home\ForkNavigationItem.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GitHub.TeamFoundation.14\Services\VSGitExt.cs">
|
||||
<Link>Services\VSGitExt.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -294,6 +294,15 @@ namespace GitHub.VisualStudio.UI {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Fork.
|
||||
/// </summary>
|
||||
public static string ForkNavigationItemText {
|
||||
get {
|
||||
return ResourceManager.GetString("ForkNavigationItemText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get Started.
|
||||
/// </summary>
|
||||
|
|
|
@ -398,4 +398,7 @@
|
|||
<data name="OpenFileInSolution" xml:space="preserve">
|
||||
<value>Open File in Solution</value>
|
||||
</data>
|
||||
<data name="ForkNavigationItemText" xml:space="preserve">
|
||||
<value>Fork</value>
|
||||
</data>
|
||||
</root>
|
|
@ -349,6 +349,12 @@
|
|||
<DependentUpon>OptionsControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\ContentView.cs" />
|
||||
<Compile Include="Views\Dialog\ForkRepositoryExecuteView.xaml.cs">
|
||||
<DependentUpon>ForkRepositoryExecuteView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Dialog\ForkRepositorySelectView.xaml.cs">
|
||||
<DependentUpon>ForkRepositorySelectView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Dialog\GistCreationView.xaml.cs">
|
||||
<DependentUpon>GistCreationView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -492,6 +498,14 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<CustomToolNamespace>GitHub.VisualStudio.UI</CustomToolNamespace>
|
||||
</Page>
|
||||
<Page Include="Views\Dialog\ForkRepositoryExecuteView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Dialog\ForkRepositorySelectView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Dialog\GistCreationView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace GitHub.VisualStudio.Views
|
|||
/// simply add an `[ExportViewFor]` attribute to this class with the type of the view model
|
||||
/// interface.
|
||||
/// </remarks>
|
||||
[ExportViewFor(typeof(IForkRepositoryViewModel))]
|
||||
[ExportViewFor(typeof(ILoginViewModel))]
|
||||
[ExportViewFor(typeof(INavigationViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<UserControl x:Class="GitHub.VisualStudio.Views.Dialog.ForkRepositoryExecuteView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:markdig="clr-namespace:Markdig.Wpf;assembly=Markdig.Wpf"
|
||||
xmlns:sampleData="clr-namespace:GitHub.SampleData;assembly=GitHub.App"
|
||||
Margin="8"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
|
||||
<d:DesignProperties.DataContext>
|
||||
<sampleData:ForkRepositoryExecuteViewModelDesigner/>
|
||||
</d:DesignProperties.DataContext>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
You're about to fork the
|
||||
<Hyperlink>
|
||||
<Run Text="{Binding SourceRepository.Owner, Mode=OneWay}"/>/<Run Text="{Binding SourceRepository.Name, Mode=OneWay}"/>
|
||||
</Hyperlink>
|
||||
repository to
|
||||
<Hyperlink>
|
||||
<Run Text="{Binding DestinationRepository.Owner, Mode=OneWay}"/>/<Run Text="{Binding DestinationRepository.Name, Mode=OneWay}"/>
|
||||
</Hyperlink>. This operation will:
|
||||
</TextBlock>
|
||||
|
||||
<ItemsControl Margin="8,16,8,8">
|
||||
<ItemsControl.Resources>
|
||||
<Style x:Key="ItemBorder" TargetType="Border">
|
||||
<Setter Property="Background" Value="#10000000"/>
|
||||
<Setter Property="CornerRadius" Value="3"/>
|
||||
<Setter Property="Margin" Value="4"/>
|
||||
<Setter Property="Padding" Value="4,8"/>
|
||||
</Style>
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Margin" Value="0,1,6,0"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</ItemsControl.Resources>
|
||||
<Border Style="{StaticResource ItemBorder}">
|
||||
<DockPanel>
|
||||
<CheckBox IsChecked="True" IsEnabled="False"/>
|
||||
<TextBlock>Fork the repository</TextBlock>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource ItemBorder}">
|
||||
<DockPanel>
|
||||
<CheckBox IsChecked="True"/>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
Update your local repository's <Run Style="{DynamicResource {x:Static markdig:Styles.CodeStyleKey}}">origin</Run> to point to
|
||||
<Hyperlink><Run Text="{Binding DestinationRepository.CloneUrl, Mode=OneWay}"/></Hyperlink>
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource ItemBorder}">
|
||||
<DockPanel>
|
||||
<CheckBox IsChecked="True"/>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
Add an <Run Style="{DynamicResource {x:Static markdig:Styles.CodeStyleKey}}">upstream</Run> remote pointing to
|
||||
<Hyperlink><Run Text="{Binding SourceRepository.CloneUrl, Mode=OneWay}"/></Hyperlink>
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<Border Style="{StaticResource ItemBorder}">
|
||||
<DockPanel>
|
||||
<CheckBox IsChecked="True"/>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
Reset the <Run Style="{DynamicResource {x:Static markdig:Styles.CodeStyleKey}}">master</Run> branch to
|
||||
<Run Style="{DynamicResource {x:Static markdig:Styles.CodeStyleKey}}">upstream/master</Run>
|
||||
</TextBlock>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</UserControl>
|
|
@ -0,0 +1,17 @@
|
|||
using System.ComponentModel.Composition;
|
||||
using System.Windows.Controls;
|
||||
using GitHub.Exports;
|
||||
using GitHub.ViewModels.Dialog;
|
||||
|
||||
namespace GitHub.VisualStudio.Views.Dialog
|
||||
{
|
||||
[ExportViewFor(typeof(IForkRepositoryExecuteViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public partial class ForkRepositoryExecuteView : UserControl
|
||||
{
|
||||
public ForkRepositoryExecuteView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<UserControl x:Class="GitHub.VisualStudio.Views.Dialog.ForkRepositorySelectView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cache="clr-namespace:GitHub.UI.Helpers;assembly=GitHub.UI"
|
||||
xmlns:ui="clr-namespace:GitHub.UI;assembly=GitHub.UI"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:GitHub.VisualStudio.Views.Dialog"
|
||||
xmlns:sampleData="clr-namespace:GitHub.SampleData;assembly=GitHub.App"
|
||||
Margin="8"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="414" d:DesignWidth="440">
|
||||
|
||||
<Control.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.UI;component/SharedDictionary.xaml" />
|
||||
<cache:SharedDictionaryManager Source="pack://application:,,,/GitHub.UI.Reactive;component/SharedDictionary.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Control.Resources>
|
||||
|
||||
<d:DesignProperties.DataContext>
|
||||
<sampleData:ForkRepositorySelectViewModelDesigner IsLoading="True"/>
|
||||
</d:DesignProperties.DataContext>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DockPanel Grid.Row="0">
|
||||
<TextBlock DockPanel.Dock="Top" FontSize="16" Margin="0 8">
|
||||
Where should we fork this repository?
|
||||
</TextBlock>
|
||||
|
||||
<ui:GitHubProgressBar DockPanel.Dock="Top"
|
||||
Foreground="{DynamicResource GitHubAccentBrush}"
|
||||
IsIndeterminate="True"
|
||||
Style="{DynamicResource GitHubProgressBar}"
|
||||
Visibility="{Binding IsLoading, Converter={ui:BooleanToHiddenVisibilityConverter}}"/>
|
||||
|
||||
<ListBox Name="accountsListBox"
|
||||
ItemsSource="{Binding Accounts}"
|
||||
Padding="8"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
SelectionChanged="accountsListBox_SelectionChanged">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel IsItemsHost="True"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DockPanel Width="100">
|
||||
<TextBlock DockPanel.Dock="Bottom"
|
||||
Text="{Binding Login, StringFormat=@{0}}"
|
||||
TextAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
<Image Margin="0,4"
|
||||
Source="{Binding Avatar}"
|
||||
Width="32"/>
|
||||
</DockPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
<DockPanel Grid.Row="1" Visibility="{Binding ExistingForks, Converter={ui:HasItemsVisibilityConverter}}">
|
||||
<TextBlock DockPanel.Dock="Top" Margin="0 8">
|
||||
<Run FontSize="16">Can't find what you're looking for?</Run>
|
||||
<LineBreak/>
|
||||
<Run>You have existing forks of this repository:</Run>
|
||||
</TextBlock>
|
||||
<ListBox DockPanel.Dock="Bottom" ItemsSource="{Binding ExistingForks}" MaxHeight="120">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ui:OcticonImage Icon="repo_forked" Margin="0,0,2,-2"/>
|
||||
<TextBlock Text="{Binding Owner, Mode=OneWay}"/>
|
||||
<TextBlock Text="/"/>
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using GitHub.Exports;
|
||||
using GitHub.Models;
|
||||
using GitHub.ViewModels.Dialog;
|
||||
|
||||
namespace GitHub.VisualStudio.Views.Dialog
|
||||
{
|
||||
[ExportViewFor(typeof(IForkRepositorySelectViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public partial class ForkRepositorySelectView : UserControl
|
||||
{
|
||||
public ForkRepositorySelectView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void accountsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
var account = e.AddedItems.OfType<IAccount>().FirstOrDefault();
|
||||
|
||||
if (account != null)
|
||||
{
|
||||
((IForkRepositorySelectViewModel)DataContext).Selected.Execute(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче