Example #1
0
        void HandleCreatedRepo(ISimpleRepositoryModel newrepo)
        {
            var msg = string.Format(CultureInfo.CurrentUICulture, Constants.Notification_RepoCreated, newrepo.Name, newrepo.CloneUrl);

            msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_CreateNewProject, newrepo.LocalPath);
            ShowNotification(newrepo, msg);
        }
        /// <summary>
        /// Gets a collection of Pull Requests. If you want to refresh existing data, pass a collection in
        /// </summary>
        /// <param name="repo"></param>
        /// <param name="collection"></param>
        /// <returns></returns>
        public ITrackingCollection <IPullRequestModel> GetPullRequests(ISimpleRepositoryModel repo,
                                                                       ITrackingCollection <IPullRequestModel> collection)
        {
            // Since the api to list pull requests returns all the data for each pr, cache each pr in its own entry
            // and also cache an index that contains all the keys for each pr. This way we can fetch prs in bulk
            // but also individually without duplicating information. We store things in a custom observable collection
            // that checks whether an item is being updated (coming from the live stream after being retrieved from cache)
            // and replaces it instead of appending, so items get refreshed in-place as they come in.

            var keyobs = GetUserFromCache()
                         .Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name));

            var source = Observable.Defer(() => keyobs
                                          .SelectMany(key =>
                                                      hostCache.GetAndFetchLatestFromIndex(key, () =>
                                                                                           apiClient.GetPullRequestsForRepository(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName)
                                                                                           .Select(PullRequestCacheItem.Create),
                                                                                           item =>
            {
                // this could blow up due to the collection being disposed somewhere else
                try { collection.RemoveItem(Create(item)); }
                catch (ObjectDisposedException) { }
            },
                                                                                           TimeSpan.Zero,
                                                                                           TimeSpan.FromDays(7))
                                                      )
                                          .Select(Create)
                                          );

            collection.Listen(source);
            return(collection);
        }
Example #3
0
        async Task <IPullRequestModel> PushAndCreatePR(IRepositoryHost host,
                                                       ISimpleRepositoryModel sourceRepository, ISimpleRepositoryModel targetRepository,
                                                       IBranch sourceBranch, IBranch targetBranch,
                                                       string title, string body)
        {
            var repo = await Task.Run(() => gitService.GetRepository(sourceRepository.LocalPath));

            var remote = await gitClient.GetHttpRemote(repo, "origin");

            await gitClient.Push(repo, sourceBranch.Name, remote.Name);

            if (!repo.Branches[sourceBranch.Name].IsTracking)
            {
                await gitClient.SetTrackingBranch(repo, sourceBranch.Name, remote.Name);
            }

            // delay things a bit to avoid a race between pushing a new branch and creating a PR on it
            if (!Splat.ModeDetector.Current.InUnitTestRunner().GetValueOrDefault())
            {
                await Task.Delay(TimeSpan.FromSeconds(5));
            }

            var ret = await host.ModelService.CreatePullRequest(sourceRepository, targetRepository, sourceBranch, targetBranch, title, body);

            usageTracker.IncrementUpstreamPullRequestCount();
            return(ret);
        }
 void ActiveRepoPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
 {
     if (e.PropertyName == "CloneUrl")
     {
         ActiveRepo = sender as ISimpleRepositoryModel;
     }
 }
Example #5
0
        public PullRequestListViewModel(
            IRepositoryHost repositoryHost,
            ISimpleRepositoryModel repository,
            IPackageSettings settings)
        {
            this.repositoryHost = repositoryHost;
            this.repository     = repository;
            this.settings       = settings;

            this.listSettings = settings.UIState
                                .GetOrCreateRepositoryState(repository.CloneUrl)
                                .PullRequests;

            openPullRequestCommand = ReactiveCommand.Create();
            openPullRequestCommand.Subscribe(_ =>
            {
                VisualStudio.Services.DefaultExportProvider.GetExportedValue <IVisualStudioBrowser>().OpenUrl(repositoryHost.Address.WebUri);
            });

            States = new List <PullRequestState> {
                new PullRequestState {
                    IsOpen = true, Name = "Open"
                },
                new PullRequestState {
                    IsOpen = false, Name = "Closed"
                },
                new PullRequestState {
                    Name = "All"
                }
            };

            trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(),
                                                                OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare);
            trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(),
                                                                  OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare);
            trackingAuthors.Subscribe();
            trackingAssignees.Subscribe();

            Authors   = trackingAuthors.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAuthor));
            Assignees = trackingAssignees.CreateListenerCollection(EmptyUser, this.WhenAnyValue(x => x.SelectedAssignee));

            PullRequests          = new TrackingCollection <IPullRequestModel>();
            pullRequests.Comparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare;

            pullRequests.NewerComparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare;

            this.WhenAny(x => x.SelectedState, x => x.Value)
            .Where(x => PullRequests != null)
            .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor));

            this.WhenAny(x => x.SelectedAssignee, x => x.Value)
            .Where(x => PullRequests != null && x != EmptyUser && IsLoaded)
            .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor));

            this.WhenAny(x => x.SelectedAuthor, x => x.Value)
            .Where(x => PullRequests != null && x != EmptyUser && IsLoaded)
            .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a));

            SelectedState = States.FirstOrDefault(x => x.Name == listSettings.SelectedState) ?? States[0];
        }
