using System; using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; using Speckle.Core.Api; using Speckle.Core.Credentials; using UnityEngine; using UnityEngine.Events; namespace Speckle.ConnectorUnity.Ops { /// /// A unity asset object for interacting with a . /// Only use this type of object if you would like editor based capabilities with a stream. /// If no editor functionality is needed, try using instead. /// [CreateAssetMenu(menuName = "Speckle/Speckle Stream", fileName = "SpeckleStream", order = 0)] public class ScriptableSpeckleStream : ScriptableObject, ISpeckleOps { [SerializeField, HideInInspector] StreamAdapter _stream; [SerializeField, HideInInspector] AccountAdapter _account; [SerializeField, HideInInspector] SpeckleUnityClient _client; [SerializeField, HideInInspector] StreamUpdateInputWrapper _update; public event Action OnUpdatesAdded; public Account account => _account?.source; public Stream stream => _stream?.source; public CancellationToken token { get; set; } public string id { get => _stream?.id; } public string streamName { get => _stream?.name; } public string description { get => _stream?.description; } public bool isPublic { get => _stream?.isPublic ?? false; } public Branch branch { get => _stream?.branch; } public List branches { get => _stream?.branches; } public Commit commit { get => _stream?.commit; } public List commits { get => _stream?.commits; } public async UniTask Initialize(Account accountToUse, string streamId) { try { if (accountToUse == null || !streamId.Valid()) { SpeckleUnity.Console.Warn( $"Invalid input during {nameof(Initialize)} for {name}\n" + $"stream :{(streamId.Valid() ? streamId : "invalid")}" + $"account :{(accountToUse != null ? account.ToString() : "invalid")}"); return; } _account = new AccountAdapter(accountToUse); _client = new SpeckleUnityClient(_account.source); if (!_client.IsValid()) { SpeckleUnity.Console.Warn($"{name} did not complete {nameof(Initialize)} properly. Seems like the client is invalid"); return; } _stream = new StreamAdapter(await _client.StreamGet(streamId)); if (!_stream.IsValid()) { SpeckleUnity.Console.Warn($"{name} did not complete {nameof(Initialize)} properly. Seems like the stream is invalid"); return; } SpeckleUnity.Console.Log($"{name} is all ready to go! {stream}"); name = _stream.name; } catch (Exception e) { SpeckleUnity.Console.Warn(e.Message); } finally { OnClientRefresh?.Invoke(); } } public async UniTask Create(Account accountToUse) { if (_client == null) { SpeckleUnity.Console.Warn("Invalid Account being used to Create this stream"); return false; } var resId = await _client.StreamCreate(new StreamCreateInput { isPublic = this.isPublic, description = this.description, name = this.streamName }); await Initialize(accountToUse, resId); return _stream != null && _stream.IsValid(); } /// /// Sets new stream details to be applied to this /// /// /// /// Will push the changes automatically /// public void Modify(bool updatedPublic, string updatedName = null, string updatedDescription = null, bool immediatelyUpdate = false) { if (_stream == null) { SpeckleUnity.Console.Warn($"{this.name} does not have a valid stream associated with it."); return; } // store any changes being made _update = new StreamUpdateInputWrapper { isPublic = updatedPublic, name = updatedName.Valid() ? updatedName : this.streamName, description = updatedDescription.Valid() ? updatedDescription : this.description }; if (!immediatelyUpdate) return; UniTask.Create(async () => { if (await _client.StreamUpdate(_update.Get(this.id))) { OnUpdatesAdded?.Invoke(); } }); } public event UnityAction OnClientRefresh; [Serializable] internal class StreamUpdateInputWrapper { public string name; public string description; public bool isPublic; public StreamUpdateInput Get(string id) => new StreamUpdateInput { id = id, name = this.name, description = this.description, isPublic = this.isPublic }; } } }