Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/GitHub.App/Controllers/UIController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ void ConfigureLogicStates()
ConfigureSingleViewLogic(UIControllerFlow.PullRequestList, UIViewType.PRList);
ConfigureSingleViewLogic(UIControllerFlow.PullRequestDetail, UIViewType.PRDetail);
ConfigureSingleViewLogic(UIControllerFlow.PullRequestCreation, UIViewType.PRCreation);
ConfigureSingleViewLogic(UIControllerFlow.StartPageClone, UIViewType.StartPageClone);
ConfigureSingleViewLogic(UIControllerFlow.ReClone, UIViewType.StartPageClone);
}

void ConfigureSingleViewLogic(UIControllerFlow flow, UIViewType type)
Expand Down
1 change: 1 addition & 0 deletions src/GitHub.App/GitHub.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
<Compile Include="SampleData\RemoteRepositoryModelDesigner.cs" />
<Compile Include="SampleData\StartPageCloneViewModelDesigner.cs" />
<Compile Include="Services\AvatarProvider.cs" />
<Compile Include="Services\DialogService.cs" />
<Compile Include="Services\GitHubCredentialProvider.cs" />
<Compile Include="Services\IGitHubCredentialProvider.cs" />
<Compile Include="Services\ImageDownloader.cs" />
Expand Down
8 changes: 3 additions & 5 deletions src/GitHub.App/SampleData/SampleViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;

namespace GitHub.SampleData
{
Expand Down Expand Up @@ -353,7 +354,7 @@ public RepositoryCloneViewModelDesigner()
.IfPathNotRooted("Please enter a valid path");
}

public IReactiveCommand<Unit> CloneCommand
public IReactiveCommand<object> CloneCommand
{
get;
private set;
Expand Down Expand Up @@ -484,10 +485,6 @@ public ObservableCollection<ILocalRepositoryModel> Repositories
get; set;
}

public void DoClone()
{
}

public void DoCreate()
{
}
Expand All @@ -506,6 +503,7 @@ public bool OpenRepository()
}

public IConnection SectionConnection { get; }
public ICommand Clone { get; }
}

public class InfoPanelDesigner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class StartPageCloneViewModelDesigner : BaseViewModel, IBaseCloneViewMode
public string BaseRepositoryPath { get; set; }
public ReactivePropertyValidator<string> BaseRepositoryPathValidator { get; }
public ICommand BrowseForDirectory { get; }
public IReactiveCommand<Unit> CloneCommand { get; }
public IReactiveCommand<object> CloneCommand { get; }
public IRepositoryModel SelectedRepository { get; set; }
}
}
72 changes: 72 additions & 0 deletions src/GitHub.App/Services/DialogService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using GitHub.Models;
using GitHub.UI;
using GitHub.ViewModels;
using NullGuard;