Example #6
0
        void UpdateRepo(ISimpleRepositoryModel repo)
        {
            var changed = ActiveRepo != repo;

            ActiveRepo = repo;
            RepoChanged(changed);
            Invalidate();
        }
Example #7
0
        public static bool MightContainSolution(this ISimpleRepositoryModel repository)
        {
            var dir = new DirectoryInfo(repository.LocalPath);

            return(dir.EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly)
                   .Any(x => ((x.Attributes.HasFlag(FileAttributes.Directory) || x.Attributes.HasFlag(FileAttributes.Normal)) &&
                              !x.Name.StartsWith(".", StringComparison.Ordinal) && !x.Name.StartsWith("readme", StringComparison.OrdinalIgnoreCase))));
        }
        public BranchModel(string name, ISimpleRepositoryModel repo)
        {
            Extensions.Guard.ArgumentNotEmptyString(name, nameof(name));
            Extensions.Guard.ArgumentNotNull(repo, nameof(repo));

            Name       = DisplayName = name;
            Repository = repo;
            Id         = String.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name);
        }
        public BranchModel(Octokit.Branch branch, ISimpleRepositoryModel repo)
        {
            Extensions.Guard.ArgumentNotNull(branch, nameof(branch));
            Extensions.Guard.ArgumentNotNull(repo, nameof(repo));

            Name       = DisplayName = branch.Name;
            Repository = repo;
            Id         = String.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name);
        }
        public IObservable <IBranch> GetBranches(ISimpleRepositoryModel repo)
        {
            var keyobs = GetUserFromCache()
                         .Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}|branch", user.Login, repo.Name));

            return(Observable.Defer(() => keyobs
                                    .SelectMany(key => apiClient.GetBranches(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName)))
                   .Select(x => new BranchModel(x, repo)));
        }
        public PullRequestListViewModel(IRepositoryHost repositoryHost, ISimpleRepositoryModel repository)
        {
            this.repositoryHost = repositoryHost;
            this.repository     = repository;

            openPullRequestCommand = ReactiveCommand.Create();
            openPullRequestCommand.Subscribe(_ =>
            {
                VisualStudio.Services.DefaultExportProvider.GetExportedValue <IVisualStudioBrowser>().OpenUrl(repositoryHost.Address.WebUri);
            });

            States = new List <PullRequestState> {
                new PullRequestState {
                    IsOpen = true, Name = "Open"
                },
                new PullRequestState {
                    IsOpen = false, Name = "Closed"
                },
                new PullRequestState {
                    Name = "All"
                }
            };
            SelectedState = States[0];

            this.WhenAny(x => x.SelectedState, x => x.Value)
            .Where(x => PullRequests != null)
            .Subscribe(s => UpdateFilter(s, SelectedAssignee, SelectedAuthor));

            this.WhenAny(x => x.SelectedAssignee, x => x.Value)
            .Where(x => PullRequests != null)
            .Subscribe(a => UpdateFilter(SelectedState, a, SelectedAuthor));

            this.WhenAny(x => x.SelectedAuthor, x => x.Value)
            .Where(x => PullRequests != null)
            .Subscribe(a => UpdateFilter(SelectedState, SelectedAssignee, a));

            trackingAuthors = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(),
                                                                OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare);
            trackingAssignees = new TrackingCollection <IAccount>(Observable.Empty <IAccount>(),
                                                                  OrderedComparer <IAccount> .OrderByDescending(x => x.Login).Compare);
            trackingAuthors.Subscribe();
            trackingAssignees.Subscribe();

            Authors = trackingAuthors.CreateListenerCollection(new List <IAccount> {
                EmptyUser
            });
            Assignees = trackingAssignees.CreateListenerCollection(new List <IAccount> {
                EmptyUser
            });

            PullRequests          = new TrackingCollection <IPullRequestModel>();
            pullRequests.Comparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare;

            pullRequests.Filter        = (pr, i, l) => pr.IsOpen;
            pullRequests.NewerComparer = OrderedComparer <IPullRequestModel> .OrderByDescending(x => x.UpdatedAt).Compare;
        }
        public BranchModel(LibGit2Sharp.Branch branch, ISimpleRepositoryModel repo)
        {
            Extensions.Guard.ArgumentNotNull(branch, nameof(branch));
            Extensions.Guard.ArgumentNotNull(repo, nameof(repo));

            Name       = DisplayName = branch.FriendlyName;
            Repository = branch.IsRemote ? new SimpleRepositoryModel(branch.Remote.Url) : repo;
            IsTracking = branch.IsTracking;
            Id         = String.Format(CultureInfo.InvariantCulture, "{0}/{1}", Repository.Owner, Name);
        }
