diff --git a/auth/lib/onboarding/view/setup_enter_secret_key_page.dart b/auth/lib/onboarding/view/setup_enter_secret_key_page.dart index 4857221638..8966e44fec 100644 --- a/auth/lib/onboarding/view/setup_enter_secret_key_page.dart +++ b/auth/lib/onboarding/view/setup_enter_secret_key_page.dart @@ -140,202 +140,207 @@ class _SetupEnterSecretKeyPageState extends State { child: Padding( padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 20), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Row( + if (widget.code != null) + GestureDetector( + onTap: () async { + await navigateToCustomIconPage(); + }, + child: CustomIconWidget(iconData: _customIconID), + ), + const SizedBox(height: 20), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - FieldLabel(l10n.codeIssuerHint), - Expanded( - child: TextFormField( - // The validator receives the text that the user has entered. - validator: (value) { - if (value == null || value.isEmpty) { - return "Please enter some text"; - } - return null; - }, - decoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric(vertical: 12.0), + Row( + children: [ + FieldLabel(l10n.codeIssuerHint), + Expanded( + child: TextFormField( + // The validator receives the text that the user has entered. + validator: (value) { + if (value == null || value.isEmpty) { + return "Please enter some text"; + } + return null; + }, + decoration: const InputDecoration( + contentPadding: + EdgeInsets.symmetric(vertical: 12.0), + ), + style: getEnteTextTheme(context).small, + controller: _issuerController, + autofocus: true, + ), ), - style: getEnteTextTheme(context).small, - controller: _issuerController, - autofocus: true, - ), + ], ), - ], - ), - Row( - children: [ - FieldLabel(l10n.secret), - Expanded( - child: TextFormField( - // The validator receives the text that the user has entered. - validator: (value) { - if (value == null || value.isEmpty) { - return "Please enter some text"; - } - return null; - }, - style: getEnteTextTheme(context).small, - decoration: InputDecoration( - contentPadding: - const EdgeInsets.symmetric(vertical: 12.0), - suffixIcon: GestureDetector( - // padding: EdgeInsets.zero, - onTap: () { - setState(() { - _secretKeyObscured = !_secretKeyObscured; - }); + Row( + children: [ + FieldLabel(l10n.secret), + Expanded( + child: TextFormField( + // The validator receives the text that the user has entered. + validator: (value) { + if (value == null || value.isEmpty) { + return "Please enter some text"; + } + return null; }, - child: _secretKeyObscured - ? const Icon( - Icons.visibility_off_rounded, - size: 18, - ) - : const Icon( - Icons.visibility_rounded, - size: 18, - ), + style: getEnteTextTheme(context).small, + decoration: InputDecoration( + contentPadding: + const EdgeInsets.symmetric(vertical: 12.0), + suffixIcon: GestureDetector( + // padding: EdgeInsets.zero, + onTap: () { + setState(() { + _secretKeyObscured = !_secretKeyObscured; + }); + }, + child: _secretKeyObscured + ? const Icon( + Icons.visibility_off_rounded, + size: 18, + ) + : const Icon( + Icons.visibility_rounded, + size: 18, + ), + ), + ), + obscureText: _secretKeyObscured, + controller: _secretController, ), ), - obscureText: _secretKeyObscured, - controller: _secretController, - ), + ], ), - ], - ), - Row( - children: [ - FieldLabel(l10n.account), - Expanded( - child: TextFormField( - // The validator receives the text that the user has entered. - validator: (value) { - if (value == null || value.isEmpty) { - return "Please enter some text"; - } - return null; - }, - decoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric(vertical: 12.0), + Row( + children: [ + FieldLabel(l10n.account), + Expanded( + child: TextFormField( + // The validator receives the text that the user has entered. + validator: (value) { + if (value == null || value.isEmpty) { + return "Please enter some text"; + } + return null; + }, + decoration: const InputDecoration( + contentPadding: + EdgeInsets.symmetric(vertical: 12.0), + ), + style: getEnteTextTheme(context).small, + controller: _accountController, + ), ), - style: getEnteTextTheme(context).small, - controller: _accountController, - ), + ], ), - ], - ), - const SizedBox(height: 12), - Row( - children: [ - FieldLabel(l10n.notes), - Expanded( - child: TextFormField( - // The validator receives the text that the user has entered. - validator: (value) { - if (value == null || value.isEmpty) { - return "Please enter some text"; - } - if (value.length > _notesLimit) { - return "Notes can't be more than 1000 characters"; - } - return null; - }, - maxLength: _notesLimit, - minLines: 1, - maxLines: 5, - decoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric(vertical: 12.0), + const SizedBox(height: 12), + Row( + children: [ + FieldLabel(l10n.notes), + Expanded( + child: TextFormField( + // The validator receives the text that the user has entered. + validator: (value) { + if (value == null || value.isEmpty) { + return "Please enter some text"; + } + if (value.length > _notesLimit) { + return "Notes can't be more than 1000 characters"; + } + return null; + }, + maxLength: _notesLimit, + minLines: 1, + maxLines: 5, + decoration: const InputDecoration( + contentPadding: + EdgeInsets.symmetric(vertical: 12.0), + ), + style: getEnteTextTheme(context).small, + controller: _notesController, + ), ), - style: getEnteTextTheme(context).small, - controller: _notesController, - ), - ), - ], - ), - const SizedBox(height: 12), - Wrap( - spacing: 12, - alignment: WrapAlignment.start, - children: [ - ...allTags.map( - (e) => TagChip( - label: e, - action: TagChipAction.check, - state: selectedTags.contains(e) - ? TagChipState.selected - : TagChipState.unselected, - onTap: () { - if (selectedTags.contains(e)) { - selectedTags.remove(e); - } else { - selectedTags.add(e); - } - setState(() {}); - }, - ), + ], ), - AddChip( - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AddTagDialog( - onTap: (tag) { - final exist = allTags.contains(tag); - if (exist && selectedTags.contains(tag)) { - return Navigator.pop(context); - } - if (!exist) allTags.add(tag); - selectedTags.add(tag); - setState(() {}); - Navigator.pop(context); + const SizedBox(height: 12), + Wrap( + spacing: 12, + alignment: WrapAlignment.start, + children: [ + ...allTags.map( + (e) => TagChip( + label: e, + action: TagChipAction.check, + state: selectedTags.contains(e) + ? TagChipState.selected + : TagChipState.unselected, + onTap: () { + if (selectedTags.contains(e)) { + selectedTags.remove(e); + } else { + selectedTags.add(e); + } + setState(() {}); + }, + ), + ), + AddChip( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddTagDialog( + onTap: (tag) { + final exist = allTags.contains(tag); + if (exist && selectedTags.contains(tag)) { + return Navigator.pop(context); + } + if (!exist) allTags.add(tag); + selectedTags.add(tag); + setState(() {}); + Navigator.pop(context); + }, + ); }, + barrierColor: Colors.black.withOpacity(0.85), + barrierDismissible: false, ); }, - barrierColor: Colors.black.withOpacity(0.85), - barrierDismissible: false, - ); - }, + ), + ], ), - ], - ), - const SizedBox(height: 32), - if (widget.code != null) - CustomIconWidget(iconData: _customIconID), - const SizedBox(height: 24), - if (widget.code != null) - GestureDetector( - onTap: () async { - await navigateToCustomIconPage(); - }, - child: Text( - "Change Icon", - style: getEnteTextTheme(context).small, + const SizedBox(height: 40), + SizedBox( + width: 400, + child: OutlinedButton( + onPressed: () async { + if ((_accountController.text.trim().isEmpty && + _issuerController.text.trim().isEmpty) || + _secretController.text.trim().isEmpty) { + String message; + if (_secretController.text.trim().isEmpty) { + message = context.l10n.secretCanNotBeEmpty; + } else { + message = + context.l10n.bothIssuerAndAccountCanNotBeEmpty; + } + _showIncorrectDetailsDialog( + context, + message: message, + ); + return; + } + await _saveCode(); + }, + child: Text(l10n.saveAction), + ), ), - ), - const SizedBox(height: 40), - SizedBox( - width: 400, - child: OutlinedButton( - onPressed: () async { - if ((_accountController.text.trim().isEmpty && - _issuerController.text.trim().isEmpty) || - _secretController.text.trim().isEmpty) { - String message; - if (_secretController.text.trim().isEmpty) { - message = context.l10n.secretCanNotBeEmpty; - } else { - message = - context.l10n.bothIssuerAndAccountCanNotBeEmpty; - } - _showIncorrectDetailsDialog(context, message: message); - return; - } - await _saveCode(); - }, - child: Text(l10n.saveAction), - ), + ], ), ], ), diff --git a/auth/lib/ui/components/custom_icon_widget.dart b/auth/lib/ui/components/custom_icon_widget.dart index 4825ddff60..7af65fc7f3 100644 --- a/auth/lib/ui/components/custom_icon_widget.dart +++ b/auth/lib/ui/components/custom_icon_widget.dart @@ -1,6 +1,7 @@ import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/utils/icon_utils.dart'; import 'package:ente_auth/utils/totp_util.dart'; +import 'package:figma_squircle/figma_squircle.dart'; import 'package:flutter/material.dart'; class CustomIconWidget extends StatelessWidget { @@ -13,23 +14,96 @@ class CustomIconWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - width: 70, - height: 70, - decoration: BoxDecoration( - border: Border.all( - width: 1.5, - color: getEnteColorScheme(context).tagChipSelectedColor, - ), - borderRadius: const BorderRadius.all(Radius.circular(12.0)), + return SizedBox( + height: 90, + width: 90, + child: Stack( + children: [ + Container( + width: 80, + height: 80, + decoration: ShapeDecoration( + shape: SmoothRectangleBorder( + side: BorderSide( + width: 1.5, + color: getEnteColorScheme(context) + .tagChipSelectedColor + .withOpacity(0.5), + ), + borderRadius: SmoothBorderRadius( + cornerRadius: 15.5, + cornerSmoothing: 1.0, + ), + ), + ), + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: FittedBox( + fit: BoxFit.contain, + child: IconUtils.instance.getIcon( + context, + safeDecode(iconData).trim(), + width: 50, + ), + ), + ), + _getEditIcon(context), + ], ), - padding: const EdgeInsets.all(8), - child: FittedBox( - fit: BoxFit.contain, - child: IconUtils.instance.getIcon( - context, - safeDecode(iconData).trim(), - width: 50, + ); + } + + Widget _getEditIcon(BuildContext context) { + return Positioned( + left: 60, + top: 60, + child: Center( + child: Container( + height: 28, + width: 28, + decoration: ShapeDecoration( + color: Colors.white, + shadows: const [ + BoxShadow( + offset: Offset(0, 0), + blurRadius: 0.84, + color: Color.fromRGBO(0, 0, 0, 0.11), + ), + BoxShadow( + offset: Offset(0.84, 0.84), + blurRadius: 1.68, + color: Color.fromRGBO(0, 0, 0, 0.09), + ), + BoxShadow( + offset: Offset(2.53, 2.53), + blurRadius: 2.53, + color: Color.fromRGBO(0, 0, 0, 0.05), + ), + BoxShadow( + offset: Offset(5.05, 4.21), + blurRadius: 2.53, + color: Color.fromRGBO(0, 0, 0, 0.02), + ), + BoxShadow( + offset: Offset(7.58, 6.74), + blurRadius: 2.53, + color: Color.fromRGBO(0, 0, 0, 0.0), + ), + ], + shape: SmoothRectangleBorder( + borderRadius: SmoothBorderRadius( + cornerRadius: 8, + cornerSmoothing: 1.0, + ), + ), + ), + child: Icon( + Icons.edit, + size: 16, + color: Colors.black.withOpacity(0.9), + ), ), ), ); diff --git a/auth/lib/ui/custom_icon_page.dart b/auth/lib/ui/custom_icon_page.dart index 01edbea9be..85d229a628 100644 --- a/auth/lib/ui/custom_icon_page.dart +++ b/auth/lib/ui/custom_icon_page.dart @@ -71,7 +71,7 @@ class _CustomIconPageState extends State { return Scaffold( appBar: AppBar( title: !_showSearchBox - ? const Text('Custom Branding') + ? const Text('Choose icon') : TextField( autocorrect: false, enableSuggestions: false, @@ -93,7 +93,7 @@ class _CustomIconPageState extends State { icon: _showSearchBox ? const Icon(Icons.clear) : const Icon(Icons.search), - tooltip: "Search", + tooltip: l10n.search, onPressed: () { setState( () { diff --git a/auth/pubspec.lock b/auth/pubspec.lock index 0d9dd2e86c..b6e72978b2 100644 --- a/auth/pubspec.lock +++ b/auth/pubspec.lock @@ -431,6 +431,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + figma_squircle: + dependency: "direct main" + description: + name: figma_squircle + sha256: "790b91a9505e90d246f6efe2fa065ff7fffe658c7b44fe9b5b20c7b0ad3818c0" + url: "https://pub.dev" + source: hosted + version: "0.5.3" file: dependency: transitive description: diff --git a/auth/pubspec.yaml b/auth/pubspec.yaml index 67e66c8ee9..1645896f45 100644 --- a/auth/pubspec.yaml +++ b/auth/pubspec.yaml @@ -1,6 +1,6 @@ name: ente_auth description: ente two-factor authenticator -version: 4.1.7+417 +version: 4.1.8+418 publish_to: none environment: @@ -31,6 +31,7 @@ dependencies: expandable: ^5.0.1 expansion_tile_card: ^3.0.0 ffi: ^2.1.0 + figma_squircle: ^0.5.3 file_picker: ^8.1.2 # https://github.com/incrediblezayed/file_saver/issues/86 file_saver: ^0.2.11