namespace GitHub.Services
{
[Export(typeof(IDialogService))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class DialogService : IDialogService
{
readonly IUIProvider uiProvider;

[ImportingConstructor]
public DialogService(IUIProvider uiProvider)
{
this.uiProvider = uiProvider;
}

public Task<CloneDialogResult> ShowCloneDialog([AllowNull] IConnection connection)
{
var controller = uiProvider.Configure(UIControllerFlow.Clone, connection);
var basePath = default(string);
var repository = default(IRepositoryModel);

controller.TransitionSignal.Subscribe(x =>
{
var vm = x.View.ViewModel as IBaseCloneViewModel;

x.View.Done.Subscribe(_ =>
{
basePath = vm?.BaseRepositoryPath;
repository = vm?.SelectedRepository;
});
});

uiProvider.RunInDialog(controller);

var result = repository != null && basePath != null ?
new CloneDialogResult(basePath, repository) : null;
return Task.FromResult(result);
}

public Task<string> ShowReCloneDialog(IRepositoryModel repository)
{
var controller = uiProvider.Configure(UIControllerFlow.ReClone);
var basePath = default(string);

controller.TransitionSignal.Subscribe(x =>
{
var vm = x.View.ViewModel as IBaseCloneViewModel;

if (vm != null)
{
vm.SelectedRepository = repository;
}

x.View.Done.Subscribe(_ =>
{
basePath = vm?.BaseRepositoryPath;
});
});

uiProvider.RunInDialog(controller);

return Task.FromResult(basePath);
}
}
}
52 changes: 29 additions & 23 deletions src/GitHub.App/Services/RepositoryCloneService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using NLog;
using Rothko;
using GitHub.Helpers;
using Task = System.Threading.Tasks.Task;

namespace GitHub.Services
{
Expand All @@ -25,45 +26,50 @@ public class RepositoryCloneService : IRepositoryCloneService
readonly IOperatingSystem operatingSystem;
readonly string defaultClonePath;
readonly IVSGitServices vsGitServices;
readonly IUsageTracker usageTracker;

[ImportingConstructor]
public RepositoryCloneService(IOperatingSystem operatingSystem, IVSGitServices vsGitServices)
public RepositoryCloneService(
IOperatingSystem operatingSystem,
IVSGitServices vsGitServices,
IUsageTracker usageTracker)
{
this.operatingSystem = operatingSystem;
this.vsGitServices = vsGitServices;
this.usageTracker = usageTracker;

defaultClonePath = GetLocalClonePathFromGitProvider(operatingSystem.Environment.GetUserRepositoriesPath());
}

public IObservable<Unit> CloneRepository(string cloneUrl, string repositoryName, string repositoryPath)
/// <inheritdoc/>
public async Task CloneRepository(
string cloneUrl,
string repositoryName,
string repositoryPath,
object progress = null)
{
Guard.ArgumentNotEmptyString(cloneUrl, nameof(cloneUrl));
Guard.ArgumentNotEmptyString(repositoryName, nameof(repositoryName));
Guard.ArgumentNotEmptyString(repositoryPath, nameof(repositoryPath));

return Observable.StartAsync(async () =>
{
string path = Path.Combine(repositoryPath, repositoryName);

operatingSystem.Directory.CreateDirectory(path);
string path = Path.Combine(repositoryPath, repositoryName);

// Once we've done IO switch to the main thread to call vsGitServices.Clone() as this must be
// called on the main thread.
await ThreadingHelper.SwitchToMainThreadAsync();
// Switch to a thread pool thread for IO then back to the main thread to call
// vsGitServices.Clone() as this must be called on the main thread.
await ThreadingHelper.SwitchToPoolThreadAsync();
operatingSystem.Directory.CreateDirectory(path);
await ThreadingHelper.SwitchToMainThreadAsync();

try
{
// this will throw if it can't find it
vsGitServices.Clone(cloneUrl, path, true);
}
catch (Exception ex)
{
log.Error("Could not clone {0} to {1}. {2}", cloneUrl, path, ex);
throw;
}

return Unit.Default;
});
try
{
await vsGitServices.Clone(cloneUrl, path, true, progress);
await usageTracker.IncrementCloneCount();
}
catch (Exception ex)
{
log.Error("Could not clone {0} to {1}. {2}", cloneUrl, path, ex);
throw;
}
}

string GetLocalClonePathFromGitProvider(string fallbackPath)
Expand Down
54 changes: 6 additions & 48 deletions src/GitHub.App/ViewModels/RepositoryCloneViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ public class RepositoryCloneViewModel : BaseViewModel, IRepositoryCloneViewModel
static readonly Logger log = LogManager.GetCurrentClassLogger();

readonly IRepositoryHost repositoryHost;
readonly IRepositoryCloneService cloneService;
readonly IOperatingSystem operatingSystem;
readonly INotificationService notificationService;
readonly IUsageTracker usageTracker;
readonly ReactiveCommand<object> browseForDirectoryCommand = ReactiveCommand.Create();
bool isLoading;
bool noRepositoriesFound;
Expand All @@ -48,25 +45,18 @@ public class RepositoryCloneViewModel : BaseViewModel, IRepositoryCloneViewModel
RepositoryCloneViewModel(
IConnectionRepositoryHostMap connectionRepositoryHostMap,
IRepositoryCloneService repositoryCloneService,
IOperatingSystem operatingSystem,
INotificationService notificationService,
IUsageTracker usageTracker)
: this(connectionRepositoryHostMap.CurrentRepositoryHost, repositoryCloneService, operatingSystem, notificationService, usageTracker)
IOperatingSystem operatingSystem)
: this(connectionRepositoryHostMap.CurrentRepositoryHost, repositoryCloneService, operatingSystem)
{ }


public RepositoryCloneViewModel(
IRepositoryHost repositoryHost,
IRepositoryCloneService cloneService,
IOperatingSystem operatingSystem,
INotificationService notificationService,
IUsageTracker usageTracker)
IOperatingSystem operatingSystem)
{
this.repositoryHost = repositoryHost;
this.cloneService = cloneService;
this.operatingSystem = operatingSystem;
this.notificationService = notificationService;
this.usageTracker = usageTracker;

Title = string.Format(CultureInfo.CurrentCulture, Resources.CloneTitle, repositoryHost.Title);

Expand Down Expand Up @@ -121,7 +111,7 @@ public RepositoryCloneViewModel(
x => x.BaseRepositoryPathValidator.ValidationResult.IsValid,
(x, y) => x.Value != null && y.Value);
canClone = canCloneObservable.ToProperty(this, x => x.CanClone);
CloneCommand = ReactiveCommand.CreateAsyncObservable(canCloneObservable, OnCloneRepository);
CloneCommand = ReactiveCommand.Create(canCloneObservable);

browseForDirectoryCommand.Subscribe(_ => ShowBrowseForDirectoryDialog());
this.WhenAny(x => x.BaseRepositoryPathValidator.ValidationResult, x => x.Value)
Expand Down Expand Up @@ -158,38 +148,6 @@ bool FilterRepository(IRemoteRepositoryModel repo, int position, IList<IRemoteRe
return repo.Name.IndexOf(FilterText ?? "", StringComparison.OrdinalIgnoreCase) != -1;
}

IObservable<Unit> OnCloneRepository(object state)
{
return Observable.Start(() =>
{
var repository = SelectedRepository;
Debug.Assert(repository != null, "Should not be able to attempt to clone a repo when it's null");
if (repository == null)
{
notificationService.ShowError(Resources.RepositoryCloneFailedNoSelectedRepo);
return Observable.Return(Unit.Default);
}

// The following is a noop if the directory already exists.
operatingSystem.Directory.CreateDirectory(BaseRepositoryPath);

return cloneService.CloneRepository(repository.CloneUrl, repository.Name, BaseRepositoryPath)
.ContinueAfter(() =>
{
usageTracker.IncrementCloneCount().Forget();
return Observable.Return(Unit.Default);
});
})
.SelectMany(_ => _)
.Catch<Unit, Exception>(e =>
{
var repository = SelectedRepository;
Debug.Assert(repository != null, "Should not be able to attempt to clone a repo when it's null");
notificationService.ShowError(e.GetUserFriendlyErrorMessage(ErrorType.ClonedFailed, repository.Name));
return Observable.Return(Unit.Default);
});
}

bool IsAlreadyRepoAtPath(string path)
{
Debug.Assert(path != null, "RepositoryCloneViewModel.IsAlreadyRepoAtPath cannot be passed null as a path parameter.");
Expand Down Expand Up @@ -244,9 +202,9 @@ public string BaseRepositoryPath
}

/// <summary>
/// Fires off the cloning process
/// Signals that the user clicked the clone button.
/// </summary>
public IReactiveCommand<Unit> CloneCommand { get; private set; }
public IReactiveCommand<object> CloneCommand { get; private set; }

TrackingCollection<IRemoteRepositoryModel> repositories;
public ObservableCollection<IRemoteRepositoryModel> Repositories
Expand Down
Loading