Example #13
0
        void HandleClonedRepo(ISimpleRepositoryModel newrepo)
        {
            var msg = string.Format(CultureInfo.CurrentUICulture, Constants.Notification_RepoCloned, newrepo.Name, newrepo.CloneUrl);

            if (newrepo.HasCommits() && newrepo.MightContainSolution())
            {
                msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_OpenProject, newrepo.LocalPath);
            }
            else
            {
                msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_CreateNewProject, newrepo.LocalPath);
            }
            ShowNotification(newrepo, msg);
        }
Example #14
0
        public IObservable <IPullRequestModel> CreatePullRequest(IRepositoryHost host,
                                                                 ISimpleRepositoryModel sourceRepository, ISimpleRepositoryModel targetRepository,
                                                                 IBranch sourceBranch, IBranch targetBranch,
                                                                 string title, string body
                                                                 )
        {
            Extensions.Guard.ArgumentNotNull(host, nameof(host));
            Extensions.Guard.ArgumentNotNull(sourceRepository, nameof(sourceRepository));
            Extensions.Guard.ArgumentNotNull(targetRepository, nameof(targetRepository));
            Extensions.Guard.ArgumentNotNull(sourceBranch, nameof(sourceBranch));
            Extensions.Guard.ArgumentNotNull(targetBranch, nameof(targetBranch));
            Extensions.Guard.ArgumentNotNull(title, nameof(title));
            Extensions.Guard.ArgumentNotNull(body, nameof(body));

            return(PushAndCreatePR(host, sourceRepository, targetRepository, sourceBranch, targetBranch, title, body).ToObservable());
        }
Example #15
0
        void RefreshRepo()
        {
            ActiveRepo = ServiceProvider.GetExportedValue<ITeamExplorerServiceHolder>().ActiveRepo;

            if (ActiveRepo == null)
            {
                var vsservices = ServiceProvider.GetExportedValue<IVSServices>();
                string path = vsservices?.GetActiveRepoPath() ?? String.Empty;
                try
                {
                    ActiveRepo = !String.IsNullOrEmpty(path) ? new SimpleRepositoryModel(path) : null;
                }
                catch (Exception ex)
                {
                    VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0}: Error loading the repository from '{1}'. {2}", GetType(), path, ex));
                }
            }
        }
Example #16
0
        void RefreshRepo()
        {
            ActiveRepo = ServiceProvider.GetExportedValue <ITeamExplorerServiceHolder>().ActiveRepo;

            if (ActiveRepo == null)
            {
                var    vsservices = ServiceProvider.GetExportedValue <IVSServices>();
                string path       = vsservices?.GetActiveRepoPath() ?? String.Empty;
                try
                {
                    ActiveRepo = !String.IsNullOrEmpty(path) ? new SimpleRepositoryModel(path) : null;
                }
                catch (Exception ex)
                {
                    VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0}: Error loading the repository from '{1}'. {2}", GetType(), path, ex));
                }
            }
        }
Example #17
0
        public IObservable <string> GetPullRequestTemplate(ISimpleRepositoryModel repository)
        {
            Extensions.Guard.ArgumentNotNull(repository, nameof(repository));

            return(Observable.Defer(() =>
            {
                var paths = TemplatePaths.Select(x => Path.Combine(repository.LocalPath, x));

                foreach (var path in paths)
                {
                    if (os.File.Exists(path))
                    {
                        try { return Observable.Return(os.File.ReadAllText(path, Encoding.UTF8)); } catch { }
                    }
                }
                return Observable.Empty <string>();
            }));
        }
Example #18
0
        void ShowNotification(ISimpleRepositoryModel newrepo, string msg)
        {
            var teServices = ServiceProvider.GetExportedValue <ITeamExplorerServices>();

            teServices.ClearNotifications();
            teServices.ShowMessage(
                msg,
                new RelayCommand(o =>
            {
                var str = o.ToString();

                /* the prefix is the action to perform:
                 * u: launch browser with url
                 * c: launch create new project dialog
                 * o: launch open existing project dialog
                 */
                var prefix = str.Substring(0, 2);
                if (prefix == "u:")
                {
                    OpenInBrowser(ServiceProvider.GetExportedValue <IVisualStudioBrowser>(), new Uri(str.Substring(2)));
                }
                else if (prefix == "o:")
                {
                    if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(str.Substring(2), 1)))
                    {
                        ServiceProvider.TryGetService <ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    }
                }
                else if (prefix == "c:")
                {
                    var vsGitServices = ServiceProvider.GetExportedValue <IVSGitServices>();
                    vsGitServices.SetDefaultProjectPath(newrepo.LocalPath);
                    if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().CreateNewProjectViaDlg(null, null, 0)))
                    {
                        ServiceProvider.TryGetService <ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    }
                }
            })
                );
