using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Speckle.ConnectorUnity.Utils; using Speckle.ConnectorUnity.Wrappers.Selection; using Speckle.Core.Credentials; using UnityEngine; using UnityEngine.UI; using VRSample.UI.Components; using Text = TMPro.TMP_Text; namespace VRSample.UI.Controllers { /// /// Reusable UI Controller for selecting account/stream/branch/commit options /// /// [DisallowMultipleComponent] public class SelectionController : MonoBehaviour { [field: SerializeField] public Transform ContentTarget { get; set; } [field: SerializeField] public OptionViewComponent OptionViewPrefab { get; set; } [field: SerializeField] public Texture SendIcon { get; set; } public Button BackButton { get; private set; } public Text BackText { get; private set; } public Button RefreshButton { get; private set; } public void GenerateOptions(OptionSelection optionSelection, Action onSelect) where T : class { ClearOptionViews(); CreateOptionViews(optionSelection, onSelect); } public OptionViewComponent CreateSendView(Action onSelect) { string title = "Create new commit"; string description = "Send current scene as a new commit"; OptionViewComponent optionElement = Instantiate(OptionViewPrefab, ContentTarget); optionElement.Initialise(title, description, () => { onSelect.Invoke(); }, "Send"); SetImage(optionElement, SendIcon); return optionElement; } public List CreateOptionViews(OptionSelection optionSelection, Action callback) where T : class { List views = new List(optionSelection.Options.Length); for (int i = 0; i < optionSelection.Options.Length; i++) { //TODO not the cleanest solution, but for now it's ok var view = optionSelection switch { AccountSelection a => CreateAccountView(a, i, callback), StreamSelection s => CreateStreamView(s, i, callback), BranchSelection b => CreateBranchView(b, i, callback), CommitSelection c => CreateCommitView(c, i, callback), _ => throw new ArgumentException($"Argument of unexpected type: {optionSelection.GetType()}", nameof(optionSelection)) }; views.Add(view); } return views; } public void ClearOptionViews() { foreach (var child in ContentTarget.GetComponentsInChildren()) { Destroy(child.gameObject); } } private static void SetImage(OptionViewComponent optionElement, Texture image) { optionElement.Image.texture = image; var rect = optionElement.Image.rectTransform; var width = (40 * 4) * ((float)image.width / image.height); var height = rect.sizeDelta.y; rect.sizeDelta = new Vector2(width, height); } private OptionViewComponent CreateAccountView(AccountSelection selection, int index, Action onSelect) { Account a = selection.Options[index]; var serverInfo = a.serverInfo; var userInfo = a.userInfo; string title = $"{serverInfo.name} | {serverInfo.url}"; string description = $"{userInfo.name} | {userInfo.email}"; Texture2D avatar = new Texture2D (1, 1); avatar.LoadImage(Convert.FromBase64String(userInfo.avatar.Split(',')[^1])); avatar.Apply(); OptionViewComponent optionElement = Instantiate(OptionViewPrefab, ContentTarget); optionElement.Initialise(title, description, () => { selection.SelectedIndex = index; onSelect.Invoke(); }); SetImage(optionElement, avatar); return optionElement; } private OptionViewComponent CreateStreamView(StreamSelection selection, int index, Action onSelect) { var s = selection.Options[index]; string title = $"{s.name} | {s.id}"; string description = $"Updated {FormatTime(s.updatedAt)} ago : {s.description}"; string imageurl = $"{selection.Client.ServerUrl}/preview/{s.id}"; OptionViewComponent optionElement = Instantiate(OptionViewPrefab, ContentTarget); optionElement.Initialise(title, description, () => { selection.SelectedIndex = index; onSelect.Invoke(); }); StartCoroutine(Utils.GetImageRoutine(imageurl, selection.Client.ApiToken, t => SetImage(optionElement, t)) ); return optionElement; } private OptionViewComponent CreateBranchView(BranchSelection selection, int index, Action onSelect) { var stream = selection.StreamSelection.Selected; var b = selection.Options[index]; string title = b.name; string description = b.description; string imageurl = $"{selection.Client.ServerUrl}/preview/{stream.id}/branches/{b.name}"; OptionViewComponent optionElement = Instantiate(OptionViewPrefab, ContentTarget); optionElement.Initialise(title, description, () => { selection.SelectedIndex = index; onSelect.Invoke(); }); StartCoroutine(Utils.GetImageRoutine(imageurl, selection.Client.ApiToken, t => SetImage(optionElement, t)) ); return optionElement; } private OptionViewComponent CreateCommitView(CommitSelection selection, int index, Action onSelect) { var stream = selection.BranchSelection.StreamSelection.Selected; var c = selection.Options[index]; string title = $"{c.message} | {c.id}"; string description = $"{c.sourceApplication} - {FormatTime(DateTime.Parse(c.createdAt))} ago"; string imageurl = $"{selection.Client.ServerUrl}/preview/{stream.id}/commits/{c.id}"; OptionViewComponent optionElement = Instantiate(OptionViewPrefab, ContentTarget); optionElement.Initialise(title, description, () => { selection.SelectedIndex = index; onSelect.Invoke(); }, "Receive"); StartCoroutine(Utils.GetImageRoutine(imageurl, selection.Client.ApiToken, t => SetImage(optionElement, t)) ); return optionElement; } void Awake() { var buttons = GetComponentsInChildren