From 0dbb8f132a756d0d0fe9d71a535f39671709ce92 Mon Sep 17 00:00:00 2001
From: LittleSheep <littlesheep.code@hotmail.com>
Date: Tue, 28 Jan 2025 19:55:35 +0800
Subject: [PATCH] :sparkles: Factor settings with TOTP, In app notify
 authenticate method

---
 assets/translations/en-US.json           |   7 +-
 assets/translations/zh-CN.json           |   7 +-
 ios/Podfile.lock                         |  12 +--
 lib/screens/account/factor_settings.dart | 111 +++++++++++++++++++++--
 lib/screens/auth/login.dart              |  12 +--
 lib/widgets/universal_image.dart         |   3 +-
 macos/Podfile.lock                       |  12 +--
 pubspec.lock                             |  76 ++++++++--------
 8 files changed, 169 insertions(+), 71 deletions(-)

diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json
index 1abd005..50dfecc 100644
--- a/assets/translations/en-US.json
+++ b/assets/translations/en-US.json
@@ -106,6 +106,8 @@
   },
   "loginEnterPassword": "Enter the code",
   "loginSuccess": "Logged in as {}",
+  "authFactorDelete": "Delete Auth Factor",
+  "authFactorDeleteDescription": "Are you sure you want delete auth factor {}?",
   "authFactorPassword": "Password",
   "authFactorPasswordDescription": "The password you set when you registered.",
   "authFactorEmail": "Email verification code",
@@ -579,5 +581,8 @@
   "newsReadingFromReader": "You're reading from HyperNet.Reader",
   "newsReadingFromOriginal": "You're reading the original article",
   "newsDisclaimer": "This article is fetched from the Internet, we do not guarantee its authenticity, please judge for yourself. All content in this article belongs to the original author.",
-  "newsToday": "Today's News"
+  "newsToday": "Today's News",
+  "totpPostSetup": "One More Thing",
+  "totpPostSetupDescription": "Scan the QR Code below with Google Authenticator, Microsoft Authenticator, 1Password, Authy, Bitwarden or any of kind of authenticator app which supports TOTP.",
+  "totpNeverShare": "Never share this QR Code"
 }
diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json
index 73e0e0a..23a798f 100644
--- a/assets/translations/zh-CN.json
+++ b/assets/translations/zh-CN.json
@@ -89,6 +89,8 @@
   },
   "loginEnterPassword": "验证代码",
   "loginSuccess": "登录为 {}",
+  "authFactorDelete": "删除验证因子",
+  "authFactorDeleteDescription": "你确定要删除 {} 验证因子吗?",
   "authFactorPassword": "密码",
   "authFactorPasswordDescription": "注册时选择设置的密码。",
   "authFactorEmail": "电邮一次性验证码",
@@ -576,5 +578,8 @@
   "newsReadingFromReader": "你正在从 HyperNet.Reader 阅读文章",
   "newsReadingFromOriginal": "你正在阅读原始文章",
   "newsDisclaimer": "本文由 HyperNet.Reader 从互联网上获取,我们不担保其内容的真实性,请自行判断。本文章的所有内容版权归原作者所有。",
-  "newsToday": "快讯"
+  "newsToday": "快讯",
+  "totpPostSetup": "还有一件事",
+  "totpPostSetupDescription": "使用 Google Authenticator, Microsoft Authenticator, 1Password, Authy, Bitwarden 或其他支持 TOTP 的验证器扫描本 QR Code 来添加。",
+  "totpNeverShare": "永远不要分享这个 QR Code"
 }
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index f3575db..804814e 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -53,14 +53,14 @@ PODS:
   - Firebase/Messaging (11.6.0):
     - Firebase/CoreOnly
     - FirebaseMessaging (~> 11.6.0)
-  - firebase_analytics (11.4.0):
+  - firebase_analytics (11.4.1):
     - Firebase/Analytics (= 11.6.0)
     - firebase_core
     - Flutter
