You've already forked CC-Framework.BriskGameServer
1370 lines
47 KiB
C#
1370 lines
47 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
|
||
public sealed class BriskQuickStartSample : MonoBehaviour
|
||
{
|
||
private const float DesignWidth = 720f;
|
||
private const float DesignHeight = 1280f;
|
||
|
||
private enum SamplePage
|
||
{
|
||
Initialize,
|
||
Login,
|
||
Home,
|
||
Announcements,
|
||
Leaderboard,
|
||
Archive,
|
||
Space
|
||
}
|
||
|
||
[Header("初始化")]
|
||
public string BaseUrl = "https://brisk.lightyears.ltd";
|
||
public string GameKey = "demo-game";
|
||
public string ClientVersion = "1.0.0";
|
||
public string DeviceId = "editor-device";
|
||
public bool ValidateSessionOnInitialize = true;
|
||
|
||
[Header("登录")]
|
||
public string LoginProvider = "tap";
|
||
public string LoginUserId = "tap_user_10001";
|
||
public string LoginCode = string.Empty;
|
||
public string Nickname = "Unity示例玩家";
|
||
public string AvatarUrl = string.Empty;
|
||
|
||
[Header("排行榜")]
|
||
public string RankKey = "season-score";
|
||
public string SubmitScoreValue = "0";
|
||
public string LeaderboardLimit = "10";
|
||
public string AroundMeRange = "5";
|
||
public string SeasonId = string.Empty;
|
||
public string SeasonHistoryLimit = "20";
|
||
|
||
[Header("云存档")]
|
||
public string ArchiveSlotNo = "1";
|
||
public string ArchiveBaseVersion = string.Empty;
|
||
[TextArea(3, 6)]
|
||
public string ArchiveContent = "{\n \"save\": 1,\n \"coins\": 128,\n \"hero\": \"mage\",\n \"title\": \"中文测试存档\"\n}";
|
||
|
||
[Header("公告")]
|
||
public string AnnouncementId = string.Empty;
|
||
|
||
[Header("玩家空间")]
|
||
public string SpacePlayerId = string.Empty;
|
||
public string SpaceLoginProvider = "tap";
|
||
public string SpaceLoginUserId = "tap_user_10001";
|
||
[TextArea(3, 6)]
|
||
public string SpacePayloadText = "这里是 unity 测试账户的空间内容。\n\n0.o";
|
||
|
||
[Header("演示")]
|
||
public bool AutoRunOnStart;
|
||
|
||
private readonly List<string> _logs = new List<string>();
|
||
|
||
private Vector2 _initScroll;
|
||
private Vector2 _loginScroll;
|
||
private Vector2 _homeScroll;
|
||
private Vector2 _announcementScroll;
|
||
private Vector2 _leaderboardScroll;
|
||
private Vector2 _archiveScroll;
|
||
private Vector2 _spaceScroll;
|
||
private Vector2 _modalScroll;
|
||
|
||
private SamplePage _page = SamplePage.Initialize;
|
||
private bool _isBusy;
|
||
private string _busyAction = string.Empty;
|
||
private string _statusText = "等待开始";
|
||
private string _errorText = string.Empty;
|
||
|
||
private BriskPlayerMe _me;
|
||
private BriskConfigCurrent _config;
|
||
private IReadOnlyList<BriskAnnouncementItem> _announcements = Array.Empty<BriskAnnouncementItem>();
|
||
private BriskAnnouncementItem _selectedAnnouncement;
|
||
private IReadOnlyList<BriskLeaderboardEntry> _leaderboardEntries = Array.Empty<BriskLeaderboardEntry>();
|
||
private BriskLeaderboardPlayerRank _leaderboardMe;
|
||
private IReadOnlyList<BriskArchiveSlot> _archiveSlots = Array.Empty<BriskArchiveSlot>();
|
||
private BriskArchiveMeta _archiveMeta;
|
||
private BriskSpaceView _mySpaceView;
|
||
private BriskSpaceStats _mySpaceStats;
|
||
private IReadOnlyList<BriskSpaceLikeItem> _mySpaceLikes = Array.Empty<BriskSpaceLikeItem>();
|
||
private BriskSpaceView _targetSpaceView;
|
||
private BriskSpaceStats _targetSpaceStats;
|
||
private string _targetSpaceContentText = string.Empty;
|
||
private bool _viewAroundMe;
|
||
private bool _showAnnouncementModal;
|
||
private bool _viewingTargetSpace;
|
||
|
||
private GUIStyle _pageStyle;
|
||
private GUIStyle _cardStyle;
|
||
private GUIStyle _titleStyle;
|
||
private GUIStyle _sectionTitleStyle;
|
||
private GUIStyle _bodyStyle;
|
||
private GUIStyle _hintStyle;
|
||
private GUIStyle _buttonStyle;
|
||
private GUIStyle _primaryButtonStyle;
|
||
private GUIStyle _dangerButtonStyle;
|
||
private GUIStyle _inputStyle;
|
||
private GUIStyle _textAreaStyle;
|
||
private GUIStyle _statusStyle;
|
||
private GUIStyle _listButtonStyle;
|
||
private GUIStyle _modalStyle;
|
||
private PropertyInfo _defaultDialogVisibleProperty;
|
||
private MethodInfo _defaultDialogRenderMethod;
|
||
|
||
private void OnEnable()
|
||
{
|
||
Brisk.OnInitialized += HandleInitialized;
|
||
Brisk.OnLoggedIn += HandleLoggedIn;
|
||
Brisk.OnLoggedOut += HandleLoggedOut;
|
||
Brisk.OnAuthExpired += HandleAuthExpired;
|
||
Brisk.OnBlockingError += HandleBlockingError;
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
Brisk.OnInitialized -= HandleInitialized;
|
||
Brisk.OnLoggedIn -= HandleLoggedIn;
|
||
Brisk.OnLoggedOut -= HandleLoggedOut;
|
||
Brisk.OnAuthExpired -= HandleAuthExpired;
|
||
Brisk.OnBlockingError -= HandleBlockingError;
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
SyncPageBySdkState();
|
||
if (AutoRunOnStart)
|
||
{
|
||
RunAction("自动冒烟", RunSmokeFlowAsync);
|
||
}
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
if (ShouldRouteInputToDefaultDialog())
|
||
{
|
||
RenderDefaultErrorDialogTopmost();
|
||
return;
|
||
}
|
||
|
||
EnsureStyles();
|
||
|
||
var scale = Mathf.Min(Screen.width / DesignWidth, Screen.height / DesignHeight);
|
||
var offsetX = (Screen.width - (DesignWidth * scale)) * 0.5f;
|
||
var offsetY = (Screen.height - (DesignHeight * scale)) * 0.5f;
|
||
|
||
var previousMatrix = GUI.matrix;
|
||
GUI.matrix = Matrix4x4.TRS(new Vector3(offsetX, offsetY, 0f), Quaternion.identity, new Vector3(scale, scale, 1f));
|
||
|
||
GUI.Box(new Rect(0f, 0f, DesignWidth, DesignHeight), GUIContent.none);
|
||
GUILayout.BeginArea(new Rect(0f, 0f, DesignWidth, DesignHeight));
|
||
DrawCurrentPage();
|
||
GUILayout.EndArea();
|
||
|
||
if (_showAnnouncementModal && _selectedAnnouncement != null)
|
||
{
|
||
DrawAnnouncementModal();
|
||
}
|
||
|
||
if (_isBusy)
|
||
{
|
||
DrawBusyOverlay();
|
||
}
|
||
|
||
GUI.matrix = previousMatrix;
|
||
|
||
RenderDefaultErrorDialogTopmost();
|
||
}
|
||
|
||
private bool ShouldRouteInputToDefaultDialog()
|
||
{
|
||
if (!IsDefaultErrorDialogVisible() || Event.current == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
switch (Event.current.type)
|
||
{
|
||
case EventType.Layout:
|
||
case EventType.Repaint:
|
||
return false;
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
private bool IsDefaultErrorDialogVisible()
|
||
{
|
||
EnsureDefaultDialogReflection();
|
||
return _defaultDialogVisibleProperty != null && (bool)_defaultDialogVisibleProperty.GetValue(null, null);
|
||
}
|
||
|
||
private void RenderDefaultErrorDialogTopmost()
|
||
{
|
||
EnsureDefaultDialogReflection();
|
||
_defaultDialogRenderMethod?.Invoke(null, null);
|
||
}
|
||
|
||
private void EnsureDefaultDialogReflection()
|
||
{
|
||
if (_defaultDialogVisibleProperty != null && _defaultDialogRenderMethod != null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var dialogType = typeof(Brisk).Assembly.GetType("BriskDefaultErrorDialog");
|
||
if (dialogType == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_defaultDialogVisibleProperty = dialogType.GetProperty("IsVisible", BindingFlags.Static | BindingFlags.NonPublic);
|
||
_defaultDialogRenderMethod = dialogType.GetMethod("RenderTopmostLegacyOverlay", BindingFlags.Static | BindingFlags.NonPublic);
|
||
}
|
||
|
||
private void DrawCurrentPage()
|
||
{
|
||
switch (_page)
|
||
{
|
||
case SamplePage.Initialize:
|
||
DrawInitializePage();
|
||
break;
|
||
case SamplePage.Login:
|
||
DrawLoginPage();
|
||
break;
|
||
case SamplePage.Home:
|
||
DrawHomePage();
|
||
break;
|
||
case SamplePage.Announcements:
|
||
DrawAnnouncementsPage();
|
||
break;
|
||
case SamplePage.Leaderboard:
|
||
DrawLeaderboardPage();
|
||
break;
|
||
case SamplePage.Archive:
|
||
DrawArchivePage();
|
||
break;
|
||
default:
|
||
DrawSpacePage();
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void DrawInitializePage()
|
||
{
|
||
var contentRect = DrawPageFrame("初始化", null, null);
|
||
GUILayout.BeginArea(contentRect);
|
||
_initScroll = GUILayout.BeginScrollView(_initScroll);
|
||
|
||
DrawCard("当前配置", () =>
|
||
{
|
||
BaseUrl = DrawInputRow("服务地址", BaseUrl);
|
||
GameKey = DrawInputRow("游戏 Key", GameKey);
|
||
ClientVersion = DrawInputRow("客户端版本", ClientVersion);
|
||
DeviceId = DrawInputRow("设备标识", DeviceId);
|
||
ValidateSessionOnInitialize = DrawToggleRow("初始化时校验旧会话", ValidateSessionOnInitialize);
|
||
});
|
||
|
||
DrawCard("说明", () =>
|
||
{
|
||
GUILayout.Label("点击“初始化”后会直接调用 Brisk.InitializeAsync。", _bodyStyle);
|
||
GUILayout.Label("成功时自动进入登录页;如果已恢复登录态,则直接进入主页。", _bodyStyle);
|
||
GUILayout.Label("失败会停留在当前页,并把错误展示在底部状态栏。", _bodyStyle);
|
||
});
|
||
|
||
DrawCard("默认弹窗外观预览", () =>
|
||
{
|
||
GUILayout.Label("这些按钮只展示 SDK 默认弹窗外观,不会修改登录状态,也不会请求服务器。", _bodyStyle);
|
||
DrawInlineButtons(
|
||
new ButtonSpec("登录过期", PreviewAuthExpiredDialogAsync, false, true),
|
||
new ButtonSpec("缺少 Token", PreviewMissingTokenDialogAsync, false, true));
|
||
GUILayout.Space(8f);
|
||
DrawInlineButtons(
|
||
new ButtonSpec("服务维护", PreviewMaintenanceDialogAsync, false, true),
|
||
new ButtonSpec("账号受限", PreviewAccountBannedDialogAsync, false, true),
|
||
new ButtonSpec("需要更新", PreviewUpdateRequiredDialogAsync, false, true));
|
||
});
|
||
|
||
GUILayout.FlexibleSpace();
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
|
||
DrawBottomButtons(
|
||
new ButtonSpec("初始化", InitializeFlowAsync, true, true),
|
||
new ButtonSpec("重新填写", ResetTransientStatusAsync, false, true));
|
||
}
|
||
|
||
private void DrawLoginPage()
|
||
{
|
||
var contentRect = DrawPageFrame("登录", "返回初始化", () => _page = SamplePage.Initialize);
|
||
GUILayout.BeginArea(contentRect);
|
||
_loginScroll = GUILayout.BeginScrollView(_loginScroll);
|
||
|
||
DrawCard("登录信息", () =>
|
||
{
|
||
LoginProvider = DrawInputRow("登录提供方", LoginProvider);
|
||
LoginUserId = DrawInputRow("用户 ID", LoginUserId);
|
||
LoginCode = DrawInputRow("登录 Code(可选)", LoginCode);
|
||
Nickname = DrawInputRow("昵称", Nickname);
|
||
AvatarUrl = DrawInputRow("头像地址", AvatarUrl);
|
||
});
|
||
|
||
DrawCard("说明", () =>
|
||
{
|
||
GUILayout.Label("点击“登录”时:如果填写了 LoginCode,则优先走 code 登录;否则走用户 ID 登录。", _bodyStyle);
|
||
GUILayout.Label("登录成功后自动拉取主页数据,并进入主页。", _bodyStyle);
|
||
});
|
||
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
|
||
DrawBottomButtons(new ButtonSpec("登录", LoginFlowAsync, true, true));
|
||
}
|
||
|
||
private void DrawHomePage()
|
||
{
|
||
var contentRect = DrawPageFrame("主页", "退出登录", () => RunAction("退出登录", LogoutFlowAsync));
|
||
GUILayout.BeginArea(contentRect);
|
||
_homeScroll = GUILayout.BeginScrollView(_homeScroll);
|
||
|
||
DrawCard("当前用户状态", () =>
|
||
{
|
||
DrawValueRow("已初始化", Brisk.IsInitialized ? "是" : "否");
|
||
DrawValueRow("已登录", Brisk.IsLoggedIn ? "是" : "否");
|
||
DrawValueRow("PlayerId", NullToDash(Brisk.PlayerId));
|
||
DrawValueRow("登录身份", Brisk.Identity == null ? "-" : Brisk.Identity.LoginProvider + " / " + Brisk.Identity.LoginUserId);
|
||
DrawValueRow("昵称", _me == null ? "-" : NullToDash(_me.Nickname));
|
||
DrawValueRow("项目账号", _me == null ? "-" : NullToDash(_me.ProjectAccountId));
|
||
});
|
||
|
||
DrawCard("动态配置", () =>
|
||
{
|
||
GUILayout.Label("FeatureFlags", _sectionTitleStyle);
|
||
GUILayout.TextArea(FormatValue(_config == null ? null : _config.FeatureFlags), _textAreaStyle, GUILayout.Height(120f));
|
||
GUILayout.Space(8f);
|
||
GUILayout.Label("DynamicConfig", _sectionTitleStyle);
|
||
GUILayout.TextArea(FormatValue(_config == null ? null : _config.DynamicConfig), _textAreaStyle, GUILayout.Height(160f));
|
||
});
|
||
|
||
DrawCard("快捷操作", () =>
|
||
{
|
||
DrawInlineButtons(
|
||
new ButtonSpec("刷新主页数据", RefreshHomeAsync, false, true),
|
||
new ButtonSpec("查看日志", ShowHomeLogsAsync, false, true));
|
||
});
|
||
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
|
||
DrawBottomNav(
|
||
new NavSpec("公告", SamplePage.Announcements),
|
||
new NavSpec("排行榜", SamplePage.Leaderboard),
|
||
new NavSpec("云存档", SamplePage.Archive),
|
||
new NavSpec("空间", SamplePage.Space));
|
||
}
|
||
|
||
private void DrawAnnouncementsPage()
|
||
{
|
||
var contentRect = DrawPageFrame("公告", "返回主页", () => _page = SamplePage.Home, "刷新", LoadAnnouncementsAsync);
|
||
GUILayout.BeginArea(contentRect);
|
||
_announcementScroll = GUILayout.BeginScrollView(_announcementScroll);
|
||
|
||
if (_announcements.Count == 0)
|
||
{
|
||
DrawCard("公告列表", () => GUILayout.Label("暂无公告,点击右上角“刷新”获取。", _bodyStyle));
|
||
}
|
||
else
|
||
{
|
||
DrawCard("公告列表", () =>
|
||
{
|
||
for (var i = 0; i < _announcements.Count; i++)
|
||
{
|
||
var item = _announcements[i];
|
||
var prefix = item.IsRead ? "[已读] " : "[未读] ";
|
||
if (GUILayout.Button(prefix + item.Title, _listButtonStyle, GUILayout.Height(58f)))
|
||
{
|
||
_selectedAnnouncement = item;
|
||
_showAnnouncementModal = true;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawLeaderboardPage()
|
||
{
|
||
var contentRect = DrawPageFrame("排行榜", "返回主页", () => _page = SamplePage.Home);
|
||
GUILayout.BeginArea(contentRect);
|
||
_leaderboardScroll = GUILayout.BeginScrollView(_leaderboardScroll);
|
||
|
||
DrawCard("排行榜配置", () =>
|
||
{
|
||
RankKey = DrawInputRow("排行榜 Key", RankKey);
|
||
LeaderboardLimit = DrawInputRow("Top 数量", LeaderboardLimit);
|
||
AroundMeRange = DrawInputRow("附近范围", AroundMeRange);
|
||
DrawInlineButtons(
|
||
new ButtonSpec("更新列表", RefreshLeaderboardAsync, false, true),
|
||
new ButtonSpec(_viewAroundMe ? "切换到 Top" : "切换到我附近", ToggleLeaderboardModeAsync, false, true));
|
||
});
|
||
|
||
DrawCard("我的排名", () =>
|
||
{
|
||
DrawValueRow("当前模式", _viewAroundMe ? "我附近的排名" : "Top 排行榜");
|
||
DrawValueRow("我的名次", _leaderboardMe == null ? "-" : _leaderboardMe.Rank.ToString());
|
||
DrawValueRow("我的分数", _leaderboardMe == null ? "-" : _leaderboardMe.Score.ToString());
|
||
DrawValueRow("当前待提交分数", SubmitScoreValue);
|
||
DrawInlineButtons(new ButtonSpec("分数 +1 并提交", IncreaseAndSubmitScoreAsync, true, true));
|
||
});
|
||
|
||
DrawCard("排行榜列表", () =>
|
||
{
|
||
if (_leaderboardEntries.Count == 0)
|
||
{
|
||
GUILayout.Label("暂无数据,请先点击“更新列表”。", _bodyStyle);
|
||
return;
|
||
}
|
||
|
||
for (var i = 0; i < _leaderboardEntries.Count; i++)
|
||
{
|
||
var entry = _leaderboardEntries[i];
|
||
GUILayout.BeginVertical(_cardStyle);
|
||
GUILayout.Label("#" + entry.Rank + " " + NullToDash(entry.Nickname), _sectionTitleStyle);
|
||
GUILayout.Label("PlayerId: " + NullToDash(entry.PlayerId), _bodyStyle);
|
||
GUILayout.Label("分数: " + entry.Score, _bodyStyle);
|
||
GUILayout.EndVertical();
|
||
}
|
||
});
|
||
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawArchivePage()
|
||
{
|
||
var contentRect = DrawPageFrame("云存档", "返回主页", () => _page = SamplePage.Home);
|
||
GUILayout.BeginArea(contentRect);
|
||
_archiveScroll = GUILayout.BeginScrollView(_archiveScroll);
|
||
|
||
DrawCard("槽位与操作", () =>
|
||
{
|
||
ArchiveSlotNo = DrawInputRow("槽位号", ArchiveSlotNo);
|
||
ArchiveBaseVersion = DrawInputRow("基准版本", ArchiveBaseVersion);
|
||
DrawInlineButtons(
|
||
new ButtonSpec("获取槽位列表", GetArchiveSlotsAsync, false, true),
|
||
new ButtonSpec("获取元信息", GetArchiveMetaAsync, false, true),
|
||
new ButtonSpec("下载存档", DownloadArchiveAsync, false, true),
|
||
new ButtonSpec("上传存档", UploadArchiveAsync, true, true));
|
||
});
|
||
|
||
DrawCard("文本存档内容", () =>
|
||
{
|
||
ArchiveContent = GUILayout.TextArea(ArchiveContent ?? string.Empty, _textAreaStyle, GUILayout.Height(260f));
|
||
});
|
||
|
||
DrawCard("槽位列表", () =>
|
||
{
|
||
if (_archiveSlots.Count == 0)
|
||
{
|
||
GUILayout.Label("暂无槽位数据。", _bodyStyle);
|
||
}
|
||
else
|
||
{
|
||
for (var i = 0; i < _archiveSlots.Count; i++)
|
||
{
|
||
var slot = _archiveSlots[i];
|
||
GUILayout.Label("槽位 " + slot.SlotNo + " | version " + slot.Version + " | " + slot.SizeBytes + " bytes", _bodyStyle);
|
||
}
|
||
}
|
||
|
||
GUILayout.Space(8f);
|
||
GUILayout.Label("当前元信息", _sectionTitleStyle);
|
||
GUILayout.TextArea(FormatValue(_archiveMeta), _textAreaStyle, GUILayout.Height(120f));
|
||
});
|
||
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawSpacePage()
|
||
{
|
||
var title = _viewingTargetSpace ? "TA 的空间" : "我的空间";
|
||
var backLabel = _viewingTargetSpace ? "返回我的空间" : "返回主页";
|
||
Action backAction = () =>
|
||
{
|
||
if (_viewingTargetSpace)
|
||
{
|
||
_viewingTargetSpace = false;
|
||
_targetSpaceView = null;
|
||
_targetSpaceStats = null;
|
||
_targetSpaceContentText = string.Empty;
|
||
return;
|
||
}
|
||
|
||
_page = SamplePage.Home;
|
||
};
|
||
|
||
var contentRect = DrawPageFrame(title, backLabel, backAction, "刷新", RefreshSpacePageAsync);
|
||
GUILayout.BeginArea(contentRect);
|
||
_spaceScroll = GUILayout.BeginScrollView(_spaceScroll);
|
||
|
||
if (_viewingTargetSpace)
|
||
{
|
||
DrawTargetSpaceContent();
|
||
}
|
||
else
|
||
{
|
||
DrawMySpaceContent();
|
||
}
|
||
|
||
GUILayout.EndScrollView();
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawMySpaceContent()
|
||
{
|
||
DrawCard("我的空间内容", () =>
|
||
{
|
||
GUILayout.Label("当前空间文本", _sectionTitleStyle);
|
||
SpacePayloadText = GUILayout.TextArea(SpacePayloadText ?? string.Empty, _textAreaStyle, GUILayout.Height(240f));
|
||
DrawInlineButtons(
|
||
new ButtonSpec("更新我的空间", UpdateMySpaceAsync, true, true),
|
||
new ButtonSpec("重新拉取我的空间", LoadMySpaceAsync, false, true));
|
||
});
|
||
|
||
DrawCard("我的空间状态", () =>
|
||
{
|
||
DrawValueRow("我的 PlayerId", NullToDash(Brisk.PlayerId));
|
||
DrawValueRow("昵称", _mySpaceView == null ? "-" : NullToDash(_mySpaceView.Nickname));
|
||
DrawValueRow("内容版本", _mySpaceView == null ? "-" : _mySpaceView.ContentVersion.ToString());
|
||
DrawValueRow("内容类型", _mySpaceView == null ? "-" : NullToDash(_mySpaceView.ContentType));
|
||
DrawValueRow("内容大小", _mySpaceView == null ? "-" : _mySpaceView.ContentSizeBytes + " bytes");
|
||
DrawValueRow("累计点赞数", _mySpaceStats == null ? "-" : _mySpaceStats.LikeCount.ToString());
|
||
DrawValueRow("今日点赞数", _mySpaceStats == null ? "-" : _mySpaceStats.TodayLikeCount.ToString());
|
||
DrawValueRow("访问数", _mySpaceStats == null ? "-" : _mySpaceStats.VisitCount.ToString());
|
||
DrawValueRow("今日点赞重置", _mySpaceStats == null ? "-" : FormatLikeResetAt(_mySpaceStats.LikeResetAt));
|
||
DrawValueRow("更新时间", _mySpaceView == null ? "-" : NullToDash(_mySpaceView.UpdatedAt));
|
||
GUILayout.Space(8f);
|
||
GUILayout.Label("今天给我点赞的用户", _sectionTitleStyle);
|
||
if (_mySpaceLikes.Count == 0)
|
||
{
|
||
GUILayout.Label("当前周期暂无点赞记录。", _bodyStyle);
|
||
}
|
||
else
|
||
{
|
||
for (var i = 0; i < _mySpaceLikes.Count; i++)
|
||
{
|
||
var item = _mySpaceLikes[i];
|
||
GUILayout.Label(NullToDash(item.Nickname) + " | " + NullToDash(item.PlayerId), _bodyStyle);
|
||
}
|
||
}
|
||
});
|
||
|
||
DrawCard("访问其他用户空间", () =>
|
||
{
|
||
SpacePlayerId = DrawInputRow("目标用户 PlayerId", SpacePlayerId);
|
||
DrawInlineButtons(new ButtonSpec("前往该用户空间", VisitTargetSpaceAsync, true, true));
|
||
});
|
||
}
|
||
|
||
private void DrawTargetSpaceContent()
|
||
{
|
||
DrawCard("TA 的空间内容", () =>
|
||
{
|
||
DrawValueRow("昵称", _targetSpaceView == null ? "-" : NullToDash(_targetSpaceView.Nickname));
|
||
DrawValueRow("PlayerId", _targetSpaceView == null ? "-" : NullToDash(_targetSpaceView.PlayerId));
|
||
DrawValueRow("内容类型", _targetSpaceView == null ? "-" : NullToDash(_targetSpaceView.ContentType));
|
||
DrawValueRow("内容大小", _targetSpaceView == null ? "-" : _targetSpaceView.ContentSizeBytes + " bytes");
|
||
DrawValueRow("累计点赞数", _targetSpaceStats == null ? "-" : _targetSpaceStats.LikeCount.ToString());
|
||
DrawValueRow("今日点赞数", _targetSpaceStats == null ? "-" : _targetSpaceStats.TodayLikeCount.ToString());
|
||
DrawValueRow("访问数", _targetSpaceStats == null ? "-" : _targetSpaceStats.VisitCount.ToString());
|
||
DrawValueRow("今日点赞状态", _targetSpaceView != null && _targetSpaceView.LikedByMe ? "今日已点赞" : "今日未点赞");
|
||
DrawValueRow("今日点赞重置", _targetSpaceView == null ? "-" : FormatLikeResetAt(_targetSpaceView.LikeResetAt));
|
||
GUILayout.Space(8f);
|
||
GUILayout.Label("空间内容", _sectionTitleStyle);
|
||
GUILayout.TextArea(_targetSpaceContentText ?? string.Empty, _textAreaStyle, GUILayout.Height(240f));
|
||
});
|
||
|
||
DrawCard("操作", () =>
|
||
{
|
||
DrawInlineButtons(
|
||
new ButtonSpec("今日点赞", LikeTargetSpaceAsync, false, _targetSpaceView != null && !_targetSpaceView.LikedByMe),
|
||
new ButtonSpec("撤销今日点赞", UnlikeTargetSpaceAsync, false, _targetSpaceView != null && _targetSpaceView.LikedByMe));
|
||
});
|
||
}
|
||
|
||
private async Task InitializeFlowAsync()
|
||
{
|
||
await Brisk.InitializeAsync(new BriskOptions
|
||
{
|
||
BaseUrl = BaseUrl,
|
||
GameKey = GameKey,
|
||
ClientVersion = ClientVersion,
|
||
DeviceId = DeviceId,
|
||
ValidateSessionOnInitialize = ValidateSessionOnInitialize,
|
||
ExitHandler = HandleExitRequested
|
||
});
|
||
|
||
_page = Brisk.IsLoggedIn ? SamplePage.Home : SamplePage.Login;
|
||
if (Brisk.IsLoggedIn)
|
||
{
|
||
await RefreshHomeAsync();
|
||
}
|
||
}
|
||
|
||
private async Task LoginFlowAsync()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(LoginCode))
|
||
{
|
||
await Brisk.Auth.LoginWithUserIdAsync(LoginProvider, LoginUserId, CreateProfile());
|
||
}
|
||
else
|
||
{
|
||
await Brisk.Auth.LoginWithCodeAsync(LoginProvider, LoginCode, CreateProfile());
|
||
}
|
||
|
||
await RefreshHomeAsync();
|
||
_page = SamplePage.Home;
|
||
}
|
||
|
||
private Task PreviewAuthExpiredDialogAsync()
|
||
{
|
||
BriskDefaultErrorPresenter.Instance.ShowAuthExpired(new BriskAuthExpiredException("invalid token"));
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private Task PreviewMissingTokenDialogAsync()
|
||
{
|
||
BriskDefaultErrorPresenter.Instance.ShowAuthExpired(new BriskAuthExpiredException("Missing access token."));
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private Task PreviewMaintenanceDialogAsync()
|
||
{
|
||
BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskMaintenanceException("maintenance"));
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private Task PreviewAccountBannedDialogAsync()
|
||
{
|
||
BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskAccountBannedException("account banned"));
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private Task PreviewUpdateRequiredDialogAsync()
|
||
{
|
||
BriskDefaultErrorPresenter.Instance.ShowBlockingError(new BriskClientUpdateRequiredException("client update required"));
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private async Task RefreshHomeAsync()
|
||
{
|
||
_me = await Brisk.Player.GetMeAsync();
|
||
_config = await Brisk.Config.GetCurrentAsync();
|
||
ApplyIdentity(_me.PlayerId, _me.LoginProvider, _me.LoginUserId);
|
||
await LoadMySpaceAsync();
|
||
}
|
||
|
||
private async Task LoadAnnouncementsAsync()
|
||
{
|
||
_announcements = await Brisk.Announcements.GetListAsync();
|
||
if (_announcements.Count > 0)
|
||
{
|
||
AnnouncementId = _announcements[0].Id.ToString();
|
||
}
|
||
}
|
||
|
||
private async Task RefreshLeaderboardAsync()
|
||
{
|
||
_leaderboardMe = await Brisk.Leaderboard.GetMeAsync(RankKey);
|
||
SubmitScoreValue = (_leaderboardMe == null ? 0L : _leaderboardMe.Score).ToString();
|
||
_leaderboardEntries = _viewAroundMe
|
||
? await Brisk.Leaderboard.GetAroundMeAsync(RankKey, ParseOrDefault(AroundMeRange, 5))
|
||
: await Brisk.Leaderboard.GetTopAsync(RankKey, ParseOrDefault(LeaderboardLimit, 10));
|
||
}
|
||
|
||
private async Task ToggleLeaderboardModeAsync()
|
||
{
|
||
_viewAroundMe = !_viewAroundMe;
|
||
await RefreshLeaderboardAsync();
|
||
}
|
||
|
||
private async Task IncreaseAndSubmitScoreAsync()
|
||
{
|
||
var score = ParseOrDefault(SubmitScoreValue, 0) + 1;
|
||
SubmitScoreValue = score.ToString();
|
||
await Brisk.Leaderboard.SubmitScoreAsync(RankKey, score);
|
||
await RefreshLeaderboardAsync();
|
||
}
|
||
|
||
private async Task GetArchiveSlotsAsync()
|
||
{
|
||
_archiveSlots = await Brisk.Archive.GetSlotsAsync();
|
||
}
|
||
|
||
private async Task GetArchiveMetaAsync()
|
||
{
|
||
_archiveMeta = await Brisk.Archive.GetMetaAsync(ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo)));
|
||
ArchiveBaseVersion = _archiveMeta == null ? ArchiveBaseVersion : _archiveMeta.Version.ToString();
|
||
}
|
||
|
||
private async Task DownloadArchiveAsync()
|
||
{
|
||
var result = await Brisk.Archive.DownloadAsync(ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo)));
|
||
ArchiveBaseVersion = result == null ? ArchiveBaseVersion : result.Version.ToString();
|
||
ArchiveContent = await Brisk.Archive.DownloadTextAsync(ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo)));
|
||
await GetArchiveMetaAsync();
|
||
}
|
||
|
||
private async Task UploadArchiveAsync()
|
||
{
|
||
var baseVersion = ParseNullableInt(ArchiveBaseVersion);
|
||
var result = await Brisk.Archive.UploadTextAsync(ParseRequiredInt(ArchiveSlotNo, nameof(ArchiveSlotNo)), ArchiveContent ?? string.Empty, baseVersion);
|
||
ArchiveBaseVersion = result == null ? ArchiveBaseVersion : result.Version.ToString();
|
||
await GetArchiveSlotsAsync();
|
||
await GetArchiveMetaAsync();
|
||
}
|
||
|
||
private async Task LoadMySpaceAsync()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(Brisk.PlayerId))
|
||
{
|
||
return;
|
||
}
|
||
|
||
_mySpaceView = await Brisk.Space.GetByPlayerIdAsync(Brisk.PlayerId);
|
||
_mySpaceStats = await Brisk.Space.GetStatsByPlayerIdAsync(Brisk.PlayerId);
|
||
_mySpaceLikes = await Brisk.Space.GetLikesByPlayerIdAsync(Brisk.PlayerId, 10, true);
|
||
SpacePayloadText = await DownloadSpaceTextAsync(_mySpaceView, () => Brisk.Space.DownloadContentByPlayerIdAsync(Brisk.PlayerId));
|
||
}
|
||
|
||
private async Task RefreshSpacePageAsync()
|
||
{
|
||
if (_viewingTargetSpace)
|
||
{
|
||
await LoadTargetSpaceAsync();
|
||
return;
|
||
}
|
||
|
||
await LoadMySpaceAsync();
|
||
}
|
||
|
||
private async Task UpdateMySpaceAsync()
|
||
{
|
||
var baseVersion = _mySpaceView != null && _mySpaceView.ContentExists ? _mySpaceView.ContentVersion : 0L;
|
||
await Brisk.Space.UpdateMyAsync(SpacePayloadText ?? string.Empty, baseVersion, "text/plain");
|
||
await LoadMySpaceAsync();
|
||
}
|
||
|
||
private async Task VisitTargetSpaceAsync()
|
||
{
|
||
await LoadTargetSpaceAsync();
|
||
_viewingTargetSpace = true;
|
||
}
|
||
|
||
private async Task LoadTargetSpaceAsync()
|
||
{
|
||
_targetSpaceView = await Brisk.Space.GetByPlayerIdAsync(SpacePlayerId);
|
||
_targetSpaceStats = await Brisk.Space.GetStatsByPlayerIdAsync(SpacePlayerId);
|
||
_targetSpaceContentText = await DownloadSpaceTextAsync(_targetSpaceView, () => Brisk.Space.DownloadContentByPlayerIdAsync(SpacePlayerId));
|
||
}
|
||
|
||
private async Task LikeTargetSpaceAsync()
|
||
{
|
||
var result = await Brisk.Space.LikeByPlayerIdAsync(SpacePlayerId);
|
||
Log("空间今日点赞结果: created=" + result.Created + ", total=" + result.LikeCount + ", today=" + result.TodayLikeCount);
|
||
await LoadTargetSpaceAsync();
|
||
}
|
||
|
||
private async Task UnlikeTargetSpaceAsync()
|
||
{
|
||
var result = await Brisk.Space.UnlikeByPlayerIdAsync(SpacePlayerId);
|
||
Log("空间撤销今日点赞结果: total=" + result.LikeCount + ", today=" + result.TodayLikeCount);
|
||
await LoadTargetSpaceAsync();
|
||
}
|
||
|
||
private async Task LogoutFlowAsync()
|
||
{
|
||
await Brisk.Auth.LogoutAsync();
|
||
_page = SamplePage.Login;
|
||
}
|
||
|
||
private Task ResetTransientStatusAsync()
|
||
{
|
||
_statusText = "等待开始";
|
||
_errorText = string.Empty;
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private Task ShowHomeLogsAsync()
|
||
{
|
||
_errorText = string.Join("\n", _logs.ToArray());
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private async Task RunSmokeFlowAsync()
|
||
{
|
||
await InitializeFlowAsync();
|
||
if (!Brisk.IsLoggedIn)
|
||
{
|
||
await LoginFlowAsync();
|
||
}
|
||
}
|
||
|
||
private void DrawAnnouncementModal()
|
||
{
|
||
GUI.color = new Color(0f, 0f, 0f, 0.55f);
|
||
GUI.Box(new Rect(0f, 0f, DesignWidth, DesignHeight), GUIContent.none);
|
||
GUI.color = Color.white;
|
||
|
||
var modalRect = new Rect(48f, 180f, DesignWidth - 96f, DesignHeight - 360f);
|
||
GUILayout.BeginArea(modalRect, GUI.skin.box);
|
||
GUILayout.Label(_selectedAnnouncement.Title, _titleStyle);
|
||
GUILayout.Label("开始: " + NullToDash(_selectedAnnouncement.StartAt), _bodyStyle);
|
||
GUILayout.Label("结束: " + NullToDash(_selectedAnnouncement.EndAt), _bodyStyle);
|
||
GUILayout.Label("状态: " + (_selectedAnnouncement.IsRead ? "已读" : "未读"), _bodyStyle);
|
||
GUILayout.Space(8f);
|
||
_modalScroll = GUILayout.BeginScrollView(_modalScroll, GUILayout.Height(520f));
|
||
GUILayout.TextArea(NullToDash(_selectedAnnouncement.Content), _textAreaStyle, GUILayout.ExpandHeight(true));
|
||
GUILayout.EndScrollView();
|
||
GUILayout.Space(10f);
|
||
DrawInlineButtons(
|
||
new ButtonSpec("标记已读", MarkSelectedAnnouncementAsync, false, true),
|
||
new ButtonSpec("关闭", CloseAnnouncementModalAsync, false, true));
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private Task CloseAnnouncementModalAsync()
|
||
{
|
||
_showAnnouncementModal = false;
|
||
return Task.CompletedTask;
|
||
}
|
||
|
||
private async Task MarkSelectedAnnouncementAsync()
|
||
{
|
||
if (_selectedAnnouncement == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
await Brisk.Announcements.MarkReadAsync(_selectedAnnouncement.Id);
|
||
_selectedAnnouncement.IsRead = true;
|
||
await LoadAnnouncementsAsync();
|
||
_showAnnouncementModal = false;
|
||
}
|
||
|
||
private void DrawBusyOverlay()
|
||
{
|
||
GUI.color = new Color(0f, 0f, 0f, 0.35f);
|
||
GUI.Box(new Rect(0f, 0f, DesignWidth, DesignHeight), GUIContent.none);
|
||
GUI.color = Color.white;
|
||
GUI.Box(new Rect(180f, 580f, 360f, 120f), "正在处理: " + _busyAction);
|
||
}
|
||
|
||
private Rect DrawPageFrame(string title, string backLabel, Action backAction, string rightLabel = null, Func<Task> rightAction = null)
|
||
{
|
||
var outerRect = new Rect(20f, 20f, DesignWidth - 40f, DesignHeight - 40f);
|
||
GUI.Box(outerRect, GUIContent.none, _pageStyle);
|
||
|
||
var headerRect = new Rect(outerRect.x + 20f, outerRect.y + 16f, outerRect.width - 40f, 70f);
|
||
GUILayout.BeginArea(headerRect);
|
||
GUILayout.BeginHorizontal();
|
||
if (!string.IsNullOrWhiteSpace(backLabel))
|
||
{
|
||
DrawHeaderButton(backLabel, backAction);
|
||
}
|
||
else
|
||
{
|
||
GUILayout.Space(150f);
|
||
}
|
||
|
||
GUILayout.FlexibleSpace();
|
||
GUILayout.Label(title, _titleStyle, GUILayout.Height(48f));
|
||
GUILayout.FlexibleSpace();
|
||
if (!string.IsNullOrWhiteSpace(rightLabel) && rightAction != null)
|
||
{
|
||
DrawHeaderButton(rightLabel, () => RunAction(rightLabel, rightAction));
|
||
}
|
||
else
|
||
{
|
||
GUILayout.Space(150f);
|
||
}
|
||
|
||
GUILayout.EndHorizontal();
|
||
GUILayout.EndArea();
|
||
|
||
var statusRect = new Rect(outerRect.x + 20f, headerRect.yMax + 6f, outerRect.width - 40f, 74f);
|
||
GUI.Box(statusRect, GUIContent.none, _cardStyle);
|
||
GUILayout.BeginArea(new Rect(statusRect.x + 12f, statusRect.y + 10f, statusRect.width - 24f, statusRect.height - 20f));
|
||
GUILayout.Label("状态: " + _statusText, _statusStyle);
|
||
GUILayout.Label("错误: " + (string.IsNullOrWhiteSpace(_errorText) ? "无" : _errorText), _hintStyle);
|
||
GUILayout.EndArea();
|
||
|
||
return new Rect(outerRect.x + 20f, statusRect.yMax + 12f, outerRect.width - 40f, outerRect.height - 250f);
|
||
}
|
||
|
||
private void DrawBottomButtons(params ButtonSpec[] buttons)
|
||
{
|
||
var rect = new Rect(40f, DesignHeight - 170f, DesignWidth - 80f, 110f);
|
||
GUILayout.BeginArea(rect);
|
||
DrawInlineButtons(buttons);
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawBottomNav(params NavSpec[] items)
|
||
{
|
||
var rect = new Rect(30f, DesignHeight - 170f, DesignWidth - 60f, 110f);
|
||
GUI.Box(new Rect(rect.x, rect.y, rect.width, rect.height), GUIContent.none, _cardStyle);
|
||
GUILayout.BeginArea(new Rect(rect.x + 10f, rect.y + 10f, rect.width - 20f, rect.height - 20f));
|
||
GUILayout.BeginHorizontal();
|
||
for (var i = 0; i < items.Length; i++)
|
||
{
|
||
var item = items[i];
|
||
if (GUILayout.Button(item.Label, _primaryButtonStyle, GUILayout.Height(64f), GUILayout.Width(150f)))
|
||
{
|
||
_page = item.Page;
|
||
if (item.Page == SamplePage.Announcements)
|
||
{
|
||
RunAction("加载公告", LoadAnnouncementsAsync);
|
||
}
|
||
else if (item.Page == SamplePage.Leaderboard)
|
||
{
|
||
RunAction("加载排行榜", RefreshLeaderboardAsync);
|
||
}
|
||
else if (item.Page == SamplePage.Archive)
|
||
{
|
||
RunAction("加载存档", GetArchiveSlotsAsync);
|
||
}
|
||
else if (item.Page == SamplePage.Space)
|
||
{
|
||
RunAction("加载空间", LoadMySpaceAsync);
|
||
}
|
||
}
|
||
}
|
||
|
||
GUILayout.EndHorizontal();
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawHeaderButton(string label, Action action)
|
||
{
|
||
if (GUILayout.Button(label, _buttonStyle, GUILayout.Width(150f), GUILayout.Height(42f)))
|
||
{
|
||
action();
|
||
}
|
||
}
|
||
|
||
private void DrawInlineButtons(params ButtonSpec[] buttons)
|
||
{
|
||
GUILayout.BeginHorizontal();
|
||
for (var i = 0; i < buttons.Length; i++)
|
||
{
|
||
var button = buttons[i];
|
||
var previous = GUI.enabled;
|
||
GUI.enabled = previous && !_isBusy && button.Enabled;
|
||
var style = button.Primary ? _primaryButtonStyle : _buttonStyle;
|
||
if (GUILayout.Button(button.Label, style, GUILayout.Height(56f)))
|
||
{
|
||
RunAction(button.Label, button.Action);
|
||
}
|
||
|
||
GUI.enabled = previous;
|
||
}
|
||
|
||
GUILayout.EndHorizontal();
|
||
}
|
||
|
||
private void DrawCard(string title, Action content)
|
||
{
|
||
GUILayout.BeginVertical(_cardStyle);
|
||
GUILayout.Label(title, _sectionTitleStyle);
|
||
GUILayout.Space(6f);
|
||
content();
|
||
GUILayout.EndVertical();
|
||
GUILayout.Space(12f);
|
||
}
|
||
|
||
private string DrawInputRow(string label, string value)
|
||
{
|
||
GUILayout.Label(label, _hintStyle);
|
||
return GUILayout.TextField(value ?? string.Empty, _inputStyle, GUILayout.Height(48f));
|
||
}
|
||
|
||
private bool DrawToggleRow(string label, bool value)
|
||
{
|
||
GUILayout.Label(label, _hintStyle);
|
||
return GUILayout.Toggle(value, value ? "开启" : "关闭", GUILayout.Height(42f));
|
||
}
|
||
|
||
private void DrawValueRow(string label, string value)
|
||
{
|
||
GUILayout.BeginHorizontal();
|
||
GUILayout.Label(label, _hintStyle, GUILayout.Width(180f));
|
||
GUILayout.Label(value, _bodyStyle);
|
||
GUILayout.EndHorizontal();
|
||
}
|
||
|
||
private void RunAction(string actionName, Func<Task> action)
|
||
{
|
||
if (_isBusy)
|
||
{
|
||
return;
|
||
}
|
||
|
||
RunActionInternal(actionName, action);
|
||
}
|
||
|
||
private async void RunActionInternal(string actionName, Func<Task> action)
|
||
{
|
||
_isBusy = true;
|
||
_busyAction = actionName;
|
||
_statusText = "执行中: " + actionName;
|
||
_errorText = string.Empty;
|
||
Log("开始执行: " + actionName);
|
||
|
||
try
|
||
{
|
||
await action();
|
||
_statusText = "执行成功: " + actionName;
|
||
Log("执行成功: " + actionName);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
_statusText = "执行失败: " + actionName;
|
||
_errorText = FormatException(exception);
|
||
Log("执行失败: " + actionName + " | " + exception.Message);
|
||
Debug.LogException(exception, this);
|
||
}
|
||
finally
|
||
{
|
||
_busyAction = string.Empty;
|
||
_isBusy = false;
|
||
}
|
||
}
|
||
|
||
private void SyncPageBySdkState()
|
||
{
|
||
if (!Brisk.IsInitialized)
|
||
{
|
||
_page = SamplePage.Initialize;
|
||
return;
|
||
}
|
||
|
||
_page = Brisk.IsLoggedIn ? SamplePage.Home : SamplePage.Login;
|
||
}
|
||
|
||
private void HandleInitialized()
|
||
{
|
||
SyncPageBySdkState();
|
||
Log("事件: 初始化完成");
|
||
}
|
||
|
||
private void HandleLoggedIn()
|
||
{
|
||
SyncPageBySdkState();
|
||
Log("事件: 登录完成");
|
||
}
|
||
|
||
private void HandleLoggedOut()
|
||
{
|
||
SyncPageBySdkState();
|
||
Log("事件: 登出完成");
|
||
}
|
||
|
||
private void HandleAuthExpired(BriskAuthExpiredException exception)
|
||
{
|
||
_page = SamplePage.Login;
|
||
Log("事件: 登录态失效 | " + exception.Message);
|
||
}
|
||
|
||
private void HandleBlockingError(BriskBlockingException exception)
|
||
{
|
||
Log("事件: 阻断错误 | " + exception.Message);
|
||
}
|
||
|
||
private void HandleExitRequested()
|
||
{
|
||
Log("Brisk 请求退出。");
|
||
}
|
||
|
||
private void Log(string message)
|
||
{
|
||
_logs.Add("[" + DateTime.Now.ToString("HH:mm:ss") + "] " + message);
|
||
if (_logs.Count > 80)
|
||
{
|
||
_logs.RemoveAt(0);
|
||
}
|
||
}
|
||
|
||
private BriskProfile CreateProfile()
|
||
{
|
||
return new BriskProfile
|
||
{
|
||
Nickname = Nickname,
|
||
AvatarUrl = AvatarUrl
|
||
};
|
||
}
|
||
|
||
private void ApplyIdentity(string playerId, string loginProvider, string loginUserId)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(playerId))
|
||
{
|
||
SpacePlayerId = playerId;
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(loginProvider))
|
||
{
|
||
SpaceLoginProvider = loginProvider;
|
||
LoginProvider = loginProvider;
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(loginUserId))
|
||
{
|
||
SpaceLoginUserId = loginUserId;
|
||
LoginUserId = loginUserId;
|
||
}
|
||
}
|
||
|
||
private void EnsureStyles()
|
||
{
|
||
if (_pageStyle != null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_pageStyle = new GUIStyle(GUI.skin.box)
|
||
{
|
||
padding = new RectOffset(18, 18, 18, 18)
|
||
};
|
||
|
||
_cardStyle = new GUIStyle(GUI.skin.box)
|
||
{
|
||
padding = new RectOffset(16, 16, 14, 14),
|
||
margin = new RectOffset(0, 0, 0, 0)
|
||
};
|
||
|
||
_titleStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 28,
|
||
fontStyle = FontStyle.Bold,
|
||
alignment = TextAnchor.MiddleCenter,
|
||
wordWrap = true
|
||
};
|
||
|
||
_sectionTitleStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 18,
|
||
fontStyle = FontStyle.Bold,
|
||
wordWrap = true
|
||
};
|
||
|
||
_bodyStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 16,
|
||
wordWrap = true
|
||
};
|
||
|
||
_hintStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 14,
|
||
wordWrap = true
|
||
};
|
||
|
||
_statusStyle = new GUIStyle(_bodyStyle)
|
||
{
|
||
fontStyle = FontStyle.Bold
|
||
};
|
||
|
||
_buttonStyle = new GUIStyle(GUI.skin.button)
|
||
{
|
||
fontSize = 16,
|
||
fontStyle = FontStyle.Bold,
|
||
wordWrap = true
|
||
};
|
||
|
||
_primaryButtonStyle = new GUIStyle(_buttonStyle);
|
||
_dangerButtonStyle = new GUIStyle(_buttonStyle);
|
||
_inputStyle = new GUIStyle(GUI.skin.textField) { fontSize = 16 };
|
||
_textAreaStyle = new GUIStyle(GUI.skin.textArea) { fontSize = 15, wordWrap = true };
|
||
_listButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 16, alignment = TextAnchor.MiddleLeft, wordWrap = true };
|
||
_modalStyle = new GUIStyle(GUI.skin.box) { padding = new RectOffset(20, 20, 20, 20) };
|
||
}
|
||
|
||
private static int ParseRequiredInt(string value, string fieldName)
|
||
{
|
||
if (!int.TryParse(value, out var result))
|
||
{
|
||
throw new InvalidOperationException(fieldName + " 必须是合法整数。");
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
private static int ParseOrDefault(string value, int fallback)
|
||
{
|
||
return int.TryParse(value, out var result) ? result : fallback;
|
||
}
|
||
|
||
private static int? ParseNullableInt(string value)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return int.TryParse(value, out var result) ? result : (int?)null;
|
||
}
|
||
|
||
private static string NullToDash(string value)
|
||
{
|
||
return string.IsNullOrWhiteSpace(value) ? "-" : value;
|
||
}
|
||
|
||
private static string FormatLikeResetAt(string value)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return "-";
|
||
}
|
||
|
||
DateTimeOffset parsed;
|
||
if (!DateTimeOffset.TryParse(value, out parsed))
|
||
{
|
||
return value;
|
||
}
|
||
|
||
return parsed.LocalDateTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||
}
|
||
|
||
private static async Task<string> DownloadSpaceTextAsync(BriskSpaceView view, Func<Task<BriskSpaceContentDownloadResult>> downloadContent)
|
||
{
|
||
if (view == null || !view.ContentExists || downloadContent == null)
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
var content = await downloadContent();
|
||
if (content == null || content.Bytes == null || content.Bytes.Length == 0)
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
return Encoding.UTF8.GetString(content.Bytes);
|
||
}
|
||
|
||
private static string FormatException(Exception exception)
|
||
{
|
||
if (exception == null)
|
||
{
|
||
return "未知错误";
|
||
}
|
||
|
||
var builder = new StringBuilder();
|
||
var current = exception;
|
||
while (current != null)
|
||
{
|
||
builder.AppendLine(current.GetType().Name + ": " + current.Message);
|
||
current = current.InnerException;
|
||
}
|
||
|
||
return builder.ToString().TrimEnd();
|
||
}
|
||
|
||
private static string FormatValue(object value)
|
||
{
|
||
var builder = new StringBuilder();
|
||
AppendValue(builder, value, 0, 0);
|
||
return builder.Length == 0 ? "-" : builder.ToString().TrimEnd();
|
||
}
|
||
|
||
private static void AppendValue(StringBuilder builder, object value, int indent, int depth)
|
||
{
|
||
if (depth > 5)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + "...");
|
||
return;
|
||
}
|
||
|
||
if (value == null)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + "null");
|
||
return;
|
||
}
|
||
|
||
if (value is string stringValue)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + stringValue);
|
||
return;
|
||
}
|
||
|
||
if (value is IDictionary dictionary)
|
||
{
|
||
foreach (DictionaryEntry entry in dictionary)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + entry.Key + ":");
|
||
AppendValue(builder, entry.Value, indent + 2, depth + 1);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (value is IEnumerable enumerable && !(value is byte[]))
|
||
{
|
||
var index = 0;
|
||
foreach (var item in enumerable)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + "[" + index + "]");
|
||
AppendValue(builder, item, indent + 2, depth + 1);
|
||
index++;
|
||
}
|
||
return;
|
||
}
|
||
|
||
var type = value.GetType();
|
||
if (type.IsPrimitive || value is decimal)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + value);
|
||
return;
|
||
}
|
||
|
||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
|
||
foreach (var field in fields)
|
||
{
|
||
builder.AppendLine(new string(' ', indent) + field.Name + ":");
|
||
AppendValue(builder, field.GetValue(value), indent + 2, depth + 1);
|
||
}
|
||
}
|
||
|
||
private readonly struct ButtonSpec
|
||
{
|
||
public ButtonSpec(string label, Func<Task> action, bool primary, bool enabled)
|
||
{
|
||
Label = label;
|
||
Action = action;
|
||
Primary = primary;
|
||
Enabled = enabled;
|
||
}
|
||
|
||
public string Label { get; }
|
||
public Func<Task> Action { get; }
|
||
public bool Primary { get; }
|
||
public bool Enabled { get; }
|
||
}
|
||
|
||
private readonly struct NavSpec
|
||
{
|
||
public NavSpec(string label, SamplePage page)
|
||
{
|
||
Label = label;
|
||
Page = page;
|
||
}
|
||
|
||
public string Label { get; }
|
||
public SamplePage Page { get; }
|
||
}
|
||
}
|