diff --git a/assets/translations/en.json b/assets/translations/en.json index cafb848a82..0b9b3f5eb5 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -19,12 +19,14 @@ "connect": "Connect", "domain": "Domain", "saving": "Saving..", - "nickname": "nickname", + "nickname": "Nickname", "loading": "Loading...", "later": "I will setup it later", "reset": "Reset", "details": "Details", - "no_data": "No data" + "no_data": "No data", + "wait": "Wait", + "remove": "Remove" }, "more": { "_comment": "'More' tab", @@ -34,7 +36,7 @@ "onboarding": "Onboarding", "console": "Console", "about_app_page": { - "text": "Тут любая служебная информация, v.{}" + "text": "Application version v.{}" }, "settings": { "title": "Application settings", @@ -188,7 +190,9 @@ "20": "\n", "21": "One more restart to apply your security certificates.", "22": "Create master account", - "23": "Enter a nickname and strong password" + "23": "Enter a nickname and strong password", + "finish": "Everything is initialized", + "checks": "Checks have been completed \n{} ouf of {}" }, "modals": { "_comment": "messages in modals", @@ -198,7 +202,8 @@ "4": "Purge all authentication keys?", "5": "Yes, purge all my tokens", "6": "Delete the server and volume?", - "7": "Yes" + "7": "Yes", + "8": "Remove task" }, "timer": { "sec": "{} sec" @@ -208,5 +213,13 @@ "title": "Jobs list", "start": "Start", "empty": "No jobs" + }, + "validations": { + "required": "Required", + "invalid_format": "Invalid format", + "root_name": "User name cannot be 'root'", + "key_format": "Invalid key format", + "length": "Length is [] shoud be {}", + "user_alredy_exist": "Already exists" } } \ No newline at end of file diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 13b9658e41..bc89ab19e5 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -24,7 +24,9 @@ "later": "Настрою потом", "reset": "Reset", "details": "Детальная информация", - "no_data": "Нет данных" + "no_data": "Нет данных", + "wait": "Ожидайте", + "remove": "Удалить" }, "more": { "_comment": "вкладка еще", @@ -188,7 +190,9 @@ "20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.", "21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности", "22": "Создайте главную учетную запись", - "23": "Введите никнейм и сложный пароль" + "23": "Введите никнейм и сложный пароль", + "finish": "Все инициализировано", + "checks": "Проверок выполнено: \n{} / {}" }, "modals": { "_comment": "messages in modals", @@ -209,5 +213,13 @@ "title": "Задачи", "start": "Начать выполенение", "empty": "Пусто" + }, + "validations": { + "required": "обязательное поле", + "invalid_format": "Неверный формат", + "root_name": "Имя пользователя не может быть'root'", + "key_format": "Неверный формат", + "length": "Длина строки [] должна быть {}", + "user_alredy_exist": "Имя уже используется" } } \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 40f32d303c..285337b4d7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -6,6 +6,8 @@ PODS: - Flutter - path_provider (0.0.1): - Flutter + - share_plus (0.0.1): + - Flutter - shared_preferences (0.0.1): - Flutter - url_launcher (0.0.1): @@ -18,6 +20,7 @@ DEPENDENCIES: - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/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`) - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) @@ -31,6 +34,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info/ios" path_provider: :path: ".symlinks/plugins/path_provider/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" shared_preferences: :path: ".symlinks/plugins/shared_preferences/ios" url_launcher: @@ -43,6 +48,7 @@ SPEC CHECKSUMS: flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index ae7b2d81e9..5fe7138093 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -17,7 +17,7 @@ class BlocAndProviderConfig extends StatelessWidget { // SchedulerBinding.instance.window.platformBrightness; // var isDark = platformBrightness == Brightness.dark; var isDark = false; - + var usersCubit = UsersCubit(); return MultiProvider( providers: [ BlocProvider( @@ -31,8 +31,8 @@ class BlocAndProviderConfig extends StatelessWidget { create: (_) => AppConfigCubit()..load(), ), BlocProvider(create: (_) => ProvidersCubit()), - BlocProvider(create: (_) => UsersCubit()), - BlocProvider(create: (_) => JobsCubit()), + BlocProvider(create: (_) => usersCubit..load(), lazy: false), + BlocProvider(create: (_) => JobsCubit(usersCubit)), ], child: child, ); diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index eaf7b0eb0a..e704b50c3b 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -19,6 +19,8 @@ class HiveConfig { Hive.registerAdapter(HetznerDataBaseAdapter()); await Hive.openBox(BNames.appSettings); + await Hive.openBox(BNames.users); + var cipher = HiveAesCipher(await getEncriptedKey()); await Hive.openBox(BNames.appConfig, encryptionCipher: cipher); @@ -42,6 +44,7 @@ class BNames { static String appConfig = 'appConfig'; static String isDarkModeOn = 'isDarkModeOn'; static String isOnbordingShowing = 'isOnbordingShowing'; + static String users = 'users'; static String appSettings = 'appSettings'; diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index ad1eacb0ff..037db18cf3 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -95,7 +95,7 @@ class HetznerApi extends ApiMap { var dbId = dbCreateResponse.data['volume']['id']; var data = jsonDecode( - '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''', + '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''' ); Response serverCreateResponse = await client.post( diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 2089e633f1..aae495389d 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/models/user.dart'; import 'api_map.dart'; @@ -40,6 +41,52 @@ class ServerApi extends ApiMap { return res; } + Future createUser(User user) async { + bool res; + Response response; + + var client = await getClient(); + try { + response = await client.post( + '/createUser', + options: Options( + headers: { + "X-User": user.login, + "X-Password": + '\$6\$${user.hashPassword.salt}\$${user.hashPassword.hash}', + }, + ), + ); + res = response.statusCode == HttpStatus.ok; + } catch (e) { + print(e); + res = false; + } + + close(client); + return res; + } + String get rootAddress => throw UnimplementedError('not used in with implementation'); + + Future apply() async { + bool res; + Response response; + + var client = await getClient(); + try { + response = await client.get( + '/apply', + ); + + res = response.statusCode == HttpStatus.ok; + } catch (e) { + print(e); + res = false; + } + + close(client); + return res; + } } diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 6b9880c929..1269f9305f 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -85,7 +85,17 @@ class AppConfigState extends Equatable { bool get isServerCreated => hetznerServer != null; bool get isFullyInitilized => _fulfilementList.every((el) => el!); - int get progress => _fulfilementList.where((el) => el!).length; + int get progress => _fulfilementList.where((el) => el!).length ; + + int get porgressBar { + if (progress < 6) { + return progress; + } else if (progress < 10) { + return 6; + } else { + return 7; + } + } List get _fulfilementList { var res = [ diff --git a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart index 123553cfdb..eda069393c 100644 --- a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart @@ -3,6 +3,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/api_maps/backblaze.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; +import 'package:easy_localization/easy_localization.dart'; class BackblazeFormCubit extends FormCubit { BackblazeFormCubit(this.initializingCubit) { @@ -10,7 +11,7 @@ class BackblazeFormCubit extends FormCubit { keyId = FieldCubit( initalValue: '', validations: [ - RequiredStringValidation('required'), + RequiredStringValidation('validations.required'.tr()), //ValidationModel( //(s) => regExp.hasMatch(s), 'invalid key format'), //LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64') diff --git a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart index 883470c895..2654caa9d8 100644 --- a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart @@ -4,6 +4,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; +import 'package:easy_localization/easy_localization.dart'; class CloudFlareFormCubit extends FormCubit { CloudFlareFormCubit(this.initializingCubit) { @@ -11,10 +12,11 @@ class CloudFlareFormCubit extends FormCubit { apiKey = FieldCubit( initalValue: '', validations: [ - RequiredStringValidation('required'), + RequiredStringValidation('validations.required'.tr()), ValidationModel( - (s) => regExp.hasMatch(s), 'invalid key format'), - LegnthStringValidationWithLenghShowing(40, 'length is [] shoud be 40') + (s) => regExp.hasMatch(s), 'validations.key_format'.tr()), + LegnthStringValidationWithLenghShowing( + 40, 'validations.length'.tr(args: ["40"])) ], ); diff --git a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart index a9e8950121..4824ff2ee9 100644 --- a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart @@ -4,6 +4,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:easy_localization/easy_localization.dart'; class HetznerFormCubit extends FormCubit { HetznerFormCubit(this.initializingCubit) { @@ -11,10 +12,10 @@ class HetznerFormCubit extends FormCubit { apiKey = FieldCubit( initalValue: '', validations: [ - RequiredStringValidation('required'), + RequiredStringValidation('validations.required'.tr()), ValidationModel( - (s) => regExp.hasMatch(s), 'invalid key format'), - LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64') + (s) => regExp.hasMatch(s), 'validations.key_format'.tr()), + LegnthStringValidationWithLenghShowing(64, 'validations.length'.tr(args: ["64"])) ], ); diff --git a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart index b82bd0f367..5fb172a2b6 100644 --- a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/user.dart'; +import 'package:easy_localization/easy_localization.dart'; class RootUserFormCubit extends FormCubit { RootUserFormCubit(this.initializingCubit) { @@ -12,18 +13,20 @@ class RootUserFormCubit extends FormCubit { userName = FieldCubit( initalValue: '', validations: [ - RequiredStringValidation('required'), ValidationModel( - (s) => userRegExp.hasMatch(s), 'invalid format'), + (s) => s.toLowerCase() == 'root', 'validations.root_name'.tr()), + RequiredStringValidation('validations.required'.tr()), + ValidationModel( + (s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()), ], ); password = FieldCubit( initalValue: '', validations: [ - RequiredStringValidation('required'), + RequiredStringValidation('validations.required'.tr()), ValidationModel( - (s) => passwordRegExp.hasMatch(s), 'invalid format'), + (s) => passwordRegExp.hasMatch(s), 'validations.invalid_format'.tr()), ], ); diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index 50de369e37..a30b90f820 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -1,13 +1,16 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; +import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/utils/password_generator.dart'; +import 'package:easy_localization/easy_localization.dart'; class UserFormCubit extends FormCubit { UserFormCubit({ - required this.usersCubit, + required this.jobsCubit, + required List users, User? user, }) { var isEdit = user != null; @@ -18,18 +21,22 @@ class UserFormCubit extends FormCubit { login = FieldCubit( initalValue: isEdit ? user!.login : '', validations: [ - RequiredStringValidation('required'), + ValidationModel( + (login) => users.any((user) => user.login == login), + 'validations.user_alredy_exist'.tr(), + ), + RequiredStringValidation('validations.required'.tr()), ValidationModel( - (s) => userRegExp.hasMatch(s), 'invalid format'), + (s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()), ], ); password = FieldCubit( initalValue: isEdit ? user!.password : genPass(), validations: [ - RequiredStringValidation('required'), - ValidationModel( - (s) => passwordRegExp.hasMatch(s), 'invalid format'), + RequiredStringValidation('validations.required'.tr()), + ValidationModel((s) => passwordRegExp.hasMatch(s), + 'validations.invalid_format'.tr()), ], ); @@ -42,7 +49,7 @@ class UserFormCubit extends FormCubit { login: login.state.value, password: password.state.value, ); - usersCubit.addUser(user); + jobsCubit.addJob(CreateUserJob(user: user)); } late FieldCubit login; @@ -52,5 +59,5 @@ class UserFormCubit extends FormCubit { password.externalSetValue(genPass()); } - late UsersCubit usersCubit; + final JobsCubit jobsCubit; } diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index 6fa19f6851..aebe2ddd47 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -1,27 +1,53 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:selfprivacy/logic/models/job.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; +import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/logic/models/user.dart'; export 'package:provider/provider.dart'; part 'jobs_state.dart'; class JobsCubit extends Cubit { - JobsCubit() : super(JobsState.emtpy()); + JobsCubit(this.usersCubit) : super(JobsStateEmpty()); - List jobsList = []; + final api = ServerApi(); + final UsersCubit usersCubit; void addJob(Job job) { - final newState = state.addJob(job); - emit(newState); + var newJobsList = []; + if (state is JobsStateWithJobs) { + newJobsList.addAll((state as JobsStateWithJobs).jobList); + } + newJobsList.add(job); + emit(JobsStateWithJobs(newJobsList)); } void removeJob(String id) { - final newState = state.removeById(id); + final newState = (state as JobsStateWithJobs).removeById(id); emit(newState); } - void applyAll() { - print(state.jobList); - emit(JobsState.emtpy()); + Future applyAll() async { + if (state is JobsStateWithJobs) { + var jobs = (state as JobsStateWithJobs).jobList; + emit(JobsStateLoading()); + + var newUsers = []; + for (var job in jobs) { + if (job is CreateUserJob) { + newUsers.add(job.user); + await api.createUser(job.user); + } + } + + usersCubit.addUsers(newUsers); + await api.apply(); + + emit(JobsStateEmpty()); + + getIt().navigator!.pop(); + } } } diff --git a/lib/logic/cubit/jobs/jobs_state.dart b/lib/logic/cubit/jobs/jobs_state.dart index e761b8170a..972f4b3dd7 100644 --- a/lib/logic/cubit/jobs/jobs_state.dart +++ b/lib/logic/cubit/jobs/jobs_state.dart @@ -1,25 +1,27 @@ part of 'jobs_cubit.dart'; -class JobsState extends Equatable { - const JobsState(this.jobList); +abstract class JobsState extends Equatable { + @override + List get props => []; +} +class JobsStateLoading extends JobsState {} + +class JobsStateEmpty extends JobsState {} + +class JobsStateWithJobs extends JobsState { + JobsStateWithJobs(this.jobList); final List jobList; - static JobsState emtpy() => JobsState([]); - - bool get isEmpty => jobList.isEmpty; - - JobsState addJob(Job job) { - var newJobsList = [...jobList]; - newJobsList.add(job); - return JobsState(newJobsList); - } - JobsState removeById(String id) { var newJobsList = jobList.where((element) => element.id != id).toList(); - return JobsState(newJobsList); + + if (newJobsList.isEmpty) { + return JobsStateEmpty(); + } + return JobsStateWithJobs(newJobsList); } @override - List get props => jobList; + List get props => jobList; } diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 6e00f7e9da..ecc0c89767 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -1,23 +1,35 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/models/user.dart'; export 'package:provider/provider.dart'; part 'users_state.dart'; class UsersCubit extends Cubit { - UsersCubit() : super(UsersState([])); + UsersCubit() : super(UsersState([])); + Box box = Hive.box(BNames.users); - void addUser(User user) { - var users = [...state.users]; - users.add(user); - - emit(UsersState(users)); + void load() async { + var loadedUsers = box.values.toList(); + if (loadedUsers.isNotEmpty) { + emit(UsersState(loadedUsers)); + } } - void remove(User? user) { + void addUsers(List users) async { + var newUserList = [...state.users, ...users]; + + await box.addAll(users); + emit(UsersState(newUserList)); + } + + void remove(User user) async { var users = [...state.users]; + var index = users.indexOf(user); users.remove(user); + await box.deleteAt(index); emit(UsersState(users)); } diff --git a/lib/logic/models/job.dart b/lib/logic/models/jobs/job.dart similarity index 53% rename from lib/logic/models/job.dart rename to lib/logic/models/jobs/job.dart index e1161daee9..3d6bc386d5 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/jobs/job.dart @@ -1,6 +1,10 @@ import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; import 'package:selfprivacy/utils/password_generator2.dart'; +import '../user.dart'; + +@immutable class Job extends Equatable { Job({ String? id, @@ -13,3 +17,14 @@ class Job extends Equatable { @override List get props => [id, title]; } + +class CreateUserJob extends Job { + CreateUserJob({ + required this.user, + }) : super(title: 'Create ${user.login}'); + + final User user; + + @override + List get props => [id, title]; +} diff --git a/lib/main.dart b/lib/main.dart index 231a7dca28..67199de055 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,40 +23,41 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); - runApp( - Localization( - child: BlocAndProviderConfig( - child: MyApp(), - ), - ), - ); + runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - AppSettingsState appSettings = context.watch().state; + return Localization( + child: BlocAndProviderConfig( + child: Builder(builder: (context) { + var appSettings = context.watch().state; - return AnnotatedRegion( - value: SystemUiOverlayStyle.light, // Manually changnig appbar color - child: MaterialApp( - navigatorKey: getIt.get().navigatorKey, - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - debugShowCheckedModeBanner: false, - title: 'SelfPrivacy', - theme: appSettings.isDarkModeOn ? darkTheme : ligtTheme, - home: appSettings.isOnbordingShowing - ? OnboardingPage(nextPage: InitializingPage()) - : RootPage(), - builder: (BuildContext context, Widget? widget) { - Widget error = Text('...rendering error...'); - if (widget is Scaffold || widget is Navigator) - error = Scaffold(body: Center(child: error)); - ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error; - return widget!; - }, + return AnnotatedRegion( + value: SystemUiOverlayStyle.light, // Manually changnig appbar color + child: MaterialApp( + navigatorKey: getIt.get().navigatorKey, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + debugShowCheckedModeBanner: false, + title: 'SelfPrivacy', + theme: appSettings.isDarkModeOn ? darkTheme : ligtTheme, + home: appSettings.isOnbordingShowing + ? OnboardingPage(nextPage: InitializingPage()) + : RootPage(), + builder: (BuildContext context, Widget? widget) { + Widget error = Text('...rendering error...'); + if (widget is Scaffold || widget is Navigator) + error = Scaffold(body: Center(child: error)); + ErrorWidget.builder = + (FlutterErrorDetails errorDetails) => error; + return widget!; + }, + ), + ); + }), ), ); } diff --git a/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart b/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart index 3ed4452590..d5718181b2 100644 --- a/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart +++ b/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart @@ -1,25 +1,58 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; class BrandBottomSheet extends StatelessWidget { - const BrandBottomSheet({Key? key, required this.child}) : super(key: key); + const BrandBottomSheet({ + Key? key, + required this.child, + this.isExpended = false, + }) : super(key: key); final Widget child; + final bool isExpended; @override Widget build(BuildContext context) { - return Container( - height: MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 60, - child: Scaffold( - body: SingleChildScrollView( - physics: ClampingScrollPhysics(), - child: Container( - padding: paddingH15V0, - child: child, + var mainHeight = MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + 100; + late Widget innerWidget; + if (isExpended) { + innerWidget = Scaffold( + body: child, + ); + } else { + final ThemeData themeData = Theme.of(context); + + innerWidget = Material( + color: themeData.scaffoldBackgroundColor, + child: IntrinsicHeight(child: child), + ); + } + return ConstrainedBox( + constraints: BoxConstraints(maxHeight: mainHeight + 4 + 6), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Container( + height: 4, + width: 30, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + color: BrandColors.gray4, + ), + ), ), - ), + SizedBox(height: 6), + ClipRRect( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + child: ConstrainedBox( + constraints: BoxConstraints(maxHeight: mainHeight), + child: innerWidget, + ), + ), + ], ), ); } diff --git a/lib/ui/components/brand_loader/brand_loader.dart b/lib/ui/components/brand_loader/brand_loader.dart new file mode 100644 index 0000000000..52b1b820d8 --- /dev/null +++ b/lib/ui/components/brand_loader/brand_loader.dart @@ -0,0 +1,21 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:easy_localization/easy_localization.dart'; + +class BrandLoader { + static horizontal() => _HorizontalLoader(); +} + +class _HorizontalLoader extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('basis.wait'.tr()), + SizedBox(height: 10), + LinearProgressIndicator(minHeight: 3), + ], + ); + } +} diff --git a/lib/ui/components/brand_md/brand_md.dart b/lib/ui/components/brand_md/brand_md.dart index c130136c72..230b56194a 100644 --- a/lib/ui/components/brand_md/brand_md.dart +++ b/lib/ui/components/brand_md/brand_md.dart @@ -56,6 +56,7 @@ class _BrandMarkdownState extends State { ), ); return Markdown( + shrinkWrap: true, styleSheet: markdown, onTapLink: (String text, String? href, String title) { if (href != null) { diff --git a/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart b/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart index 7670c94dbd..6435f7bc38 100644 --- a/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart +++ b/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart @@ -1,63 +1,63 @@ -import 'package:flutter/material.dart'; +// import 'package:flutter/material.dart'; -var navigatorKey = GlobalKey(); +// var navigatorKey = GlobalKey(); -class BrandModalSheet extends StatelessWidget { - const BrandModalSheet({ - Key? key, - this.child, - }) : super(key: key); +// class BrandModalSheet extends StatelessWidget { +// const BrandModalSheet({ +// Key? key, +// this.child, +// }) : super(key: key); - final Widget? child; - @override - Widget build(BuildContext context) { - return DraggableScrollableSheet( - minChildSize: 0.95, - initialChildSize: 1, - maxChildSize: 1, - builder: (context, scrollController) { - return SingleChildScrollView( - controller: scrollController, - physics: ClampingScrollPhysics(), - child: Container( - child: Column( - children: [ - GestureDetector( - onTap: () => Navigator.of(context).pop(), - behavior: HitTestBehavior.opaque, - child: Container( - width: double.infinity, - child: Center( - child: Padding( - padding: EdgeInsets.only(top: 132, bottom: 6), - child: Container( - height: 4, - width: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2), - color: Color(0xFFE3E3E3).withOpacity(0.65), - ), - ), - ), - ), - ), - ), - Container( - constraints: BoxConstraints( - minHeight: MediaQuery.of(context).size.height - 132, - maxHeight: MediaQuery.of(context).size.height - 132, - ), - decoration: BoxDecoration( - borderRadius: - BorderRadius.vertical(top: Radius.circular(20)), - color: Theme.of(context).scaffoldBackgroundColor, - ), - width: double.infinity, - child: child), - ], - ), - ), - ); - }); - } -} +// final Widget? child; +// @override +// Widget build(BuildContext context) { +// return DraggableScrollableSheet( +// minChildSize: 1, +// initialChildSize: 1, +// maxChildSize: 1, +// builder: (context, scrollController) { +// return SingleChildScrollView( +// controller: scrollController, +// physics: ClampingScrollPhysics(), +// child: Container( +// child: Column( +// children: [ +// GestureDetector( +// onTap: () => Navigator.of(context).pop(), +// behavior: HitTestBehavior.opaque, +// child: Container( +// width: double.infinity, +// child: Center( +// child: Padding( +// padding: EdgeInsets.only(top: 132, bottom: 6), +// child: Container( +// height: 4, +// width: 30, +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(2), +// color: Color(0xFFE3E3E3).withOpacity(0.65), +// ), +// ), +// ), +// ), +// ), +// ), +// Container( +// constraints: BoxConstraints( +// minHeight: MediaQuery.of(context).size.height - 132, +// maxHeight: MediaQuery.of(context).size.height - 132, +// ), +// decoration: BoxDecoration( +// borderRadius: +// BorderRadius.vertical(top: Radius.circular(20)), +// color: Theme.of(context).scaffoldBackgroundColor, +// ), +// width: double.infinity, +// child: child), +// ], +// ), +// ), +// ); +// }); +// } +// } diff --git a/lib/ui/components/brand_text/brand_text.dart b/lib/ui/components/brand_text/brand_text.dart index 1acbb4e8ba..9f272782e2 100644 --- a/lib/ui/components/brand_text/brand_text.dart +++ b/lib/ui/components/brand_text/brand_text.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/config/text_themes.dart'; +export 'package:selfprivacy/utils/extensions/text_extensions.dart'; enum TextType { h1, // right now only at onboarding and opened providers diff --git a/lib/ui/components/jobs_content/jobs_content.dart b/lib/ui/components/jobs_content/jobs_content.dart index c89773ab82..aa4b1c6555 100644 --- a/lib/ui/components/jobs_content/jobs_content.dart +++ b/lib/ui/components/jobs_content/jobs_content.dart @@ -1,9 +1,13 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; +import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; class JobsContent extends StatelessWidget { @@ -11,57 +15,71 @@ class JobsContent extends StatelessWidget { @override Widget build(BuildContext context) { - var jobs = context.watch().state; - return Column( - children: [ - SizedBox(height: 15), - Center( - child: BrandText.h2( - 'jobs.title'.tr(), - ), - ), - if (jobs.isEmpty) - Padding( - padding: const EdgeInsets.only(top: 50), - child: BrandText.body1('jobs.empty'.tr()), - ), - if (!jobs.isEmpty) ...[ - ...jobs.jobList - .map( - (j) => Row( - children: [ - Expanded( - child: BrandCards.small( - child: Row( - children: [ - BrandText.body1(j.title), - ], + return BlocBuilder( + builder: (context, state) { + late final List widgets; + if (state is JobsStateEmpty) { + widgets = [ + SizedBox(height: 80), + Center(child: BrandText.body1('jobs.empty'.tr())), + ]; + } else if (state is JobsStateLoading) { + widgets = [ + SizedBox(height: 80), + BrandLoader.horizontal(), + ]; + } else if (state is JobsStateWithJobs) { + widgets = [ + ...state.jobList + .map( + (j) => Row( + children: [ + Expanded( + child: BrandCards.small( + child: Row( + children: [ + BrandText.body1(j.title), + ], + ), ), ), - ), - SizedBox(width: 10), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: BrandColors.red1, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + SizedBox(width: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: BrandColors.red1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), ), + onPressed: () => + context.read().removeJob(j.id), + child: Text('basis.remove'.tr()), ), - onPressed: () => - context.read().removeJob(j.id), - child: Text('Remove'), - ), - ], - ), - ) - .toList(), - SizedBox(height: 20), - BrandButton.rised( - onPressed: () => context.read().applyAll(), - text: 'jobs.start'.tr(), - ), - ], - ], + ], + ), + ) + .toList(), + SizedBox(height: 20), + BrandButton.rised( + onPressed: () => context.read().applyAll(), + text: 'jobs.start'.tr(), + ), + ]; + } + return ListView( + padding: paddingH15V0, + children: [ + SizedBox(height: 15), + Center( + child: BrandText.h2( + 'jobs.title'.tr(), + ), + ), + SizedBox(height: 20), + ...widgets + ], + ); + }, ); } } diff --git a/lib/ui/components/pre_styled_buttons/flash.dart b/lib/ui/components/pre_styled_buttons/flash.dart index f591c3b11a..5e9b187552 100644 --- a/lib/ui/components/pre_styled_buttons/flash.dart +++ b/lib/ui/components/pre_styled_buttons/flash.dart @@ -15,12 +15,25 @@ class _BrandFlashButtonState extends State<_BrandFlashButton> @override void initState() { _animationController = - AnimationController(vsync: this, duration: Duration(milliseconds: 600)); + AnimationController(vsync: this, duration: Duration(milliseconds: 800)); _colorTween = ColorTween( begin: BrandColors.black, end: BrandColors.primary, ).animate(_animationController); + super.initState(); + WidgetsBinding.instance!.addPostFrameCallback(_afterLayout); + } + + void _afterLayout(_) { + if (Theme.of(context).brightness == Brightness.dark) { + setState(() { + _colorTween = ColorTween( + begin: BrandColors.white, + end: BrandColors.primary, + ).animate(_animationController); + }); + } } @override @@ -29,29 +42,27 @@ class _BrandFlashButtonState extends State<_BrandFlashButton> super.dispose(); } - late bool wasPrevStateIsEmpty; + bool wasPrevStateIsEmpty = true; @override Widget build(BuildContext context) { - var hasNoJobs = context.watch().state.isEmpty; - wasPrevStateIsEmpty = hasNoJobs; - var icon = hasNoJobs ? Ionicons.flash_outline : Ionicons.flash; - return BlocListener( listener: (context, state) { - if (wasPrevStateIsEmpty && state.jobList.isNotEmpty) { + if (wasPrevStateIsEmpty && state is! JobsStateEmpty) { wasPrevStateIsEmpty = false; _animationController.forward(); - } else if (!wasPrevStateIsEmpty && state.jobList.isEmpty) { + } else if (!wasPrevStateIsEmpty && state is JobsStateEmpty) { + wasPrevStateIsEmpty = true; + _animationController.reverse(); } }, child: IconButton( onPressed: () { - showCupertinoModalBottomSheet( - expand: false, + showBrandBottomSheet( context: context, builder: (context) => BrandBottomSheet( + isExpended: true, child: JobsContent(), ), ); @@ -59,9 +70,14 @@ class _BrandFlashButtonState extends State<_BrandFlashButton> icon: AnimatedBuilder( animation: _colorTween, builder: (context, child) { - return Icon( - icon, - color: _colorTween.value, + var v = _animationController.value; + var icon = v > 0.5 ? Ionicons.flash : Ionicons.flash_outline; + return Transform.scale( + scale: 1 + (v < 0.5 ? v : 1 - v) * 2, + child: Icon( + icon, + color: _colorTween.value, + ), ); }), ), diff --git a/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart b/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart index f2ce87c95a..e9b7cc85ba 100644 --- a/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart +++ b/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:ionicons/ionicons.dart'; import 'package:selfprivacy/config/brand_colors.dart'; @@ -6,8 +7,8 @@ import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; part 'close.dart'; part 'flash.dart'; diff --git a/lib/ui/components/progress_bar/progress_bar.dart b/lib/ui/components/progress_bar/progress_bar.dart index a75c44fa6a..41b23c8654 100644 --- a/lib/ui/components/progress_bar/progress_bar.dart +++ b/lib/ui/components/progress_bar/progress_bar.dart @@ -53,9 +53,9 @@ class _ProgressBarState extends State { width: 10, ), ); - even.add( + odd.add( SizedBox( - width: 10, + width: 20, ), ); diff --git a/lib/ui/helpers/modals.dart b/lib/ui/helpers/modals.dart new file mode 100644 index 0000000000..69f6b6d805 --- /dev/null +++ b/lib/ui/helpers/modals.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; + +Future showBrandBottomSheet({ + required BuildContext context, + required WidgetBuilder builder, +}) => + showCupertinoModalBottomSheet( + builder: builder, + barrierColor: Colors.black45, + context: context, + shadow: BoxShadow(color: Colors.transparent), + backgroundColor: Colors.transparent, + ); diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index ea0e854413..202d22f45b 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -10,10 +10,10 @@ import 'package:selfprivacy/logic/cubit/forms/initializing/hetzner_form_cubit.da import 'package:selfprivacy/logic/cubit/forms/initializing/root_user_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; +import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; -import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart'; import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; @@ -36,7 +36,7 @@ class InitializingPage extends StatelessWidget { () => _stepCheck(cubit), () => _stepCheck(cubit), () => _stepCheck(cubit), - () => Container(child: Text('Everythigng is initialized')) + () => Container(child: Center(child: Text('initializing.finish'.tr()))) ][cubit.state.progress](); return BlocListener( listener: (context, state) { @@ -59,12 +59,9 @@ class InitializingPage extends StatelessWidget { 'Domain', 'User', 'Server', - ' ✅', - ' ✅', - ' ✅', - ' ✅', + '✅ Check', ], - activeIndex: cubit.state.progress, + activeIndex: cubit.state.porgressBar, ), ), _addCard( @@ -443,21 +440,29 @@ class InitializingPage extends StatelessWidget { Widget _stepCheck(AppConfigCubit appConfigCubit) { assert(appConfigCubit.state is TimerState, 'wronge state'); var state = appConfigCubit.state as TimerState; - + late int doneCount; late String? text; if (state.isServerResetedSecondTime) { text = 'initializing.13'.tr(); + doneCount = 3; } else if (state.isServerResetedFirstTime) { text = 'initializing.21'.tr(); + doneCount = 2; } else if (state.isServerStarted) { text = 'initializing.14'.tr(); + doneCount = 1; } else if (state.isServerCreated) { text = 'initializing.15'.tr(); + doneCount = 0; } return Builder(builder: (context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox(height: 15), + BrandText.h4( + 'initializing.checks'.tr(args: [doneCount.toString(), "4"]), + ), Spacer(flex: 2), SizedBox(height: 10), BrandText.body2(text), @@ -501,12 +506,14 @@ class _HowHetzner extends StatelessWidget { @override Widget build(BuildContext context) { - return BrandModalSheet( + return BrandBottomSheet( + isExpended: true, child: Padding( - padding: paddingH15V0.copyWith(top: 25), - child: BrandMarkdown( - fileName: 'how_hetzner', - )), + padding: paddingH15V0, + child: BrandMarkdown( + fileName: 'how_hetzner', + ), + ), ); } } diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 44e0bcfd6b..5e7ec7a6b9 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -3,21 +3,17 @@ import 'package:selfprivacy/config/brand_theme.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/cubit/providers/providers_cubit.dart'; -import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/provider.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; +import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; -import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; -import 'package:selfprivacy/ui/components/one_page/one_page.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; import 'package:selfprivacy/ui/pages/server_details/server_details.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/utils/route_transitions/slide_bottom.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; var navigatorKey = GlobalKey(); @@ -79,7 +75,7 @@ class _Card extends StatelessWidget { String? message; late String stableText; late VoidCallback onTap; - + var isReady = context.watch().state.isFullyInitilized; AppConfigState appConfig = context.watch().state; var domainName = @@ -89,24 +85,22 @@ class _Card extends StatelessWidget { case ProviderType.server: title = 'providers.server.card_title'.tr(); stableText = 'providers.server.status'.tr(); - onTap = () => Navigator.of(context).push( - SlideBottomRoute( - OnePage( - title: title, - child: ServerDetails(), - ), + onTap = () => showBrandBottomSheet( + context: context, + builder: (context) => BrandBottomSheet( + isExpended: true, + child: ServerDetails(), ), ); + break; case ProviderType.domain: title = 'providers.domain.card_title'.tr(); message = domainName; stableText = 'providers.domain.status'.tr(); - onTap = () => showModalBottomSheet( + onTap = () => showBrandBottomSheet( context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, builder: (BuildContext context) { return _ProviderDetails( provider: provider, @@ -133,7 +127,7 @@ class _Card extends StatelessWidget { break; } return GestureDetector( - onTap: onTap, + onTap: isReady ? onTap : null, child: BrandCards.big( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -199,45 +193,34 @@ class _ProviderDetails extends StatelessWidget { 'providers.backup.bottom_sheet.2'.tr(args: [domainName, 'Time'])), SizedBox(height: 10), BrandText.body1('providers.backup.status'.tr()), - BrandButton.rised( - onPressed: () => - context.read().addJob(Job(title: 'text')), - text: 'add job', - ) ]; break; } - return BrandModalSheet( - child: Navigator( - key: navigatorKey, - initialRoute: '/', - onGenerateRoute: (_) { - return materialRoute( - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 40), - Padding( - padding: paddingH15V0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IconStatusMask( - status: provider.state, - child: - Icon(provider.icon, size: 40, color: Colors.white), - ), - SizedBox(height: 10), - BrandText.h1(title), - SizedBox(height: 10), - ...children - ], + return BrandBottomSheet( + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 40), + Padding( + padding: paddingH15V0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + IconStatusMask( + status: provider.state, + child: Icon(provider.icon, size: 40, color: Colors.white), ), - ) - ], - ), - ); - }, + SizedBox(height: 10), + BrandText.h1(title), + SizedBox(height: 10), + ...children, + SizedBox(height: 30), + ], + ), + ) + ], + ), ), ); } diff --git a/lib/ui/pages/server_details/cpu_chart.dart b/lib/ui/pages/server_details/cpu_chart.dart index da1283d460..88163caaf8 100644 --- a/lib/ui/pages/server_details/cpu_chart.dart +++ b/lib/ui/pages/server_details/cpu_chart.dart @@ -82,7 +82,6 @@ class CpuChart extends StatelessWidget { double appliedInterval, double value, ) { - print(value); if (value < 0) { return false; } else if (value == 0) { diff --git a/lib/ui/pages/server_details/server_details.dart b/lib/ui/pages/server_details/server_details.dart index fd1200d7d8..965e018bd7 100644 --- a/lib/ui/pages/server_details/server_details.dart +++ b/lib/ui/pages/server_details/server_details.dart @@ -56,41 +56,58 @@ class _ServerDetailsState extends State var isReady = context.watch().state.isFullyInitilized; var providerState = isReady ? StateType.stable : StateType.uninitialized; - return TabBarView( - physics: NeverScrollableScrollPhysics(), - controller: tabController, - children: [ - SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: paddingH15V0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _Header( - providerState: providerState, - tabController: tabController), - BrandText.body1('providers.server.bottom_sheet.1'.tr()), - SizedBox(height: 10), - BlocProvider( - create: (context) => HetznerMetricsCubit()..restart(), - child: _Chart(), - ), - SizedBox(height: 20), - BlocProvider( - create: (context) => ServerDetailsCubit()..check(), - child: _TextDetails(), - ), - ], - ), - ), - ], - ), + return Scaffold( + appBar: PreferredSize( + child: Column( + children: [ + Container( + height: 51, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 15), + child: BrandText.h4('basis.details'.tr()), + ), + BrandDivider(), + ], ), - _ServerSettings(tabController: tabController), - ], + preferredSize: Size.fromHeight(52), + ), + body: TabBarView( + physics: NeverScrollableScrollPhysics(), + controller: tabController, + children: [ + SingleChildScrollView( + physics: ClampingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: paddingH15V0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _Header( + providerState: providerState, + tabController: tabController), + BrandText.body1('providers.server.bottom_sheet.1'.tr()), + SizedBox(height: 10), + BlocProvider( + create: (context) => HetznerMetricsCubit()..restart(), + child: _Chart(), + ), + SizedBox(height: 20), + BlocProvider( + create: (context) => ServerDetailsCubit()..check(), + child: _TextDetails(), + ), + ], + ), + ), + ], + ), + ), + _ServerSettings(tabController: tabController), + ], + ), ); } } diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index d0fee3257a..3957e22f7e 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -108,20 +108,23 @@ class _Card extends StatelessWidget { var isReady = context.watch().state.isFullyInitilized; var changeTab = context.read().onPress; return GestureDetector( - onTap: () => showDialog( - context: context, - // isScrollControlled: true, - // backgroundColor: Colors.transparent, - builder: (BuildContext context) { - return _ServiceDetails( - serviceType: serviceType, - status: isReady ? StateType.stable : StateType.uninitialized, - title: title, - icon: iconData, - changeTab: changeTab, - ); - }, - ), + onTap: isReady + ? () => showDialog( + context: context, + // isScrollControlled: true, + // backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return _ServiceDetails( + serviceType: serviceType, + status: + isReady ? StateType.stable : StateType.uninitialized, + title: title, + icon: iconData, + changeTab: changeTab, + ); + }, + ) + : null, child: BrandCards.big( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/ui/pages/users/new_user.dart b/lib/ui/pages/users/new_user.dart index 04792623d1..4f6da178a6 100644 --- a/lib/ui/pages/users/new_user.dart +++ b/lib/ui/pages/users/new_user.dart @@ -7,10 +7,26 @@ class _NewUser extends StatelessWidget { var domainName = UiHelpers.getDomainName(config); - return BrandModalSheet( + return BrandBottomSheet( child: BlocProvider( - create: (context) => - UserFormCubit(usersCubit: context.read()), + create: (context) { + var jobCubit = context.read(); + var jobState = jobCubit.state; + var users = []; + users.addAll(context.read().state.users); + if (jobState is JobsStateWithJobs) { + var jobs = jobState.jobList; + jobs.forEach((job) { + if (job is CreateUserJob) { + users.add(job.user); + } + }); + } + return UserFormCubit( + jobsCubit: jobCubit, + users: users, + ); + }, child: Builder(builder: (context) { var formCubitState = context.watch().state; @@ -22,6 +38,7 @@ class _NewUser extends StatelessWidget { }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ BrandHeader( title: 'users.new_user'.tr(), @@ -30,12 +47,15 @@ class _NewUser extends StatelessWidget { Padding( padding: paddingH15V0, child: Column( + mainAxisSize: MainAxisSize.min, children: [ - CubitFormTextField( - formFieldCubit: context.read().login, - decoration: InputDecoration( - labelText: 'users.login'.tr(), - suffixText: '@$domainName', + IntrinsicHeight( + child: CubitFormTextField( + formFieldCubit: context.read().login, + decoration: InputDecoration( + labelText: 'users.login'.tr(), + suffixText: '@$domainName', + ), ), ), SizedBox(height: 20), diff --git a/lib/ui/pages/users/user.dart b/lib/ui/pages/users/user.dart index dc7249483b..844082b75a 100644 --- a/lib/ui/pages/users/user.dart +++ b/lib/ui/pages/users/user.dart @@ -1,17 +1,15 @@ part of 'users.dart'; class _User extends StatelessWidget { - const _User({Key? key, this.user}) : super(key: key); + const _User({Key? key, required this.user}) : super(key: key); - final User? user; + final User user; @override Widget build(BuildContext context) { return InkWell( onTap: () { - showModalBottomSheet( + showBrandBottomSheet( context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, builder: (BuildContext context) { return _UserDetails(user: user); }, @@ -26,12 +24,12 @@ class _User extends StatelessWidget { width: 17, height: 17, decoration: BoxDecoration( - color: user!.color, + color: user.color, shape: BoxShape.circle, ), ), SizedBox(width: 20), - BrandText.h4(user!.login), + BrandText.h4(user.login), ], ), ), diff --git a/lib/ui/pages/users/user_details.dart b/lib/ui/pages/users/user_details.dart index 517c98ab4f..8faf5a83af 100644 --- a/lib/ui/pages/users/user_details.dart +++ b/lib/ui/pages/users/user_details.dart @@ -3,10 +3,10 @@ part of 'users.dart'; class _UserDetails extends StatelessWidget { const _UserDetails({ Key? key, - this.user, + required this.user, }) : super(key: key); - final User? user; + final User user; @override Widget build(BuildContext context) { @@ -14,14 +14,16 @@ class _UserDetails extends StatelessWidget { var domainName = UiHelpers.getDomainName(config); - return BrandModalSheet( + return BrandBottomSheet( + isExpended: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ Container( height: 200, decoration: BoxDecoration( - color: user!.color, + color: user.color, borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), @@ -114,7 +116,7 @@ class _UserDetails extends StatelessWidget { horizontal: 15, ), child: BrandText.h1( - user!.login, + user.login, softWrap: true, overflow: TextOverflow.ellipsis, )), @@ -131,14 +133,14 @@ class _UserDetails extends StatelessWidget { Container( height: 40, alignment: Alignment.centerLeft, - child: BrandText.h4('${user!.login}@$domainName'), + child: BrandText.h4('${user.login}@$domainName'), ), SizedBox(height: 14), BrandText.small('basis.password'.tr()), Container( height: 40, alignment: Alignment.centerLeft, - child: BrandText.h4(user!.password), + child: BrandText.h4(user.password), ), SizedBox(height: 24), BrandDivider(), @@ -146,7 +148,10 @@ class _UserDetails extends StatelessWidget { BrandButton.emptyWithIconText( title: 'users.send_regisration_data'.tr(), icon: Icon(BrandIcons.share), - onPressed: () {}, + onPressed: () { + Share.share( + 'login: ${user.login}, password: ${user.password}'); + }, ), SizedBox(height: 20), ], diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index 6200e5d9b2..da994ef962 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -4,17 +4,21 @@ import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/user/user_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; +import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; +import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.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'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; +import 'package:share_plus/share_plus.dart'; part 'fab.dart'; part 'new_user.dart'; @@ -31,7 +35,6 @@ class UsersPage extends StatelessWidget { var isReady = context.watch().state.isFullyInitilized; final users = usersCubitState.users; final isEmpty = usersCubitState.isEmpty; - Widget child; if (!isReady) { @@ -46,7 +49,7 @@ class UsersPage extends StatelessWidget { ) : ListView( children: [ - ...users.map((user) => _User(user: user)), + ...users.map((user) => _User(user: user)).toList(), ], ); } diff --git a/lib/utils/extensions/text_extensions.dart b/lib/utils/extensions/text_extensions.dart new file mode 100644 index 0000000000..7e378d0cfe --- /dev/null +++ b/lib/utils/extensions/text_extensions.dart @@ -0,0 +1,51 @@ +import 'dart:ui'; +import 'package:flutter/cupertino.dart'; + +extension TextExtension on Text { + Text withColor(Color color) => Text( + data!, + key: this.key, + strutStyle: this.strutStyle, + textAlign: this.textAlign, + textDirection: this.textDirection, + locale: this.locale, + softWrap: this.softWrap, + overflow: this.overflow, + textScaleFactor: this.textScaleFactor, + maxLines: this.maxLines, + semanticsLabel: this.semanticsLabel, + textWidthBasis: textWidthBasis ?? this.textWidthBasis, + style: this.style != null + ? this.style!.copyWith(color: color) + : TextStyle(color: color), + ); + + Text copyWith({ + Key? key, + StrutStyle? strutStyle, + TextAlign? textAlign, + TextDirection? textDirection, + Locale? locale, + bool? softWrap, + TextOverflow? overflow, + double? textScaleFactor, + int? maxLines, + String? semanticsLabel, + TextWidthBasis? textWidthBasis, + TextStyle? style, + }) { + return Text(data!, + key: key ?? this.key, + strutStyle: strutStyle ?? this.strutStyle, + textAlign: textAlign ?? this.textAlign, + textDirection: textDirection ?? this.textDirection, + locale: locale ?? this.locale, + softWrap: softWrap ?? this.softWrap, + overflow: overflow ?? this.overflow, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + maxLines: maxLines ?? this.maxLines, + semanticsLabel: semanticsLabel ?? this.semanticsLabel, + textWidthBasis: textWidthBasis ?? this.textWidthBasis, + style: style != null ? this.style?.merge(style) ?? style : this.style); + } +} diff --git a/pubspec.lock b/pubspec.lock index d35a69f0ad..dca1a9511c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "19.0.0" + version: "22.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.1" archive: dependency: transitive description: @@ -35,14 +35,14 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" basic_utils: dependency: "direct dev" description: name: basic_utils url: "https://pub.dartlang.org" source: hosted - version: "3.0.0-nullsafety.3" + version: "3.1.0" bloc: dependency: transitive description: @@ -63,7 +63,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" build_config: dependency: transitive description: @@ -84,7 +84,7 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.3" build_runner: dependency: "direct dev" description: @@ -105,14 +105,14 @@ packages: name: built_collection url: "https://pub.dartlang.org" source: hosted - version: "5.0.0" + version: "5.1.0" built_value: dependency: transitive description: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.0.4" + version: "8.1.0" characters: dependency: transitive description: @@ -175,7 +175,7 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" crypt: dependency: "direct main" description: @@ -189,28 +189,28 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.1" cubit_form: dependency: "direct main" description: name: cubit_form url: "https://pub.dartlang.org" source: hosted - version: "1.0.2-nullsafety.0" + version: "1.0.16" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" dart_style: dependency: transitive description: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" dio: dependency: "direct main" description: @@ -252,7 +252,14 @@ packages: name: equatable url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.3" + extended_masked_text: + dependency: transitive + description: + name: extended_masked_text + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" fake_async: dependency: transitive description: @@ -266,14 +273,14 @@ packages: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.2" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.1.2" fixnum: dependency: transitive description: @@ -299,7 +306,7 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "7.0.1" flutter_launcher_icons: dependency: "direct dev" description: @@ -318,14 +325,14 @@ packages: name: flutter_markdown url: "https://pub.dartlang.org" source: hosted - version: "0.6.1" + version: "0.6.2" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.2.0" flutter_test: dependency: "direct dev" description: flutter @@ -342,7 +349,7 @@ packages: name: get_it url: "https://pub.dartlang.org" source: hosted - version: "6.0.0" + version: "6.1.1" glob: dependency: transitive description: @@ -363,35 +370,35 @@ packages: name: hive url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.4" hive_flutter: dependency: "direct main" description: name: hive_flutter url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.0" hive_generator: dependency: "direct dev" description: name: hive_generator url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.1.0" http: dependency: transitive description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.1" + version: "0.13.3" http_multi_server: dependency: transitive description: name: http_multi_server url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.1" http_parser: dependency: transitive description: @@ -447,7 +454,7 @@ packages: name: json_serializable url: "https://pub.dartlang.org" source: hosted - version: "4.1.0" + version: "4.1.3" logging: dependency: transitive description: @@ -462,13 +469,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" - mask_text_input_formatter: - dependency: transitive - description: - name: mask_text_input_formatter - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0-nullsafety.2" matcher: dependency: transitive description: @@ -517,7 +517,7 @@ packages: name: node_preamble url: "https://pub.dartlang.org" source: hosted - version: "1.4.13" + version: "2.0.1" package_config: dependency: transitive description: @@ -531,7 +531,7 @@ packages: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" path: dependency: transitive description: @@ -545,7 +545,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" path_provider_linux: dependency: transitive description: @@ -573,21 +573,21 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.11.0" + version: "1.11.1" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "4.0.2" + version: "4.1.0" platform: dependency: transitive description: @@ -608,7 +608,7 @@ packages: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.1.2" pool: dependency: transitive description: @@ -651,13 +651,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + share_plus: + dependency: "direct main" + description: + name: share_plus + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + share_plus_linux: + dependency: transitive + description: + name: share_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + share_plus_macos: + dependency: transitive + description: + name: share_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + share_plus_web: + dependency: transitive + description: + name: share_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + share_plus_windows: + dependency: transitive + description: + name: share_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" shared_preferences: dependency: transitive description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.6" shared_preferences_linux: dependency: transitive description: @@ -699,7 +741,7 @@ packages: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.4" shelf_packages_handler: dependency: transitive description: @@ -732,7 +774,14 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" source_map_stack_trace: dependency: transitive description: @@ -753,7 +802,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -795,21 +844,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.16.5" + version: "1.16.8" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.15" + version: "0.3.19" timing: dependency: transitive description: @@ -837,7 +886,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.3" + version: "6.0.6" url_launcher_linux: dependency: transitive description: @@ -858,14 +907,14 @@ packages: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" url_launcher_windows: dependency: transitive description: @@ -886,35 +935,35 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "6.1.0+1" + version: "6.2.0" wakelock: dependency: "direct main" description: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.5.0+2" + version: "0.5.2" wakelock_macos: dependency: transitive description: name: wakelock_macos url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "0.1.0+1" wakelock_platform_interface: dependency: transitive description: name: wakelock_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.1+1" wakelock_web: dependency: transitive description: name: wakelock_web url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.0+1" wakelock_windows: dependency: transitive description: @@ -935,7 +984,7 @@ packages: name: web_socket_channel url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" webkit_inspection_protocol: dependency: transitive description: @@ -949,7 +998,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.2.1" xdg_directories: dependency: transitive description: @@ -963,7 +1012,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.0.2" + version: "5.1.2" yaml: dependency: transitive description: @@ -972,5 +1021,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.13.0 <3.0.0" flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index a3ced3fe2e..d8a735891c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: selfprivacy description: selfprivacy.org publish_to: 'none' -version: 0.1.1+1 +version: 0.1.3+1 environment: sdk: '>=2.12.0 <3.0.0' @@ -31,6 +31,7 @@ dependencies: package_info: ^2.0.0 pretty_dio_logger: ^1.1.1 provider: ^5.0.0 + share_plus: ^2.1.4 unicons: ^1.0.2 url_launcher: ^6.0.2 wakelock: ^0.5.0+2