From 6272ad7213c6d8722935e5bbcc6c543d179ec589 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sun, 26 Feb 2023 10:56:42 +0530 Subject: [PATCH 1/3] Replace InkWell with GestureDetector --- lib/ui/settings_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index 280d10d1d..5c9ad44da 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -52,7 +52,7 @@ class SettingsPage extends StatelessWidget { final enteTextTheme = getEnteTextTheme(context); final List contents = []; contents.add( - InkWell( + GestureDetector( onDoubleTap: () { routeToPage(context, VerifyIdentifyScreen(self: true)); }, From d752ddd9eda1400b020aff3b7d8f9f0c726373a2 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sun, 26 Feb 2023 11:28:30 +0530 Subject: [PATCH 2/3] Replace screen with dialog --- lib/ui/settings_page.dart | 15 +- lib/ui/sharing/add_partipant_page.dart | 12 +- lib/ui/sharing/verify_identify_screen.dart | 248 --------------------- lib/ui/sharing/verify_identity_dialog.dart | 209 +++++++++++++++++ 4 files changed, 228 insertions(+), 256 deletions(-) delete mode 100644 lib/ui/sharing/verify_identify_screen.dart create mode 100644 lib/ui/sharing/verify_identity_dialog.dart diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index 5c9ad44da..e535ae144 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -25,7 +25,7 @@ import 'package:photos/ui/settings/social_section_widget.dart'; import 'package:photos/ui/settings/storage_card_widget.dart'; import 'package:photos/ui/settings/support_section_widget.dart'; import 'package:photos/ui/settings/theme_switch_widget.dart'; -import "package:photos/ui/sharing/verify_identify_screen.dart"; +import "package:photos/ui/sharing/verify_identity_dialog.dart"; import "package:photos/utils/navigation_util.dart"; class SettingsPage extends StatelessWidget { @@ -54,10 +54,10 @@ class SettingsPage extends StatelessWidget { contents.add( GestureDetector( onDoubleTap: () { - routeToPage(context, VerifyIdentifyScreen(self: true)); + _showVerifyIdentityDialog(context); }, onLongPress: () { - routeToPage(context, VerifyIdentifyScreen(self: true)); + _showVerifyIdentityDialog(context); }, child: Container( constraints: const BoxConstraints(maxWidth: 350), @@ -165,4 +165,13 @@ class SettingsPage extends StatelessWidget { ), ); } + + Future _showVerifyIdentityDialog(BuildContext context) async { + await showDialog( + context: context, + builder: (BuildContext context) { + return VerifyIdentifyDialog(self: true); + }, + ); + } } diff --git a/lib/ui/sharing/add_partipant_page.dart b/lib/ui/sharing/add_partipant_page.dart index 9ce1d6ce4..d2de5d195 100644 --- a/lib/ui/sharing/add_partipant_page.dart +++ b/lib/ui/sharing/add_partipant_page.dart @@ -13,9 +13,8 @@ import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/ui/components/menu_section_title.dart'; import 'package:photos/ui/components/models/button_type.dart'; import 'package:photos/ui/sharing/user_avator_widget.dart'; -import "package:photos/ui/sharing/verify_identify_screen.dart"; +import "package:photos/ui/sharing/verify_identity_dialog.dart"; import "package:photos/utils/dialog_util.dart"; -import "package:photos/utils/navigation_util.dart"; class AddParticipantPage extends StatefulWidget { final Collection collection; @@ -212,9 +211,12 @@ class _AddParticipantPage extends State { } final emailToAdd = selectedEmail == '' ? _email : selectedEmail; - routeToPage( - context, - VerifyIdentifyScreen(self: false, email: emailToAdd), + showDialog( + context: context, + builder: (BuildContext context) { + return VerifyIdentifyDialog( + self: false, email: emailToAdd); + }, ); }, child: Text( diff --git a/lib/ui/sharing/verify_identify_screen.dart b/lib/ui/sharing/verify_identify_screen.dart deleted file mode 100644 index a4dc51e08..000000000 --- a/lib/ui/sharing/verify_identify_screen.dart +++ /dev/null @@ -1,248 +0,0 @@ -import "dart:convert"; - -import 'package:bip39/bip39.dart' as bip39; -import 'package:crypto/crypto.dart'; -import "package:dotted_border/dotted_border.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; -import "package:logging/logging.dart"; -import "package:photos/core/configuration.dart"; -import "package:photos/services/user_service.dart"; -import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/common/loading_widget.dart"; -import "package:photos/ui/components/button_widget.dart"; -import "package:photos/ui/components/icon_button_widget.dart"; -import "package:photos/ui/components/models/button_type.dart"; -import "package:photos/ui/components/title_bar_title_widget.dart"; -import "package:photos/ui/components/title_bar_widget.dart"; -import "package:photos/utils/share_util.dart"; - -class VerifyIdentifyScreen extends StatefulWidget { - // email id of the user who's verification ID is being displayed for - // verification - final String email; - - // self is true when the user is viewing their own verification ID - final bool self; - - VerifyIdentifyScreen({ - Key? key, - required this.self, - this.email = '', - }) : super(key: key) { - if (!self && email.isEmpty) { - throw ArgumentError("email cannot be empty when self is false"); - } - } - - @override - State createState() => _VerifyIdentifyScreenState(); -} - -class _VerifyIdentifyScreenState extends State { - final _logger = Logger("VerifyIdentifyScreen"); - final bool doesUserExist = true; - - @override - Widget build(BuildContext context) { - final textStyle = getEnteTextTheme(context); - final String subTitle = widget.self - ? "This is your Verification ID" - : "This is ${widget.email}'s Verification ID"; - final String bottomText = widget.self - ? "Someone sharing albums with you should see the same ID on their " - "device." - : "Please ask them to long-press their email address on the settings " - "screen, and verify that the IDs on both devices match."; - - return Scaffold( - body: CustomScrollView( - primary: false, - slivers: [ - TitleBarWidget( - flexibleSpaceTitle: TitleBarTitleWidget( - title: widget.self ? "Verification ID" : "Verify ${widget.email}", - ), - actionIcons: [ - IconButtonWidget( - icon: Icons.close_outlined, - iconButtonType: IconButtonType.secondary, - onTap: () { - Navigator.pop(context); - }, - ), - ], - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (delegateBuildContext, index) { - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FutureBuilder( - future: _getPublicKey(), - builder: (context, snapshot) { - if (snapshot.hasData) { - final publicKey = snapshot.data!; - if (publicKey.isEmpty) { - return Column( - children: [ - const SizedBox(height: 20), - Text( - "${widget.email} does not have an ente " - "account\n" - "\nSend them an invite to add them after they sign up", - ), - const SizedBox(height: 20), - ButtonWidget( - buttonType: ButtonType.neutral, - icon: Icons.adaptive.share, - labelText: "Send invite", - isInAlert: true, - onTap: () async { - shareText( - "Download ente so we can easily share original quality photos" - " and videos\n\nhttps://ente.io/#download", - ); - }, - ), - ], - ); - } else { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - subTitle, - style: textStyle.bodyMuted, - ), - const SizedBox(height: 20), - _verificationIDWidget(context, publicKey), - const SizedBox(height: 16), - Text(bottomText, style: textStyle.body), - ], - ); - } - } else if (snapshot.hasError) { - Logger("VerificationID") - .severe("failed to end userID", snapshot.error); - return Text( - "Something went wrong", - style: textStyle.bodyMuted, - ); - } else { - return const SizedBox( - height: 200, - child: EnteLoadingWidget(), - ); - } - }, - ), - ], - ), - ); - }, - childCount: 1, - ), - ), - SliverFillRemaining( - hasScrollBody: false, - child: Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 32, horizontal: 20), - child: ButtonWidget( - buttonType: ButtonType.neutral, - isInAlert: true, - labelText: widget.self ? "Ok" : "Done", - ), - ), - ), - ), - ], - ), - ); - } - - // getPublicKey will return empty string if the user is not found for given - Future _getPublicKey() async { - if (widget.self) { - return Configuration.instance.getKeyAttributes()!.publicKey; - } - final String? userPublicKey = - await UserService.instance.getPublicKey(widget.email); - if (userPublicKey == null) { - // user not found - return ""; - } - return userPublicKey; - } - - Widget _verificationIDWidget(BuildContext context, String publicKey) { - final colorScheme = getEnteColorScheme(context); - final textStyle = getEnteTextTheme(context); - final String verificationID = _generateVerificationID(publicKey); - return DottedBorder( - color: colorScheme.strokeMuted, - //color of dotted/dash line - strokeWidth: 1, - - dashPattern: const [12, 6], - radius: const Radius.circular(8), - //dash patterns, 10 is dash width, 6 is space width - child: Column( - children: [ - GestureDetector( - onTap: () async { - if (verificationID.isEmpty) { - return; - } - await Clipboard.setData( - ClipboardData(text: verificationID), - ); - shareText( - widget.self - ? "Here's my verification ID: " - "$verificationID for ente.io." - : "Hey, " - "can you confirm that " - "this is your ente.io verification " - "ID: $verificationID", - ); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(2), - ), - color: colorScheme.backgroundElevated2, - ), - padding: const EdgeInsets.all(20), - width: double.infinity, - child: Text( - verificationID, - style: textStyle.bodyBold, - ), - ), - ), - ], - ), - ); - } - - String _generateVerificationID(String publicKey) { - final inputBytes = base64.decode(publicKey); - final shaValue = sha256.convert(inputBytes); - return bip39.generateMnemonic( - strength: 256, - randomBytes: (int size) { - return Uint8List.fromList(shaValue.bytes); - }, - ); - } -} diff --git a/lib/ui/sharing/verify_identity_dialog.dart b/lib/ui/sharing/verify_identity_dialog.dart new file mode 100644 index 000000000..650ab717b --- /dev/null +++ b/lib/ui/sharing/verify_identity_dialog.dart @@ -0,0 +1,209 @@ +import "dart:convert"; + +import 'package:bip39/bip39.dart' as bip39; +import "package:crypto/crypto.dart"; +import "package:dotted_border/dotted_border.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; +import "package:logging/logging.dart"; +import "package:photos/core/configuration.dart"; +import "package:photos/services/user_service.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/common/loading_widget.dart"; +import "package:photos/ui/components/button_widget.dart"; +import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/utils/share_util.dart"; + +class VerifyIdentifyDialog extends StatefulWidget { + // email id of the user who's verification ID is being displayed for + // verification + final String email; + + // self is true when the user is viewing their own verification ID + final bool self; + + VerifyIdentifyDialog({ + Key? key, + required this.self, + this.email = '', + }) : super(key: key) { + if (!self && email.isEmpty) { + throw ArgumentError("email cannot be empty when self is false"); + } + } + + @override + State createState() => _VerifyIdentifyDialogState(); +} + +class _VerifyIdentifyDialogState extends State { + final bool doesUserExist = true; + + @override + Widget build(BuildContext context) { + final textStyle = getEnteTextTheme(context); + final String subTitle = widget.self + ? "This is your Verification ID" + : "This is ${widget.email}'s Verification ID"; + final String bottomText = widget.self + ? "Someone sharing albums with you should see the same ID on their " + "device." + : "Please ask them to long-press their email address on the settings " + "screen, and verify that the IDs on both devices match."; + + final AlertDialog alert = AlertDialog( + title: Text(widget.self ? "Verification ID" : "Verify ${widget.email}"), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + FutureBuilder( + future: _getPublicKey(), + builder: (context, snapshot) { + if (snapshot.hasData) { + final publicKey = snapshot.data!; + if (publicKey.isEmpty) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${widget.email} does not have an ente " + "account.\n" + "\nSend them an invite to share photos.", + ), + const SizedBox(height: 24), + ButtonWidget( + buttonType: ButtonType.neutral, + icon: Icons.adaptive.share, + labelText: "Send invite", + isInAlert: true, + onTap: () async { + shareText( + "Download ente so we can easily share original quality photos" + " and videos\n\nhttps://ente.io/", + ); + }, + ), + ], + ); + } else { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + subTitle, + style: textStyle.bodyMuted, + ), + const SizedBox(height: 20), + _verificationIDWidget(context, publicKey), + const SizedBox(height: 16), + Text( + bottomText, + style: textStyle.bodyMuted, + ), + const SizedBox(height: 24), + ButtonWidget( + buttonType: ButtonType.neutral, + isInAlert: true, + labelText: widget.self ? "OK" : "Done", + ), + ], + ); + } + } else if (snapshot.hasError) { + Logger("VerificationID") + .severe("failed to end userID", snapshot.error); + return Text( + "Something went wrong", + style: textStyle.bodyMuted, + ); + } else { + return const SizedBox( + height: 200, + child: EnteLoadingWidget(), + ); + } + }, + ), + ], + ), + ); + return alert; + } + + Future _getPublicKey() async { + if (widget.self) { + return Configuration.instance.getKeyAttributes()!.publicKey; + } + final String? userPublicKey = + await UserService.instance.getPublicKey(widget.email); + if (userPublicKey == null) { + // user not found + return ""; + } + return userPublicKey; + } + + Widget _verificationIDWidget(BuildContext context, String publicKey) { + final colorScheme = getEnteColorScheme(context); + final textStyle = getEnteTextTheme(context); + final String verificationID = _generateVerificationID(publicKey); + return DottedBorder( + color: colorScheme.strokeMuted, + //color of dotted/dash line + strokeWidth: 1, + + dashPattern: const [12, 6], + radius: const Radius.circular(8), + //dash patterns, 10 is dash width, 6 is space width + child: Column( + children: [ + GestureDetector( + onTap: () async { + if (verificationID.isEmpty) { + return; + } + await Clipboard.setData( + ClipboardData(text: verificationID), + ); + shareText( + widget.self + ? "Here's my verification ID: " + "$verificationID for ente.io." + : "Hey, " + "can you confirm that " + "this is your ente.io verification " + "ID: $verificationID", + ); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(2), + ), + color: colorScheme.backgroundElevated2, + ), + padding: const EdgeInsets.all(20), + width: double.infinity, + child: Text( + verificationID, + style: textStyle.bodyBold, + ), + ), + ), + ], + ), + ); + } + + String _generateVerificationID(String publicKey) { + final inputBytes = base64.decode(publicKey); + final shaValue = sha256.convert(inputBytes); + return bip39.generateMnemonic( + strength: 256, + randomBytes: (int size) { + return Uint8List.fromList(shaValue.bytes); + }, + ); + } +} From fba48726e2365059d5b213b19bc7d57c934566e4 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sun, 26 Feb 2023 12:32:27 +0530 Subject: [PATCH 3/3] Fix formatting --- lib/ui/sharing/add_partipant_page.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ui/sharing/add_partipant_page.dart b/lib/ui/sharing/add_partipant_page.dart index d2de5d195..cb38e6d4e 100644 --- a/lib/ui/sharing/add_partipant_page.dart +++ b/lib/ui/sharing/add_partipant_page.dart @@ -215,7 +215,9 @@ class _AddParticipantPage extends State { context: context, builder: (BuildContext context) { return VerifyIdentifyDialog( - self: false, email: emailToAdd); + self: false, + email: emailToAdd, + ); }, ); },