diff --git a/src/GitHub.Api/Application/ApiClient.cs b/src/GitHub.Api/Application/ApiClient.cs index 9783078d4..d456e35af 100644 --- a/src/GitHub.Api/Application/ApiClient.cs +++ b/src/GitHub.Api/Application/ApiClient.cs @@ -19,7 +19,7 @@ public static IApiClient Create(UriString repositoryUrl, IKeychain keychain) new GitHubClient(AppConfiguration.ProductHeader, credentialStore, hostAddress.ApiUri)); } - private static readonly Unity.ILogging logger = Unity.Logging.GetLogger(); + private static readonly ILogging logger = Logging.GetLogger(); public HostAddress HostAddress { get; } public UriString OriginalUrl { get; } @@ -27,9 +27,6 @@ public static IApiClient Create(UriString repositoryUrl, IKeychain keychain) private readonly IGitHubClient githubClient; private readonly ILoginManager loginManager; - IList organizationsCache; - Octokit.User userCache; - public ApiClient(UriString hostUrl, IKeychain keychain, IGitHubClient githubClient) { Guard.ArgumentNotNull(hostUrl, nameof(hostUrl)); @@ -53,7 +50,7 @@ private async Task LogoutInternal(UriString host) await loginManager.Logout(host); } - public async Task CreateRepository(NewRepository newRepository, Action callback, string organization = null) + public async Task CreateRepository(NewRepository newRepository, Action callback, string organization = null) { Guard.ArgumentNotNull(callback, "callback"); try @@ -67,21 +64,27 @@ public async Task CreateRepository(NewRepository newRepository, Action> callback) + public async Task GetOrganizations(Action onSuccess, Action onError = null) { - Guard.ArgumentNotNull(callback, "callback"); - var organizations = await GetOrganizationInternal(); - callback(organizations); + Guard.ArgumentNotNull(onSuccess, nameof(onSuccess)); + await GetOrganizationInternal(onSuccess, onError); } - public async Task LoadKeychain(Action callback) + public async Task ValidateCurrentUser(Action onSuccess, Action onError = null) { - Guard.ArgumentNotNull(callback, "callback"); - var hasLoadedKeys = await LoadKeychainInternal(); - callback(hasLoadedKeys); + Guard.ArgumentNotNull(onSuccess, nameof(onSuccess)); + try + { + await ValidateCurrentUserInternal(); + onSuccess(); + } + catch (Exception e) + { + onError?.Invoke(e); + } } - public async Task GetCurrentUser(Action callback) + public async Task GetCurrentUser(Action callback) { Guard.ArgumentNotNull(callback, "callback"); var user = await GetCurrentUserInternal(); @@ -184,29 +187,27 @@ public async Task ContinueLoginAsync(LoginResult loginResult, Func CreateRepositoryInternal(NewRepository newRepository, string organization) + private async Task CreateRepositoryInternal(NewRepository newRepository, string organization) { try { logger.Trace("Creating repository"); - if (!await LoadKeychainInternal()) - { - throw new InvalidOperationException("The keychain did not load"); - } + await ValidateKeychain(); + await ValidateCurrentUserInternal(); - Octokit.Repository repository; + GitHubRepository repository; if (!string.IsNullOrEmpty(organization)) { logger.Trace("Creating repository for organization"); - repository = await githubClient.Repository.Create(organization, newRepository); + repository = (await githubClient.Repository.Create(organization, newRepository)).ToGitHubRepository(); } else { logger.Trace("Creating repository for user"); - repository = await githubClient.Repository.Create(newRepository); + repository = (await githubClient.Repository.Create(newRepository)).ToGitHubRepository(); } logger.Trace("Created Repository"); @@ -219,16 +220,14 @@ public async Task ContinueLoginAsync(LoginResult loginResult, Func> GetOrganizationInternal() + private async Task GetOrganizationInternal(Action onSuccess, Action onError = null) { try { logger.Trace("Getting Organizations"); - if (!await LoadKeychainInternal()) - { - return new List(); - } + await ValidateKeychain(); + await ValidateCurrentUserInternal(); var organizations = await githubClient.Organization.GetAllForCurrent(); @@ -236,49 +235,63 @@ private async Task> GetOrganizationInternal() if (organizations != null) { - organizationsCache = organizations.ToArray(); + var array = organizations.Select(organization => new Organization() { + Name = organization.Name, + Login = organization.Login + }).ToArray(); + onSuccess(array); } } catch(Exception ex) { logger.Error(ex, "Error Getting Organizations"); - throw; + onError?.Invoke(ex); } - - return organizationsCache; } - private async Task GetCurrentUserInternal() + private async Task GetCurrentUserInternal() { try { - logger.Trace("Getting Organizations"); - - if (!await LoadKeychainInternal()) - { - return null; - } + logger.Trace("Getting Current User"); + await ValidateKeychain(); - userCache = await githubClient.User.Current(); + return (await githubClient.User.Current()).ToGitHubUser(); } - catch(Exception ex) + catch (KeychainEmptyException) + { + logger.Warning("Keychain is empty"); + throw; + } + catch (Exception ex) { logger.Error(ex, "Error Getting Current User"); throw; } + } + + private async Task ValidateCurrentUserInternal() + { + logger.Trace("Validating User"); - return userCache; + var apiUser = await GetCurrentUserInternal(); + var apiUsername = apiUser.Login; + + var cachedUsername = keychain.Connections.First().Username; + + if (apiUsername != cachedUsername) + { + throw new TokenUsernameMismatchException(cachedUsername, apiUsername); + } } private async Task LoadKeychainInternal() { - logger.Trace("LoadKeychainInternal"); - if (keychain.HasKeys) { if (!keychain.NeedsLoad) { - logger.Trace("LoadKeychainInternal: Has keys does not need load"); + logger.Trace("LoadKeychainInternal: Previously Loaded"); return true; } @@ -288,6 +301,8 @@ private async Task LoadKeychainInternal() var uriString = keychain.Connections.First().Host; var keychainAdapter = await keychain.Load(uriString); + logger.Trace("LoadKeychainInternal: Loaded"); + return keychainAdapter.OctokitCredentials != Credentials.Anonymous; } @@ -296,23 +311,54 @@ private async Task LoadKeychainInternal() return false; } - public async Task ValidateCredentials() + private async Task ValidateKeychain() { - try + if (!await LoadKeychainInternal()) { - var store = keychain.Connect(OriginalUrl); - - if (store.OctokitCredentials != Credentials.Anonymous) - { - var credential = store.Credential; - await githubClient.Authorization.CheckApplicationAuthentication(ApplicationInfo.ClientId, credential.Token); - } + throw new KeychainEmptyException(); } - catch - { - return false; - } - return true; } } + + class GitHubUser + { + public string Name { get; set; } + public string Login { get; set; } + } + + class GitHubRepository + { + public string Name { get; set; } + public string CloneUrl { get; set; } + } + + class ApiClientException : Exception + { + public ApiClientException() + { } + + public ApiClientException(string message) : base(message) + { } + + public ApiClientException(string message, Exception innerException) : base(message, innerException) + { } + } + + class TokenUsernameMismatchException : ApiClientException + { + public string CachedUsername { get; } + public string CurrentUsername { get; } + + public TokenUsernameMismatchException(string cachedUsername, string currentUsername) + { + CachedUsername = cachedUsername; + CurrentUsername = currentUsername; + } + } + + class KeychainEmptyException : ApiClientException + { + public KeychainEmptyException() + { } + } } diff --git a/src/GitHub.Api/Application/IApiClient.cs b/src/GitHub.Api/Application/IApiClient.cs index 1ebbcd414..d4a87e3e2 100644 --- a/src/GitHub.Api/Application/IApiClient.cs +++ b/src/GitHub.Api/Application/IApiClient.cs @@ -9,14 +9,13 @@ interface IApiClient { HostAddress HostAddress { get; } UriString OriginalUrl { get; } - Task CreateRepository(NewRepository newRepository, Action callback, string organization = null); - Task GetOrganizations(Action> callback); + Task CreateRepository(NewRepository newRepository, Action callback, string organization = null); + Task GetOrganizations(Action onSuccess, Action onError = null); Task Login(string username, string password, Action need2faCode, Action result); Task ContinueLogin(LoginResult loginResult, string code); Task LoginAsync(string username, string password, Func need2faCode); - Task ValidateCredentials(); Task Logout(UriString host); - Task GetCurrentUser(Action callback); - Task LoadKeychain(Action callback); + Task GetCurrentUser(Action callback); + Task ValidateCurrentUser(Action onSuccess, Action onError = null); } } diff --git a/src/GitHub.Api/Application/OctokitExtensions.cs b/src/GitHub.Api/Application/OctokitExtensions.cs new file mode 100644 index 000000000..c53101c74 --- /dev/null +++ b/src/GitHub.Api/Application/OctokitExtensions.cs @@ -0,0 +1,21 @@ +namespace GitHub.Unity +{ + static class OctokitExtensions + { + public static GitHubUser ToGitHubUser(this Octokit.User user) + { + return new GitHubUser() { + Name = user.Name, + Login = user.Login, + }; + } + + public static GitHubRepository ToGitHubRepository(this Octokit.Repository repository) + { + return new GitHubRepository { + Name = repository.Name, + CloneUrl = repository.CloneUrl + }; + } + } +} \ No newline at end of file diff --git a/src/GitHub.Api/Application/Organization.cs b/src/GitHub.Api/Application/Organization.cs new file mode 100644 index 000000000..e78849dd6 --- /dev/null +++ b/src/GitHub.Api/Application/Organization.cs @@ -0,0 +1,8 @@ +namespace GitHub.Unity +{ + class Organization + { + public string Name { get; set; } + public string Login { get; set; } + } +} \ No newline at end of file diff --git a/src/GitHub.Api/Authentication/Keychain.cs b/src/GitHub.Api/Authentication/Keychain.cs index b531735f9..bab311239 100644 --- a/src/GitHub.Api/Authentication/Keychain.cs +++ b/src/GitHub.Api/Authentication/Keychain.cs @@ -68,7 +68,7 @@ public async Task Load(UriString host) { if (keychainItem.Username != cachedConnection.Username) { - logger.Warning("Keychain Username: {0} does not match; Hopefully it works", keychainItem.Username); + logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", keychainItem.Username, cachedConnection.Username); } logger.Trace("Loaded from Credential Manager Host:\"{0}\" Username:\"{1}\"", keychainItem.Host, keychainItem.Username); diff --git a/src/GitHub.Api/GitHub.Api.csproj b/src/GitHub.Api/GitHub.Api.csproj index a87766cee..87f8e2c30 100644 --- a/src/GitHub.Api/GitHub.Api.csproj +++ b/src/GitHub.Api/GitHub.Api.csproj @@ -99,6 +99,8 @@ + + diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/Misc/Styles.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/Misc/Styles.cs index 8162d7252..eb3b4489f 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/Misc/Styles.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/Misc/Styles.cs @@ -591,7 +591,6 @@ public static GUIStyle AuthHeaderBoxStyle { authHeaderBoxStyle = new GUIStyle(HeaderBoxStyle); authHeaderBoxStyle.name = "AuthHeaderBoxStyle"; - authHeaderBoxStyle.padding = new RectOffset(10, 10, 0, 5); } return authHeaderBoxStyle; } diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs index d20ab8fa7..c7795c05a 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/AuthenticationView.cs @@ -9,6 +9,8 @@ class AuthenticationView : Subview { private static readonly Vector2 viewSize = new Vector2(290, 290); + private const string CredentialsNeedRefreshMessage = "We've detected that your stored credentials are out of sync with your current user. This can happen if you have signed in to git outside of Unity. Sign in again to refresh your credentials."; + private const string NeedAuthenticationMessage = "We need you to authenticate first"; private const string WindowTitle = "Authenticate"; private const string UsernameLabel = "Username"; private const string PasswordLabel = "Password"; @@ -21,14 +23,15 @@ class AuthenticationView : Subview private const string TwofaButton = "Verify"; [SerializeField] private Vector2 scroll; - [SerializeField] private string username = ""; - [SerializeField] private string two2fa = ""; + [SerializeField] private string username = string.Empty; + [SerializeField] private string two2fa = string.Empty; + [SerializeField] private string message; + [SerializeField] private string errorMessage; + [SerializeField] private bool need2fa; - [NonSerialized] private bool need2fa; [NonSerialized] private bool isBusy; - [NonSerialized] private string errorMessage; [NonSerialized] private bool enterPressed; - [NonSerialized] private string password = ""; + [NonSerialized] private string password = string.Empty; [NonSerialized] private AuthenticationService authenticationService; @@ -36,18 +39,30 @@ public override void InitializeView(IView parent) { base.InitializeView(parent); need2fa = isBusy = false; + message = errorMessage = null; Title = WindowTitle; Size = viewSize; } - public override void OnEnable() + public void Initialize(Exception exception) { - base.OnEnable(); - } + var usernameMismatchException = exception as TokenUsernameMismatchException; + if (usernameMismatchException != null) + { + message = CredentialsNeedRefreshMessage; + username = usernameMismatchException.CachedUsername; + } - public override void OnDisable() - { - base.OnDisable(); + var keychainEmptyException = exception as KeychainEmptyException; + if (keychainEmptyException != null) + { + message = NeedAuthenticationMessage; + } + + if (usernameMismatchException == null && keychainEmptyException == null) + { + message = exception.Message; + } } public override void OnGUI() @@ -58,30 +73,13 @@ public override void OnGUI() scroll = GUILayout.BeginScrollView(scroll); { - Rect authHeader = EditorGUILayout.BeginHorizontal(Styles.AuthHeaderBoxStyle); + GUILayout.BeginHorizontal(Styles.AuthHeaderBoxStyle); { - GUILayout.BeginVertical(GUILayout.Width(16)); - { - GUILayout.Space(9); - GUILayout.Label(Styles.BigLogo, GUILayout.Height(20), GUILayout.Width(20)); - } - GUILayout.EndVertical(); - - GUILayout.BeginVertical(); - { - GUILayout.Space(11); - GUILayout.Label(AuthTitle, Styles.HeaderRepoLabelStyle); - } - GUILayout.EndVertical(); + GUILayout.Label(AuthTitle, Styles.HeaderRepoLabelStyle); } - GUILayout.EndHorizontal(); - EditorGUI.DrawRect( - new Rect(authHeader.x, authHeader.yMax, authHeader.xMax, 1), - new Color(0.455F, 0.455F, 0.455F, 1F) - ); - GUILayout.BeginVertical(Styles.GenericBoxStyle); + GUILayout.BeginVertical(); { if (!need2fa) { @@ -97,7 +95,7 @@ public override void OnGUI() } GUILayout.EndScrollView(); } - + private void HandleEnterPressed() { if (Event.current.type != EventType.KeyDown) @@ -112,20 +110,26 @@ private void OnGUILogin() { EditorGUI.BeginDisabledGroup(isBusy); { - GUILayout.Space(3); + ShowMessage(); + + EditorGUILayout.Space(); + GUILayout.BeginHorizontal(); { username = EditorGUILayout.TextField(UsernameLabel ,username, Styles.TextFieldStyle); } GUILayout.EndHorizontal(); - GUILayout.Space(Styles.BaseSpacing); + EditorGUILayout.Space(); + GUILayout.BeginHorizontal(); { password = EditorGUILayout.PasswordField(PasswordLabel, password, Styles.TextFieldStyle); } GUILayout.EndHorizontal(); + EditorGUILayout.Space(); + ShowErrorMessage(); GUILayout.Space(Styles.BaseSpacing + 3); @@ -153,25 +157,18 @@ private void OnGUI2FA() EditorGUI.BeginDisabledGroup(isBusy); { - GUILayout.Space(Styles.BaseSpacing); - GUILayout.BeginHorizontal(); - { - two2fa = EditorGUILayout.TextField(TwofaLabel, two2fa, Styles.TextFieldStyle); - } - GUILayout.EndHorizontal(); - - GUILayout.Space(Styles.BaseSpacing); + EditorGUILayout.Space(); + two2fa = EditorGUILayout.TextField(TwofaLabel, two2fa, Styles.TextFieldStyle); + EditorGUILayout.Space(); ShowErrorMessage(); - GUILayout.Space(Styles.BaseSpacing); GUILayout.BeginHorizontal(); { GUILayout.FlexibleSpace(); if (GUILayout.Button(BackButton)) { GUI.FocusControl(null); - need2fa = false; - Redraw(); + Clear(); } if (GUILayout.Button(TwofaButton) || (!isBusy && enterPressed)) @@ -183,7 +180,7 @@ private void OnGUI2FA() } GUILayout.EndHorizontal(); - GUILayout.Space(Styles.BaseSpacing); + EditorGUILayout.Space(); } EditorGUI.EndDisabledGroup(); } @@ -192,7 +189,7 @@ private void OnGUI2FA() private void DoRequire2fa(string msg) { - Logger.Trace("Strating 2FA - Message:\"{0}\"", msg); + Logger.Trace("Starting 2FA - Message:\"{0}\"", msg); need2fa = true; errorMessage = msg; @@ -200,28 +197,45 @@ private void DoRequire2fa(string msg) Redraw(); } + private void Clear() + { + need2fa = false; + errorMessage = null; + isBusy = false; + Redraw(); + } + private void DoResult(bool success, string msg) { Logger.Trace("DoResult - Success:{0} Message:\"{1}\"", success, msg); - errorMessage = msg; isBusy = false; if (success == true) { + Clear(); Finish(true); } else { + errorMessage = msg; Redraw(); } } + private void ShowMessage() + { + if (message != null) + { + EditorGUILayout.HelpBox(message, MessageType.Warning); + } + } + private void ShowErrorMessage() { if (errorMessage != null) { - GUILayout.Label(errorMessage, Styles.ErrorLabel); + EditorGUILayout.HelpBox(errorMessage, MessageType.Error); } } diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/HistoryView.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/HistoryView.cs index ee0386a9f..6810cd329 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/HistoryView.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/HistoryView.cs @@ -254,15 +254,11 @@ public void OnEmbeddedGUI() else { // Publishing a repo - EditorGUI.BeginDisabledGroup(!Platform.Keychain.Connections.Any()); + var publishedClicked = GUILayout.Button(PublishButton, Styles.HistoryToolbarButtonStyle); + if (publishedClicked) { - var publishedClicked = GUILayout.Button(PublishButton, Styles.HistoryToolbarButtonStyle); - if (publishedClicked) - { - PopupWindow.Open(PopupWindow.PopupViewType.PublishView); - } + PopupWindow.OpenWindow(PopupWindow.PopupViewType.PublishView); } - EditorGUI.EndDisabledGroup(); } } GUILayout.EndHorizontal(); diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs index c7cd2dc44..b7c860686 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PopupWindow.cs @@ -14,36 +14,27 @@ public enum PopupViewType AuthenticationView, } - [SerializeField] private PopupViewType activeViewType; + [NonSerialized] private IApiClient client; + [SerializeField] private PopupViewType activeViewType; [SerializeField] private AuthenticationView authenticationView; - [SerializeField] private PublishView publishView; [SerializeField] private LoadingView loadingView; + [SerializeField] private PublishView publishView; + [SerializeField] private bool shouldCloseOnFinish; public event Action OnClose; [MenuItem("GitHub/Authenticate")] public static void Launch() { - Open(PopupViewType.AuthenticationView); + OpenWindow(PopupViewType.AuthenticationView); } - public static PopupWindow Open(PopupViewType popupViewType, Action onClose = null) + public static PopupWindow OpenWindow(PopupViewType popupViewType, Action onClose = null) { var popupWindow = GetWindow(true); - popupWindow.OnClose.SafeInvoke(false); - - if (onClose != null) - { - popupWindow.OnClose += onClose; - } - - popupWindow.ActiveViewType = popupViewType; - popupWindow.titleContent = new GUIContent(popupWindow.ActiveView.Title, Styles.SmallLogo); - popupWindow.OnEnable(); - popupWindow.Show(); - popupWindow.Refresh(); + popupWindow.Open(popupViewType, onClose); return popupWindow; } @@ -59,6 +50,8 @@ public override void Initialize(IApplicationManager applicationManager) publishView.InitializeView(this); authenticationView.InitializeView(this); loadingView.InitializeView(this); + + titleContent = new GUIContent(ActiveView.Title, Styles.SmallLogo); } public override void OnEnable() @@ -102,7 +95,13 @@ public override void Finish(bool result) { OnClose.SafeInvoke(result); OnClose = null; - Close(); + + if (shouldCloseOnFinish) + { + shouldCloseOnFinish = false; + Close(); + } + base.Finish(result); } @@ -113,6 +112,97 @@ public override void OnDestroy() OnClose = null; } + private void Open(PopupViewType popupViewType, Action onClose) + { + OnClose.SafeInvoke(false); + OnClose = null; + + Logger.Trace("OpenView: {0}", popupViewType.ToString()); + + var viewNeedsAuthentication = popupViewType == PopupViewType.PublishView; + if (viewNeedsAuthentication) + { + Logger.Trace("Validating to open view"); + + Client.ValidateCurrentUser(() => { + + Logger.Trace("User validated opening view"); + + OpenInternal(popupViewType, onClose); + shouldCloseOnFinish = true; + + }, exception => { + Logger.Trace("User required validation opening AuthenticationView"); + authenticationView.Initialize(exception); + OpenInternal(PopupViewType.AuthenticationView, completedAuthentication => { + if (completedAuthentication) + { + Logger.Trace("User completed validation opening view: {0}", popupViewType.ToString()); + + Open(popupViewType, onClose); + } + }); + + shouldCloseOnFinish = false; + }); + } + else + { + OpenInternal(popupViewType, onClose); + shouldCloseOnFinish = true; + } + } + + private void OpenInternal(PopupViewType popupViewType, Action onClose) + { + if (onClose != null) + { + OnClose += onClose; + } + + var fromView = ActiveView; + ActiveViewType = popupViewType; + SwitchView(fromView, ActiveView); + Show(); + } + + private void SwitchView(Subview fromView, Subview toView) + { + GUI.FocusControl(null); + + if (fromView != null) + fromView.OnDisable(); + toView.OnEnable(); + titleContent = new GUIContent(ActiveView.Title, Styles.SmallLogo); + + // this triggers a repaint + Repaint(); + } + + public IApiClient Client + { + get + { + if (client == null) + { + var repository = Environment.Repository; + UriString host; + if (repository != null && !string.IsNullOrEmpty(repository.CloneUrl)) + { + host = repository.CloneUrl.ToRepositoryUrl(); + } + else + { + host = UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri); + } + + client = ApiClient.Create(host, Platform.Keychain); + } + + return client; + } + } + private Subview ActiveView { get diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs index 14f7ed227..99ab79516 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/PublishView.cs @@ -12,7 +12,6 @@ class PublishView : Subview private static readonly Vector2 viewSize = new Vector2(400, 350); private const string WindowTitle = "Publish"; - private const string Header = "Publish this repository to GitHub"; private const string PrivateRepoMessage = "You choose who can see and commit to this repository"; private const string PublicRepoMessage = "Anyone can see this repository. You choose who can commit"; private const string PublishViewCreateButton = "Publish"; @@ -26,7 +25,7 @@ class PublishView : Subview [SerializeField] private string username; [SerializeField] private string[] owners = { OwnersDefaultText }; - [SerializeField] private IList organizations; + [SerializeField] private string[] publishOwners; [SerializeField] private int selectedOwner; [SerializeField] private string repoName = String.Empty; [SerializeField] private string repoDescription = ""; @@ -35,7 +34,7 @@ class PublishView : Subview [NonSerialized] private IApiClient client; [NonSerialized] private bool isBusy; [NonSerialized] private string error; - [NonSerialized] private bool organizationsNeedLoading; + [NonSerialized] private bool ownersNeedLoading; public IApiClient Client { @@ -64,7 +63,7 @@ public IApiClient Client public override void OnEnable() { base.OnEnable(); - organizationsNeedLoading = organizations == null && !isBusy; + ownersNeedLoading = publishOwners == null && !isBusy; } public override void OnDataUpdate() @@ -75,10 +74,10 @@ public override void OnDataUpdate() private void MaybeUpdateData() { - if (organizationsNeedLoading) + if (ownersNeedLoading) { - organizationsNeedLoading = false; - LoadOrganizations(); + ownersNeedLoading = false; + LoadOwners(); } } @@ -89,59 +88,55 @@ public override void InitializeView(IView parent) Size = viewSize; } - private void LoadOrganizations() + private void LoadOwners() { var keychainConnections = Platform.Keychain.Connections; //TODO: ONE_USER_LOGIN This assumes only ever one user can login - if (keychainConnections.Any()) - { - try - { - Logger.Trace("Loading Keychain"); - isBusy = true; + isBusy = true; - Client.LoadKeychain(hasKeys => { - if (!hasKeys) - { - Logger.Warning("Unable to get current user"); - isBusy = false; - return; - } + //TODO: ONE_USER_LOGIN This assumes only ever one user can login + username = keychainConnections.First().Username; - //TODO: ONE_USER_LOGIN This assumes only ever one user can login - username = keychainConnections.First().Username; + Logger.Trace("Loading Owners"); - Logger.Trace("Loading Organizations"); + Client.GetOrganizations(orgs => + { + Logger.Trace("Loaded {0} Owners", orgs.Length); - Client.GetOrganizations(orgs => { - organizations = orgs; - Logger.Trace("Loaded {0} organizations", organizations.Count); + publishOwners = orgs + .OrderBy(organization => organization.Login) + .Select(organization => organization.Login) + .ToArray(); - var organizationLogins = organizations - .OrderBy(organization => organization.Login).Select(organization => organization.Login); + owners = new[] { OwnersDefaultText, username }.Union(publishOwners).ToArray(); - owners = new[] { OwnersDefaultText, username }.Union(organizationLogins).ToArray(); + isBusy = false; - isBusy = false; - }); - }); - } - catch (Exception e) + Redraw(); + }, exception => + { + isBusy = false; + + var keychainEmptyException = exception as KeychainEmptyException; + if (keychainEmptyException != null) { - Logger.Error(e, "Error PopulateView & GetOrganizations"); - isBusy = false; + Logger.Trace("Keychain empty"); + PopupWindow.OpenWindow(PopupWindow.PopupViewType.AuthenticationView); + return; } - } - else - { - Logger.Warning("No Keychain connections to use"); - } + + Logger.Error(exception, "Unhandled Exception"); + }); } public override void OnGUI() { - GUILayout.Label(PublishToGithubLabel, EditorStyles.boldLabel); + GUILayout.BeginHorizontal(Styles.AuthHeaderBoxStyle); + { + GUILayout.Label(PublishToGithubLabel, EditorStyles.boldLabel); + } + GUILayout.EndHorizontal(); EditorGUI.BeginDisabledGroup(isBusy); { diff --git a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs index 5ac4da704..3c15385b6 100644 --- a/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs +++ b/src/UnityExtension/Assets/Editor/GitHub.Unity/UI/Window.cs @@ -339,7 +339,7 @@ private void DoAccountDropdown() private void SignIn(object obj) { - PopupWindow.Open(PopupWindow.PopupViewType.AuthenticationView); + PopupWindow.OpenWindow(PopupWindow.PopupViewType.AuthenticationView); } private void GoToProfile(object obj)