#if DEBUG
            VsOutputLogger.WriteLine(String.Format(CultureInfo.InvariantCulture, "{0} Notification", DateTime.Now));
#endif
        }
Example #19
0
        async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                // if we're cloning or creating, only one repo will be added to the list
                // so we can handle just one new entry separately
                if (isCloning || isCreating)
                {
                    var newrepo = e.NewItems.Cast <ISimpleRepositoryModel>().First();

                    SelectedRepository = newrepo;
                    if (isCreating)
                    {
                        HandleCreatedRepo(newrepo);
                    }
                    else
                    {
                        HandleClonedRepo(newrepo);
                    }

                    isCreating = isCloning = false;
                    var repo = await ApiFactory.Create(newrepo.CloneUrl).GetRepository();

                    newrepo.SetIcon(repo.Private, repo.Fork);
                }
                // looks like it's just a refresh with new stuff on the list, update the icons
                else
                {
                    e.NewItems
                    .Cast <ISimpleRepositoryModel>()
                    .ForEach(async r =>
                    {
                        if (Equals(Holder.ActiveRepo, r))
                        {
                            SelectedRepository = r;
                        }
                        var repo = await ApiFactory.Create(r.CloneUrl).GetRepository();
                        r.SetIcon(repo.Private, repo.Fork);
                    });
                }
            }
        }
        public bool OpenRepository()
        {
            var old = Repositories.FirstOrDefault(x => x.Equals(Holder.ActiveRepo));

            // open the solution selection dialog when the user wants to switch to a different repo
            // since there's no other way of changing the source control context in VS
            if (!Equals(SelectedRepository, old))
            {
                if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(SelectedRepository.LocalPath, 1)))
                {
                    ServiceProvider.TryGetService <ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    return(true);
                }
                else
                {
                    SelectedRepository = old;
                    return(false);
                }
            }
            return(false);
        }
        void CheckAndUpdate(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "ActiveRepositories")
            {
                return;
            }

            var service = GitService;

            if (service == null)
            {
                return;
            }

            var repo = service.ActiveRepositories.FirstOrDefault()?.ToModel();

            if (repo != ActiveRepo)
            {
                // so annoying that this is on the wrong thread
                syncContext.Post(r => ActiveRepo = r as ISimpleRepositoryModel, repo);
            }
        }
    public void CreatePullRequestAllArgsMandatory()
    {
        var serviceProvider = Substitutes.ServiceProvider;
        var service         = new PullRequestService(Substitute.For <IGitClient>(), serviceProvider.GetGitService(), serviceProvider.GetOperatingSystem(), Substitute.For <IUsageTracker>());

        IRepositoryHost        host       = null;
        ISimpleRepositoryModel sourceRepo = null;
        ISimpleRepositoryModel targetRepo = null;
        string  title  = null;
        string  body   = null;
        IBranch source = null;
        IBranch target = null;

        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        host = serviceProvider.GetRepositoryHosts().GitHubHost;
        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        sourceRepo = new SimpleRepositoryModel("name", new GitHub.Primitives.UriString("http://github.com/github/stuff"));
        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        targetRepo = new SimpleRepositoryModel("name", new GitHub.Primitives.UriString("http://github.com/github/stuff"));
        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        title = "a title";
        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        body = "a body";
        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        source = new BranchModel("source", sourceRepo);
        Assert.Throws <ArgumentNullException>(() => service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body));

        target = new BranchModel("target", targetRepo);
        var pr = service.CreatePullRequest(host, sourceRepo, targetRepo, source, target, title, body);

        Assert.NotNull(pr);
    }
        async void UIContextChanged(bool active, bool refresh)
        {
            Debug.Assert(ServiceProvider != null, "UIContextChanged called before service provider is set");
            if (ServiceProvider == null)
            {
                return;
            }

            if (active)
            {
                GitService = GitService ?? ServiceProvider.GetService <IGitExt>();
                if (ActiveRepo == null || refresh)
                {
                    ActiveRepo = await System.Threading.Tasks.Task.Run(() =>
                    {
                        var repos = GitService?.ActiveRepositories;
                        // Looks like this might return null after a while, for some unknown reason
                        // if it does, let's refresh the GitService instance in case something got wonky
                        // and try again. See issue #23
                        if (repos == null)
                        {
                            VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "Error 2001: ActiveRepositories is null. GitService: '{0}'", GitService));
                            GitService = ServiceProvider?.GetService <IGitExt>();
                            repos      = GitService?.ActiveRepositories;
                            if (repos == null)
                            {
                                VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "Error 2002: ActiveRepositories is null. GitService: '{0}'", GitService));
                            }
                        }
                        return(repos?.FirstOrDefault()?.ToModel());
                    });
                }
            }
            else
            {
                ActiveRepo = null;
            }
        }
    public void GenerateUrl(int testid, bool createRootedPath, string baseUrl, string sha, string path, int startLine, int endLine, string expected)
    {
        SetupRepository(sha);

        var basePath = Directory.CreateSubdirectory("generate-url-test1-" + testid);

        if (createRootedPath && path != null)
        {
            path = System.IO.Path.Combine(basePath.FullName, path);
        }
        ISimpleRepositoryModel model = null;

        if (!String.IsNullOrEmpty(baseUrl))
        {
            model = new SimpleRepositoryModel("bar", new UriString(baseUrl), basePath.FullName);
        }
        else
        {
            model = new SimpleRepositoryModel(basePath.FullName);
        }
        var result = model.GenerateUrl(path, startLine, endLine);

        Assert.Equal(expected, result?.ToString());
    }
 public bool OpenRepository()
 {
     var old = Repositories.FirstOrDefault(x => x.Equals(Holder.ActiveRepo));
     // open the solution selection dialog when the user wants to switch to a different repo
     // since there's no other way of changing the source control context in VS
     if (!Equals(SelectedRepository, old))
     {
         if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(SelectedRepository.LocalPath, 1)))
         {
             ServiceProvider.TryGetService<ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
             return true;
         }
         else
         {
             SelectedRepository = old;
             return false;
         }
     }
     return false;
 }
 void UpdateRepo(ISimpleRepositoryModel repo)
 {
     ActiveRepo = repo;
     RepoChanged();
     Invalidate();
 }
 void HandleClonedRepo(ISimpleRepositoryModel newrepo)
 {
     var msg = string.Format(CultureInfo.CurrentUICulture, Constants.Notification_RepoCloned, newrepo.Name, newrepo.CloneUrl);
     if (newrepo.HasCommits() && newrepo.MightContainSolution())
         msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_OpenProject, newrepo.LocalPath);
     else
         msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_CreateNewProject, newrepo.LocalPath);
     ShowNotification(newrepo, msg);
 }
        void ShowNotification(ISimpleRepositoryModel newrepo, string msg)
        {
            var vsservices = ServiceProvider.GetExportedValue<IVSServices>();
            vsservices.ClearNotifications();
            vsservices.ShowMessage(
                msg,
                new RelayCommand(o =>
                {
                    var str = o.ToString();
                    /* the prefix is the action to perform:
                     * u: launch browser with url
                     * c: launch create new project dialog
                     * o: launch open existing project dialog 
                    */
                    var prefix = str.Substring(0, 2);
                    if (prefix == "u:")
                        OpenInBrowser(ServiceProvider.TryGetService<IVisualStudioBrowser>(), new Uri(str.Substring(2)));
                    else if (prefix == "o:")
                    {
                        if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(str.Substring(2), 1)))
                            ServiceProvider.TryGetService<ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    }
                    else if (prefix == "c:")
                    {
                        vsservices.SetDefaultProjectPath(newrepo.LocalPath);
                        if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().CreateNewProjectViaDlg(null, null, 0)))
                            ServiceProvider.TryGetService<ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    }
                })
            );
