Exemple #1
0
 void End(bool success)
 {
     uiProvider.RemoveService(typeof(IConnection));
     completion?.OnNext(success);
     completion?.OnCompleted();
     transition.OnCompleted();
 }
        CloneRequest ShowCloneDialog(IUIProvider uiProvider, IGitRepositoriesExt gitRepositories, ISimpleRepositoryModel repository = null)
        {
            string basePath = null;

            uiProvider.AddService(this, gitRepositories);

            var load = uiProvider.SetupUI(repository == null ? UIControllerFlow.Clone : UIControllerFlow.StartPageClone,
                                          null //TODO: set the connection corresponding to the repository if the repository is not null
                                          );

            load.Subscribe(x =>
            {
                if ((repository == null && x.Data.ViewType == Exports.UIViewType.Clone) ||       // fire the normal clone dialog
                    (repository != null && x.Data.ViewType == Exports.UIViewType.StartPageClone) // fire the clone dialog for re-acquiring a repo
                    )
                {
                    var vm = x.View.ViewModel as IBaseCloneViewModel;
                    if (repository != null)
                    {
                        vm.SelectedRepository = repository;
                    }
                    x.View.Done.Subscribe(_ =>
                    {
                        basePath = vm.BaseRepositoryPath;
                        if (repository == null)
                        {
                            repository = vm.SelectedRepository;
                        }
                    });
                }
            });

            uiProvider.RunUI();
            uiProvider.RemoveService(typeof(IGitRepositoriesExt), this);

            return(new CloneRequest(basePath, repository));
        }
        public UIController(IUIProvider uiProvider, IRepositoryHosts hosts, IExportFactoryProvider factory,
            IConnectionManager connectionManager, Lazy<ITwoFactorChallengeHandler> lazyTwoFactorChallengeHandler)
        {
            this.factory = factory;
            this.uiProvider = uiProvider;
            this.hosts = hosts;
            this.connectionManager = connectionManager;
            this.lazyTwoFactorChallengeHandler = lazyTwoFactorChallengeHandler;

#if DEBUG
            if (Application.Current != null && !Splat.ModeDetector.InUnitTestRunner())
            {
                var waitDispatcher = RxApp.MainThreadScheduler as WaitForDispatcherScheduler;
                if (waitDispatcher != null)
                {
                    Debug.Assert(DispatcherScheduler.Current.Dispatcher == Application.Current.Dispatcher,
                       "DispatcherScheduler is set correctly");
                }
                else
                {
                    Debug.Assert(((DispatcherScheduler)RxApp.MainThreadScheduler).Dispatcher == Application.Current.Dispatcher,
                        "The MainThreadScheduler is using the wrong dispatcher");
                }
            }
#endif
            machine = new StateMachine<UIViewType, Trigger>(UIViewType.None);

            machine.Configure(UIViewType.Login)
                .OnEntry(() =>
                {
                    RunView(UIViewType.Login);
                })
                .Permit(Trigger.Next, UIViewType.TwoFactor)
                // Added the following line to make it easy to login to both GitHub and GitHub Enterprise 
                // in DesignTimeStyleHelper in order to test Publish.
                .Permit(Trigger.Cancel, UIViewType.End)
                .PermitIf(Trigger.Finish, UIViewType.End, () => currentFlow == UIControllerFlow.Authentication)
                .PermitIf(Trigger.Finish, UIViewType.Create, () => currentFlow == UIControllerFlow.Create)
                .PermitIf(Trigger.Finish, UIViewType.Clone, () => currentFlow == UIControllerFlow.Clone)
                .PermitIf(Trigger.Finish, UIViewType.Publish, () => currentFlow == UIControllerFlow.Publish);

            machine.Configure(UIViewType.TwoFactor)
                .SubstateOf(UIViewType.Login)
                .OnEntry(() =>
                {
                    RunView(UIViewType.TwoFactor);
                })
                .Permit(Trigger.Cancel, UIViewType.End)
                .PermitIf(Trigger.Next, UIViewType.End, () => currentFlow == UIControllerFlow.Authentication)
                .PermitIf(Trigger.Next, UIViewType.Create, () => currentFlow == UIControllerFlow.Create)
                .PermitIf(Trigger.Next, UIViewType.Clone, () => currentFlow == UIControllerFlow.Clone)
                .PermitIf(Trigger.Next, UIViewType.Publish, () => currentFlow == UIControllerFlow.Publish);

            machine.Configure(UIViewType.Create)
                .OnEntry(() =>
                {
                    RunView(UIViewType.Create);
                })
                .Permit(Trigger.Cancel, UIViewType.End)
                .Permit(Trigger.Next, UIViewType.End);

            machine.Configure(UIViewType.Clone)
                .OnEntry(() =>
                {
                    RunView(UIViewType.Clone);
                })
                .Permit(Trigger.Cancel, UIViewType.End)
                .Permit(Trigger.Next, UIViewType.End);

            machine.Configure(UIViewType.Publish)
                .OnEntry(() =>
                {
                    RunView(UIViewType.Publish);
                })
                .Permit(Trigger.Cancel, UIViewType.End)
                .Permit(Trigger.Next, UIViewType.End);

            machine.Configure(UIViewType.End)
                .OnEntry(() =>
                {
                    uiProvider.RemoveService(typeof(IConnection));
                    transition.OnCompleted();
                })
                .Permit(Trigger.Next, UIViewType.Finished);

            // it might be useful later to check which triggered
            // made us enter here (Cancel or Next) and set a final
            // result accordingly, which is why UIViewType.End only
            // allows a Next trigger
            machine.Configure(UIViewType.Finished);
        }
        public UIController(IUIProvider uiProvider, IRepositoryHosts hosts, IExportFactoryProvider factory,
                            IConnectionManager connectionManager, Lazy <ITwoFactorChallengeHandler> lazyTwoFactorChallengeHandler)
        {
            this.factory                       = factory;
            this.uiProvider                    = uiProvider;
            this.hosts                         = hosts;
            this.connectionManager             = connectionManager;
            this.lazyTwoFactorChallengeHandler = lazyTwoFactorChallengeHandler;

#if DEBUG
            if (Application.Current != null && !Splat.ModeDetector.InUnitTestRunner())
            {
                var waitDispatcher = RxApp.MainThreadScheduler as WaitForDispatcherScheduler;
                if (waitDispatcher != null)
                {
                    Debug.Assert(DispatcherScheduler.Current.Dispatcher == Application.Current.Dispatcher,
                                 "DispatcherScheduler is set correctly");
                }
                else
                {
                    Debug.Assert(((DispatcherScheduler)RxApp.MainThreadScheduler).Dispatcher == Application.Current.Dispatcher,
                                 "The MainThreadScheduler is using the wrong dispatcher");
                }
            }
#endif
            machine = new StateMachine <UIViewType, Trigger>(UIViewType.None);

            machine.Configure(UIViewType.Login)
            .OnEntry(() =>
            {
                RunView(UIViewType.Login);
            })
            .Permit(Trigger.Next, UIViewType.TwoFactor)
            // Added the following line to make it easy to login to both GitHub and GitHub Enterprise
            // in DesignTimeStyleHelper in order to test Publish.
            .Permit(Trigger.Cancel, UIViewType.End)
            .PermitIf(Trigger.Finish, UIViewType.End, () => currentFlow == UIControllerFlow.Authentication)
            .PermitIf(Trigger.Finish, UIViewType.Create, () => currentFlow == UIControllerFlow.Create)
            .PermitIf(Trigger.Finish, UIViewType.Clone, () => currentFlow == UIControllerFlow.Clone)
            .PermitIf(Trigger.Finish, UIViewType.Publish, () => currentFlow == UIControllerFlow.Publish);

            machine.Configure(UIViewType.TwoFactor)
            .SubstateOf(UIViewType.Login)
            .OnEntry(() =>
            {
                RunView(UIViewType.TwoFactor);
            })
            .Permit(Trigger.Cancel, UIViewType.End)
            .PermitIf(Trigger.Next, UIViewType.End, () => currentFlow == UIControllerFlow.Authentication)
            .PermitIf(Trigger.Next, UIViewType.Create, () => currentFlow == UIControllerFlow.Create)
            .PermitIf(Trigger.Next, UIViewType.Clone, () => currentFlow == UIControllerFlow.Clone)
            .PermitIf(Trigger.Next, UIViewType.Publish, () => currentFlow == UIControllerFlow.Publish);

            machine.Configure(UIViewType.Create)
            .OnEntry(() =>
            {
                RunView(UIViewType.Create);
            })
            .Permit(Trigger.Cancel, UIViewType.End)
            .Permit(Trigger.Next, UIViewType.End);

            machine.Configure(UIViewType.Clone)
            .OnEntry(() =>
            {
                RunView(UIViewType.Clone);
            })
            .Permit(Trigger.Cancel, UIViewType.End)
            .Permit(Trigger.Next, UIViewType.End);

            machine.Configure(UIViewType.Publish)
            .OnEntry(() =>
            {
                RunView(UIViewType.Publish);
            })
            .Permit(Trigger.Cancel, UIViewType.End)
            .Permit(Trigger.Next, UIViewType.End);

            machine.Configure(UIViewType.End)
            .OnEntry(() =>
            {
                uiProvider.RemoveService(typeof(IConnection));
                transition.OnCompleted();
            })
            .Permit(Trigger.Next, UIViewType.Finished);

            // it might be useful later to check which triggered
            // made us enter here (Cancel or Next) and set a final
            // result accordingly, which is why UIViewType.End only
            // allows a Next trigger
            machine.Configure(UIViewType.Finished);
        }
        /// <summary>
        /// Configures the UI that gets loaded when entering a certain state and which state
        /// to go to for each trigger. Which state to go to depends on which ui flow state machine
        /// is currently active - when a trigger happens, the PermitDynamic conditions will
        /// lookup the currently active state machine from the list of available ones in `machines`,
        /// fire the requested trigger on it, and return the state that it went to, which causes
        /// `uiStateMachine` to load a new UI (or exit)
        /// There is a bit of redundant information regarding valid state transitions between this
        /// state machine and the individual state machines for each ui flow. This is unavoidable
        /// because permited transition triggers have to be explicit, so care should be taken to
        /// make sure permitted triggers per view here match the permitted triggers in the individual
        /// state machines.
        /// </summary>
        void ConfigureUIHandlingStates()
        {
            uiStateMachine.Configure(UIViewType.None)
            .OnEntry(tr => stopping = false)
            .PermitDynamic(Trigger.Next, () =>
            {
                var loggedIn = connection != null && hosts.LookupHost(connection.HostAddress).IsLoggedIn;
                activeFlow   = loggedIn ? mainFlow : UIControllerFlow.Authentication;
                return(Go(Trigger.Next));
            })
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.Clone)
            .OnEntry(tr => RunView(UIViewType.Clone, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.Create)
            .OnEntry(tr => RunView(UIViewType.Create, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.Publish)
            .OnEntry(tr => RunView(UIViewType.Publish, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.PRList)
            .OnEntry(tr => RunView(UIViewType.PRList, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.PRDetail, () => Go(Trigger.PRDetail))
            .PermitDynamic(Trigger.PRCreation, () => Go(Trigger.PRCreation))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            triggers.Add(Trigger.PRDetail, uiStateMachine.SetTriggerParameters <ViewWithData>(Trigger.PRDetail));
            uiStateMachine.Configure(UIViewType.PRDetail)
            .OnEntryFrom(triggers[Trigger.PRDetail], (arg, tr) => RunView(UIViewType.PRDetail, CalculateDirection(tr), arg))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.PRCreation)
            .OnEntry(tr => RunView(UIViewType.PRCreation, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.Login)
            .OnEntry(tr => RunView(UIViewType.Login, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.TwoFactor)
            .OnEntry(tr => RunView(UIViewType.TwoFactor, CalculateDirection(tr)))
            .PermitDynamic(Trigger.Next, () => Go(Trigger.Next))
            .PermitDynamic(Trigger.Cancel, () => Go(Trigger.Cancel))
            .PermitDynamic(Trigger.Finish, () => Go(Trigger.Finish));

            uiStateMachine.Configure(UIViewType.End)
            .OnEntryFrom(Trigger.Cancel, () => End(false))
            .OnEntryFrom(Trigger.Next, () => End(true))
            .OnEntryFrom(Trigger.Finish, () => End(true))
            // clear all the views and viewmodels created by a subflow
            .OnExit(() =>
            {
                // it's important to have the stopping flag set before we do this
                if (activeFlow == mainFlow)
                {
                    completion?.OnNext(Success.Value);
                    completion?.OnCompleted();
                }

                DisposeFlow(activeFlow);

                if (activeFlow == mainFlow)
                {
                    uiProvider.RemoveService(typeof(IConnection), this);
                    transition.OnCompleted();
                    Reset();
                }
                else
                {
                    activeFlow = stopping || LoggedIn ? mainFlow : UIControllerFlow.Authentication;
                }
            })
            .PermitDynamic(Trigger.Next, () =>
            {
                // sets the state to None for the current flow
                var state = Go(Trigger.Next);

                if (activeFlow != mainFlow)
                {
                    if (stopping)
                    {
                        // triggering the End state again so that it can clear up the main flow and really end
                        state = Go(Trigger.Finish, mainFlow);
                    }
                    else
                    {
                        // sets the state to the first UI of the main flow or the auth flow, depending on whether you're logged in
                        state = Go(Trigger.Next, LoggedIn ? mainFlow : UIControllerFlow.Authentication);
                    }
                }
                return(state);
            })
            .Permit(Trigger.Finish, UIViewType.None);
        }
Exemple #6
0
 public void RemoveService(Type t, object owner) => theRealProvider.RemoveService(t, owner);