✨ Bootstrapper
This commit is contained in:
		
							
								
								
									
										149
									
								
								lib/bootstrapper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								lib/bootstrapper.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:get/get.dart'; | ||||||
|  | import 'package:solian/exts.dart'; | ||||||
|  | import 'package:solian/providers/auth.dart'; | ||||||
|  | import 'package:solian/providers/content/channel.dart'; | ||||||
|  | import 'package:solian/providers/relation.dart'; | ||||||
|  | import 'package:solian/providers/websocket.dart'; | ||||||
|  | import 'package:solian/services.dart'; | ||||||
|  |  | ||||||
|  | class BootstrapperShell extends StatefulWidget { | ||||||
|  |   final Widget child; | ||||||
|  |  | ||||||
|  |   const BootstrapperShell({super.key, required this.child}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   State<BootstrapperShell> createState() => _BootstrapperShellState(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _BootstrapperShellState extends State<BootstrapperShell> { | ||||||
|  |   bool _isBusy = true; | ||||||
|  |   bool _isErrored = false; | ||||||
|  |   String? _subtitle; | ||||||
|  |  | ||||||
|  |   Color get _unFocusColor => | ||||||
|  |       Theme.of(context).colorScheme.onSurface.withOpacity(0.75); | ||||||
|  |  | ||||||
|  |   int _periodCursor = 0; | ||||||
|  |  | ||||||
|  |   late final List<({String label, Future<void> Function() action})> _periods = [ | ||||||
|  |     ( | ||||||
|  |       label: 'bsCheckingServer', | ||||||
|  |       action: () async { | ||||||
|  |         final client = ServiceFinder.configureClient('dealer'); | ||||||
|  |         final resp = await client.get('/.well-known'); | ||||||
|  |         if (resp.statusCode != 200) { | ||||||
|  |           setState(() { | ||||||
|  |             _isErrored = true; | ||||||
|  |             _subtitle = 'bsCheckingServerFail'.tr; | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ), | ||||||
|  |     ( | ||||||
|  |       label: 'bsAuthorizing', | ||||||
|  |       action: () async { | ||||||
|  |         final AuthProvider auth = Get.find(); | ||||||
|  |         await auth.refreshAuthorizeStatus(); | ||||||
|  |         if (auth.isAuthorized.isTrue) { | ||||||
|  |           await auth.refreshUserProfile(); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ), | ||||||
|  |     ( | ||||||
|  |       label: 'bsEstablishingConn', | ||||||
|  |       action: () async { | ||||||
|  |         final AuthProvider auth = Get.find(); | ||||||
|  |         if (auth.isAuthorized.isTrue) { | ||||||
|  |           await Get.find<WebSocketProvider>().connect(); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ), | ||||||
|  |     ( | ||||||
|  |       label: 'bsPreparingData', | ||||||
|  |       action: () async { | ||||||
|  |         final AuthProvider auth = Get.find(); | ||||||
|  |         if (auth.isAuthorized.isTrue) { | ||||||
|  |           await Future.wait([ | ||||||
|  |             Get.find<ChannelProvider>().refreshAvailableChannel(), | ||||||
|  |             Get.find<RelationshipProvider>().refreshFriendList(), | ||||||
|  |           ]); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ), | ||||||
|  |     ( | ||||||
|  |       label: 'bsRegisteringPushNotify', | ||||||
|  |       action: () async { | ||||||
|  |         final AuthProvider auth = Get.find(); | ||||||
|  |         if (auth.isAuthorized.isTrue) { | ||||||
|  |           try { | ||||||
|  |             Get.find<WebSocketProvider>().registerPushNotifications(); | ||||||
|  |           } catch (err) { | ||||||
|  |             context.showSnackbar( | ||||||
|  |               'pushNotifyRegisterFailed'.trParams({'reason': err.toString()}), | ||||||
|  |             ); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ), | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   Future<void> _runPeriods() async { | ||||||
|  |     for (var idx = 0; idx < _periods.length; idx++) { | ||||||
|  |       await _periods[idx].action(); | ||||||
|  |       setState(() => _periodCursor++); | ||||||
|  |     } | ||||||
|  |     setState(() => _isBusy = false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     _runPeriods(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     if (_isBusy || _isErrored) { | ||||||
|  |       return Material( | ||||||
|  |         color: Theme.of(context).colorScheme.surface, | ||||||
|  |         child: Column( | ||||||
|  |           mainAxisSize: MainAxisSize.max, | ||||||
|  |           mainAxisAlignment: MainAxisAlignment.spaceAround, | ||||||
|  |           children: [ | ||||||
|  |             SizedBox( | ||||||
|  |               height: 280, | ||||||
|  |               child: Align( | ||||||
|  |                 alignment: Alignment.bottomCenter, | ||||||
|  |                 child: Image.asset('assets/logo.png', width: 80, height: 80), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Column( | ||||||
|  |               children: [ | ||||||
|  |                 if (_isErrored) | ||||||
|  |                   const Icon(Icons.cancel, size: 24) | ||||||
|  |                 else | ||||||
|  |                   const SizedBox( | ||||||
|  |                     width: 24, | ||||||
|  |                     height: 24, | ||||||
|  |                     child: CircularProgressIndicator(strokeWidth: 3), | ||||||
|  |                   ), | ||||||
|  |                 const SizedBox(height: 12), | ||||||
|  |                 Text( | ||||||
|  |                   _subtitle ?? | ||||||
|  |                       '${_periods[_periodCursor].label.tr} (${_periodCursor + 1}/${_periods.length})', | ||||||
|  |                   style: TextStyle( | ||||||
|  |                     fontSize: 13, | ||||||
|  |                     color: _unFocusColor, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ) | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return widget.child; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -5,7 +5,7 @@ import 'package:get/get.dart'; | |||||||
| import 'package:media_kit/media_kit.dart'; | import 'package:media_kit/media_kit.dart'; | ||||||
| import 'package:protocol_handler/protocol_handler.dart'; | import 'package:protocol_handler/protocol_handler.dart'; | ||||||
| import 'package:sentry_flutter/sentry_flutter.dart'; | import 'package:sentry_flutter/sentry_flutter.dart'; | ||||||
| import 'package:solian/exts.dart'; | import 'package:solian/bootstrapper.dart'; | ||||||
| import 'package:solian/firebase_options.dart'; | import 'package:solian/firebase_options.dart'; | ||||||
| import 'package:solian/platform.dart'; | import 'package:solian/platform.dart'; | ||||||
| import 'package:solian/providers/websocket.dart'; | import 'package:solian/providers/websocket.dart'; | ||||||
| @@ -87,7 +87,9 @@ class SolianApp extends StatelessWidget { | |||||||
|       builder: (context, child) { |       builder: (context, child) { | ||||||
|         return SystemShell( |         return SystemShell( | ||||||
|           child: ScaffoldMessenger( |           child: ScaffoldMessenger( | ||||||
|             child: child ?? const SizedBox(), |             child: BootstrapperShell( | ||||||
|  |               child: child ?? const SizedBox(), | ||||||
|  |             ), | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|       }, |       }, | ||||||
| @@ -104,23 +106,5 @@ class SolianApp extends StatelessWidget { | |||||||
|     Get.lazyPut(() => ChannelProvider()); |     Get.lazyPut(() => ChannelProvider()); | ||||||
|     Get.lazyPut(() => RealmProvider()); |     Get.lazyPut(() => RealmProvider()); | ||||||
|     Get.lazyPut(() => ChatCallProvider()); |     Get.lazyPut(() => ChatCallProvider()); | ||||||
|  |  | ||||||
|     final AuthProvider auth = Get.find(); |  | ||||||
|  |  | ||||||
|     auth.refreshAuthorizeStatus().then((_) { |  | ||||||
|       if (auth.isAuthorized.isFalse) return; |  | ||||||
|  |  | ||||||
|       Get.find<WebSocketProvider>().connect(); |  | ||||||
|       Get.find<ChannelProvider>().refreshAvailableChannel(); |  | ||||||
|       Get.find<RelationshipProvider>().refreshFriendList(); |  | ||||||
|  |  | ||||||
|       try { |  | ||||||
|         Get.find<WebSocketProvider>().registerPushNotifications(); |  | ||||||
|       } catch (err) { |  | ||||||
|         context.showSnackbar( |  | ||||||
|           'pushNotifyRegisterFailed'.trParams({'reason': err.toString()}), |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ class WebSocketProvider extends GetxController { | |||||||
|     super.onInit(); |     super.onInit(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void connect({noRetry = false}) async { |   Future<void> connect({noRetry = false}) async { | ||||||
|     if (isConnected.value) { |     if (isConnected.value) { | ||||||
|       return; |       return; | ||||||
|     } else { |     } else { | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ abstract class ServiceFinder { | |||||||
|     } else if (serviceName == 'passport') { |     } else if (serviceName == 'passport') { | ||||||
|       return '$passportUrl$append'; |       return '$passportUrl$append'; | ||||||
|     } |     } | ||||||
|     return '$dealerUrl/srv/$serviceName$append'; |     return '$dealerUrl/cgi/$serviceName$append'; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static GetConnect configureClient(String serviceName, |   static GetConnect configureClient(String serviceName, | ||||||
|   | |||||||
| @@ -276,4 +276,10 @@ const messagesEnglish = { | |||||||
|   'accountStatusNegative': 'Negative', |   'accountStatusNegative': 'Negative', | ||||||
|   'accountStatusNeutral': 'Neutral', |   'accountStatusNeutral': 'Neutral', | ||||||
|   'accountStatusPositive': 'Positive', |   'accountStatusPositive': 'Positive', | ||||||
|  |   'bsCheckingServer': 'Checking Server Status', | ||||||
|  |   'bsCheckingServerFail': 'Unable connect to server, check your network connection or follow our service maintenance status', | ||||||
|  |   'bsAuthorizing': 'Authorizing', | ||||||
|  |   'bsEstablishingConn': 'Establishing Connection', | ||||||
|  |   'bsPreparingData': 'Preparing User Data', | ||||||
|  |   'bsRegisteringPushNotify': 'Enabling Push Notifications', | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -255,4 +255,10 @@ const simplifiedChineseMessages = { | |||||||
|   'accountStatusNegative': '负面', |   'accountStatusNegative': '负面', | ||||||
|   'accountStatusNeutral': '中性', |   'accountStatusNeutral': '中性', | ||||||
|   'accountStatusPositive': '积极', |   'accountStatusPositive': '积极', | ||||||
|  |   'bsCheckingServer': '检查服务器状态中', | ||||||
|  |   'bsCheckingServerFail': '无法连接至服务器,请检查你的网络状态或我们服务维护状态', | ||||||
|  |   'bsAuthorizing': '正在授权中', | ||||||
|  |   'bsEstablishingConn': '部署连接中', | ||||||
|  |   'bsPreparingData': '正在准备用户资料', | ||||||
|  |   'bsRegisteringPushNotify': '正在启用推送通知', | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ name: solian | |||||||
| description: "The Solar Network App" | description: "The Solar Network App" | ||||||
| publish_to: "none" | publish_to: "none" | ||||||
|  |  | ||||||
| version: 1.1.0+1 | version: 1.1.0+46 | ||||||
|  |  | ||||||
| environment: | environment: | ||||||
|   sdk: ">=3.3.4 <4.0.0" |   sdk: ">=3.3.4 <4.0.0" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user