Make auth obligatory
This commit is contained in:
		| @@ -19,7 +19,7 @@ ENV PATH="$FLUTTER_SDK/bin:$FLUTTER_SDK/bin/cache/dart-sdk/bin:${PATH}" | ||||
| RUN mkdir $APP | ||||
| COPY . $APP | ||||
| WORKDIR $APP | ||||
| RUN flutter build web | ||||
| RUN flutter build web --release | ||||
|  | ||||
| # once heare the app will be compiled and ready to deploy | ||||
|  | ||||
|   | ||||
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @@ -2,3 +2,6 @@ format: | ||||
| 	dart format $$(find . -type f -name '*.dart') | ||||
| fix: | ||||
| 	dart fix ./lib --apply | ||||
|  | ||||
| serve: | ||||
| 	flutter run -d web-server --web-port 8080 | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/login_background.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/login_background.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.4 MiB | 
| @@ -1,4 +1,3 @@ | ||||
|  | ||||
| import 'package:grpc/grpc_web.dart'; | ||||
| import 'package:softplayer_dart_proto/accounts/accounts_v1.pbgrpc.dart'; | ||||
| import 'package:softplayer_dart_proto/main.dart'; | ||||
| @@ -11,6 +10,7 @@ class AccountLocalData { | ||||
|   String uuid; | ||||
|   String token; | ||||
| } | ||||
|  | ||||
| class AccountsGrpc { | ||||
|   final GrpcWebClientChannel channel; | ||||
|   late AccountsClient accountsStub; | ||||
| @@ -22,7 +22,8 @@ class AccountsGrpc { | ||||
|     accountsStub = AccountsClient(channel); | ||||
|   } | ||||
|  | ||||
|   Future<AccountLocalData> signIn(String username, String email, String password) async { | ||||
|   Future<AccountLocalData> signIn( | ||||
|       String username, String email, String password) async { | ||||
|     final request = AccountWithPassword( | ||||
|         data: AccountData( | ||||
|           name: username, | ||||
| @@ -39,7 +40,8 @@ class AccountsGrpc { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<AccountLocalData> signUp(String username, String email, String password) async { | ||||
|   Future<AccountLocalData> signUp( | ||||
|       String username, String email, String password) async { | ||||
|     final request = AccountWithPassword( | ||||
|         data: AccountData( | ||||
|           name: username, | ||||
|   | ||||
							
								
								
									
										41
									
								
								lib/api/grpc/environments.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/api/grpc/environments.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| import 'dart:html'; | ||||
|  | ||||
| import 'package:grpc/grpc_web.dart'; | ||||
| import 'package:softplayer_dart_proto/main.dart'; | ||||
|  | ||||
| class EnvironmentLocalData { | ||||
|   EnvironmentLocalData({ | ||||
|     required this.uuid, | ||||
|     required this.token, | ||||
|   }); | ||||
|   String uuid; | ||||
|   String token; | ||||
| } | ||||
|  | ||||
| class EnvironmentsGrpc { | ||||
|   final GrpcWebClientChannel channel; | ||||
|   late EnvironmentsClient envStub; | ||||
|   EnvironmentsGrpc({ | ||||
|     required this.channel, | ||||
|   }); | ||||
|  | ||||
|   void init() { | ||||
|     envStub = EnvironmentsClient(channel); | ||||
|   } | ||||
|  | ||||
|   Stream<List<String>> list() async* { | ||||
|     List<String> envs = []; | ||||
|     try { | ||||
|       await for (var feature in envStub.list(Empty(), | ||||
|           options: CallOptions(metadata: { | ||||
|             "uuid": window.localStorage["uuid"]!, | ||||
|             "token": window.localStorage["token"]!, | ||||
|           }))) { | ||||
|         envs.add(feature.data.name); | ||||
|       } | ||||
|     } catch (e) { | ||||
|       rethrow; | ||||
|     } | ||||
|     yield envs; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										100
									
								
								lib/components/environments.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								lib/components/environments.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| import 'dart:js_interop'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:grpc/grpc_web.dart'; | ||||
| import 'package:softplayer_web/api/grpc/environments.dart'; | ||||
|  | ||||
| class EnvirnomentList extends StatefulWidget { | ||||
|   EnvirnomentList({ | ||||
|     super.key, | ||||
|     required this.channel, | ||||
|   }); | ||||
|  | ||||
|   final GrpcWebClientChannel channel; | ||||
|   @override | ||||
|   State<StatefulWidget> createState() => _EnvirnomentListState(); | ||||
| } | ||||
|  | ||||
| class _EnvirnomentListState extends State<EnvirnomentList> { | ||||
|   late EnvironmentsGrpc envGrpc; | ||||
|   List<String> envs = []; | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     envGrpc = EnvironmentsGrpc(channel: widget.channel); | ||||
|     envGrpc.init(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|         body: SafeArea( | ||||
|             child: StreamBuilder( | ||||
|                 stream: envGrpc.list(), | ||||
|                 builder: (context, snapshot) { | ||||
|                   if (snapshot.connectionState == ConnectionState.waiting) { | ||||
|                     return CircularProgressIndicator(); | ||||
|                   } else if (snapshot.connectionState == ConnectionState.done) { | ||||
|                     if (snapshot.hasError) { | ||||
|                       return Text('Error!'); | ||||
|                     } else { | ||||
|                       return Column( | ||||
|                         children: snapshot.data!.map((e) => Text(e)).toList(), | ||||
|                       ); | ||||
|                     } | ||||
|                   } | ||||
|                     return Text("err"); | ||||
|                 }))); | ||||
|   } | ||||
| } | ||||
|  | ||||
| //class EnvirnomentList extends StatelessWidget { | ||||
| //  EnvirnomentList({ | ||||
| //    super.key, | ||||
| //    required this.channel, | ||||
| //  }); | ||||
| //  final GrpcWebClientChannel channel; | ||||
| //  late List<String> envs; | ||||
| //  late EnvironmentsGrpc envGrpc; | ||||
| //  List<String> getEnvs() { | ||||
| //    List<String> envs = []; | ||||
| //    envGrpc.list().then((value) { | ||||
| //      return value; | ||||
| //    }).catchError((e) { | ||||
| //      return envs; | ||||
| //    }); | ||||
| //    return envs; | ||||
| //  } | ||||
| // | ||||
| //  List<Widget> bootstrapCards(List<String> envs) { | ||||
| //    List<Widget> cards = []; | ||||
| //    envs.forEach((element) { | ||||
| //      cards.add(Center(child: Text(element))); | ||||
| //    }); | ||||
| //    return cards; | ||||
| //  } | ||||
| // | ||||
| // | ||||
| //  @override | ||||
| //  Widget build(BuildContext context) { | ||||
| //    envGrpc = EnvironmentsGrpc(channel: channel); | ||||
| //    envGrpc.init(); | ||||
| //    envs = getEnvs(); | ||||
| //    return GridView.count( | ||||
| //      crossAxisCount: 2, | ||||
| //      children: bootstrapCards(envs), | ||||
| //    ); | ||||
| // // children: List.generate(100, (index) { | ||||
| // //   return Center( | ||||
| // //     child: Text( | ||||
| // //       'Item $index', | ||||
| // //       style: Theme.of(context).textTheme.headlineSmall, | ||||
| // //     ), | ||||
| // //   ); | ||||
| // // }), | ||||
| // // ); | ||||
| //   // return GridView.count( | ||||
| //   //       children: bootstrapCards(getEnvs()), | ||||
| //     //   ); | ||||
| //  } | ||||
| //} | ||||
							
								
								
									
										217
									
								
								lib/components/login_form.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								lib/components/login_form.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| import 'dart:html'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:grpc/grpc_web.dart'; | ||||
| import 'package:softplayer_web/api/grpc/accounts.dart'; | ||||
|  | ||||
| class LoginForm extends StatefulWidget { | ||||
|   const LoginForm({ | ||||
|     super.key, | ||||
|     required this.grpcChannel, | ||||
|   }); | ||||
|  | ||||
|   final GrpcWebClientChannel grpcChannel; | ||||
|   @override | ||||
|   State<StatefulWidget> createState() => _LoginFormState(); | ||||
| } | ||||
|  | ||||
| enum Action { singIn, signUp } | ||||
|  | ||||
| class _LoginFormState extends State<LoginForm> { | ||||
|   final _formKey = GlobalKey<FormState>(); | ||||
|  | ||||
|   final usernameCtrl = TextEditingController(); | ||||
|   final passwordCtrl = TextEditingController(); | ||||
|   final passwordVerifyCtrl = TextEditingController(); | ||||
|   final emailCtrl = TextEditingController(); | ||||
|  | ||||
|   late AccountsGrpc accountsGrpc; | ||||
|   Action action = Action.singIn; | ||||
|   static const dialogName = "Login"; | ||||
|  | ||||
|   void submitSignUp() { | ||||
|     // Validate returns true if the form is valid, or false otherwise. | ||||
|     if (_formKey.currentState!.validate()) { | ||||
|       final username = usernameCtrl.text; | ||||
|       final password = passwordCtrl.text; | ||||
|       final email = emailCtrl.text; | ||||
|       accountsGrpc.signUp(username, email, password).then((rs) { | ||||
|         window.localStorage["token"] = rs.token; | ||||
|         window.localStorage["uuid"] = rs.uuid; | ||||
|         Navigator.of(context, rootNavigator: true).pop(); | ||||
|       }).catchError((e) { | ||||
|         GrpcError error = e; | ||||
|         String msg; | ||||
|         if (error.message != null) { | ||||
|           msg = error.message!; | ||||
|         } else { | ||||
|           msg = error.toString(); | ||||
|         } | ||||
|         ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|           content: Text(msg), | ||||
|           backgroundColor: Colors.red, | ||||
|           showCloseIcon: true, | ||||
|           behavior: SnackBarBehavior.floating, | ||||
|         )); | ||||
|         passwordCtrl.clear(); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void submitSignIn() { | ||||
|     // Validate returns true if the form is valid, or false otherwise. | ||||
|     if (_formKey.currentState!.validate()) { | ||||
|       final username = usernameCtrl.text; | ||||
|       final password = passwordCtrl.text; | ||||
|       accountsGrpc.signIn(username, "", password).then((rs) { | ||||
|         window.localStorage["token"] = rs.token; | ||||
|         window.localStorage["uuid"] = rs.uuid; | ||||
|         setState(() { | ||||
|                    | ||||
|                 }); | ||||
|       }).catchError((e) { | ||||
|         print(e); | ||||
|         GrpcError error = e; | ||||
|         String msg; | ||||
|         if (error.message != null) { | ||||
|           msg = error.message!; | ||||
|         } else { | ||||
|           msg = error.toString(); | ||||
|         } | ||||
|         ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|           content: Text(msg), | ||||
|           backgroundColor: Colors.red, | ||||
|           showCloseIcon: true, | ||||
|           behavior: SnackBarBehavior.floating, | ||||
|         )); | ||||
|         passwordCtrl.clear(); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Widget signInForm() => SizedBox( | ||||
|       width: 420, | ||||
|       height: 280, | ||||
|       child: Form( | ||||
|           key: _formKey, | ||||
|           child: Center( | ||||
|               child: Column(children: [ | ||||
|             TextFormField( | ||||
|               autofocus: true, | ||||
|               controller: usernameCtrl, | ||||
|               decoration: const InputDecoration( | ||||
|                 hintText: "Enter your username", | ||||
|                 icon: Icon(Icons.account_circle), | ||||
|                 label: Text("Username"), | ||||
|               ), | ||||
|               cursorWidth: 1, | ||||
|               cursorHeight: 18, | ||||
|               cursorRadius: const Radius.circular(10), | ||||
|             ), | ||||
|             TextFormField( | ||||
|               controller: passwordCtrl, | ||||
|               obscureText: true, | ||||
|               decoration: const InputDecoration( | ||||
|                   hintText: "Enter your password", | ||||
|                   icon: Icon(Icons.password), | ||||
|                   label: Text("Password")), | ||||
|               cursorWidth: 1, | ||||
|               cursorHeight: 18, | ||||
|               cursorRadius: const Radius.circular(10), | ||||
|             ), | ||||
|           ])))); | ||||
|  | ||||
|   List<Widget> signInActions() => [ | ||||
|         TextButton( | ||||
|           onPressed: () => setState(() { | ||||
|             action = Action.signUp; | ||||
|           }), | ||||
|           child: const Text('Sing Up'), | ||||
|         ), | ||||
|         TextButton( | ||||
|           onPressed: submitSignIn, | ||||
|           child: const Text('OK'), | ||||
|         ), | ||||
|       ]; | ||||
|  | ||||
|   List<Widget> signUpActions() => [ | ||||
|         TextButton( | ||||
|           onPressed: () => setState(() { | ||||
|             action = Action.singIn; | ||||
|           }), | ||||
|           child: const Text('Sing In'), | ||||
|         ), | ||||
|         TextButton( | ||||
|           onPressed: submitSignUp, | ||||
|           child: const Text('OK'), | ||||
|         ), | ||||
|       ]; | ||||
|   Widget signUpForm() => SizedBox( | ||||
|       width: 420, | ||||
|       height: 280, | ||||
|       child: Form( | ||||
|           key: _formKey, | ||||
|           child: Center( | ||||
|               child: Column(children: [ | ||||
|             TextFormField( | ||||
|               autofocus: true, | ||||
|               controller: usernameCtrl, | ||||
|               decoration: const InputDecoration( | ||||
|                 hintText: "Enter your username", | ||||
|                 icon: Icon(Icons.account_circle), | ||||
|                 label: Text("Username"), | ||||
|               ), | ||||
|               cursorWidth: 1, | ||||
|               cursorHeight: 18, | ||||
|               cursorRadius: const Radius.circular(10), | ||||
|             ), | ||||
|             TextFormField( | ||||
|               controller: emailCtrl, | ||||
|               autofocus: true, | ||||
|               decoration: const InputDecoration( | ||||
|                 hintText: "Enter your email", | ||||
|                 icon: Icon(Icons.email), | ||||
|                 label: Text("Email"), | ||||
|               ), | ||||
|               cursorWidth: 1, | ||||
|               cursorHeight: 18, | ||||
|               cursorRadius: const Radius.circular(10), | ||||
|             ), | ||||
|             TextFormField( | ||||
|               controller: passwordCtrl, | ||||
|               obscureText: true, | ||||
|               decoration: const InputDecoration( | ||||
|                   hintText: "Enter your password", | ||||
|                   icon: Icon(Icons.password), | ||||
|                   label: Text("Password")), | ||||
|               cursorWidth: 1, | ||||
|               cursorHeight: 18, | ||||
|               cursorRadius: const Radius.circular(10), | ||||
|             ), | ||||
|             TextFormField( | ||||
|               controller: passwordVerifyCtrl, | ||||
|               obscureText: true, | ||||
|               decoration: const InputDecoration( | ||||
|                   hintText: "Verify your password", | ||||
|                   icon: Icon(Icons.password), | ||||
|                   label: Text("Confirm Password")), | ||||
|               cursorWidth: 1, | ||||
|               cursorHeight: 18, | ||||
|               cursorRadius: const Radius.circular(10), | ||||
|             ), | ||||
|           ])))); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     accountsGrpc = AccountsGrpc(channel: widget.grpcChannel); | ||||
|     accountsGrpc.init(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) => AlertDialog( | ||||
|         title: const Text(dialogName), | ||||
|         content: action == Action.singIn ? signInForm() : signUpForm(), | ||||
|         actions: action == Action.singIn ? signInActions() : signUpActions(), | ||||
|       ); | ||||
| } | ||||
| @@ -52,20 +52,16 @@ class _MenuPanel extends State<MenuPanel> { | ||||
|               showDialog( | ||||
|                   context: context, | ||||
|                   builder: (BuildContext context) => SignUpForm( | ||||
|                     accountsGrpc: widget.accountsGrpc, | ||||
|                     )); | ||||
|                         accountsGrpc: widget.accountsGrpc, | ||||
|                       )); | ||||
|             }, | ||||
|             child: const Text("sign up")), | ||||
|       ]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   final String linkCatalog = "catalog"; | ||||
|   final String linkAbout = "about"; | ||||
|   final String linkHome = "home"; | ||||
|   @override | ||||
|   PreferredSizeWidget build(BuildContext context) { | ||||
|     final TabName tab = widget.tab; | ||||
|     return AppBar( | ||||
|       title: Row(children: [ | ||||
|         TextButton( | ||||
| @@ -73,36 +69,6 @@ class _MenuPanel extends State<MenuPanel> { | ||||
|             onPressed: () { | ||||
|               Navigator.pushNamed(context, "/"); | ||||
|             }), | ||||
|         TextButton( | ||||
|             child: Text( | ||||
|               linkHome, | ||||
|               style: (tab == TabName.home) | ||||
|                   ? const TextStyle(decoration: TextDecoration.underline) | ||||
|                   : const TextStyle(), | ||||
|             ), | ||||
|             onPressed: () { | ||||
|               Navigator.pushNamed(context, "/"); | ||||
|             }), | ||||
|         TextButton( | ||||
|             child: Text( | ||||
|               linkCatalog, | ||||
|               style: (tab == TabName.catalog) | ||||
|                   ? const TextStyle(decoration: TextDecoration.underline) | ||||
|                   : const TextStyle(), | ||||
|             ), | ||||
|             onPressed: () { | ||||
|               Navigator.pushNamed(context, "/catalog"); | ||||
|             }), | ||||
|         TextButton( | ||||
|             child: Text( | ||||
|               linkAbout, | ||||
|               style: (tab == TabName.about) | ||||
|                   ? const TextStyle(decoration: TextDecoration.underline) | ||||
|                   : const TextStyle(), | ||||
|             ), | ||||
|             onPressed: () { | ||||
|               Navigator.pushNamed(context, "/about"); | ||||
|             }), | ||||
|       ]), | ||||
|       backgroundColor: const Color.fromRGBO(46, 51, 78, 1.0), | ||||
|       automaticallyImplyLeading: false, | ||||
|   | ||||
| @@ -25,29 +25,26 @@ class _SignInFormState extends State<SignInForm> { | ||||
|     if (_formKey.currentState!.validate()) { | ||||
|       final username = usernameCtrl.text; | ||||
|       final password = passwordCtrl.text; | ||||
|       widget.accountsGrpc | ||||
|           .signIn(username, "", password) | ||||
|           .then((rs) { | ||||
|             window.localStorage["token"] = rs.token; | ||||
|             window.localStorage["uuid"] = rs.uuid; | ||||
|             Navigator.of(context, rootNavigator: true).pop(); | ||||
|           }) | ||||
|           .catchError((e) { | ||||
|                 GrpcError error = e; | ||||
|                 String msg; | ||||
|                 if (error.message != null) { | ||||
|                   msg = error.message!; | ||||
|                 } else { | ||||
|                   msg = error.toString(); | ||||
|                 } | ||||
|                 ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|                   content: Text(msg), | ||||
|                   backgroundColor: Colors.red, | ||||
|                   showCloseIcon: true, | ||||
|                   behavior: SnackBarBehavior.floating, | ||||
|                 )); | ||||
|                 passwordCtrl.clear(); | ||||
|               }); | ||||
|       widget.accountsGrpc.signIn(username, "", password).then((rs) { | ||||
|         window.localStorage["token"] = rs.token; | ||||
|         window.localStorage["uuid"] = rs.uuid; | ||||
|         Navigator.of(context, rootNavigator: true).pop(); | ||||
|       }).catchError((e) { | ||||
|         GrpcError error = e; | ||||
|         String msg; | ||||
|         if (error.message != null) { | ||||
|           msg = error.message!; | ||||
|         } else { | ||||
|           msg = error.toString(); | ||||
|         } | ||||
|         ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|           content: Text(msg), | ||||
|           backgroundColor: Colors.red, | ||||
|           showCloseIcon: true, | ||||
|           behavior: SnackBarBehavior.floating, | ||||
|         )); | ||||
|         passwordCtrl.clear(); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,6 @@ class SignUpForm extends StatefulWidget { | ||||
|   const SignUpForm({ | ||||
|     super.key, | ||||
|     required this.accountsGrpc, | ||||
|  | ||||
|   }); | ||||
|  | ||||
|   final AccountsGrpc accountsGrpc; | ||||
| @@ -18,46 +17,43 @@ class SignUpForm extends StatefulWidget { | ||||
|  | ||||
| class _SignUpFormState extends State<SignUpForm> { | ||||
|   final _formKey = GlobalKey<FormState>(); | ||||
|    | ||||
|  | ||||
|   final usernameCtrl = TextEditingController(); | ||||
|   final passwordCtrl = TextEditingController(); | ||||
|   final passwordVerifyCtrl = TextEditingController(); | ||||
|   final emailCtrl = TextEditingController(); | ||||
|  | ||||
|   static const dialogName = "Sign Up"; | ||||
|    | ||||
|  | ||||
|   void submitForm() { | ||||
|     // Validate returns true if the form is valid, or false otherwise. | ||||
|     if (_formKey.currentState!.validate()) { | ||||
|       final username = usernameCtrl.text; | ||||
|       final password = passwordCtrl.text; | ||||
|       final email = emailCtrl.text; | ||||
|       widget.accountsGrpc | ||||
|           .signUp(username, email, password) | ||||
|           .then((rs) { | ||||
|             window.localStorage["token"] = rs.token; | ||||
|             window.localStorage["uuid"] = rs.uuid; | ||||
|             Navigator.of(context, rootNavigator: true).pop(); | ||||
|           }) | ||||
|  | ||||
|           .catchError((e) { | ||||
|                 GrpcError error = e; | ||||
|                 String msg; | ||||
|                 if (error.message != null) { | ||||
|                   msg = error.message!; | ||||
|                 } else { | ||||
|                   msg = error.toString(); | ||||
|                 } | ||||
|                 ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|                   content: Text(msg), | ||||
|                   backgroundColor: Colors.red, | ||||
|                   showCloseIcon: true, | ||||
|                   behavior: SnackBarBehavior.floating, | ||||
|                 )); | ||||
|                 passwordCtrl.clear(); | ||||
|               }); | ||||
|       widget.accountsGrpc.signUp(username, email, password).then((rs) { | ||||
|         window.localStorage["token"] = rs.token; | ||||
|         window.localStorage["uuid"] = rs.uuid; | ||||
|         Navigator.of(context, rootNavigator: true).pop(); | ||||
|       }).catchError((e) { | ||||
|         GrpcError error = e; | ||||
|         String msg; | ||||
|         if (error.message != null) { | ||||
|           msg = error.message!; | ||||
|         } else { | ||||
|           msg = error.toString(); | ||||
|         } | ||||
|         ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|           content: Text(msg), | ||||
|           backgroundColor: Colors.red, | ||||
|           showCloseIcon: true, | ||||
|           behavior: SnackBarBehavior.floating, | ||||
|         )); | ||||
|         passwordCtrl.clear(); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) => AlertDialog( | ||||
|         title: const Text(dialogName), | ||||
|   | ||||
| @@ -1,10 +1,22 @@ | ||||
| import 'dart:html'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:softplayer_web/api/grpc/accounts.dart'; | ||||
| import 'package:softplayer_web/components/menubar.dart'; | ||||
|  | ||||
| class PageWrapper extends StatelessWidget { | ||||
|   final Widget child; | ||||
|   final MenuPanel appBar; | ||||
|   const PageWrapper({super.key, required this.child, required this.appBar}); | ||||
|   const PageWrapper( | ||||
|       {super.key, | ||||
|       required this.child, | ||||
|       required this.appBar, | ||||
|       required this.accountsGrpc}); | ||||
|  | ||||
|   final AccountsGrpc accountsGrpc; | ||||
|   bool isSignedIn() { | ||||
|     return window.localStorage.containsKey("token"); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| import 'dart:html'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:grpc/grpc_web.dart'; | ||||
| import 'package:softplayer_web/api/grpc/accounts.dart'; | ||||
| import 'package:softplayer_web/components/menubar.dart'; | ||||
| import 'package:softplayer_web/helpers/page_wrapper.dart'; | ||||
| import 'package:softplayer_web/pages/about.dart'; | ||||
| import 'package:softplayer_web/pages/catalog.dart'; | ||||
| import 'package:softplayer_web/pages/home.dart'; | ||||
| import 'package:softplayer_web/components/environments.dart'; | ||||
| import 'package:softplayer_web/components/login_form.dart'; | ||||
|  | ||||
| void main() async { | ||||
|   const String backendURL = String.fromEnvironment( | ||||
| @@ -28,29 +27,13 @@ class MyApp extends StatelessWidget { | ||||
|     return MaterialApp( | ||||
|       debugShowCheckedModeBanner: false, | ||||
|       title: 'Softplayer', | ||||
|       routes: { | ||||
|         '/': (context) => PageWrapper( | ||||
|               appBar: MenuPanel( | ||||
|                 tab: TabName.home, | ||||
|                 accountsGrpc: accountsGrpc, | ||||
|               ), | ||||
|               child: const HomePage(), | ||||
|             ), | ||||
|         '/catalog': (context) => PageWrapper( | ||||
|               appBar: MenuPanel( | ||||
|                 tab: TabName.catalog, | ||||
|                 accountsGrpc: accountsGrpc, | ||||
|               ), | ||||
|               child: const CatalogPage(), | ||||
|             ), | ||||
|         '/about': (context) => PageWrapper( | ||||
|               appBar: MenuPanel( | ||||
|                 tab: TabName.about, | ||||
|                 accountsGrpc: accountsGrpc, | ||||
|               ), | ||||
|               child: const AboutPage(), | ||||
|             ) | ||||
|       }, | ||||
|       home: Scaffold( | ||||
|         body: TestAlert(channel: channel), | ||||
|         appBar: AppBar(), | ||||
|         floatingActionButton: FloatingActionButton( | ||||
|           onPressed: () => print("1"), | ||||
|         ), | ||||
|       ), | ||||
|       theme: ThemeData( | ||||
|         colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), | ||||
|         useMaterial3: true, | ||||
| @@ -58,3 +41,29 @@ class MyApp extends StatelessWidget { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class TestAlert extends StatelessWidget { | ||||
|   final GrpcWebClientChannel channel; | ||||
|   TestAlert({super.key, required this.channel}); | ||||
|   late final AccountsGrpc accountsGrpc = AccountsGrpc(channel: channel); | ||||
|   bool isSignedIn() { | ||||
|     return window.localStorage.containsKey("token"); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     if (!isSignedIn()) { | ||||
|       return Container( | ||||
|         decoration: const BoxDecoration( | ||||
|           image: DecorationImage( | ||||
|             image: AssetImage("assets/login_background.jpg"), | ||||
|             fit: BoxFit.fill, | ||||
|           ), | ||||
|         ), | ||||
|         child: LoginForm(grpcChannel: channel), | ||||
|       ); | ||||
|     } else { | ||||
|       return EnvirnomentList(channel: channel); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										10
									
								
								lib/models/environments.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/models/environments.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| class Environment { | ||||
|   final String name; | ||||
|   final List<String> apps; | ||||
|   final String provider; | ||||
|   Environment({ | ||||
|     required this.name, | ||||
|     required this.apps, | ||||
|     required this.provider, | ||||
|   }); | ||||
| } | ||||
| @@ -26,3 +26,5 @@ dev_dependencies: | ||||
|  | ||||
| flutter: | ||||
|   uses-material-design: true | ||||
|   assets: | ||||
|     - assets/ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user