#if DEBUG
            VsOutputLogger.WriteLine(String.Format(CultureInfo.InvariantCulture, "{0} Notification", DateTime.Now));
#endif
        }
Example #29
0
        async Task <CodeContainer> RunAcquisition(IProgress <ServiceProgressData> downloadProgress, CancellationToken cancellationToken, ISimpleRepositoryModel repository)
        {
            CloneRequest request = null;

            try
            {
                var uiProvider = await Task.Run(() => Package.GetGlobalService(typeof(IUIProvider)) as IUIProvider);

                var cm = uiProvider.TryGetService <IConnectionManager>();
                var gitRepositories = await GetGitRepositoriesExt(uiProvider);

                request = ShowCloneDialog(uiProvider, gitRepositories, repository);
            }
            catch
            {
                // TODO: log
            }

            if (request == null)
            {
                return(null);
            }

            var path = Path.Combine(request.BasePath, request.Repository.Name);
            var uri  = request.Repository.CloneUrl.ToRepositoryUrl();

            return(new CodeContainer(
                       localProperties: new CodeContainerLocalProperties(path, CodeContainerType.Folder,
                                                                         new CodeContainerSourceControlProperties(request.Repository.Name, path, GitSccProvider)),
                       remote: new RemoteCodeContainer(request.Repository.Name,
                                                       GitSccProvider,
                                                       uri,
                                                       new Uri(uri.ToString().TrimSuffix(".git")),
                                                       DateTimeOffset.UtcNow),
                       isFavorite: false,
                       lastAccessed: DateTimeOffset.UtcNow));
        }
 void HandleCreatedRepo(ISimpleRepositoryModel newrepo)
 {
     var msg = string.Format(CultureInfo.CurrentUICulture, Constants.Notification_RepoCreated, newrepo.Name, newrepo.CloneUrl);
     msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_CreateNewProject, newrepo.LocalPath);
     ShowNotification(newrepo, msg);
 }
