Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
6.6 KiB

  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:http/http.dart' as http;
  4. import '/components/flash_message.dart';
  5. import '/models/my_profile.dart';
  6. import '/utils/storage/session_cookie.dart';
  7. class LoginResponse {
  8. final String status;
  9. final String message;
  10. final String publicKey;
  11. final String privateKey;
  12. final String userId;
  13. final String username;
  14. const LoginResponse({
  15. required this.status,
  16. required this.message,
  17. required this.publicKey,
  18. required this.privateKey,
  19. required this.userId,
  20. required this.username,
  21. });
  22. factory LoginResponse.fromJson(Map<String, dynamic> json) {
  23. return LoginResponse(
  24. status: json['status'],
  25. message: json['message'],
  26. publicKey: json['asymmetric_public_key'],
  27. privateKey: json['asymmetric_private_key'],
  28. userId: json['user_id'],
  29. username: json['username'],
  30. );
  31. }
  32. }
  33. class Login extends StatelessWidget {
  34. const Login({Key? key}) : super(key: key);
  35. @override
  36. Widget build(BuildContext context) {
  37. return Scaffold(
  38. appBar: AppBar(
  39. title: null,
  40. automaticallyImplyLeading: true,
  41. leading: IconButton(icon: const Icon(Icons.arrow_back),
  42. onPressed:() => {
  43. Navigator.pop(context)
  44. }
  45. ),
  46. backgroundColor: Colors.transparent,
  47. shadowColor: Colors.transparent,
  48. ),
  49. body: const SafeArea(
  50. child: LoginWidget(),
  51. ),
  52. );
  53. }
  54. }
  55. class LoginWidget extends StatefulWidget {
  56. const LoginWidget({Key? key}) : super(key: key);
  57. @override
  58. State<LoginWidget> createState() => _LoginWidgetState();
  59. }
  60. class _LoginWidgetState extends State<LoginWidget> {
  61. final _formKey = GlobalKey<FormState>();
  62. final TextEditingController _usernameController = TextEditingController();
  63. final TextEditingController _passwordController = TextEditingController();
  64. @override
  65. Widget build(BuildContext context) {
  66. const TextStyle inputTextStyle = TextStyle(
  67. fontSize: 18,
  68. );
  69. final OutlineInputBorder inputBorderStyle = OutlineInputBorder(
  70. borderRadius: BorderRadius.circular(5),
  71. borderSide: const BorderSide(
  72. color: Colors.transparent,
  73. )
  74. );
  75. final ButtonStyle buttonStyle = ElevatedButton.styleFrom(
  76. primary: Theme.of(context).colorScheme.surface,
  77. onPrimary: Theme.of(context).colorScheme.onSurface,
  78. minimumSize: const Size.fromHeight(50),
  79. padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
  80. textStyle: TextStyle(
  81. fontSize: 20,
  82. fontWeight: FontWeight.bold,
  83. color: Theme.of(context).colorScheme.error,
  84. ),
  85. );
  86. return Center(
  87. child: Form(
  88. key: _formKey,
  89. child: Center(
  90. child: Padding(
  91. padding: const EdgeInsets.only(
  92. left: 20,
  93. right: 20,
  94. top: 0,
  95. bottom: 80,
  96. ),
  97. child: Column(
  98. mainAxisAlignment: MainAxisAlignment.center,
  99. crossAxisAlignment: CrossAxisAlignment.center,
  100. children: [
  101. Text(
  102. 'Login',
  103. style: TextStyle(
  104. fontSize: 35,
  105. color: Theme.of(context).colorScheme.onBackground,
  106. ),
  107. ),
  108. const SizedBox(height: 30),
  109. TextFormField(
  110. controller: _usernameController,
  111. decoration: InputDecoration(
  112. hintText: 'Username',
  113. enabledBorder: inputBorderStyle,
  114. focusedBorder: inputBorderStyle,
  115. ),
  116. style: inputTextStyle,
  117. // The validator receives the text that the user has entered.
  118. validator: (value) {
  119. if (value == null || value.isEmpty) {
  120. return 'Enter a username';
  121. }
  122. return null;
  123. },
  124. ),
  125. const SizedBox(height: 10),
  126. TextFormField(
  127. controller: _passwordController,
  128. obscureText: true,
  129. enableSuggestions: false,
  130. autocorrect: false,
  131. decoration: InputDecoration(
  132. hintText: 'Password',
  133. enabledBorder: inputBorderStyle,
  134. focusedBorder: inputBorderStyle,
  135. ),
  136. style: inputTextStyle,
  137. // The validator receives the text that the user has entered.
  138. validator: (value) {
  139. if (value == null || value.isEmpty) {
  140. return 'Enter a password';
  141. }
  142. return null;
  143. },
  144. ),
  145. const SizedBox(height: 15),
  146. ElevatedButton(
  147. style: buttonStyle,
  148. onPressed: () {
  149. if (_formKey.currentState!.validate()) {
  150. ScaffoldMessenger.of(context).showSnackBar(
  151. const SnackBar(content: Text('Processing Data')),
  152. );
  153. login()
  154. .then((val) {
  155. Navigator.
  156. pushNamedAndRemoveUntil(
  157. context,
  158. '/home',
  159. ModalRoute.withName('/home'),
  160. );
  161. }).catchError((error) {
  162. showMessage(
  163. 'Could not login to Envelope, please try again later.',
  164. context,
  165. );
  166. });
  167. }
  168. },
  169. child: const Text('Submit'),
  170. ),
  171. ],
  172. )
  173. )
  174. )
  175. )
  176. );
  177. }
  178. Future<dynamic> login() async {
  179. final resp = await http.post(
  180. await MyProfile.getServerUrl('api/v1/login'),
  181. headers: <String, String>{
  182. 'Content-Type': 'application/json; charset=UTF-8',
  183. },
  184. body: jsonEncode(<String, String>{
  185. 'username': _usernameController.text,
  186. 'password': _passwordController.text,
  187. }),
  188. );
  189. if (resp.statusCode != 200) {
  190. throw Exception(resp.body);
  191. }
  192. String? rawCookie = resp.headers['set-cookie'];
  193. if (rawCookie != null) {
  194. int index = rawCookie.indexOf(';');
  195. setSessionCookie((index == -1) ? rawCookie : rawCookie.substring(0, index));
  196. }
  197. return await MyProfile.login(
  198. json.decode(resp.body),
  199. _passwordController.text,
  200. );
  201. }
  202. }