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

Commit 1c561dd

Browse files
committed
Add missing files
1 parent 8c89ef0 commit 1c561dd

File tree

4 files changed

+607
-0
lines changed

4 files changed

+607
-0
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel.Composition;
4+
using System.Diagnostics;
5+
using System.Globalization;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Reactive;
9+
using System.Reactive.Linq;
10+
using System.Text.RegularExpressions;
11+
using System.Windows.Input;
12+
using GitHub.App;
13+
using GitHub.Exports;
14+
using GitHub.Extensions;
15+
using GitHub.Models;
16+
using GitHub.Services;
17+
using GitHub.Validation;
18+
using NLog;
19+
using NullGuard;
20+
using ReactiveUI;
21+
using Rothko;
22+
using System.Collections.ObjectModel;
23+
using GitHub.Collections;
24+
using GitHub.UI;
25+
using GitHub.Extensions.Reactive;
26+
27+
namespace GitHub.ViewModels
28+
{
29+
[ExportViewModel(ViewType=UIViewType.StartPageClone)]
30+
[PartCreationPolicy(CreationPolicy.NonShared)]
31+
public class StartPageCloneViewModel : BaseViewModel, IBaseCloneViewModel
32+
{
33+
static readonly Logger log = LogManager.GetCurrentClassLogger();
34+
35+
readonly IRepositoryCloneService cloneService;
36+
readonly IOperatingSystem operatingSystem;
37+
readonly INotificationService notificationService;
38+
readonly IUsageTracker usageTracker;
39+
readonly ReactiveCommand<object> browseForDirectoryCommand = ReactiveCommand.Create();
40+
readonly ObservableAsPropertyHelper<bool> canClone;
41+
string baseRepositoryPath;
42+
43+
[ImportingConstructor]
44+
StartPageCloneViewModel(
45+
IConnectionRepositoryHostMap connectionRepositoryHostMap,
46+
IRepositoryCloneService repositoryCloneService,
47+
IOperatingSystem operatingSystem,
48+
INotificationService notificationService,
49+
IUsageTracker usageTracker)
50+
: this(connectionRepositoryHostMap.CurrentRepositoryHost, repositoryCloneService, operatingSystem, notificationService, usageTracker)
51+
{ }
52+
53+
public StartPageCloneViewModel(
54+
IRepositoryHost repositoryHost,
55+
IRepositoryCloneService cloneService,
56+
IOperatingSystem operatingSystem,
57+
INotificationService notificationService,
58+
IUsageTracker usageTracker)
59+
{
60+
this.cloneService = cloneService;
61+
this.operatingSystem = operatingSystem;
62+
this.notificationService = notificationService;
63+
this.usageTracker = usageTracker;
64+
65+
Title = string.Format(CultureInfo.CurrentCulture, Resources.CloneTitle, repositoryHost.Title);
66+
67+
var baseRepositoryPath = this.WhenAnyValue(x => x.BaseRepositoryPath);
68+
69+
BaseRepositoryPathValidator = ReactivePropertyValidator.ForObservable(baseRepositoryPath)
70+
.IfNullOrEmpty(Resources.RepositoryCreationClonePathEmpty)
71+
.IfTrue(x => x.Length > 200, Resources.RepositoryCreationClonePathTooLong)
72+
.IfContainsInvalidPathChars(Resources.RepositoryCreationClonePathInvalidCharacters)
73+
.IfPathNotRooted(Resources.RepositoryCreationClonePathInvalid)
74+
.IfTrue(IsAlreadyRepoAtPath, Resources.RepositoryNameValidatorAlreadyExists);
75+
76+
var canCloneObservable = this.WhenAny(
77+
x => x.SelectedRepository,
78+
x => x.BaseRepositoryPathValidator.ValidationResult.IsValid,
79+
(x, y) => x.Value != null && y.Value);
80+
canClone = canCloneObservable.ToProperty(this, x => x.CanClone);
81+
CloneCommand = ReactiveCommand.CreateAsyncObservable(canCloneObservable, OnCloneRepository);
82+
83+
browseForDirectoryCommand.Subscribe(_ => ShowBrowseForDirectoryDialog());
84+
this.WhenAny(x => x.BaseRepositoryPathValidator.ValidationResult, x => x.Value)
85+
.Subscribe();
86+
BaseRepositoryPath = cloneService.DefaultClonePath;
87+
}
88+
89+
90+
IObservable<Unit> OnCloneRepository(object state)
91+
{
92+
return Observable.Start(() =>
93+
{
94+
var repository = SelectedRepository;
95+
Debug.Assert(repository != null, "Should not be able to attempt to clone a repo when it's null");
96+
if (repository == null)
97+
{
98+
notificationService.ShowError(Resources.RepositoryCloneFailedNoSelectedRepo);
99+
return Observable.Return(Unit.Default);
100+
}
101+
102+
// The following is a noop if the directory already exists.
103+
operatingSystem.Directory.CreateDirectory(BaseRepositoryPath);
104+
105+
return cloneService.CloneRepository(repository.CloneUrl, repository.Name, BaseRepositoryPath)
106+
.ContinueAfter(() =>
107+
{
108+
usageTracker.IncrementCloneCount();
109+
return Observable.Return(Unit.Default);
110+
});
111+
})
112+
.SelectMany(_ => _)
113+
.Catch<Unit, Exception>(e =>
114+
{
115+
var repository = SelectedRepository;
116+
Debug.Assert(repository != null, "Should not be able to attempt to clone a repo when it's null");
117+
notificationService.ShowError(e.GetUserFriendlyErrorMessage(ErrorType.ClonedFailed, repository.Name));
118+
return Observable.Return(Unit.Default);
119+
});
120+
}
121+
122+
bool IsAlreadyRepoAtPath(string path)
123+
{
124+
Debug.Assert(path != null, "RepositoryCloneViewModel.IsAlreadyRepoAtPath cannot be passed null as a path parameter.");
125+
126+
bool isAlreadyRepoAtPath = false;
127+
128+
if (SelectedRepository != null)
129+
{
130+
string potentialPath = Path.Combine(path, SelectedRepository.Name);
131+
isAlreadyRepoAtPath = operatingSystem.Directory.Exists(potentialPath);
132+
}
133+
134+
return isAlreadyRepoAtPath;
135+
}
136+
137+
IObservable<Unit> ShowBrowseForDirectoryDialog()
138+
{
139+
return Observable.Start(() =>
140+
{
141+
// We store this in a local variable to prevent it changing underneath us while the
142+
// folder dialog is open.
143+
var localBaseRepositoryPath = BaseRepositoryPath;
144+
var browseResult = operatingSystem.Dialog.BrowseForDirectory(localBaseRepositoryPath, Resources.BrowseForDirectory);
145+
146+
if (!browseResult.Success)
147+
return;
148+
149+
var directory = browseResult.DirectoryPath ?? localBaseRepositoryPath;
150+
151+
try
152+
{
153+
BaseRepositoryPath = directory;
154+
}
155+
catch (Exception e)
156+
{
157+
// TODO: We really should limit this to exceptions we know how to handle.
158+
log.Error(string.Format(CultureInfo.InvariantCulture,
159+
"Failed to set base repository path.{0}localBaseRepositoryPath = \"{1}\"{0}BaseRepositoryPath = \"{2}\"{0}Chosen directory = \"{3}\"",
160+
System.Environment.NewLine, localBaseRepositoryPath ?? "(null)", BaseRepositoryPath ?? "(null)", directory ?? "(null)"), e);
161+
}
162+
}, RxApp.MainThreadScheduler);
163+
}
164+
165+
/// <summary>
166+
/// Path to clone repositories into
167+
/// </summary>
168+
public string BaseRepositoryPath
169+
{
170+
[return: AllowNull]
171+
get { return baseRepositoryPath; }
172+
set { this.RaiseAndSetIfChanged(ref baseRepositoryPath, value); }
173+
}
174+
175+
/// <summary>
176+
/// Fires off the cloning process
177+
/// </summary>
178+
public IReactiveCommand<Unit> CloneCommand { get; private set; }
179+
180+
ISimpleRepositoryModel selectedRepository;
181+
/// <summary>
182+
/// Selected repository to clone
183+
/// </summary>
184+
[AllowNull]
185+
public ISimpleRepositoryModel SelectedRepository
186+
{
187+
[return: AllowNull]
188+
get { return selectedRepository; }
189+
set { this.RaiseAndSetIfChanged(ref selectedRepository, value); }
190+
}
191+
192+
public ICommand BrowseForDirectory
193+
{
194+
get { return browseForDirectoryCommand; }
195+
}
196+
197+
public bool CanClone
198+
{
199+
get { return canClone.Value; }
200+
}
201+
202+
public ReactivePropertyValidator<string> BaseRepositoryPathValidator
203+
{
204+
get;
205+
private set;
206+
}
207+
}
208+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Collections.Generic;
2+
using System.Reactive;
3+
using GitHub.Models;
4+
using ReactiveUI;
5+
using System.Collections.ObjectModel;
6+
7+
namespace GitHub.ViewModels
8+
{
9+
/// <summary>
10+
/// ViewModel for the the Clone Repository dialog
11+
/// </summary>
12+
public interface IBaseCloneViewModel : IViewModel, IRepositoryCreationTarget
13+
{
14+
/// <summary>
15+
/// Command to clone the currently selected repository.
16+
/// </summary>
17+
IReactiveCommand<Unit> CloneCommand { get; }
18+
19+
ISimpleRepositoryModel SelectedRepository { get; set; }
20+
}
21+
}

0 commit comments

Comments
 (0)