Example #31
0
        public static bool HasCommits(this ISimpleRepositoryModel repository)
        {
            var repo = GitService.GitServiceHelper.GetRepository(repository.LocalPath);

            return(repo?.Commits.Any() ?? false);
        }
 void ActiveRepoPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
 {
     if (e.PropertyName == "CloneUrl")
         ActiveRepo = sender as ISimpleRepositoryModel;
 }
 async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs e)
 {
     if (e.Action == NotifyCollectionChangedAction.Add)
     {
         // if we're cloning or creating, only one repo will be added to the list
         // so we can handle just one new entry separately
         if (isCloning || isCreating)
         {
             var newrepo = e.NewItems.Cast<ISimpleRepositoryModel>().First();
             SelectedRepository = newrepo;
             if (isCreating)
             {
                 var vsservices = ServiceProvider.GetExportedValue<IVSServices>();
                 vsservices.ClearNotifications();
                 vsservices.ShowMessage(string.Format(CultureInfo.CurrentUICulture, "[{0}]({1}) has been successfully created.", newrepo.Name, newrepo.CloneUrl));
             }
             // if we've cloned a repo but the user didn't open a project in it,
             // then update the newly-cloned repo icon because we're not going to
             // switch to the TE home page
             if ((isCloning && !OpenRepository()) || isCreating)
             {
                 var repo = await ApiFactory.Create(newrepo.CloneUrl).GetRepository();
                 newrepo.SetIcon(repo.Private, repo.Fork);
             }
         }
         // looks like it's just a refresh with new stuff on the list, update the icons
         else
         {
             e.NewItems
                 .Cast<ISimpleRepositoryModel>()
                 .ForEach(async r =>
             {
                 if (String.Equals(Holder.ActiveRepo?.RepositoryPath, r.LocalPath, StringComparison.CurrentCultureIgnoreCase))
                     SelectedRepository = r;
                 var repo = await ApiFactory.Create(r.CloneUrl).GetRepository();
                 r.SetIcon(repo.Private, repo.Fork);
             });
         }
     }
 }
        public PullRequestCreationViewModel(IRepositoryHost repositoryHost, ISimpleRepositoryModel activeRepo,
                                            IPullRequestService service, INotificationService notifications)
        {
            Extensions.Guard.ArgumentNotNull(repositoryHost, nameof(repositoryHost));
            Extensions.Guard.ArgumentNotNull(activeRepo, nameof(activeRepo));
            Extensions.Guard.ArgumentNotNull(service, nameof(service));
            Extensions.Guard.ArgumentNotNull(notifications, nameof(notifications));

            this.repositoryHost = repositoryHost;

            var obs = repositoryHost.ApiClient.GetRepository(activeRepo.Owner, activeRepo.Name)
                      .Select(r => new RepositoryModel(r))
                      .PublishLast();

            disposables.Add(obs.Connect());
            githubObs = obs;

            githubRepository = githubObs.ToProperty(this, x => x.GitHubRepository);

            this.WhenAnyValue(x => x.GitHubRepository)
            .WhereNotNull()
            .Subscribe(r =>
            {
                TargetBranch = r.IsFork ? r.Parent.DefaultBranch : r.DefaultBranch;
            });

            SourceBranch = activeRepo.CurrentBranch;
            service.GetPullRequestTemplate(activeRepo)
            .Subscribe(x => Description = x ?? String.Empty, () => Description = Description ?? String.Empty);

            this.WhenAnyValue(x => x.Branches)
            .WhereNotNull()
            .Where(_ => TargetBranch != null)
            .Subscribe(x =>
            {
                if (!x.Any(t => t.Equals(TargetBranch)))
                {
                    TargetBranch = GitHubRepository.IsFork ? GitHubRepository.Parent.DefaultBranch : GitHubRepository.DefaultBranch;
                }
            });

            SetupValidators();

            var whenAnyValidationResultChanges = this.WhenAny(
                x => x.TitleValidator.ValidationResult,
                x => x.BranchValidator.ValidationResult,
                x => x.IsBusy,
                (x, y, z) => (x.Value?.IsValid ?? false) && (y.Value?.IsValid ?? false) && !z.Value);

            this.WhenAny(x => x.BranchValidator.ValidationResult, x => x.GetValue())
            .WhereNotNull()
            .Where(x => !x.IsValid && x.DisplayValidationError)
            .Subscribe(x => notifications.ShowError(BranchValidator.ValidationResult.Message));

            createPullRequest = ReactiveCommand.CreateAsyncObservable(whenAnyValidationResultChanges,
                                                                      _ => service
                                                                      .CreatePullRequest(repositoryHost, activeRepo, TargetBranch.Repository, SourceBranch, TargetBranch, PRTitle, Description ?? String.Empty)
                                                                      .Catch <IPullRequestModel, Exception>(ex =>
            {
                log.Error(ex);

                //TODO:Will need a uniform solution to HTTP exception message handling
                var apiException = ex as ApiValidationException;
                var error        = apiException?.ApiError?.Errors?.FirstOrDefault();
                notifications.ShowError(error?.Message ?? ex.Message);
                return(Observable.Empty <IPullRequestModel>());
            }))

                                .OnExecuteCompleted(pr =>
            {
                notifications.ShowMessage(String.Format(CultureInfo.CurrentCulture, Resources.PRCreatedUpstream, TargetBranch.Id,
                                                        TargetBranch.Repository.CloneUrl.ToRepositoryUrl().Append("pull/" + pr.Number)));
            });

            isExecuting = CreatePullRequest.IsExecuting.ToProperty(this, x => x.IsExecuting);

            this.WhenAnyValue(x => x.Initialized, x => x.GitHubRepository, x => x.Description, x => x.IsExecuting)
            .Select(x => !(x.Item1 && x.Item2 != null && x.Item3 != null && !x.Item4))
            .Subscribe(x => IsBusy = x);
        }
 void UIContextChanged(object sender, UIContextChangedEventArgs e)
 {
     ActiveRepo = null;
     UIContextChanged(e.Activated, false);
 }
 void UpdateRepo(ISimpleRepositoryModel repo)
 {
     ActiveRepo = repo;
     RepoChanged();
     Invalidate();
 }
        public static string CurrentSha(this ISimpleRepositoryModel repository)
        {
            var repo = GitService.GitServiceHelper.GetRepo(repository.LocalPath);

            return(repo.Commits.FirstOrDefault()?.Sha);
        }
        void CheckAndUpdate(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "ActiveRepositories")
                return;

            var service = GitService;
            if (service == null)
                return;

            var repo = service.ActiveRepositories.FirstOrDefault()?.ToModel();
            if (repo != ActiveRepo)
                // so annoying that this is on the wrong thread
                syncContext.Post(r => ActiveRepo = r as ISimpleRepositoryModel, repo);
        }
        async void UIContextChanged(bool active, bool refresh)
        {
            Debug.Assert(ServiceProvider != null, "UIContextChanged called before service provider is set");
            if (ServiceProvider == null)
                return;

            if (active)
            {
                GitService = GitService ?? ServiceProvider.GetService<IGitExt>();
                if (ActiveRepo == null || refresh)
                    ActiveRepo = await System.Threading.Tasks.Task.Run(() =>
                    {
                        var repos = GitService?.ActiveRepositories;
                        // Looks like this might return null after a while, for some unknown reason
                        // if it does, let's refresh the GitService instance in case something got wonky
                        // and try again. See issue #23
                        if (repos == null)
                        {
                            VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "Error 2001: ActiveRepositories is null. GitService: '{0}'", GitService));
                            GitService = ServiceProvider?.GetService<IGitExt>();
                            repos = GitService?.ActiveRepositories;
                            if (repos == null)
                                VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "Error 2002: ActiveRepositories is null. GitService: '{0}'", GitService));
                        }
                        return repos?.FirstOrDefault()?.ToModel();
                    });
            }
            else
                ActiveRepo = null;
        }
        void OnPropertyChange(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsVisible" && IsVisible && View == null)
            {
                View = new GitHubConnectContent {
                    DataContext = this
                }
            }
            ;
        }

        async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                // if we're cloning or creating, only one repo will be added to the list
                // so we can handle just one new entry separately
                if (isCloning || isCreating)
                {
                    var newrepo = e.NewItems.Cast <ISimpleRepositoryModel>().First();

                    SelectedRepository = newrepo;
                    if (isCreating)
                    {
                        HandleCreatedRepo(newrepo);
                    }
                    else
                    {
                        HandleClonedRepo(newrepo);
                    }

                    var repo = await ApiFactory.Create(newrepo.CloneUrl).GetRepository();

                    newrepo.SetIcon(repo.Private, repo.Fork);
                }
                // looks like it's just a refresh with new stuff on the list, update the icons
                else
                {
                    e.NewItems
                    .Cast <ISimpleRepositoryModel>()
                    .ForEach(async r =>
                    {
                        if (Equals(Holder.ActiveRepo, r))
                        {
                            SelectedRepository = r;
                        }
                        var repo = await ApiFactory.Create(r.CloneUrl).GetRepository();
                        r.SetIcon(repo.Private, repo.Fork);
                    });
                }
            }
        }

        void HandleCreatedRepo(ISimpleRepositoryModel newrepo)
        {
            var msg = string.Format(CultureInfo.CurrentUICulture, Constants.Notification_RepoCreated, newrepo.Name, newrepo.CloneUrl);

            msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_CreateNewProject, newrepo.LocalPath);
            ShowNotification(newrepo, msg);
        }

        void HandleClonedRepo(ISimpleRepositoryModel newrepo)
        {
            var msg = string.Format(CultureInfo.CurrentUICulture, Constants.Notification_RepoCloned, newrepo.Name, newrepo.CloneUrl);

            if (newrepo.HasCommits() && newrepo.MightContainSolution())
            {
                msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_OpenProject, newrepo.LocalPath);
            }
            else
            {
                msg += " " + string.Format(CultureInfo.CurrentUICulture, Constants.Notification_CreateNewProject, newrepo.LocalPath);
            }
            ShowNotification(newrepo, msg);
        }

        void ShowNotification(ISimpleRepositoryModel newrepo, string msg)
        {
            var vsservices = ServiceProvider.GetExportedValue <IVSServices>();

            vsservices.ClearNotifications();
            vsservices.ShowMessage(
                msg,
                new RelayCommand(o =>
            {
                var str = o.ToString();

                /* the prefix is the action to perform:
                 * u: launch browser with url
                 * c: launch create new project dialog
                 * o: launch open existing project dialog
                 */
                var prefix = str.Substring(0, 2);
                if (prefix == "u:")
                {
                    OpenInBrowser(ServiceProvider.TryGetService <IVisualStudioBrowser>(), new Uri(str.Substring(2)));
                }
                else if (prefix == "o:")
                {
                    if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().OpenSolutionViaDlg(str.Substring(2), 1)))
                    {
                        ServiceProvider.TryGetService <ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    }
                }
                else if (prefix == "c:")
                {
                    vsservices.SetDefaultProjectPath(newrepo.LocalPath);
                    if (ErrorHandler.Succeeded(ServiceProvider.GetSolution().CreateNewProjectViaDlg(null, null, 0)))
                    {
                        ServiceProvider.TryGetService <ITeamExplorer>()?.NavigateToPage(new Guid(TeamExplorerPageIds.Home), null);
                    }
                }
            })
                );
