SHA256
267 lines
8.1 KiB
Dart
267 lines
8.1 KiB
Dart
part of 'studio_app.dart';
|
|
|
|
class StudioShell extends StatefulWidget {
|
|
const StudioShell({
|
|
super.key,
|
|
required this.runtime,
|
|
required this.languageCode,
|
|
required this.themeMode,
|
|
required this.onToggleTheme,
|
|
required this.onLogout,
|
|
});
|
|
|
|
final StudioRuntime runtime;
|
|
final String languageCode;
|
|
final ThemeMode themeMode;
|
|
final VoidCallback onToggleTheme;
|
|
final VoidCallback onLogout;
|
|
|
|
@override
|
|
State<StudioShell> createState() => _StudioShellState();
|
|
}
|
|
|
|
class _StudioShellState extends State<StudioShell> {
|
|
final List<StudioActivity> _activities = <StudioActivity>[];
|
|
final List<StudioTab> _tabs = <StudioTab>[StudioTab.dashboard()];
|
|
String _searchQuery = '';
|
|
String _selectedTabId = StudioTab.dashboardId;
|
|
int _refreshRevision = 0;
|
|
|
|
StudioUser get user => widget.runtime.user;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_activities.add(
|
|
StudioActivity(
|
|
title: '登录成功',
|
|
detail: '${user.displayName} / ${user.workspace}',
|
|
),
|
|
);
|
|
}
|
|
|
|
StudioTab get _selectedTab =>
|
|
_tabs.firstWhere((tab) => tab.id == _selectedTabId);
|
|
|
|
void _log(String title, String detail) {
|
|
setState(() {
|
|
_activities.insert(0, StudioActivity(title: title, detail: detail));
|
|
if (_activities.length > 24) {
|
|
_activities.removeLast();
|
|
}
|
|
});
|
|
}
|
|
|
|
void _openNode(StudioNavNode node) {
|
|
final existingIndex = _tabs.indexWhere((tab) => tab.id == node.id);
|
|
setState(() {
|
|
if (existingIndex == -1) {
|
|
_tabs.add(
|
|
StudioTab(
|
|
id: node.id,
|
|
title: node.title,
|
|
summary: node.summary,
|
|
pageType: node.pageType,
|
|
icon: node.icon,
|
|
),
|
|
);
|
|
}
|
|
_selectedTabId = node.id;
|
|
});
|
|
_log('打开页面', node.title);
|
|
}
|
|
|
|
void _selectTab(String tabId) {
|
|
setState(() {
|
|
_selectedTabId = tabId;
|
|
});
|
|
}
|
|
|
|
void _closeTab(String tabId) {
|
|
if (tabId == StudioTab.dashboardId) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_tabs.removeWhere((tab) => tab.id == tabId);
|
|
if (_selectedTabId == tabId) {
|
|
_selectedTabId = _tabs.isNotEmpty
|
|
? _tabs.last.id
|
|
: StudioTab.dashboardId;
|
|
}
|
|
});
|
|
_log('关闭页面', tabId);
|
|
}
|
|
|
|
void _refreshPage() {
|
|
setState(() {
|
|
_refreshRevision += 1;
|
|
});
|
|
_log('刷新页面', _selectedTab.title);
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(SnackBar(content: Text('已刷新 ${_selectedTab.title}')));
|
|
}
|
|
|
|
void _handlePageAction(String title, String detail) {
|
|
_log(title, detail);
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(detail)));
|
|
}
|
|
|
|
List<StudioNavNode> _allLeaves(List<StudioNavNode> nodes) {
|
|
final result = <StudioNavNode>[];
|
|
for (final node in nodes) {
|
|
if (node.children.isEmpty) {
|
|
result.add(node);
|
|
} else {
|
|
result.addAll(_allLeaves(node.children));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final wide = MediaQuery.of(context).size.width >= 1080;
|
|
final visibleRoots = _filterNavNodes(
|
|
studioNavTree,
|
|
widget.runtime.allowedMenuIds,
|
|
);
|
|
final leaves = _allLeaves(visibleRoots);
|
|
final query = _searchQuery.trim().toLowerCase();
|
|
final filteredLeaves = query.isEmpty
|
|
? leaves
|
|
: leaves
|
|
.where(
|
|
(node) =>
|
|
node.title.toLowerCase().contains(query) ||
|
|
node.summary.toLowerCase().contains(query),
|
|
)
|
|
.toList();
|
|
|
|
return Scaffold(
|
|
appBar: wide
|
|
? null
|
|
: AppBar(
|
|
title: const Text('TTX Studio'),
|
|
actions: [
|
|
IconButton(
|
|
tooltip: widget.themeMode == ThemeMode.dark
|
|
? '切换亮色主题'
|
|
: '切换暗色主题',
|
|
onPressed: widget.onToggleTheme,
|
|
icon: Icon(
|
|
widget.themeMode == ThemeMode.dark
|
|
? Icons.light_mode_rounded
|
|
: Icons.dark_mode_rounded,
|
|
),
|
|
),
|
|
IconButton(
|
|
tooltip: '刷新',
|
|
onPressed: _refreshPage,
|
|
icon: const Icon(Icons.refresh_rounded),
|
|
),
|
|
IconButton(
|
|
tooltip: '退出登录',
|
|
onPressed: widget.onLogout,
|
|
icon: const Icon(Icons.logout_rounded),
|
|
),
|
|
],
|
|
),
|
|
drawer: wide
|
|
? null
|
|
: Drawer(
|
|
child: SafeArea(
|
|
child: StudioSidebar(
|
|
user: user,
|
|
themeMode: widget.themeMode,
|
|
onToggleTheme: widget.onToggleTheme,
|
|
searchQuery: _searchQuery,
|
|
filteredLeaves: filteredLeaves,
|
|
rootNodes: visibleRoots,
|
|
onSearchChanged: (value) {
|
|
setState(() {
|
|
_searchQuery = value;
|
|
});
|
|
},
|
|
onOpenNode: (node) {
|
|
Navigator.of(context).pop();
|
|
_openNode(node);
|
|
},
|
|
onLogout: widget.onLogout,
|
|
),
|
|
),
|
|
),
|
|
body: Container(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: _shellGradientColors(context),
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
),
|
|
),
|
|
child: SafeArea(
|
|
child: wide
|
|
? Row(
|
|
children: [
|
|
SizedBox(
|
|
width: 336,
|
|
child: StudioSidebar(
|
|
user: user,
|
|
themeMode: widget.themeMode,
|
|
onToggleTheme: widget.onToggleTheme,
|
|
searchQuery: _searchQuery,
|
|
filteredLeaves: filteredLeaves,
|
|
rootNodes: visibleRoots,
|
|
onSearchChanged: (value) {
|
|
setState(() {
|
|
_searchQuery = value;
|
|
});
|
|
},
|
|
onOpenNode: _openNode,
|
|
onLogout: widget.onLogout,
|
|
),
|
|
),
|
|
Expanded(
|
|
child: StudioWorkspaceView(
|
|
runtime: widget.runtime,
|
|
user: user,
|
|
selectedTab: _selectedTab,
|
|
tabs: List<StudioTab>.unmodifiable(_tabs),
|
|
activities: List<StudioActivity>.unmodifiable(
|
|
_activities,
|
|
),
|
|
refreshToken: _refreshRevision,
|
|
onSelectTab: _selectTab,
|
|
onCloseTab: _closeTab,
|
|
onRefresh: _refreshPage,
|
|
onLogout: widget.onLogout,
|
|
themeMode: widget.themeMode,
|
|
onToggleTheme: widget.onToggleTheme,
|
|
onPageAction: _handlePageAction,
|
|
onOpenNode: _openNode,
|
|
),
|
|
),
|
|
],
|
|
)
|
|
: StudioWorkspaceView(
|
|
runtime: widget.runtime,
|
|
user: user,
|
|
selectedTab: _selectedTab,
|
|
tabs: List<StudioTab>.unmodifiable(_tabs),
|
|
activities: List<StudioActivity>.unmodifiable(_activities),
|
|
refreshToken: _refreshRevision,
|
|
onSelectTab: _selectTab,
|
|
onCloseTab: _closeTab,
|
|
onRefresh: _refreshPage,
|
|
onLogout: widget.onLogout,
|
|
themeMode: widget.themeMode,
|
|
onToggleTheme: widget.onToggleTheme,
|
|
onPageAction: _handlePageAction,
|
|
onOpenNode: _openNode,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|