security_section_widget.dart 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:photos/core/configuration.dart';
  4. import 'package:photos/core/event_bus.dart';
  5. import 'package:photos/ente_theme_data.dart';
  6. import 'package:photos/events/two_factor_status_change_event.dart';
  7. import 'package:photos/services/local_authentication_service.dart';
  8. import 'package:photos/services/user_service.dart';
  9. import 'package:photos/theme/ente_theme.dart';
  10. import 'package:photos/ui/account/sessions_page.dart';
  11. import 'package:photos/ui/components/captioned_text_widget.dart';
  12. import 'package:photos/ui/components/expandable_menu_item_widget.dart';
  13. import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart';
  14. import 'package:photos/ui/components/toggle_switch_widget.dart';
  15. import 'package:photos/ui/settings/common_settings.dart';
  16. class SecuritySectionWidget extends StatefulWidget {
  17. const SecuritySectionWidget({Key? key}) : super(key: key);
  18. @override
  19. State<SecuritySectionWidget> createState() => _SecuritySectionWidgetState();
  20. }
  21. class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
  22. final _config = Configuration.instance;
  23. late StreamSubscription<TwoFactorStatusChangeEvent>
  24. _twoFactorStatusChangeEvent;
  25. @override
  26. void initState() {
  27. super.initState();
  28. _twoFactorStatusChangeEvent =
  29. Bus.instance.on<TwoFactorStatusChangeEvent>().listen((event) async {
  30. if (mounted) {
  31. setState(() {});
  32. }
  33. });
  34. }
  35. @override
  36. void dispose() {
  37. _twoFactorStatusChangeEvent.cancel();
  38. super.dispose();
  39. }
  40. @override
  41. Widget build(BuildContext context) {
  42. return ExpandableMenuItemWidget(
  43. title: "Security",
  44. selectionOptionsWidget: _getSectionOptions(context),
  45. leadingIcon: Icons.local_police_outlined,
  46. );
  47. }
  48. Widget _getSectionOptions(BuildContext context) {
  49. final Completer completer = Completer();
  50. final List<Widget> children = [];
  51. if (_config.hasConfiguredAccount()) {
  52. children.addAll(
  53. [
  54. sectionOptionSpacing,
  55. MenuItemWidget(
  56. captionedTextWidget: const CaptionedTextWidget(
  57. title: "Two-factor",
  58. ),
  59. trailingWidget: ToggleSwitchWidget(
  60. value: () => UserService.instance.hasEnabledTwoFactor(),
  61. onChanged: () async {
  62. final hasAuthenticated = await LocalAuthenticationService
  63. .instance
  64. .requestLocalAuthentication(
  65. context,
  66. "Please authenticate to configure two-factor authentication",
  67. );
  68. final isTwoFactorEnabled =
  69. UserService.instance.hasEnabledTwoFactor();
  70. if (hasAuthenticated) {
  71. if (isTwoFactorEnabled) {
  72. await _disableTwoFactor();
  73. completer.isCompleted ? null : completer.complete();
  74. } else {
  75. await UserService.instance
  76. .setupTwoFactor(context, completer);
  77. }
  78. return completer.future;
  79. }
  80. },
  81. ),
  82. ),
  83. sectionOptionSpacing,
  84. ],
  85. );
  86. }
  87. children.addAll([
  88. MenuItemWidget(
  89. captionedTextWidget: const CaptionedTextWidget(
  90. title: "Lockscreen",
  91. ),
  92. trailingWidget: ToggleSwitchWidget(
  93. value: () => _config.shouldShowLockScreen(),
  94. onChanged: () async {
  95. await LocalAuthenticationService.instance
  96. .requestLocalAuthForLockScreen(
  97. context,
  98. !_config.shouldShowLockScreen(),
  99. "Please authenticate to change lockscreen setting",
  100. "To enable lockscreen, please setup device passcode or screen lock in your system settings.",
  101. );
  102. },
  103. ),
  104. ),
  105. sectionOptionSpacing,
  106. MenuItemWidget(
  107. captionedTextWidget: const CaptionedTextWidget(
  108. title: "View active sessions",
  109. ),
  110. pressedColor: getEnteColorScheme(context).fillFaint,
  111. trailingIcon: Icons.chevron_right_outlined,
  112. trailingIconIsMuted: true,
  113. showOnlyLoadingState: true,
  114. onTap: () async {
  115. final hasAuthenticated = await LocalAuthenticationService.instance
  116. .requestLocalAuthentication(
  117. context,
  118. "Please authenticate to view your active sessions",
  119. );
  120. if (hasAuthenticated) {
  121. unawaited(
  122. Navigator.of(context).push(
  123. MaterialPageRoute(
  124. builder: (BuildContext context) {
  125. return const SessionsPage();
  126. },
  127. ),
  128. ),
  129. );
  130. }
  131. },
  132. ),
  133. sectionOptionSpacing,
  134. ]);
  135. return Column(
  136. children: children,
  137. );
  138. }
  139. Future<void> _disableTwoFactor() async {
  140. final AlertDialog alert = AlertDialog(
  141. title: const Text("Disable two-factor"),
  142. content: const Text(
  143. "Are you sure you want to disable two-factor authentication?",
  144. ),
  145. actions: [
  146. TextButton(
  147. child: Text(
  148. "No",
  149. style: TextStyle(
  150. color: Theme.of(context).colorScheme.greenAlternative,
  151. ),
  152. ),
  153. onPressed: () {
  154. Navigator.of(context, rootNavigator: true).pop('dialog');
  155. },
  156. ),
  157. TextButton(
  158. child: const Text(
  159. "Yes",
  160. style: TextStyle(
  161. color: Colors.red,
  162. ),
  163. ),
  164. onPressed: () async {
  165. await UserService.instance.disableTwoFactor(context);
  166. Navigator.of(context, rootNavigator: true).pop('dialog');
  167. },
  168. ),
  169. ],
  170. );
  171. await showDialog(
  172. context: context,
  173. builder: (BuildContext context) {
  174. return alert;
  175. },
  176. );
  177. }
  178. }