#if DEBUG
            VsOutputLogger.WriteLine(String.Format(CultureInfo.InvariantCulture, "{0} Notification", DateTime.Now));
#endif
        }

        void RefreshRepositories()
        {
            connectionManager.RefreshRepositories();
            RaisePropertyChanged("Repositories"); // trigger a re-check of the visibility of the listview based on item count
        }
        async void UpdateRepositoryList(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                // if we're cloning or creating, only one repo will be added to the list
                // so we can handle just one new entry separately
                if (isCloning || isCreating)
                {
                    var newrepo = e.NewItems.Cast<ISimpleRepositoryModel>().First();

                    SelectedRepository = newrepo;
                    if (isCreating)
                        HandleCreatedRepo(newrepo);
                    else
                        HandleClonedRepo(newrepo);

                    isCreating = isCloning = false;
                    var repo = await ApiFactory.Create(newrepo.CloneUrl).GetRepository();
                    newrepo.SetIcon(repo.Private, repo.Fork);
                }
                // looks like it's just a refresh with new stuff on the list, update the icons
                else
                {
                    e.NewItems
                        .Cast<ISimpleRepositoryModel>()
                        .ForEach(async r =>
                    {
                        if (Equals(Holder.ActiveRepo, r))
                            SelectedRepository = r;
                        var repo = await ApiFactory.Create(r.CloneUrl).GetRepository();
                        r.SetIcon(repo.Private, repo.Fork);
                    });
                }
            }
        }
        public ITrackingCollection<IPullRequestModel> GetPullRequests(ISimpleRepositoryModel repo)
        {
            // Since the api to list pull requests returns all the data for each pr, cache each pr in its own entry
            // and also cache an index that contains all the keys for each pr. This way we can fetch prs in bulk
            // but also individually without duplicating information. We store things in a custom observable collection
            // that checks whether an item is being updated (coming from the live stream after being retrieved from cache)
            // and replaces it instead of appending, so items get refreshed in-place as they come in.

            var keyobs = GetUserFromCache()
                .Select(user => string.Format(CultureInfo.InvariantCulture, "{0}|{1}|pr", user.Login, repo.Name));

            var col = new TrackingCollection<IPullRequestModel>();

            var source = Observable.Defer(() => keyobs
                .SelectMany(key =>
                    hostCache.GetAndFetchLatestFromIndex(key, () =>
                        apiClient.GetPullRequestsForRepository(repo.CloneUrl.Owner, repo.CloneUrl.RepositoryName)
                                 .Select(PullRequestCacheItem.Create),
                        item => col.RemoveItem(Create(item)),
                        TimeSpan.FromMinutes(5),
                        TimeSpan.FromDays(1))
                )
                .Select(Create)
            );

            col.Listen(source);
            return col;
        }
Example #43
0
 public static IObservable <IConnection> LookupConnection(this IConnectionManager cm, ISimpleRepositoryModel repository)
 {
     return(Observable.Return(repository?.CloneUrl != null
         ? cm.Connections.FirstOrDefault(c => c.HostAddress.Equals(HostAddress.Create(repository.CloneUrl)))
         : null));
 }