diff --git a/assets/translations/en.json b/assets/translations/en.json index 42178a89..8cb0235a 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -39,6 +39,16 @@ "generate_key": "Generate key", "generate_key_text": "You can generate ssh key", "console": "Console", + "remove": "Remove", + "enable": "Enable", + "ok": "ok", + "continue": "Continue", + "ssh_key_exist_text": "You have generated ssh key", + "yes_delete": "Yes, delete my SSH key", + "share": "Share", + "copy_buffer": "Copy to buffer", + "copied_ssh": "SSH copied to clipboard", + "delete_ssh_text": "Delete SSH key?", "about_app_page": { "text": "Application version v.{}" }, diff --git a/assets/translations/ru.json b/assets/translations/ru.json index ac731421..855adf60 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -39,6 +39,16 @@ "create_ssh_key": "Создать ssh ключ", "generate_key": "Сгенерировать ключ", "generate_key_text": "Вы сможете сгенерировать ключ", + "remove": "Отключить", + "enable": "Включить", + "ok": "ok", + "continue": "Продолжить", + "ssh_key_exist_text": "У вас уже есть сгенерированный ssk ключ", + "yes_delete": "Да, удалить", + "share": "Поделиться", + "copy_buffer": "Копировать в буфер", + "copied_ssh": "SSH копировано в буфер", + "delete_ssh_text": "Удалить SSH ключ?", "about_app_page": { "text": "Версия приложения: v.{}" }, diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 373cec68..7eefa660 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,6 +2,8 @@ PODS: - Flutter (1.0.0) - flutter_secure_storage (3.3.1): - Flutter + - local_auth (0.0.1): + - Flutter - package_info (0.0.1): - Flutter - path_provider (0.0.1): @@ -18,6 +20,7 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - local_auth (from `.symlinks/plugins/local_auth/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - path_provider (from `.symlinks/plugins/path_provider/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) @@ -30,6 +33,8 @@ EXTERNAL SOURCES: :path: Flutter flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" + local_auth: + :path: ".symlinks/plugins/local_auth/ios" package_info: :path: ".symlinks/plugins/package_info/ios" path_provider: @@ -46,6 +51,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec + local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 9140ef0c..106017ec 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -361,6 +361,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = UVNTKR53DD; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -499,6 +500,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = UVNTKR53DD; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -529,6 +531,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = UVNTKR53DD; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 1e296c4f..fb07346c 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index dff68e1d..c9e0c452 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index af5f0604..93c986fb 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 7da4d77f..6cffb871 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 856c5156..9e785db5 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index ce53aa9f..17a096ee 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 0495ef99..622fae1d 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index af5f0604..93c986fb 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index bb04514b..e5c3c043 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index abf56f6c..5f7802aa 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index c95dfa17..4382a3b6 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 8b55ab5b..bae4d5c1 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 23e713a5..cfb62108 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index caf8cb40..565fd300 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/build/Pods.build/Release-iphonesimulator/local_auth.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/local_auth.build/dgph new file mode 100644 index 00000000..0c4ca337 Binary files /dev/null and b/ios/build/Pods.build/Release-iphonesimulator/local_auth.build/dgph differ diff --git a/lib/config/get_it_config.dart b/lib/config/get_it_config.dart index 99b61cc8..eb5c0902 100644 --- a/lib/config/get_it_config.dart +++ b/lib/config/get_it_config.dart @@ -2,7 +2,7 @@ import 'package:get_it/get_it.dart'; import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/get_it/navigation.dart'; -import 'package:selfprivacy/logic/get_it/ssh_helper.dart'; +import 'package:selfprivacy/logic/get_it/ssh.dart'; import 'package:selfprivacy/logic/get_it/timer.dart'; export 'package:selfprivacy/logic/get_it/api_config.dart'; diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index be47b879..a9659869 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -24,6 +24,7 @@ class HiveConfig { var cipher = HiveAesCipher(await getEncriptedKey(BNames.key)); await Hive.openBox(BNames.appConfig, encryptionCipher: cipher); + var sshCipher = HiveAesCipher(await getEncriptedKey(BNames.sshEnckey)); await Hive.openBox(BNames.sshConfig, encryptionCipher: sshCipher); } diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 6e5686f6..18807b90 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -33,7 +33,7 @@ class ServerApi extends ApiMap { var client = await getClient(); try { - response = await client.get('/serviceStatus'); + response = await client.get('/services/status'); res = response.statusCode == HttpStatus.ok; } catch (e) { res = false; @@ -49,7 +49,7 @@ class ServerApi extends ApiMap { var client = await getClient(); try { response = await client.post( - '/createUser', + '/users/create', options: Options( headers: { "X-User": user.login, @@ -78,7 +78,7 @@ class ServerApi extends ApiMap { var client = await getClient(); try { response = await client.get( - '/apply', + '/system/configuration/apply', ); res = response.statusCode == HttpStatus.ok; diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index e86cead1..d73eb8b3 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/get_it/ssh.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; @@ -274,6 +276,7 @@ class AppConfigCubit extends Cubit { if (state.hetznerServer != null) { await repository.deleteServer(state.cloudFlareDomain!); + await getIt().clear(); } await repository.deleteRecords(); emit(AppConfigState( diff --git a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart index 5291e2c8..6e62b3f1 100644 --- a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart +++ b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart @@ -6,7 +6,6 @@ import 'hetzner_metrics_cubit.dart'; class HetznerMetricsRepository { Future getMetrics(Period period) async { - print(period); var end = DateTime.now(); DateTime start; diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index d9d902a9..09be466c 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; -import 'package:selfprivacy/logic/get_it/ssh_helper.dart'; +import 'package:selfprivacy/logic/get_it/ssh.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -29,10 +31,7 @@ class JobsCubit extends Cubit { newJobsList.addAll((state as JobsStateWithJobs).jobList); } newJobsList.add(job); - getIt().showSnackBar(SnackBar( - content: Text('jobs.jobAdded'.tr()), - duration: const Duration(seconds: 2), - )); + getIt().showSnackBar('jobs.jobAdded'.tr()); emit(JobsStateWithJobs(newJobsList)); } @@ -54,10 +53,7 @@ class JobsCubit extends Cubit { removeJob(removingJob.id); } else { newJobsList.add(job); - getIt().showSnackBar(SnackBar( - content: Text('jobs.jobAdded'.tr()), - duration: const Duration(seconds: 2), - )); + getIt().showSnackBar('jobs.jobAdded'.tr()); emit(JobsStateWithJobs(newJobsList)); } } @@ -70,10 +66,7 @@ class JobsCubit extends Cubit { var isExistInJobList = newJobsList.any((el) => el is CreateSSHKeyJob); if (!isExistInJobList) { newJobsList.add(job); - getIt().showSnackBar(SnackBar( - content: Text('jobs.jobAdded'.tr()), - duration: const Duration(seconds: 2), - )); + getIt().showSnackBar('jobs.jobAdded'.tr()); emit(JobsStateWithJobs(newJobsList)); } } diff --git a/lib/logic/get_it/navigation.dart b/lib/logic/get_it/navigation.dart index 6d179fa7..4f06c843 100644 --- a/lib/logic/get_it/navigation.dart +++ b/lib/logic/get_it/navigation.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/config/text_themes.dart'; class NavigationService { final GlobalKey scaffoldMessengerKey = @@ -17,9 +19,13 @@ class NavigationService { ); } - void showSnackBar(SnackBar snackBar) { + void showSnackBar(String text) { final state = scaffoldMessengerKey.currentState!; - - state.showSnackBar(snackBar); + final snack = SnackBar( + backgroundColor: BrandColors.black.withOpacity(0.8), + content: Text(text, style: buttonTitleText), + duration: const Duration(seconds: 2), + ); + state.showSnackBar(snack); } } diff --git a/lib/logic/get_it/ssh_helper.dart b/lib/logic/get_it/ssh.dart similarity index 82% rename from lib/logic/get_it/ssh_helper.dart rename to lib/logic/get_it/ssh.dart index 46ded1a6..0e833c7e 100644 --- a/lib/logic/get_it/ssh_helper.dart +++ b/lib/logic/get_it/ssh.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:hive/hive.dart'; import 'package:pointycastle/pointycastle.dart'; import 'package:rsa_encrypt/rsa_encrypt.dart'; @@ -26,8 +24,16 @@ class SSHModel { await _box.put(BNames.sshPublicKey, savedPubKey); } - void init() { + void init() async { savedPrivateKey = _box.get(BNames.sshPrivateKey); savedPubKey = _box.get(BNames.sshPublicKey); } + + bool get isSSHKeyGenerated => savedPrivateKey != null && savedPubKey != null; + + Future clear() async { + savedPrivateKey = null; + savedPubKey = null; + await _box.clear(); + } } diff --git a/lib/ui/components/brand_text/brand_text.dart b/lib/ui/components/brand_text/brand_text.dart index beaf2937..bd3119dc 100644 --- a/lib/ui/components/brand_text/brand_text.dart +++ b/lib/ui/components/brand_text/brand_text.dart @@ -127,7 +127,6 @@ class BrandText extends StatelessWidget { Text build(BuildContext context) { TextStyle style; var isDark = Theme.of(context).brightness == Brightness.dark; - switch (type) { case TextType.h1: style = isDark diff --git a/lib/ui/pages/more/more.dart b/lib/ui/pages/more/more.dart index da9b116a..2c008b88 100644 --- a/lib/ui/pages/more/more.dart +++ b/lib/ui/pages/more/more.dart @@ -1,11 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:ionicons/ionicons.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/get_it/ssh.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; +import 'package:selfprivacy/ui/components/action_button/action_button.dart'; +import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; @@ -17,6 +23,7 @@ import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:share_plus/share_plus.dart'; import 'about/about.dart'; import 'app_settings/app_setting.dart'; @@ -29,6 +36,7 @@ class MorePage extends StatelessWidget { @override Widget build(BuildContext context) { var jobsCubit = context.watch(); + var isReady = context.watch().state.isFullyInitilized; return Scaffold( appBar: PreferredSize( @@ -78,21 +86,69 @@ class MorePage extends StatelessWidget { _MoreMenuTapItem( title: 'more.create_ssh_key'.tr(), iconData: Ionicons.key_outline, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return _MoreDetails( - title: 'more.create_ssh_key'.tr(), - icon: Ionicons.key_outline, - onTap: () { - jobsCubit.createShhJobIfNotExist(CreateSSHKeyJob()); - }, - text: 'more.generate_key_text'.tr(), - ); - }, - ); - }, + onTap: isReady + ? () { + if (getIt().isSSHKeyGenerated) { + showDialog( + context: context, + builder: (BuildContext context) { + return _SSHExitsDetails( + onShareTap: () { + Share.share( + getIt().savedPrivateKey!); + }, + onDeleteTap: () { + showDialog( + context: context, + builder: (_) { + return BrandAlert( + title: 'modals.3'.tr(), + contentText: + 'more.delete_ssh_text'.tr(), + acitons: [ + ActionButton( + text: 'more.yes_delete'.tr(), + isRed: true, + onPressed: () { + getIt().clear(); + Navigator.of(context).pop(); + }), + ActionButton( + text: 'basis.cancel'.tr(), + ), + ], + ); + }, + ); + }, + onCopyTap: () { + Clipboard.setData(ClipboardData( + text: getIt() + .savedPrivateKey!)); + getIt() + .showSnackBar('more.copied_ssh'.tr()); + }, + ); + }, + ); + } else { + showDialog( + context: context, + builder: (BuildContext context) { + return _MoreDetails( + title: 'more.create_ssh_key'.tr(), + icon: Ionicons.key_outline, + onTap: () { + jobsCubit.createShhJobIfNotExist( + CreateSSHKeyJob()); + }, + text: 'more.generate_key_text'.tr(), + ); + }, + ); + } + } + : null, ), ], ), @@ -103,6 +159,83 @@ class MorePage extends StatelessWidget { } } +class _SSHExitsDetails extends StatelessWidget { + const _SSHExitsDetails({ + Key? key, + required this.onDeleteTap, + required this.onShareTap, + required this.onCopyTap, + }) : super(key: key); + final Function onDeleteTap; + final Function onShareTap; + final Function onCopyTap; + + @override + Widget build(BuildContext context) { + var textStyle = body1Style.copyWith( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white + : BrandColors.black); + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SingleChildScrollView( + child: Container( + width: 350, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: paddingH15V30, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox(height: 10), + Text( + 'more.ssh_key_exist_text'.tr(), + style: textStyle, + ), + SizedBox(height: 10), + Container( + child: BrandButton.text( + onPressed: () { + Navigator.of(context).pop(); + onShareTap(); + }, + title: 'more.share'.tr(), + ), + ), + Container( + alignment: Alignment.centerLeft, + child: BrandButton.text( + onPressed: () { + Navigator.of(context).pop(); + onDeleteTap(); + }, + title: 'basis.delete'.tr(), + ), + ), + Container( + child: BrandButton.text( + onPressed: () { + Navigator.of(context).pop(); + onCopyTap(); + }, + title: 'more.copy_buffer'.tr(), + ), + ), + ], + ), + ) + ], + ), + ), + ), + ); + } +} + class _MoreDetails extends StatelessWidget { const _MoreDetails({ Key? key, @@ -189,6 +322,7 @@ class _NavItem extends StatelessWidget { child: _MoreMenuItem( iconData: iconData, title: title, + isActive: true, ), ); } @@ -203,15 +337,14 @@ class _MoreMenuTapItem extends StatelessWidget { }) : super(key: key); final IconData iconData; - final Function onTap; + final VoidCallback? onTap; final String title; @override Widget build(BuildContext context) { return GestureDetector( - onTap: () { - onTap(); - }, + onTap: onTap, child: _MoreMenuItem( + isActive: onTap != null, iconData: iconData, title: title, ), @@ -224,10 +357,12 @@ class _MoreMenuItem extends StatelessWidget { Key? key, required this.iconData, required this.title, + required this.isActive, }) : super(key: key); final IconData iconData; final String title; + final bool isActive; @override Widget build(BuildContext context) { @@ -243,13 +378,19 @@ class _MoreMenuItem extends StatelessWidget { ), child: Row( children: [ - BrandText.body1(title), + BrandText.body1( + title, + style: TextStyle( + color: isActive ? null : Colors.grey, + ), + ), Spacer(), SizedBox( width: 56, child: Icon( iconData, size: 20, + color: isActive ? null : Colors.grey, ), ), ], diff --git a/pubspec.lock b/pubspec.lock index 944a7ea6..903ec020 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -333,6 +333,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.5" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" flutter_secure_storage: dependency: "direct main" description: @@ -469,6 +476,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.1.4" + local_auth: + dependency: "direct main" + description: + name: local_auth + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.7" logging: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e3ed9839..504d4387 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: pointycastle: ^3.3.2 rsa_encrypt: ^2.0.0 ssh_key: ^0.7.0 + local_auth: ^1.1.7 dev_dependencies: flutter_test: