This commit is contained in:
2026-05-13 23:02:02 +07:00
parent 5025383676
commit 93da00c206
885 changed files with 980996 additions and 1987 deletions

View File

@@ -0,0 +1,323 @@
using DA_Assets.Constants;
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.FCU.Model;
using DA_Assets.Logging;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using Random = UnityEngine.Random;
using System.Linq;
#if JSONNET_EXISTS
using Newtonsoft.Json;
#endif
namespace DA_Assets.FCU
{
[Serializable]
public class Authorizer : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
[SerializeField] int _selectedTableIndex;
public int SelectedTableIndex { get => _selectedTableIndex; set => SetValue(ref _selectedTableIndex, value); }
[SerializeField] GUIContent[] _options;
public GUIContent[] Options { get => _options; set => SetValue(ref _options, value); }
[SerializeField] List<FigmaSessionItem> _recentSessions;
public List<FigmaSessionItem> RecentSessions { get => _recentSessions; set => SetValue(ref _recentSessions, value); }
public FigmaSessionItem CurrentSession { get; set; }
public string Token => this.CurrentSession.AuthResult.AccessToken;
public override void OnLink()
{
base.OnLink();
Init();
}
public async void Init()
{
if (monoBeh.IsJsonNetExists() == false)
{
_options = new GUIContent[]
{
new GUIContent($"Error.")
};
DALogger.LogError(FcuLocKey.log_cant_find_package.Localize(DAConstants.JsonNetPackageName));
return;
}
_recentSessions = await monoBeh.Authorizer.GetSessionItems();
_options = await GetOptions(false);
_options = await GetOptions(true);
}
private async Task<GUIContent[]> GetOptions(bool includeImages)
{
List<GUIContent> options = new List<GUIContent>();
if (_recentSessions.IsEmpty())
{
options.Add(new GUIContent(FcuLocKey.label_no_recent_sessions.Localize()));
}
else
{
foreach (FigmaSessionItem session in _recentSessions)
{
if (includeImages)
{
var tex = await DA_Assets.Networking.RequestSender.LoadImage(session.User.ImgUrl);
options.Add(new GUIContent($"{session.User.Name} | {session.User.Email}", tex));
}
else
{
options.Add(new GUIContent($"{session.User.Name} | {session.User.Email}"));
}
}
}
return options.ToArray();
}
public void Auth()
{
if (monoBeh.IsJsonNetExists() == false)
{
DALogger.LogError(FcuLocKey.log_cant_find_package.Localize(DAConstants.JsonNetPackageName));
return;
}
monoBeh.AssetTools.StopImport(StopImportReason.Hidden);
CancellationTokenSource cts = monoBeh.CancellationTokenController.CreateNew(TokenType.Import);
_ = AuthAsync(cts);
}
public async Task AuthAsync(CancellationTokenSource cts)
{
DAResult<AuthResult> authResult = await StartAuthThread();
if (authResult.Success)
{
_ = monoBeh.Authorizer.AddNew(authResult.Object, AuthType.OAuth2);
}
else
{
DALogger.LogError(FcuLocKey.log_cant_auth.Localize(authResult.Error.Message, authResult.Error.Status));
}
}
private async Task<DAResult<AuthResult>> StartAuthThread()
{
string code = "";
bool gettingCode = true;
Thread thread = null;
DALogger.Log(FcuLocKey.log_open_auth_page.Localize());
thread = new Thread(x =>
{
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 1923);
server.Bind(endpoint);
server.Listen(1);
Socket socket = server.Accept();
byte[] bytes = new byte[1000];
socket.Receive(bytes);
string rawCode = Encoding.UTF8.GetString(bytes);
string toSend = "HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\n\n" + @"
<html>
<head>
<style type='text/css'>body,html{background-color: #000000;color: #fff;font-family: Segoe UI;text-align: center;}h2{left: 0; position: absolute; top: calc(50% - 25px); width: 100%;}</style>
<title>Wait for redirect...</title>
<script type='text/javascript'> window.onload=function(){window.location.href='https://figma.com';}</script>
</head>
<body>
<h2>Authorization completed. The page will close automatically.</h2>
</body>
</html>";
bytes = Encoding.UTF8.GetBytes(toSend);
NetworkStream stream = new NetworkStream(socket);
stream.Write(bytes, 0, bytes.Length);
stream.Flush();
stream.Close();
socket.Close();
server.Close();
code = rawCode.GetBetween("?code=", "&state=");
gettingCode = false;
thread.Abort();
});
thread.Start();
int state = Random.Range(0, int.MaxValue);
string formattedOauthUrl = string.Format(FcuConfig.OAuthUrl, FcuConfig.ClientId, FcuConfig.RedirectUri, state.ToString());
Application.OpenURL(formattedOauthUrl);
while (gettingCode)
{
await Task.Delay(100);
}
DARequest tokenRequest = RequestCreator.CreateTokenRequest(code);
return await monoBeh.RequestSender.SendRequest<AuthResult>(tokenRequest);
}
public bool IsAuthed()
{
if (this.CurrentSession.User.Name.IsEmpty() || this.Token.IsEmpty())
{
return false;
}
else
{
return true;
}
}
public async Task AddNew(AuthResult authResult, AuthType authType)
{
DAResult<FigmaUser> result = await GetCurrentFigmaUser(authResult.AccessToken, authType);
if (result.Success)
{
FigmaSessionItem newSess = new FigmaSessionItem
{
User = result.Object,
AuthResult = authResult,
AuthType = authType
};
await SetLastSession(newSess);
monoBeh.Authorizer.Init();
DALogger.LogSuccess(FcuLocKey.log_auth_complete.Localize());
}
else
{
DALogger.LogError(FcuLocKey.log_cant_auth.Localize(result.Error.Status, result.Error.Message, result.Error.Exception));
}
}
private async Task<DAResult<FigmaUser>> GetCurrentFigmaUser(string token, AuthType authType)
{
DARequest request = new DARequest
{
Query = "https://api.figma.com/v1/me",
RequestType = RequestType.Get,
RequestHeader = monoBeh.RequestSender.GetRequestHeader(token, authType)
};
return await monoBeh.RequestSender.SendRequest<FigmaUser>(request);
}
public async Task TryRestoreSession()
{
if (monoBeh.IsJsonNetExists() == false)
return;
if (IsAuthed() == false)
{
FigmaSessionItem item = await GetLastSessionItem();
this.CurrentSession = item;
}
}
private async Task SetLastSession(FigmaSessionItem sessionItem)
{
this.CurrentSession = sessionItem;
List<FigmaSessionItem> sessionItems = await GetSessionItems();
FigmaSessionItem targetItem = sessionItems.FirstOrDefault(item => item.AuthResult.AccessToken == sessionItem.AuthResult.AccessToken);
sessionItems.Remove(targetItem);
sessionItems.Insert(0, sessionItem);
if (sessionItems.Count > FcuConfig.Instance.FigmaSessionsLimit)
{
sessionItems = sessionItems.Take(FcuConfig.Instance.FigmaSessionsLimit).ToList();
}
SaveDataToPrefs(sessionItems);
}
private async Task<FigmaSessionItem> GetLastSessionItem()
{
List<FigmaSessionItem> items = await GetSessionItems();
return items.FirstOrDefault();
}
public async Task<List<FigmaSessionItem>> GetSessionItems()
{
string json = "";
#if UNITY_EDITOR
json = UnityEditor.EditorPrefs.GetString(FcuConfig.FIGMA_SESSIONS_PREFS_KEY, "");
#endif
List<FigmaSessionItem> sessionItems = new List<FigmaSessionItem>();
if (json.IsEmpty())
return sessionItems;
await Task.Run(() =>
{
try
{
sessionItems = DAJson.FromJson<List<FigmaSessionItem>>(json);
}
catch
{
}
});
return sessionItems;
}
private void SaveDataToPrefs(List<FigmaSessionItem> sessionItems)
{
#if UNITY_EDITOR && JSONNET_EXISTS
string json = JsonConvert.SerializeObject(sessionItems);
UnityEditor.EditorPrefs.SetString(FcuConfig.FIGMA_SESSIONS_PREFS_KEY, json);
#endif
}
}
public struct FigmaSessionItem
{
public AuthType AuthType { get; set; }
public AuthResult AuthResult { get; set; }
public FigmaUser User { get; set; }
}
public enum AuthType
{
OAuth2 = 0,
Manual = 1
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f278baa75f0c202478ce70a6b894ad63
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,169 @@
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
namespace DA_Assets.FCU
{
[Serializable]
public class DaGoogleFontsApi : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
[SerializeField] FontSubset fontSubsets = FontSubset.Latin;
[SerializeProperty(nameof(fontSubsets))]
public FontSubset FontSubsets
{
get
{
return fontSubsets;
}
set
{
value |= FontSubset.Latin;
SetValue(ref fontSubsets, value);
}
}
public List<FontSubset> SelectedFontAssets
{
get
{
List<FontSubset> selectedSubsets = Enum.GetValues(fontSubsets.GetType())
.Cast<FontSubset>()
.Where(x => fontSubsets.HasFlag(x))
.ToList();
return selectedSubsets;
}
}
private Dictionary<FontSubset, List<FontItem>> googleFontsBySubset = new Dictionary<FontSubset, List<FontItem>>();
public async Task GetGoogleFontsBySubset(FontSubset fontSubset)
{
List<FontSubset> selectedSubsets = Enum.GetValues(fontSubsets.GetType())
.Cast<FontSubset>()
.Where(x => fontSubsets.HasFlag(x))
.ToList();
List<FontSubset> missingSubsets = new List<FontSubset>();
foreach (FontSubset subset in Enum.GetValues(fontSubsets.GetType()))
{
if (fontSubsets.HasFlag(subset) == false)
continue;
if (googleFontsBySubset.TryGetValue(subset, out var _) == false)
{
missingSubsets.Add(subset);
}
}
if (missingSubsets.Count == 0)
{
return;
}
foreach (FontSubset missingSubset in missingSubsets)
{
string missingSubsetName = missingSubset.ToLower();
if (missingSubsetName == FontSubset.LatinExt.ToLower())
{
missingSubsetName = "latin-ext";
}
DALogger.Log(FcuLocKey.loading_google_fonts.Localize(missingSubset.ToString()));
string gfontsUrl = "https://content-webfonts.googleapis.com/v1/webfonts?subset={0}&key={1}";
string url = string.Format(gfontsUrl, missingSubsetName, FcuConfig.Instance.GoogleFontsApiKey);
DARequest request = new DARequest
{
RequestType = RequestType.Get,
Query = url
};
DAResult<FontRoot> return0 = await monoBeh.RequestSender.SendRequest<FontRoot>(request);
if (@return0.Success)
{
googleFontsBySubset.Add(missingSubset, @return0.Object.Items);
}
else
{
DALogger.LogError($"Can't get '{missingSubset}' fonts from Google repository.");
}
}
}
public string GetUrlByWeight(FontItem fontItem, int weight, FontStyle fontStyle)
{
try
{
if (fontStyle == FontStyle.Normal)
{
switch (weight)
{
case 100: return fontItem.Files["100"];
case 200: return fontItem.Files["200"];
case 300: return fontItem.Files["300"];
case 400: return fontItem.Files["regular"];
case 500: return fontItem.Files["500"];
case 600: return fontItem.Files["600"];
case 700: return fontItem.Files["700"];
case 800: return fontItem.Files["800"];
case 900: return fontItem.Files["900"];
}
}
else if (fontStyle == FontStyle.Italic)
{
switch (weight)
{
case 100: return fontItem.Files["100italic"];
case 200: return fontItem.Files["200italic"];
case 300: return fontItem.Files["300italic"];
case 400: return fontItem.Files["italic"];
case 500: return fontItem.Files["500italic"];
case 600: return fontItem.Files["600italic"];
case 700: return fontItem.Files["700italic"];
case 800: return fontItem.Files["800italic"];
case 900: return fontItem.Files["900italic"];
}
}
}
catch
{
}
return null;
}
public FontItem GetFontItem(FontMetadata fontMetadata, FontSubset fontSubset)
{
try
{
googleFontsBySubset.TryGetValue(fontSubset, out var googleFonts);
foreach (FontItem item in googleFonts)
{
if (item.Family.ToLower() == fontMetadata.Family.ToLower())
{
return item;
}
}
}
catch
{
}
return default;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5ce20cbdc8f5db5488f24bbce2fcbaf7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,274 @@
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.Logging;
using DA_Assets.Networking;
using System;
using System.Threading.Tasks;
using UnityEngine;
#pragma warning disable IDE0052
namespace DA_Assets.FCU
{
[Serializable]
public class RequestSender : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
[SerializeField] float pbarProgress;
public float PbarProgress => pbarProgress;
[SerializeField] float pbarBytes;
public float PbarBytes => pbarBytes;
[SerializeField] int _requestCount;
[SerializeField] bool _timeoutActive;
[SerializeField] int _remainingTime;
private static int requestCount = 0;
private static bool timeoutActive = false;
private static int remainingTime = 0;
public RequestHeader GetRequestHeader(string token, AuthType? authType = null)
{
AuthType currentAuthType = authType != null ? (AuthType)authType : monoBeh.Authorizer.CurrentSession.AuthType;
switch (currentAuthType)
{
case AuthType.OAuth2:
return new RequestHeader
{
Name = "Authorization",
Value = $"Bearer {token}"
};
case AuthType.Manual:
return new RequestHeader
{
Name = "X-Figma-Token",
Value = $"{token}"
};
default:
throw new NotImplementedException();
}
}
public void RefreshLimiterData()
{
_requestCount = requestCount;
_timeoutActive = timeoutActive;
_remainingTime = remainingTime;
}
private async Task CheckRateLimit()
{
while (requestCount >= FcuConfig.Instance.ApiRequestsCountLimit)
{
if (timeoutActive == false)
{
timeoutActive = true;
remainingTime = FcuConfig.Instance.ApiTimeoutSec;
_ = LogRemainingTime();
}
await Task.Delay(1000);
}
requestCount++;
}
private async Task LogRemainingTime()
{
while (remainingTime > 0)
{
DALogger.Log(FcuLocKey.log_api_waiting.Localize(remainingTime));
RefreshLimiterData();
await Task.Delay(1000);
remainingTime--;
}
requestCount = 0;
timeoutActive = false;
}
public async Task<DAResult<T>> SendRequest<T>(DARequest request)
{
await CheckRateLimit();
UnityHttpClient webRequest;
switch (request.RequestType)
{
case RequestType.Post:
webRequest = UnityHttpClient.Post(request.Query, request.WWWForm);
break;
default:
webRequest = UnityHttpClient.Get(request.Query);
break;
}
if (monoBeh.IsDebug())
{
Debug.Log(request.Query);
}
using (webRequest)
{
if (request.RequestHeader.IsDefault() == false)
{
webRequest.SetRequestHeader(request.RequestHeader.Name, request.RequestHeader.Value);
}
try
{
_ = webRequest.SendWebRequest();
}
catch (InvalidOperationException)
{
DALogger.LogError(FcuLocKey.log_enable_http_project_settings.Localize());
monoBeh.AssetTools.StopImport(StopImportReason.Error);
return default;
}
catch (Exception ex)
{
DALogger.LogException(ex);
}
await UpdateRequestProgressBar(webRequest);
await MoveRequestProgressBarToEnd();
DAResult<T> result = new DAResult<T>();
if (request.RequestType == RequestType.GetFile)
{
result.Success = true;
result.Object = (T)(object)webRequest.downloadHandler.data;
}
else
{
_ = request.WriteLog(webRequest);
string text = webRequest.downloadHandler.text;
if (typeof(T) == typeof(string))
{
result.Success = true;
result.Object = (T)(object)text;
}
else
{
result = await TryParseResponse<T>(text, request, webRequest);
}
}
return result;
}
}
private async Task<DAResult<T>> TryParseResponse<T>(string text, DARequest request, UnityHttpClient webRequest)
{
DAResult<T> result = new DAResult<T>();
int state;
DAResult<WebError> figmaApiError = await DAJson.FromJsonAsync<WebError>(text);
bool isRequestError = webRequest.result != WR_Result.Success;
if (figmaApiError.Object.Message != null)
{
state = 1;
result.Success = false;
result.Error = figmaApiError.Object;
}
else if (isRequestError)
{
result.Success = false;
if (webRequest.error.Contains("SSL"))
{
state = 2;
result.Error = new WebError(909, text);
}
else
{
state = 3;
result.Error = new WebError((int)webRequest.responseCode, webRequest.error);
}
}
else if (text.Contains("<pre>Cannot GET "))
{
state = 4;
result.Error = new WebError(404, text);
}
else
{
DAResult<T> obj = await DAJson.FromJsonAsync<T>(text);
if (obj.Success)
{
state = 5;
result.Success = true;
result.Object = obj.Object;
if (request.Name == RequestName.Project)
{
monoBeh.ProjectCacher.Cache(obj.Object);
}
}
else
{
state = 6;
result.Success = false;
result.Error = obj.Error;
}
}
DALogger.Log($"TryParseResponse | {state}");
return result;
}
private async Task UpdateRequestProgressBar(UnityHttpClient webRequest)
{
while (webRequest.isDone == false)
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
return;
if (webRequest.downloadProgress == 0 || webRequest.downloadedBytes == 0)
{
if (pbarProgress < 1f)
{
pbarProgress += 0.01f;
}
else
{
pbarProgress = 0;
}
pbarBytes += 100;
}
else
{
pbarProgress = webRequest.downloadProgress;
pbarBytes = webRequest.downloadedBytes;
}
await Task.Yield();
}
}
private async Task MoveRequestProgressBarToEnd()
{
float left = 1f - pbarProgress;
int steps = 10;
float stepIncrement = left / steps;
for (int i = 0; i < steps; i++)
{
pbarProgress += stepIncrement;
await Task.Yield();
}
pbarProgress = 0f;
pbarBytes = 0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7458c269f0369504a8fa25fdae6fedca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: