update libs
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
using AssetStoreTools.Api.Models;
|
||||
using AssetStoreTools.Uploader.Services.Api;
|
||||
using AssetStoreTools.Utility;
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace AssetStoreTools.Uploader.UI.Views
|
||||
{
|
||||
internal class LoginView : VisualElement
|
||||
{
|
||||
// Data
|
||||
private IAuthenticationService _authenticationService;
|
||||
private double _cloudLoginRefreshTime = 1d;
|
||||
private double _lastRefreshTime;
|
||||
|
||||
// UI
|
||||
private Button _cloudLoginButton;
|
||||
private Label _cloudLoginLabel;
|
||||
|
||||
private Box _errorBox;
|
||||
private Label _errorLabel;
|
||||
|
||||
private TextField _emailField;
|
||||
private TextField _passwordField;
|
||||
private Button _credentialsLoginButton;
|
||||
|
||||
public event Action<User> OnAuthenticated;
|
||||
|
||||
public LoginView(IAuthenticationService authenticationService)
|
||||
{
|
||||
_authenticationService = authenticationService;
|
||||
Create();
|
||||
}
|
||||
|
||||
public void Create()
|
||||
{
|
||||
styleSheets.Add(StyleSelector.UploaderWindow.LoginViewStyle);
|
||||
styleSheets.Add(StyleSelector.UploaderWindow.LoginViewTheme);
|
||||
|
||||
CreateAssetStoreLogo();
|
||||
CreateCloudLogin();
|
||||
CreateErrorBox();
|
||||
CreateCredentialsLogin();
|
||||
}
|
||||
|
||||
private void CreateAssetStoreLogo()
|
||||
{
|
||||
// Asset Store logo
|
||||
Image assetStoreLogo = new Image { name = "AssetStoreLogo" };
|
||||
assetStoreLogo.AddToClassList("asset-store-logo");
|
||||
|
||||
Add(assetStoreLogo);
|
||||
}
|
||||
|
||||
private void CreateCloudLogin()
|
||||
{
|
||||
VisualElement cloudLogin = new VisualElement { name = "CloudLogin" };
|
||||
|
||||
_cloudLoginButton = new Button(LoginWithCloudToken) { name = "LoginButtonCloud" };
|
||||
_cloudLoginButton.AddToClassList("cloud-button-login");
|
||||
_cloudLoginButton.SetEnabled(false);
|
||||
|
||||
_cloudLoginLabel = new Label { text = "Cloud login unavailable" };
|
||||
_cloudLoginLabel.AddToClassList("cloud-button-login-label");
|
||||
|
||||
Label orLabel = new Label { text = "or" };
|
||||
orLabel.AddToClassList("cloud-label-or");
|
||||
|
||||
_cloudLoginButton.Add(_cloudLoginLabel);
|
||||
|
||||
cloudLogin.Add(_cloudLoginButton);
|
||||
cloudLogin.Add(orLabel);
|
||||
|
||||
UpdateCloudLoginButton();
|
||||
EditorApplication.update += UpdateCloudLoginButton;
|
||||
Add(cloudLogin);
|
||||
}
|
||||
|
||||
private void CreateErrorBox()
|
||||
{
|
||||
_errorBox = new Box() { name = "LoginErrorBox" };
|
||||
_errorBox.AddToClassList("error-container");
|
||||
_errorBox.style.display = DisplayStyle.None;
|
||||
|
||||
var errorImage = new Image();
|
||||
_errorBox.Add(errorImage);
|
||||
|
||||
_errorLabel = new Label();
|
||||
_errorBox.Add(_errorLabel);
|
||||
|
||||
Add(_errorBox);
|
||||
}
|
||||
|
||||
public void DisplayError(string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
_errorLabel.text = message;
|
||||
Debug.LogError(message);
|
||||
|
||||
_errorBox.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
private void ClearError()
|
||||
{
|
||||
_errorLabel.text = string.Empty;
|
||||
_errorBox.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void CreateCredentialsLogin()
|
||||
{
|
||||
// Manual login
|
||||
VisualElement manualLoginBox = new VisualElement { name = "ManualLoginBox" };
|
||||
manualLoginBox.AddToClassList("credentials-container");
|
||||
|
||||
// Email input box
|
||||
VisualElement inputBoxEmail = new VisualElement();
|
||||
inputBoxEmail.AddToClassList("credentials-input-container");
|
||||
|
||||
Label emailTitle = new Label { text = "Email" };
|
||||
_emailField = new TextField();
|
||||
|
||||
inputBoxEmail.Add(emailTitle);
|
||||
inputBoxEmail.Add(_emailField);
|
||||
|
||||
manualLoginBox.Add(inputBoxEmail);
|
||||
|
||||
// Password input box
|
||||
VisualElement inputBoxPassword = new VisualElement();
|
||||
inputBoxPassword.AddToClassList("credentials-input-container");
|
||||
|
||||
Label passwordTitle = new Label { text = "Password" };
|
||||
_passwordField = new TextField { isPasswordField = true };
|
||||
|
||||
inputBoxPassword.Add(passwordTitle);
|
||||
inputBoxPassword.Add(_passwordField);
|
||||
|
||||
manualLoginBox.Add(inputBoxPassword);
|
||||
|
||||
// Login button
|
||||
_credentialsLoginButton = new Button(LoginWithCredentials) { name = "LoginButtonCredentials" };
|
||||
_credentialsLoginButton.AddToClassList("credentials-button-login");
|
||||
|
||||
Label loginDescriptionCredentials = new Label { text = "Login" };
|
||||
loginDescriptionCredentials.AddToClassList("credentials-button-login-label");
|
||||
|
||||
_credentialsLoginButton.Add(loginDescriptionCredentials);
|
||||
|
||||
manualLoginBox.Add(_credentialsLoginButton);
|
||||
|
||||
Add(manualLoginBox);
|
||||
|
||||
// Credentials login helpers
|
||||
VisualElement helperBox = new VisualElement { name = "HelperBox" };
|
||||
helperBox.AddToClassList("help-section-container");
|
||||
|
||||
Button createAccountButton = new Button { name = "CreateAccountButton", text = "Create Publisher ID" };
|
||||
Button forgotPasswordButton = new Button { name = "ForgotPasswordButton", text = "Reset Password" };
|
||||
|
||||
createAccountButton.AddToClassList("help-section-hyperlink-button");
|
||||
forgotPasswordButton.AddToClassList("help-section-hyperlink-button");
|
||||
|
||||
createAccountButton.clicked += () => Application.OpenURL(Constants.Uploader.AccountRegistrationUrl);
|
||||
forgotPasswordButton.clicked += () => Application.OpenURL(Constants.Uploader.AccountForgottenPasswordUrl);
|
||||
|
||||
helperBox.Add(createAccountButton);
|
||||
helperBox.Add(forgotPasswordButton);
|
||||
|
||||
Add(helperBox);
|
||||
}
|
||||
|
||||
public async void LoginWithSessionToken()
|
||||
{
|
||||
ASDebug.Log("Authenticating with session token...");
|
||||
ClearError();
|
||||
SetEnabled(false);
|
||||
|
||||
var result = await _authenticationService.AuthenticateWithSessionToken();
|
||||
if (!result.Success)
|
||||
{
|
||||
// Session authentication fail does not display errors in the UI
|
||||
ASDebug.Log("No existing session was found");
|
||||
SetEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
OnLoginSuccess(result.User);
|
||||
}
|
||||
|
||||
private async void LoginWithCloudToken()
|
||||
{
|
||||
ASDebug.Log("Authenticating with cloud token...");
|
||||
ClearError();
|
||||
SetEnabled(false);
|
||||
|
||||
var result = await _authenticationService.AuthenticateWithCloudToken();
|
||||
if (!result.Success)
|
||||
{
|
||||
OnLoginFail(result.Exception.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
OnLoginSuccess(result.User);
|
||||
}
|
||||
|
||||
private async void LoginWithCredentials()
|
||||
{
|
||||
ASDebug.Log("Authenticating with credentials...");
|
||||
ClearError();
|
||||
var isValid = IsLoginDataValid(_emailField.text, _passwordField.value);
|
||||
SetEnabled(!isValid);
|
||||
|
||||
if (!isValid)
|
||||
return;
|
||||
|
||||
var result = await _authenticationService.AuthenticateWithCredentials(_emailField.text, _passwordField.text);
|
||||
if (result.Success)
|
||||
OnLoginSuccess(result.User);
|
||||
else
|
||||
OnLoginFail(result.Exception.Message);
|
||||
}
|
||||
|
||||
private bool IsLoginDataValid(string email, string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
DisplayError("Email field cannot be empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(password))
|
||||
{
|
||||
DisplayError("Password field cannot be empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateCloudLoginButton()
|
||||
{
|
||||
if (_cloudLoginLabel == null)
|
||||
return;
|
||||
|
||||
if (_lastRefreshTime + _cloudLoginRefreshTime > EditorApplication.timeSinceStartup)
|
||||
return;
|
||||
|
||||
_lastRefreshTime = EditorApplication.timeSinceStartup;
|
||||
|
||||
// Cloud login
|
||||
if (_authenticationService.CloudAuthenticationAvailable(out var username, out var _))
|
||||
{
|
||||
_cloudLoginLabel.text = $"Login as {username}";
|
||||
_cloudLoginButton.SetEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cloudLoginLabel.text = "Cloud login unavailable";
|
||||
_cloudLoginButton.SetEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoginSuccess(User user)
|
||||
{
|
||||
ASDebug.Log($"Successfully authenticated as {user.Username}\n{user}");
|
||||
|
||||
_emailField.value = string.Empty;
|
||||
_passwordField.value = string.Empty;
|
||||
|
||||
OnAuthenticated?.Invoke(user);
|
||||
SetEnabled(true);
|
||||
}
|
||||
|
||||
private void OnLoginFail(string message)
|
||||
{
|
||||
ASDebug.LogError($"Authentication failed: {message}");
|
||||
DisplayError(message);
|
||||
SetEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e20b8602a9bd8ca48a5689b3f32cdd90
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 12.0.1
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/LoginView.cs
|
||||
uploadId: 724584
|
||||
@@ -0,0 +1,249 @@
|
||||
using AssetStoreTools.Uploader.Data;
|
||||
using AssetStoreTools.Uploader.Services;
|
||||
using AssetStoreTools.Uploader.Services.Api;
|
||||
using AssetStoreTools.Uploader.UI.Elements;
|
||||
using AssetStoreTools.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UIElements;
|
||||
using PackageModel = AssetStoreTools.Api.Models.Package;
|
||||
|
||||
namespace AssetStoreTools.Uploader.UI.Views
|
||||
{
|
||||
internal class PackageListView : VisualElement
|
||||
{
|
||||
// Data
|
||||
private List<IPackage> _packages;
|
||||
private readonly string[] _priorityGroupNames = { "draft", "published" };
|
||||
|
||||
private IPackageDownloadingService _packageDownloadingService;
|
||||
private IPackageFactoryService _packageFactory;
|
||||
|
||||
// UI
|
||||
private LoadingSpinner _loadingSpinner;
|
||||
private ScrollView _packageScrollView;
|
||||
private PackageListToolbar _packageListToolbar;
|
||||
|
||||
public event Action<Exception> OnInitializeError;
|
||||
|
||||
public PackageListView(IPackageDownloadingService packageDownloadingService, IPackageFactoryService elementFactory)
|
||||
{
|
||||
_packages = new List<IPackage>();
|
||||
_packageDownloadingService = packageDownloadingService;
|
||||
_packageFactory = elementFactory;
|
||||
|
||||
Create();
|
||||
EditorSceneManager.activeSceneChangedInEditMode += OnSceneChange;
|
||||
}
|
||||
|
||||
private void Create()
|
||||
{
|
||||
styleSheets.Add(StyleSelector.UploaderWindow.PackageListViewStyle);
|
||||
styleSheets.Add(StyleSelector.UploaderWindow.PackageListViewTheme);
|
||||
|
||||
AddToClassList("package-list-view");
|
||||
|
||||
CreateFilteringTools();
|
||||
CreateLoadingSpinner();
|
||||
CreateScrollView();
|
||||
|
||||
ShowPackagesView();
|
||||
}
|
||||
|
||||
private void CreateScrollView()
|
||||
{
|
||||
_packageScrollView = new ScrollView();
|
||||
Add(_packageScrollView);
|
||||
}
|
||||
|
||||
private void CreateFilteringTools()
|
||||
{
|
||||
_packageListToolbar = new PackageListToolbar();
|
||||
Add(_packageListToolbar);
|
||||
}
|
||||
|
||||
private void CreateLoadingSpinner()
|
||||
{
|
||||
_loadingSpinner = new LoadingSpinner();
|
||||
Add(_loadingSpinner);
|
||||
}
|
||||
|
||||
private void InsertReadOnlyInfoBox(string infoText)
|
||||
{
|
||||
var groupHeader = new Box { name = "GroupReadOnlyInfoBox" };
|
||||
groupHeader.AddToClassList("package-group-info-box");
|
||||
|
||||
var infoImage = new Image();
|
||||
groupHeader.Add(infoImage);
|
||||
|
||||
var infoLabel = new Label { text = infoText };
|
||||
groupHeader.Add(infoLabel);
|
||||
|
||||
_packageScrollView.Add(groupHeader);
|
||||
}
|
||||
|
||||
public async Task LoadPackages(bool useCachedData)
|
||||
{
|
||||
_packages.Clear();
|
||||
_packageScrollView.Clear();
|
||||
_packageListToolbar.SetEnabled(false);
|
||||
|
||||
if (!useCachedData)
|
||||
{
|
||||
_packageDownloadingService.ClearPackageData();
|
||||
}
|
||||
|
||||
_loadingSpinner.Show();
|
||||
await Task.Delay(100);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _packageDownloadingService.GetPackageData();
|
||||
|
||||
if (response.Cancelled)
|
||||
{
|
||||
ASDebug.Log("Package retrieval was cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
ASDebug.LogError(response.Exception);
|
||||
OnInitializeError?.Invoke(response.Exception);
|
||||
return;
|
||||
}
|
||||
|
||||
var packageModels = response.Packages;
|
||||
ASDebug.Log($"Found {packageModels.Count} packages");
|
||||
|
||||
if (packageModels.Count == 0)
|
||||
{
|
||||
InsertReadOnlyInfoBox("You do not have any packages yet. Please visit the Publishing Portal if you " +
|
||||
"would like to create one.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create package groups
|
||||
_packages = CreatePackages(packageModels);
|
||||
var packageGroups = CreatePackageGroups(_packages);
|
||||
var packageGroupElements = CreatePackageGroupElements(packageGroups);
|
||||
PopulatePackageList(packageGroupElements);
|
||||
|
||||
// Setup filtering and thumbnails
|
||||
SetupFilteringToolbar(packageGroups);
|
||||
DownloadAndSetThumbnails();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loadingSpinner.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private List<IPackage> CreatePackages(List<PackageModel> packageModels)
|
||||
{
|
||||
return _packages = packageModels.Select(x => _packageFactory.CreatePackage(x)).ToList();
|
||||
}
|
||||
|
||||
private List<IPackageGroup> CreatePackageGroups(List<IPackage> packages)
|
||||
{
|
||||
var packageGroups = new List<IPackageGroup>();
|
||||
var packagesByStatus = packages.GroupBy(x => x.Status).ToDictionary(x => x.Key, x => x.ToList());
|
||||
|
||||
foreach (var kvp in packagesByStatus)
|
||||
{
|
||||
var groupName = char.ToUpper(kvp.Key[0]) + kvp.Key.Substring(1);
|
||||
var groupPackages = kvp.Value;
|
||||
var packageGroup = _packageFactory.CreatePackageGroup(groupName, groupPackages);
|
||||
packageGroups.Add(packageGroup);
|
||||
}
|
||||
|
||||
return packageGroups;
|
||||
}
|
||||
|
||||
private List<PackageGroupElement> CreatePackageGroupElements(List<IPackageGroup> packageGroups)
|
||||
{
|
||||
var elements = new List<PackageGroupElement>();
|
||||
foreach (var packageGroup in packageGroups)
|
||||
elements.Add(new PackageGroupElement(packageGroup, _packageFactory));
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
private void PopulatePackageList(List<PackageGroupElement> packageGroups)
|
||||
{
|
||||
// Draft group
|
||||
var draftGroup = packageGroups.FirstOrDefault(x => x.Name.Equals("draft", StringComparison.OrdinalIgnoreCase));
|
||||
if (draftGroup != null)
|
||||
{
|
||||
draftGroup.Toggle(true);
|
||||
_packageScrollView.Add(draftGroup);
|
||||
}
|
||||
|
||||
// Infobox will only be shown if:
|
||||
// 1) There is more than 1 group OR
|
||||
// 2) There is only 1 group, but it is not draft
|
||||
var showInfoBox = packageGroups.Count > 1
|
||||
|| packageGroups.Count == 1 && !packageGroups[0].Name.Equals("draft", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (showInfoBox)
|
||||
InsertReadOnlyInfoBox("Only packages with a 'Draft' status can be selected for uploading Assets");
|
||||
|
||||
// Priority groups
|
||||
foreach (var priorityName in _priorityGroupNames)
|
||||
{
|
||||
var priorityGroup = packageGroups.FirstOrDefault(x => x.Name.Equals(priorityName, StringComparison.OrdinalIgnoreCase));
|
||||
if (priorityGroup == null || _packageScrollView.Contains(priorityGroup))
|
||||
continue;
|
||||
|
||||
_packageScrollView.Add(priorityGroup);
|
||||
}
|
||||
|
||||
// The rest
|
||||
foreach (var group in packageGroups)
|
||||
{
|
||||
if (!_packageScrollView.Contains(group))
|
||||
_packageScrollView.Add(group);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupFilteringToolbar(List<IPackageGroup> packageGroups)
|
||||
{
|
||||
_packageListToolbar.SetPackageGroups(packageGroups);
|
||||
_packageListToolbar.Sort(PackageSorting.Name);
|
||||
_packageListToolbar.SetEnabled(true);
|
||||
}
|
||||
|
||||
private void DownloadAndSetThumbnails()
|
||||
{
|
||||
foreach (var package in _packages)
|
||||
{
|
||||
DownloadAndSetThumbnail(package);
|
||||
}
|
||||
}
|
||||
|
||||
private async void DownloadAndSetThumbnail(IPackage package)
|
||||
{
|
||||
var response = await _packageDownloadingService.GetPackageThumbnail(package);
|
||||
if (!response.Success)
|
||||
return;
|
||||
|
||||
package.UpdateIcon(response.Thumbnail);
|
||||
}
|
||||
|
||||
private void ShowPackagesView()
|
||||
{
|
||||
_packageScrollView.style.display = DisplayStyle.Flex;
|
||||
_packageListToolbar.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
private void OnSceneChange(Scene _, Scene __)
|
||||
{
|
||||
DownloadAndSetThumbnails();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c499c2d0a5e8fd4b9984184c59893e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 115
|
||||
packageName: Asset Store Publishing Tools
|
||||
packageVersion: 12.0.1
|
||||
assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI/Views/PackageListView.cs
|
||||
uploadId: 724584
|
||||
Reference in New Issue
Block a user