diff --git a/src/GitHub.VisualStudio/UI/Views/GitHubPaneViewModel.cs b/src/GitHub.VisualStudio/UI/Views/GitHubPaneViewModel.cs index b902ee6ba0..8707d8cfb7 100644 --- a/src/GitHub.VisualStudio/UI/Views/GitHubPaneViewModel.cs +++ b/src/GitHub.VisualStudio/UI/Views/GitHubPaneViewModel.cs @@ -26,6 +26,8 @@ namespace GitHub.VisualStudio.UI.Views [PartCreationPolicy(CreationPolicy.NonShared)] public class GitHubPaneViewModel : TeamExplorerItemBase, IGitHubPaneViewModel { + const UIControllerFlow DefaultControllerFlow = UIControllerFlow.PullRequests; + bool initialized; readonly CompositeDisposable disposables = new CompositeDisposable(); IUIController uiController; @@ -40,6 +42,7 @@ public class GitHubPaneViewModel : TeamExplorerItemBase, IGitHubPaneViewModel bool navigatingViaArrows; bool disabled; Microsoft.VisualStudio.Shell.OleMenuCommand back, forward, refresh; + int latestReloadCallId; [ImportingConstructor] public GitHubPaneViewModel(ISimpleApiClientFactory apiFactory, ITeamExplorerServiceHolder holder, @@ -51,8 +54,6 @@ public GitHubPaneViewModel(ISimpleApiClientFactory apiFactory, ITeamExplorerServ syncContext = SynchronizationContext.Current; CancelCommand = ReactiveCommand.Create(); Title = "GitHub"; - - hosts.WhenAnyValue(x => x.IsLoggedInToAnyHost).Subscribe(_ => Reload().Forget()); } public override void Initialize(IServiceProvider serviceProvider) @@ -87,6 +88,8 @@ public override void Initialize(IServiceProvider serviceProvider) initialized = true; base.Initialize(serviceProvider); + + hosts.WhenAnyValue(x => x.IsLoggedInToAnyHost).Subscribe(_ => Reload().Forget()); } public void Initialize([AllowNull] ViewWithData data) @@ -105,53 +108,103 @@ protected override void RepoChanged(bool changed) if (!changed) return; + Stop(); IsGitHubRepo = null; Reload().Forget(); } + /// + /// This method is reentrant, so all await calls need to be done before + /// any actions are performed on the data. More recent calls to this method + /// will cause previous calls pending on await calls to exit early. + /// + /// async Task Reload([AllowNull] ViewWithData data = null, bool navigating = false) { + if (!initialized) + return; + + latestReloadCallId++; + var reloadCallId = latestReloadCallId; + navigatingViaArrows = navigating; if (!IsGitHubRepo.HasValue) - IsGitHubRepo = await IsAGitHubRepo(); - - if (!IsGitHubRepo.Value) { - if (uiController != null) - { - Stop(); - //var factory = ServiceProvider.GetExportedValue(); - //var c = factory.CreateViewAndViewModel(UIViewType.LoggedOut); - //Control = c.View; - } - return; + var isGitHubRepo = await IsAGitHubRepo(); + if (reloadCallId != latestReloadCallId) + return; + + IsGitHubRepo = isGitHubRepo; } var connection = await connectionManager.LookupConnection(ActiveRepo); - IsLoggedIn = await connection.IsLoggedIn(hosts); + if (reloadCallId != latestReloadCallId) + return; - if (IsLoggedIn) + if (connection == null) + IsLoggedIn = false; + else { - if (uiController == null || (data != null && data.ActiveFlow != uiController.SelectedFlow)) - StartFlow(data?.ActiveFlow ?? UIControllerFlow.PullRequests, connection, data); - else if (data != null || currentNavItem >= 0) - uiController.Jump(data ?? navStack[currentNavItem]); + var isLoggedIn = await connection.IsLoggedIn(hosts); + if (reloadCallId != latestReloadCallId) + return; + + IsLoggedIn = isLoggedIn; } + + if (!IsGitHubRepo.Value) + { + //LoadView(UIViewType.NotAGitHubRepo); + } + + else if (!IsLoggedIn) + { + LoadView(UIViewType.LoggedOut); + } + else + { + LoadView(data?.ActiveFlow ?? DefaultControllerFlow, connection, data); + } + } + + void LoadView(UIControllerFlow flow, IConnection connection = null, ViewWithData data = null, UIViewType type = UIViewType.None) + { + // if we're loading a single view or a different flow, we need to stop the current controller + var restart = flow == UIControllerFlow.None || uiController?.SelectedFlow != flow; + + if (restart) + Stop(); + + // if there's no selected flow, then just load a view directly + if (flow == UIControllerFlow.None) { var factory = ServiceProvider.GetExportedValue(); - var c = factory.CreateViewAndViewModel(UIViewType.LoggedOut); + var c = factory.CreateViewAndViewModel(type); c.View.DataContext = c.ViewModel; Control = c.View; } - return; + // it's a new flow! + else if (restart) + { + StartFlow(flow, connection, data); + } + // navigate to a requested view within the currently running uiController + else + { + uiController.Jump(data ?? navStack[currentNavItem]); + } + } + + void LoadView(UIViewType type) + { + LoadView(UIControllerFlow.None, type: type); } void StartFlow(UIControllerFlow controllerFlow, [AllowNull]IConnection conn, ViewWithData data = null) { - if (uiController != null) - Stop(); + Stop(); if (conn == null) return; @@ -249,12 +302,17 @@ void DisableButtons() void Stop() { + if (uiController == null) + return; + + DisableButtons(); windowController?.Close(); uiController.Stop(); disposables.Clear(); uiController = null; currentNavItem = -1; navStack.Clear(); + UpdateToolbar(); } string title; @@ -307,4 +365,4 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } } -} \ No newline at end of file +}