-  - firebase_core (3.10.0):
+  - firebase_core (3.10.1):
     - Firebase/CoreOnly (= 11.6.0)
     - Flutter
-  - firebase_messaging (15.2.0):
+  - firebase_messaging (15.2.1):
     - Firebase/Messaging (= 11.6.0)
     - firebase_core
     - Flutter
@@ -382,9 +382,9 @@ SPEC CHECKSUMS:
   file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
   file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
   Firebase: 374a441a91ead896215703a674d58cdb3e9d772b
-  firebase_analytics: 07bd7cfbac54bfcdccf2bb2530f9a65486f7ef3f
-  firebase_core: feb37e79f775c2bd08dd35e02d83678291317e10
-  firebase_messaging: e2f0ba891b1509668c07f5099761518a5af8fe3c
+  firebase_analytics: 13ea4ad8a42c5060bad7e6694304dabb8b02fe7e
+  firebase_core: e2aa06dbd854d961f8ce46c2e20933bee1bf2d2b
+  firebase_messaging: 96cf6d67121b3f39746b2a4f29a26c0eee4af70e
   FirebaseAnalytics: 7114c698cac995602e3b1b96663473e50d54d6e7
   FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa
   FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
diff --git a/lib/screens/account/factor_settings.dart b/lib/screens/account/factor_settings.dart
index 45385ab..d89e31f 100644
--- a/lib/screens/account/factor_settings.dart
+++ b/lib/screens/account/factor_settings.dart
@@ -1,8 +1,10 @@
 import 'package:dropdown_button2/dropdown_button2.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
+import 'package:gap/gap.dart';
 import 'package:material_symbols_icons/symbols.dart';
 import 'package:provider/provider.dart';
+import 'package:qr_flutter/qr_flutter.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:surface/providers/sn_network.dart';
 import 'package:surface/types/auth.dart';
@@ -10,7 +12,7 @@ import 'package:surface/widgets/dialog.dart';
 import 'package:surface/widgets/loading_indicator.dart';
 import 'package:surface/widgets/navigation/app_scaffold.dart';
 
