✨ Splash screen loading
This commit is contained in:
parent
7052b5b635
commit
71b41d470a
@ -870,5 +870,16 @@
|
|||||||
"chatUnjoined": "Unjoined Channel",
|
"chatUnjoined": "Unjoined Channel",
|
||||||
"chatUnjoinedDescription": "You haven't joined this channel, so you can't send messages either view messages in it.",
|
"chatUnjoinedDescription": "You haven't joined this channel, so you can't send messages either view messages in it.",
|
||||||
"chatUnjoinedPublicDescription": "Fortunately, this is a public channel, so you can join it as you want.",
|
"chatUnjoinedPublicDescription": "Fortunately, this is a public channel, so you can join it as you want.",
|
||||||
"chatJoin": "Join the Channel"
|
"chatJoin": "Join the Channel",
|
||||||
|
"appInitStarting": "Starting",
|
||||||
|
"appInitNetwork": "Initializing Network",
|
||||||
|
"appInitUserdata": "Initializing User Data",
|
||||||
|
"appInitWebsocket": "Establishing Solar Link",
|
||||||
|
"appInitNotification": "Initializing Push Notifications",
|
||||||
|
"appInitKeyPair": "Initializing Key Pairs",
|
||||||
|
"appInitStickers": "Initializing Stickers",
|
||||||
|
"appInitUserDirectory": "Initializing User Directory",
|
||||||
|
"appInitRealm": "Initializing Realms",
|
||||||
|
"appInitChat": "Initializing Chat",
|
||||||
|
"appInitDone": "Completed"
|
||||||
}
|
}
|
||||||
|
@ -868,5 +868,16 @@
|
|||||||
"chatUnjoined": "未加入频道",
|
"chatUnjoined": "未加入频道",
|
||||||
"chatUnjoinedDescription": "你没有加入这个频道,所以你也无法发送消息或者查看这个频道中的消息。",
|
"chatUnjoinedDescription": "你没有加入这个频道,所以你也无法发送消息或者查看这个频道中的消息。",
|
||||||
"chatUnjoinedPublicDescription": "但幸运的是,这是一个公开频道,所以你可以主动加入。",
|
"chatUnjoinedPublicDescription": "但幸运的是,这是一个公开频道,所以你可以主动加入。",
|
||||||
"chatJoin": "加入频道"
|
"chatJoin": "加入频道",
|
||||||
|
"appInitStarting": "启动中",
|
||||||
|
"appInitNetwork": "正在初始化网络",
|
||||||
|
"appInitUserdata": "正在初始化用户数据",
|
||||||
|
"appInitWebsocket": "正在建立 Solar Link",
|
||||||
|
"appInitNotification": "正在初始化推送通知",
|
||||||
|
"appInitKeyPair": "正在初始化密钥对",
|
||||||
|
"appInitStickers": "正在初始化贴图包",
|
||||||
|
"appInitUserDirectory": "正在初始化用户目录",
|
||||||
|
"appInitRealm": "正在初始化领域信息",
|
||||||
|
"appInitChat": "正在初始化聊天",
|
||||||
|
"appInitDone": "完成"
|
||||||
}
|
}
|
||||||
|
@ -864,5 +864,20 @@
|
|||||||
"other": "登入時最多要求 {} 步驗證"
|
"other": "登入時最多要求 {} 步驗證"
|
||||||
},
|
},
|
||||||
"authAlwaysRisky": "總是風險",
|
"authAlwaysRisky": "總是風險",
|
||||||
"authAlwaysRiskyDescription": "在登入時始終按最高標準要求驗證。"
|
"authAlwaysRiskyDescription": "在登入時始終按最高標準要求驗證。",
|
||||||
|
"chatUnjoined": "未加入頻道",
|
||||||
|
"chatUnjoinedDescription": "你沒有加入這個頻道,所以你也無法發送消息或者查看這個頻道中的消息。",
|
||||||
|
"chatUnjoinedPublicDescription": "但幸運的是,這是一個公開頻道,所以你可以主動加入。",
|
||||||
|
"chatJoin": "加入頻道",
|
||||||
|
"appInitStarting": "啓動中",
|
||||||
|
"appInitNetwork": "正在初始化網絡",
|
||||||
|
"appInitUserdata": "正在初始化用户數據",
|
||||||
|
"appInitWebsocket": "正在建立 Solar Link",
|
||||||
|
"appInitNotification": "正在初始化推送通知",
|
||||||
|
"appInitKeyPair": "正在初始化密鑰對",
|
||||||
|
"appInitStickers": "正在初始化貼圖包",
|
||||||
|
"appInitUserDirectory": "正在初始化用户目錄",
|
||||||
|
"appInitRealm": "正在初始化領域信息",
|
||||||
|
"appInitChat": "正在初始化聊天",
|
||||||
|
"appInitDone": "完成"
|
||||||
}
|
}
|
||||||
|
@ -864,5 +864,20 @@
|
|||||||
"other": "登入時最多要求 {} 步驗證"
|
"other": "登入時最多要求 {} 步驗證"
|
||||||
},
|
},
|
||||||
"authAlwaysRisky": "總是風險",
|
"authAlwaysRisky": "總是風險",
|
||||||
"authAlwaysRiskyDescription": "在登入時始終按最高標準要求驗證。"
|
"authAlwaysRiskyDescription": "在登入時始終按最高標準要求驗證。",
|
||||||
|
"chatUnjoined": "未加入頻道",
|
||||||
|
"chatUnjoinedDescription": "你沒有加入這個頻道,所以你也無法發送消息或者查看這個頻道中的消息。",
|
||||||
|
"chatUnjoinedPublicDescription": "但幸運的是,這是一個公開頻道,所以你可以主動加入。",
|
||||||
|
"chatJoin": "加入頻道",
|
||||||
|
"appInitStarting": "啟動中",
|
||||||
|
"appInitNetwork": "正在初始化網絡",
|
||||||
|
"appInitUserdata": "正在初始化用戶數據",
|
||||||
|
"appInitWebsocket": "正在建立 Solar Link",
|
||||||
|
"appInitNotification": "正在初始化推送通知",
|
||||||
|
"appInitKeyPair": "正在初始化密鑰對",
|
||||||
|
"appInitStickers": "正在初始化貼圖包",
|
||||||
|
"appInitUserDirectory": "正在初始化用戶目錄",
|
||||||
|
"appInitRealm": "正在初始化領域信息",
|
||||||
|
"appInitChat": "正在初始化聊天",
|
||||||
|
"appInitDone": "完成"
|
||||||
}
|
}
|
||||||
|
111
lib/main.dart
111
lib/main.dart
@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math' hide log;
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
@ -12,6 +13,7 @@ import 'package:firebase_core/firebase_core.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
@ -19,6 +21,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:relative_time/relative_time.dart';
|
import 'package:relative_time/relative_time.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/firebase_options.dart';
|
import 'package:surface/firebase_options.dart';
|
||||||
import 'package:surface/logger.dart';
|
import 'package:surface/logger.dart';
|
||||||
import 'package:surface/providers/channel.dart';
|
import 'package:surface/providers/channel.dart';
|
||||||
@ -46,6 +49,7 @@ import 'package:surface/router.dart';
|
|||||||
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/menu_bar.dart';
|
import 'package:surface/widgets/menu_bar.dart';
|
||||||
|
import 'package:surface/widgets/version_label.dart';
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
import 'package:workmanager/workmanager.dart';
|
import 'package:workmanager/workmanager.dart';
|
||||||
@ -228,6 +232,9 @@ class _AppSplashScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
||||||
|
bool _isBusy = false;
|
||||||
|
String _phaseText = 'appInitStarting';
|
||||||
|
|
||||||
void _tryRequestRating() async {
|
void _tryRequestRating() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
if (prefs.containsKey('first_boot_time')) {
|
if (prefs.containsKey('first_boot_time')) {
|
||||||
@ -287,6 +294,11 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _setPhaseText(String text) {
|
||||||
|
_phaseText = 'appInit${text.capitalize()}'.tr();
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _initialize() async {
|
Future<void> _initialize() async {
|
||||||
try {
|
try {
|
||||||
final cfg = context.read<ConfigProvider>();
|
final cfg = context.read<ConfigProvider>();
|
||||||
@ -299,34 +311,45 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
// The Network initialization must be done after the HomeWidget initialization
|
// The Network initialization must be done after the HomeWidget initialization
|
||||||
// The Network initialization will save the server url to the HomeWidget
|
// The Network initialization will save the server url to the HomeWidget
|
||||||
// The Network initialization will also save initialize the Config, so it not need to be initialized again
|
// The Network initialization will also save initialize the Config, so it not need to be initialized again
|
||||||
|
_setPhaseText('network');
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
await sn.initializeUserAgent();
|
await sn.initializeUserAgent();
|
||||||
await sn.setConfigWithNative();
|
await sn.setConfigWithNative();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('userdata');
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
await ua.initialize();
|
await ua.initialize();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('websocket');
|
||||||
final ws = context.read<WebSocketProvider>();
|
final ws = context.read<WebSocketProvider>();
|
||||||
await ws.tryConnect();
|
await ws.tryConnect();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('notification');
|
||||||
final notify = context.read<NotificationProvider>();
|
final notify = context.read<NotificationProvider>();
|
||||||
notify.listen();
|
notify.listen();
|
||||||
await notify.registerPushNotifications();
|
await notify.registerPushNotifications();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('keyPair');
|
||||||
final kp = context.read<KeyPairProvider>();
|
final kp = context.read<KeyPairProvider>();
|
||||||
await kp.reloadActive();
|
await kp.reloadActive();
|
||||||
kp.listen();
|
kp.listen();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('stickers');
|
||||||
final sticker = context.read<SnStickerProvider>();
|
final sticker = context.read<SnStickerProvider>();
|
||||||
await sticker.listSticker();
|
await sticker.listSticker();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('userDirectory');
|
||||||
final ud = context.read<UserDirectoryProvider>();
|
final ud = context.read<UserDirectoryProvider>();
|
||||||
final userCacheSize = await ud.loadAccountCache();
|
await ud.loadAccountCache();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_setPhaseText('realm');
|
||||||
final rm = context.read<SnRealmProvider>();
|
final rm = context.read<SnRealmProvider>();
|
||||||
await rm.refreshAvailableRealms();
|
await rm.refreshAvailableRealms();
|
||||||
logging.info('[Users] Loaded local user cache, size: $userCacheSize');
|
if (!mounted) return;
|
||||||
logging.info('[Bootstrap] Everything initialized!');
|
_setPhaseText('chat');
|
||||||
|
final ct = context.read<ChatChannelProvider>();
|
||||||
|
await ct.refreshAvailableChannels();
|
||||||
|
_setPhaseText('done');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
await context.showErrorDialog(err);
|
await context.showErrorDialog(err);
|
||||||
@ -402,6 +425,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
_isBusy = true;
|
||||||
if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) {
|
if (!kIsWeb && !(Platform.isIOS || Platform.isAndroid)) {
|
||||||
_appLifecycleListener = AppLifecycleListener(
|
_appLifecycleListener = AppLifecycleListener(
|
||||||
onExitRequested: _onExitRequested,
|
onExitRequested: _onExitRequested,
|
||||||
@ -415,6 +439,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
_postInitialization();
|
_postInitialization();
|
||||||
_tryRequestRating();
|
_tryRequestRating();
|
||||||
_checkForUpdate();
|
_checkForUpdate();
|
||||||
|
setState(() => _isBusy = false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +529,44 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return SizeChangedLayoutNotifier(
|
return SizeChangedLayoutNotifier(
|
||||||
child: widget.child,
|
child: _isBusy
|
||||||
|
? Material(
|
||||||
|
key: Key('app-splash-screen-$_isBusy'),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
CustomPaint(painter: GraphPainter()),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 240,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/icon/icon.png',
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
Text('Solar Network').bold(),
|
||||||
|
AppVersionLabel(),
|
||||||
|
Gap(8),
|
||||||
|
Text(
|
||||||
|
_phaseText,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Gap(16),
|
||||||
|
const LinearProgressIndicator(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: widget.child,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -512,3 +574,44 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GraphPainter extends CustomPainter {
|
||||||
|
final Random random = Random();
|
||||||
|
final int numNodes = 20;
|
||||||
|
final double maxDistance = 100; // Max distance to draw a line
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final paintNode = Paint()..color = Colors.white;
|
||||||
|
final paintEdge = Paint()
|
||||||
|
..color = Colors.white.withOpacity(0.3)
|
||||||
|
..strokeWidth = 1;
|
||||||
|
|
||||||
|
// Generate random points
|
||||||
|
List<Offset> nodes = List.generate(
|
||||||
|
numNodes,
|
||||||
|
(_) => Offset(
|
||||||
|
random.nextDouble() * size.width,
|
||||||
|
random.nextDouble() * size.height,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw edges between close nodes
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
for (var j = i + 1; j < nodes.length; j++) {
|
||||||
|
double distance = (nodes[i] - nodes[j]).distance;
|
||||||
|
if (distance < maxDistance) {
|
||||||
|
canvas.drawLine(nodes[i], nodes[j], paintEdge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw nodes
|
||||||
|
for (var node in nodes) {
|
||||||
|
canvas.drawCircle(node, 4, paintNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(CustomPainter oldDelegate) => false;
|
||||||
|
}
|
||||||
|
@ -28,6 +28,19 @@ class ChatChannelProvider extends ChangeNotifier {
|
|||||||
_rels = context.read<SnRealmProvider>();
|
_rels = context.read<SnRealmProvider>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<SnChannel> _availableChannels = List.empty(growable: true);
|
||||||
|
|
||||||
|
List<SnChannel> get availableChannels => _availableChannels;
|
||||||
|
|
||||||
|
Future<void> refreshAvailableChannels() async {
|
||||||
|
final stream = fetchChannels();
|
||||||
|
stream.listen((ele) {
|
||||||
|
_availableChannels.clear();
|
||||||
|
_availableChannels.addAll(ele);
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async {
|
Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async {
|
||||||
await Future.wait(
|
await Future.wait(
|
||||||
channels.map(
|
channels.map(
|
||||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
|
|
||||||
class AppNavDestination {
|
class AppNavDestination {
|
||||||
final String label;
|
final String label;
|
||||||
@ -143,4 +144,11 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
_currentIndex = idx;
|
_currentIndex = idx;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SnRealm? focusedRealm;
|
||||||
|
|
||||||
|
void setFocusedRealm(SnRealm? realm) {
|
||||||
|
focusedRealm = realm;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,7 @@ Future<ThemeData> createAppTheme(
|
|||||||
TargetPlatform.windows: ZoomPageTransitionsBuilder(),
|
TargetPlatform.windows: ZoomPageTransitionsBuilder(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
progressIndicatorTheme: ProgressIndicatorThemeData(year2023: false),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class ChatMessage extends StatelessWidget {
|
|||||||
key: Key('chat-message-${data.id}'),
|
key: Key('chat-message-${data.id}'),
|
||||||
iconOnLeftSwipe: Symbols.reply,
|
iconOnLeftSwipe: Symbols.reply,
|
||||||
iconOnRightSwipe: Symbols.edit,
|
iconOnRightSwipe: Symbols.edit,
|
||||||
swipeSensitivity: 20,
|
swipeSensitivity: 10,
|
||||||
onLeftSwipe: onReply != null ? (_) => onReply!(data) : null,
|
onLeftSwipe: onReply != null ? (_) => onReply!(data) : null,
|
||||||
onRightSwipe: (onEdit != null && isOwner) ? (_) => onEdit!(data) : null,
|
onRightSwipe: (onEdit != null && isOwner) ? (_) => onEdit!(data) : null,
|
||||||
child: ContextMenuArea(
|
child: ContextMenuArea(
|
||||||
|
@ -176,7 +176,7 @@ class MarkdownTextContent extends StatelessWidget {
|
|||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: attachment.metadata['ratio'] ??
|
aspectRatio: attachment.metadata['ratio']?.toDouble() ??
|
||||||
switch (attachment.mimetype
|
switch (attachment.mimetype
|
||||||
.split('/')
|
.split('/')
|
||||||
.firstOrNull) {
|
.firstOrNull) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:animations/animations.dart';
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -11,9 +12,11 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/config.dart';
|
import 'package:surface/providers/config.dart';
|
||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/sn_realm.dart';
|
import 'package:surface/providers/sn_realm.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
import 'package:surface/widgets/version_label.dart';
|
import 'package:surface/widgets/version_label.dart';
|
||||||
|
|
||||||
class AppNavigationDrawer extends StatefulWidget {
|
class AppNavigationDrawer extends StatefulWidget {
|
||||||
@ -39,6 +42,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.read<UserProvider>();
|
final ua = context.read<UserProvider>();
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
final nav = context.watch<NavigationProvider>();
|
final nav = context.watch<NavigationProvider>();
|
||||||
final cfg = context.watch<ConfigProvider>();
|
final cfg = context.watch<ConfigProvider>();
|
||||||
final rel = context.read<SnRealmProvider>();
|
final rel = context.read<SnRealmProvider>();
|
||||||
@ -72,42 +76,111 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
child: WindowTitleBarBox(),
|
child: WindowTitleBarBox(),
|
||||||
),
|
),
|
||||||
Gap(MediaQuery.of(context).padding.top),
|
Gap(MediaQuery.of(context).padding.top),
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('Solar Network').bold(),
|
|
||||||
AppVersionLabel(),
|
|
||||||
],
|
|
||||||
).padding(
|
|
||||||
horizontal: 32,
|
|
||||||
vertical: 12,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: PageTransitionSwitcher(
|
||||||
padding: EdgeInsets.zero,
|
duration: const Duration(milliseconds: 300),
|
||||||
children: [
|
transitionBuilder: (Widget child,
|
||||||
...rel.availableRealms.map((ele) {
|
Animation<double> primaryAnimation,
|
||||||
return ListTile(
|
Animation<double> secondaryAnimation) {
|
||||||
minTileHeight: 48,
|
return SharedAxisTransition(
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
animation: primaryAnimation,
|
||||||
leading: AccountImage(
|
secondaryAnimation: secondaryAnimation,
|
||||||
content: ele.avatar,
|
fillColor: Colors.transparent,
|
||||||
radius: 16,
|
transitionType: SharedAxisTransitionType.horizontal,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: nav.focusedRealm == null
|
||||||
|
? ListView(
|
||||||
|
key: const Key('realm-list-view'),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Solar Network').bold(),
|
||||||
|
AppVersionLabel(),
|
||||||
|
],
|
||||||
|
).padding(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
...rel.availableRealms.map((ele) {
|
||||||
|
return ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: ele.avatar,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
title: Text(ele.name),
|
||||||
|
onTap: () {
|
||||||
|
if (Scaffold.of(context).isDrawerOpen) {
|
||||||
|
GoRouter.of(context).goNamed(
|
||||||
|
'realmDetail',
|
||||||
|
pathParameters: {
|
||||||
|
'alias': ele.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
}
|
||||||
|
nav.setFocusedRealm(ele);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: ListView(
|
||||||
|
key: ValueKey(nav.focusedRealm),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
if (nav.focusedRealm!.banner != null)
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(
|
||||||
|
nav.focusedRealm!.banner!,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
tileColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainer,
|
||||||
|
contentPadding: EdgeInsets.only(
|
||||||
|
left: 24,
|
||||||
|
right: 16,
|
||||||
|
),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: nav.focusedRealm!.avatar,
|
||||||
|
radius: 16,
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Symbols.close),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
onPressed: () {
|
||||||
|
nav.setFocusedRealm(null);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(nav.focusedRealm!.name),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'realmDetail',
|
||||||
|
pathParameters: {
|
||||||
|
'alias': nav.focusedRealm!.alias,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
title: Text(ele.name),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).goNamed(
|
|
||||||
'realmDetail',
|
|
||||||
pathParameters: {
|
|
||||||
'alias': ele.alias,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Scaffold.of(context).closeDrawer();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -115,13 +188,17 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
children: nav.destinations.where((ele) => ele.isPinned).map(
|
children: nav.destinations.where((ele) => ele.isPinned).map(
|
||||||
(ele) {
|
(ele) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: IconButton.filledTonal(
|
child: Tooltip(
|
||||||
icon: ele.icon,
|
message: ele.label.tr(),
|
||||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
child: IconButton.filledTonal(
|
||||||
onPressed: () {
|
icon: ele.icon,
|
||||||
GoRouter.of(context).goNamed(ele.screen);
|
color:
|
||||||
Scaffold.of(context).closeDrawer();
|
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
},
|
onPressed: () {
|
||||||
|
GoRouter.of(context).goNamed(ele.screen);
|
||||||
|
Scaffold.of(context).closeDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user