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.

245 lines
7.1 KiB

  1. import 'dart:convert';
  2. import 'package:Envelope/utils/encryption/crypto_utils.dart';
  3. import 'package:Envelope/views/main/profile/change_password.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_dotenv/flutter_dotenv.dart';
  6. import 'package:qr_flutter/qr_flutter.dart';
  7. import 'package:sliding_up_panel/sliding_up_panel.dart';
  8. import '/components/custom_circle_avatar.dart';
  9. import '/components/custom_title_bar.dart';
  10. import '/models/my_profile.dart';
  11. import '/utils/storage/database.dart';
  12. class Profile extends StatefulWidget {
  13. final MyProfile profile;
  14. const Profile({
  15. Key? key,
  16. required this.profile,
  17. }) : super(key: key);
  18. @override
  19. State<Profile> createState() => _ProfileState();
  20. }
  21. class _ProfileState extends State<Profile> {
  22. final PanelController _panelController = PanelController();
  23. @override
  24. Widget build(BuildContext context) {
  25. return Scaffold(
  26. appBar: const CustomTitleBar(
  27. title: Text(
  28. 'Profile',
  29. style: TextStyle(
  30. fontSize: 32,
  31. fontWeight: FontWeight.bold
  32. )
  33. ),
  34. showBack: false,
  35. backgroundColor: Colors.transparent,
  36. ),
  37. body: SlidingUpPanel(
  38. controller: _panelController,
  39. slideDirection: SlideDirection.DOWN,
  40. defaultPanelState: PanelState.CLOSED,
  41. color: Theme.of(context).scaffoldBackgroundColor,
  42. backdropTapClosesPanel: true,
  43. backdropEnabled: true,
  44. backdropOpacity: 0.2,
  45. minHeight: 0,
  46. maxHeight: 450,
  47. panel: Center(
  48. child: _profileQrCode(),
  49. ),
  50. body: Padding(
  51. padding: const EdgeInsets.only(top: 16,left: 16,right: 16),
  52. child: Column(
  53. children: <Widget>[
  54. usernameHeading(),
  55. const SizedBox(height: 30),
  56. settings(),
  57. const SizedBox(height: 30),
  58. logout(),
  59. ],
  60. )
  61. ),
  62. ),
  63. );
  64. }
  65. Widget usernameHeading() {
  66. return Row(
  67. children: <Widget> [
  68. const CustomCircleAvatar(
  69. icon: Icon(Icons.person, size: 40),
  70. imagePath: null, // TODO: Add image here
  71. radius: 30,
  72. ),
  73. const SizedBox(width: 20),
  74. Expanded(
  75. flex: 1,
  76. child: Text(
  77. widget.profile.username,
  78. style: const TextStyle(
  79. fontSize: 25,
  80. fontWeight: FontWeight.w500,
  81. ),
  82. ),
  83. ),
  84. IconButton(
  85. onPressed: () => _panelController.open(),
  86. icon: const Icon(Icons.qr_code_2),
  87. ),
  88. ],
  89. );
  90. }
  91. Widget logout() {
  92. bool isTesting = dotenv.env['ENVIRONMENT'] == 'development';
  93. return Align(
  94. alignment: Alignment.centerLeft,
  95. child: Column(
  96. crossAxisAlignment: CrossAxisAlignment.stretch,
  97. children: [
  98. TextButton.icon(
  99. label: const Text(
  100. 'Logout',
  101. style: TextStyle(fontSize: 16)
  102. ),
  103. icon: const Icon(Icons.exit_to_app),
  104. style: const ButtonStyle(
  105. alignment: Alignment.centerLeft,
  106. ),
  107. onPressed: () {
  108. deleteDb();
  109. MyProfile.logout();
  110. Navigator.pushNamedAndRemoveUntil(context, '/landing', ModalRoute.withName('/landing'));
  111. }
  112. ),
  113. isTesting ? TextButton.icon(
  114. label: const Text(
  115. 'Delete Database',
  116. style: TextStyle(fontSize: 16)
  117. ),
  118. icon: const Icon(Icons.delete_forever),
  119. style: const ButtonStyle(
  120. alignment: Alignment.centerLeft,
  121. ),
  122. onPressed: () {
  123. deleteDb();
  124. }
  125. ) : const SizedBox.shrink(),
  126. ],
  127. ),
  128. );
  129. }
  130. Widget settings() {
  131. return Align(
  132. alignment: Alignment.centerLeft,
  133. child: Column(
  134. crossAxisAlignment: CrossAxisAlignment.stretch,
  135. children: [
  136. const SizedBox(height: 5),
  137. TextButton.icon(
  138. label: const Text(
  139. 'Disappearing Messages',
  140. style: TextStyle(fontSize: 16)
  141. ),
  142. icon: const Icon(Icons.timer),
  143. style: ButtonStyle(
  144. alignment: Alignment.centerLeft,
  145. foregroundColor: MaterialStateProperty.resolveWith<Color>(
  146. (Set<MaterialState> states) {
  147. return Theme.of(context).colorScheme.onBackground;
  148. },
  149. )
  150. ),
  151. onPressed: () {
  152. print('Disappearing Messages');
  153. }
  154. ),
  155. const SizedBox(height: 5),
  156. TextButton.icon(
  157. label: const Text(
  158. 'Server URL',
  159. style: TextStyle(fontSize: 16)
  160. ),
  161. icon: const Icon(Icons.dataset_linked_outlined),
  162. style: ButtonStyle(
  163. alignment: Alignment.centerLeft,
  164. foregroundColor: MaterialStateProperty.resolveWith<Color>(
  165. (Set<MaterialState> states) {
  166. return Theme.of(context).colorScheme.onBackground;
  167. },
  168. )
  169. ),
  170. onPressed: () {
  171. print('Server URL');
  172. }
  173. ),
  174. const SizedBox(height: 5),
  175. TextButton.icon(
  176. label: const Text(
  177. 'Change Password',
  178. style: TextStyle(fontSize: 16)
  179. ),
  180. icon: const Icon(Icons.password),
  181. style: ButtonStyle(
  182. alignment: Alignment.centerLeft,
  183. foregroundColor: MaterialStateProperty.resolveWith<Color>(
  184. (Set<MaterialState> states) {
  185. return Theme.of(context).colorScheme.onBackground;
  186. },
  187. )
  188. ),
  189. onPressed: () {
  190. Navigator.of(context).push(
  191. MaterialPageRoute(builder: (context) => ChangePassword(
  192. privateKey: widget.profile.privateKey!,
  193. ))
  194. );
  195. }
  196. ),
  197. ],
  198. ),
  199. );
  200. }
  201. Widget _profileQrCode() {
  202. String payload = jsonEncode({
  203. 'i': widget.profile.id,
  204. 'u': widget.profile.username,
  205. 'k': base64.encode(
  206. CryptoUtils.encodeRSAPublicKeyToPem(widget.profile.publicKey!).codeUnits
  207. ),
  208. });
  209. return Column(
  210. children: [
  211. Padding(
  212. padding: const EdgeInsets.all(20),
  213. child: QrImage(
  214. backgroundColor: Theme.of(context).colorScheme.primary,
  215. data: payload,
  216. version: QrVersions.auto,
  217. gapless: true,
  218. ),
  219. ),
  220. Align(
  221. alignment: Alignment.centerRight,
  222. child: Padding(
  223. padding: const EdgeInsets.only(right: 20),
  224. child: IconButton(
  225. onPressed: () => _panelController.close(),
  226. icon: const Icon(Icons.arrow_upward),
  227. ),
  228. ),
  229. ),
  230. ]
  231. );
  232. }
  233. }