-final Map<int, (String, String, IconData)> _kFactorTypes = {
+final Map<int, (String, String, IconData)> kFactorTypes = {
   0: ('authFactorPassword', 'authFactorPasswordDescription', Symbols.password),
   1: ('authFactorEmail', 'authFactorEmailDescription', Symbols.email),
   2: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer),
@@ -25,10 +27,12 @@ class FactorSettingsScreen extends StatefulWidget {
 }
 
 class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
+  bool _isBusy = false;
   List<SnAuthFactor>? _factors;
 
   Future<void> _fetchFactors() async {
     try {
+      setState(() => _isBusy = true);
       final sn = context.read<SnNetworkProvider>();
       final resp = await sn.client.get('/cgi/id/users/me/factors');
       _factors = List<SnAuthFactor>.from(
@@ -38,7 +42,7 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
       if (!mounted) return;
       context.showErrorDialog(err);
     } finally {
-      setState(() {});
+      setState(() => _isBusy = false);
     }
   }
 
@@ -59,7 +63,7 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
           LoadingIndicator(
-            isActive: _factors == null,
+            isActive: _isBusy,
           ),
           ListTile(
             title: Text('authFactorAdd').tr(),
@@ -90,10 +94,34 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
                   itemBuilder: (context, idx) {
                     final ele = _factors![idx];
                     return ListTile(
-                      title: Text(_kFactorTypes[ele.type]!.$1).tr(),
-                      subtitle: Text(_kFactorTypes[ele.type]!.$2).tr(),
-                      contentPadding: const EdgeInsets.symmetric(horizontal: 24),
-                      leading: Icon(_kFactorTypes[ele.type]!.$3),
+                      title: Text(kFactorTypes[ele.type]!.$1).tr(),
+                      subtitle: Text(kFactorTypes[ele.type]!.$2).tr(),
+                      contentPadding: const EdgeInsets.only(left: 24, right: 12),
+                      leading: Icon(kFactorTypes[ele.type]!.$3),
+                      trailing: IconButton(
+                        icon: const Icon(Symbols.close),
+                        onPressed: ele.type > 0
+                            ? () {
+                                context
+                                    .showConfirmDialog(
+                                  'authFactorDelete'.tr(),
+                                  'authFactorDeleteDescription'.tr(args: [kFactorTypes[ele.type]!.$1.tr()]),
+                                )
+                                    .then((val) async {
+                                  if (!val) return;
+                                  try {
+                                    if (!context.mounted) return;
+                                    final sn = context.read<SnNetworkProvider>();
+                                    await sn.client.delete('/cgi/id/users/me/factors/${ele.id}');
+                                    _fetchFactors();
+                                  } catch (err) {
+                                    if (!context.mounted) return;
+                                    context.showErrorDialog(err);
+                                  }
+                                });
+                              }
+                            : null,
+                      ),
                     );
                   },
                 ),
@@ -126,7 +154,14 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
       final resp = await sn.client.post('/cgi/id/users/me/factors', data: {
         'type': _factorType,
       });
-      // TODO show qrcode when creating totp factor
+      final factor = SnAuthFactor.fromJson(resp.data);
+      if (!mounted) return;
+      if (factor.type == 2) {
+        await showModalBottomSheet(
+          context: context,
+          builder: (context) => _FactorTotpFactorDialog(factor: factor),
+        );
+      }
       if (!mounted) return;
       Navigator.of(context).pop(true);
     } catch (err) {
@@ -154,7 +189,7 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
                 overflow: TextOverflow.ellipsis,
               ),
               value: _factorType,
-              items: _kFactorTypes.entries.map(
+              items: kFactorTypes.entries.map(
                 (ele) {
                   final contains = widget.currentlyHave.map((ele) => ele.type).contains(ele.key);
                   return DropdownMenuItem<int>(
@@ -199,3 +234,61 @@ class _FactorNewDialogState extends State<_FactorNewDialog> {
     );
   }
 }
+
+class _FactorTotpFactorDialog extends StatelessWidget {
+  final SnAuthFactor factor;
+
+  const _FactorTotpFactorDialog({super.key, required this.factor});
+
+  @override
+  Widget build(BuildContext context) {
+    return Center(
+      child: Column(
+        mainAxisSize: MainAxisSize.min,
+        crossAxisAlignment: CrossAxisAlignment.center,
+        children: [
+          Center(
+            child: Text(
+              'totpPostSetup',
+              textAlign: TextAlign.center,
+              style: Theme.of(context).textTheme.titleLarge,
+            ).tr().width(280),
+          ),
+          const Gap(4),
+          Center(
+            child: Text(
+              'totpPostSetupDescription',
+              textAlign: TextAlign.center,
+              style: Theme.of(context).textTheme.bodySmall,
+            ).tr().width(280),
+          ),
+          const Gap(16),
+          QrImageView(
+            padding: EdgeInsets.zero,
+            data: factor.config!['url'],
+            errorCorrectionLevel: QrErrorCorrectLevel.H,
+            version: QrVersions.auto,
+            size: 160,
+            gapless: true,
+            eyeStyle: QrEyeStyle(
+              eyeShape: QrEyeShape.circle,
+              color: Theme.of(context).colorScheme.onSurface,
+            ),
+            dataModuleStyle: QrDataModuleStyle(
+              dataModuleShape: QrDataModuleShape.square,
+              color: Theme.of(context).colorScheme.onSurface,
+            ),
+          ),
+          const Gap(16),
+          Center(
+            child: Text(
+              'totpNeverShare',
+              textAlign: TextAlign.center,
+              style: Theme.of(context).textTheme.bodyMedium,
+            ).tr().bold().width(280),
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/screens/auth/login.dart b/lib/screens/auth/login.dart
index 8ef3048..f805b83 100644
--- a/lib/screens/auth/login.dart
+++ b/lib/screens/auth/login.dart
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:surface/providers/sn_network.dart';
 import 'package:surface/providers/userinfo.dart';
+import 'package:surface/screens/account/factor_settings.dart';
 import 'package:surface/types/auth.dart';
 import 'package:surface/widgets/dialog.dart';
 import 'package:surface/widgets/navigation/app_scaffold.dart';
@@ -14,11 +15,6 @@ import 'package:url_launcher/url_launcher_string.dart';
 
 import '../../providers/websocket.dart';
 
-final Map<int, (String label, IconData icon, bool isOtp)> _factorLabelMap = {
-  0: ('authFactorPassword'.tr(), Symbols.password, false),
-  1: ('authFactorEmail'.tr(), Symbols.email, true),
-};
-
 class LoginScreen extends StatefulWidget {
   const LoginScreen({super.key});
 
@@ -212,7 +208,7 @@ class _LoginCheckScreenState extends State<_LoginCheckScreen> {
           controller: _passwordController,
           obscureText: true,
           autofillHints: [
-            (_factorLabelMap[widget.factor!.type]?.$3 ?? true) ? AutofillHints.password : AutofillHints.oneTimeCode
+            widget.factor!.type == 0 ? AutofillHints.password : AutofillHints.oneTimeCode
           ],
           decoration: InputDecoration(
             isDense: true,
@@ -328,10 +324,10 @@ class _LoginPickerScreenState extends State<_LoginPickerScreen> {
                           ),
                         ),
                         secondary: Icon(
-                          _factorLabelMap[x.type]?.$2 ?? Symbols.question_mark,
+                          kFactorTypes[x.type]?.$3 ?? Symbols.question_mark,
                         ),
                         title: Text(
-                          _factorLabelMap[x.type]?.$1 ?? 'unknown'.tr(),
+                          kFactorTypes[x.type]?.$1 ?? 'unknown'.tr(),
                         ),
                         enabled: !widget.ticket!.factorTrail.contains(x.id),
                         value: _factorPicked == x.id,
diff --git a/lib/widgets/universal_image.dart b/lib/widgets/universal_image.dart
index 54b2aaa..0adab33 100644
--- a/lib/widgets/universal_image.dart
+++ b/lib/widgets/universal_image.dart
@@ -5,10 +5,9 @@ import 'package:material_symbols_icons/symbols.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'package:flutter_animate/flutter_animate.dart';
-
+import 'package:surface/providers/config.dart';
 // Keep this import to make the web image render work
 import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
-import 'package:surface/providers/config.dart';
 
 class UniversalImage extends StatelessWidget {
   final String url;
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index 7652a30..979aec5 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -22,14 +22,14 @@ PODS:
   - Firebase/Messaging (11.6.0):
     - Firebase/CoreOnly
     - FirebaseMessaging (~> 11.6.0)
-  - firebase_analytics (11.4.0):
+  - firebase_analytics (11.4.1):
     - Firebase/Analytics (= 11.6.0)
     - firebase_core
     - FlutterMacOS
-  - firebase_core (3.10.0):
+  - firebase_core (3.10.1):
     - Firebase/CoreOnly (~> 11.6.0)
     - FlutterMacOS
-  - firebase_messaging (15.2.0):
+  - firebase_messaging (15.2.1):
     - Firebase/CoreOnly (~> 11.6.0)
     - Firebase/Messaging (~> 11.6.0)
     - firebase_core
@@ -296,9 +296,9 @@ SPEC CHECKSUMS:
   file_saver: 44e6fbf666677faf097302460e214e977fdd977b
   file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
   Firebase: 374a441a91ead896215703a674d58cdb3e9d772b
-  firebase_analytics: 5249f87da6fed852901581aab2602e0280ec2fdb
-  firebase_core: 6d9bb8b0ea817e8fe0d928177d42275b45fdba6f
-  firebase_messaging: ae8e88b586e4d50abc7cac5bacf74d21967fd226
+  firebase_analytics: 91efc58e8e37964469fdd59ad11ba36bc97e75d6
+  firebase_core: 75e003524565fb5bd80c9960bc5892e8475821cd
+  firebase_messaging: 082a385eb98b5bb843a566cb30404859c4bd6e25
   FirebaseAnalytics: 7114c698cac995602e3b1b96663473e50d54d6e7
   FirebaseCore: 48b0dd707581cf9c1a1220da68223fb0a562afaa
   FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
diff --git a/pubspec.lock b/pubspec.lock
index e69ee3c..3eaac7b 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -13,10 +13,10 @@ packages:
     dependency: transitive
     description:
       name: _flutterfire_internals
-      sha256: "27899c95f9e7ec06c8310e6e0eac967707714b9f1450c4a58fa00ca011a4a8ae"
+      sha256: e4f2a7ef31b0ab2c89d2bde35ef3e6e6aff1dce5e66069c6540b0e9cfe33ee6b
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.49"
+    version: "1.3.50"
   _macros:
     dependency: transitive
     description: dart
@@ -338,10 +338,10 @@ packages:
     dependency: transitive
     description:
       name: dart_style
-      sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
+      sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.7"
+    version: "2.3.8"
   dart_webrtc:
     dependency: "direct main"
     description:
@@ -418,10 +418,10 @@ packages:
     dependency: "direct main"
     description:
       name: easy_localization
-      sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
+      sha256: "0f5239c7b8ab06c66440cfb0e9aa4b4640429c6668d5a42fe389c5de42220b12"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.7"
+    version: "3.0.7+1"
   easy_localization_loader:
     dependency: "direct main"
     description:
@@ -538,34 +538,34 @@ packages:
     dependency: "direct main"
     description:
       name: firebase_analytics
-      sha256: "498c6cb8468e348a556709c745d92a52173ab3a9b906aa0593393f0787f201ea"
+      sha256: eac382bbcd5ae78c1d1ce5619d13f5a7424429f4bf55df9e3ad5110da34d1060
       url: "https://pub.dev"
     source: hosted
-    version: "11.4.0"
+    version: "11.4.1"
   firebase_analytics_platform_interface:
     dependency: transitive
     description:
       name: firebase_analytics_platform_interface
-      sha256: ccbb350554e98afdb4b59852689292d194d31232a2647b5012a66622b3711df9
+      sha256: a34db46c367265c4c961626e4b128bfb7b7e50958e7add4c27ba103f5f81b9b0
       url: "https://pub.dev"
     source: hosted
-    version: "4.3.0"
+    version: "4.3.1"
   firebase_analytics_web:
     dependency: transitive
     description:
       name: firebase_analytics_web
-      sha256: "68e1f18fc16482c211c658e739c25f015b202a260d9ad8249c6d3d7963b8105f"
+      sha256: b6b4cef08e45e4c7d48476d9fc49fe9577081809a59026fe95b1a1b1eea165fa
       url: "https://pub.dev"
     source: hosted
-    version: "0.5.10+6"
+    version: "0.5.10+7"
   firebase_core:
     dependency: "direct main"
     description:
       name: firebase_core
-      sha256: "0307c1fde82e2b8b97e0be2dab93612aff9a72f31ebe9bfac66ed8b37ef7c568"
+      sha256: d851c1ca98fd5a4c07c747f8c65dacc2edd84a4d9ac055d32a5f0342529069f5
       url: "https://pub.dev"
     source: hosted
-    version: "3.10.0"
+    version: "3.10.1"
   firebase_core_platform_interface:
     dependency: transitive
     description:
@@ -586,26 +586,26 @@ packages:
     dependency: "direct main"
     description:
       name: firebase_messaging
-      sha256: "48a8a59197c1c5174060ba9aa1e0036e9b5a0d28a0cc22d19c1fcabc67fafe3c"
+      sha256: e20ea2a0ecf9b0971575ab3ab42a6e285a94e50092c555b090c1a588a81b4d54
       url: "https://pub.dev"
     source: hosted
-    version: "15.2.0"
+    version: "15.2.1"
   firebase_messaging_platform_interface:
     dependency: transitive
     description:
       name: firebase_messaging_platform_interface
-      sha256: "9770a8e91f54296829dcaa61ce9b7c2f9ae9abbf99976dd3103a60470d5264dd"
+      sha256: c57a92b5ae1857ef4fe4ae2e73452b44d32e984e15ab8b53415ea1bb514bdabd
       url: "https://pub.dev"
     source: hosted
-    version: "4.6.0"
+    version: "4.6.1"
   firebase_messaging_web:
     dependency: transitive
     description:
       name: firebase_messaging_web
-      sha256: "329ca4ef45ec616abe6f1d5e58feed0934a50840a65aa327052354ad3c64ed77"
+      sha256: "83694a990d8525d6b01039240b97757298369622ca0253ad0ebcfed221bf8ee0"
       url: "https://pub.dev"
     source: hosted
-    version: "3.10.0"
+    version: "3.10.1"
   fixnum:
     dependency: transitive
     description:
@@ -830,10 +830,10 @@ packages:
     dependency: "direct main"
     description:
       name: flutter_webrtc
-      sha256: "188401cc3275bc4f1f965babdff6cac612a4b46572f1e49f49db8af5361d5712"
+      sha256: "4c76069f724f79dc6e8739c8f16c2ee00ca30a1f8e3aa75c0e830f8183278f03"
       url: "https://pub.dev"
     source: hosted
-    version: "0.12.6"
+    version: "0.12.7"
   freezed:
     dependency: "direct dev"
     description:
@@ -878,18 +878,18 @@ packages:
     dependency: transitive
     description:
       name: glob
-      sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
+      sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.3"
   go_router:
     dependency: "direct main"
     description:
       name: go_router
-      sha256: "7c2d40b59890a929824f30d442e810116caf5088482629c894b9e4478c67472d"
+      sha256: daf3ff5570f55396b2d2c9bf8136d7db3a8acf208ac0cef92a3ae2beb9a81550
       url: "https://pub.dev"
     source: hosted
-    version: "14.6.3"
+    version: "14.7.1"
   google_fonts:
     dependency: "direct main"
     description:
@@ -950,10 +950,10 @@ packages:
     dependency: transitive
     description:
       name: http
-      sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
+      sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
       url: "https://pub.dev"
     source: hosted
-    version: "1.2.2"
+    version: "1.3.0"
   http_multi_server:
     dependency: transitive
     description:
@@ -1030,10 +1030,10 @@ packages:
     dependency: transitive
     description:
       name: image_picker_macos
-      sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
+      sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
       url: "https://pub.dev"
     source: hosted
-    version: "0.2.1+1"
+    version: "0.2.1+2"
   image_picker_platform_interface:
     dependency: transitive
     description:
@@ -1710,18 +1710,18 @@ packages:
     dependency: "direct main"
     description:
       name: shared_preferences
-      sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
+      sha256: c59819dacc6669a1165d54d2735a9543f136f9b3cec94ca65cea6ab8dffc422e
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.5"
+    version: "2.4.0"
   shared_preferences_android:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: "138b7bbbc7f59c56236e426c37afb8f78cbc57b094ac64c440e0bb90e380a4f5"
+      sha256: "650584dcc0a39856f369782874e562efd002a9c94aec032412c9eb81419cce1f"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.2"
+    version: "2.4.4"
   shared_preferences_foundation:
     dependency: transitive
     description:
@@ -2171,10 +2171,10 @@ packages:
     dependency: "direct main"
     description:
       name: web_socket_channel
-      sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
+      sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.1"
+    version: "3.0.2"
   webrtc_interface:
     dependency: transitive
     description:
@@ -2187,10 +2187,10 @@ packages:
     dependency: transitive
     description:
       name: win32
-      sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29"
+      sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
       url: "https://pub.dev"
     source: hosted
-    version: "5.10.0"
+    version: "5.10.1"
   win32_registry:
     dependency: transitive
     description: